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

FREE Ganja : 1.0.0 1.0.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Random = UnityEngine.Random;

/**
 * VERSION HISTORY
 * 
 * V 1.0.7
 * - fixed gahter permission being ignored for collectable hemp
 * - added smoke effect
 * - color effects rework
 * 
 * V 1.0.8
 * - fixed gathering not working
 * - improved ui, image library not needed anymore
 * - added weed skin ids to config
 * - added custom item support for loottable
 * 
 * V 1.0.9
 * - fixed drop chance when collecting hemp
 * 
 * V 2.0.0
 * - full rewrite
 * 
 * V 2.0.1
 * - add support for deployable nature
 * - brought back gene support
 * - amount of produced items can now be configured
 * 
 * V 2.0.2
 * - fix hook error with deployable nature
 * - modify internal crafting system
 * - change joint break effect
 * - add option to disable gathering from growable hemp
 * - add page navigation to crafting ui
 * 
 * V 2.0.3
 * - added the ability to extinguish joints
 * - joint burn time and loss per hit can now be configured
 * 
 * V 2.0.4
 * - apply stacking logic only to joints and weed
 * - prevent joints from being repaired
 * - burn time can now be configured per joint
 * - add support for AutoFarm
 * 
 * V 2.0.5
 * - display numbers with up to 4 digits
 * - drop overflow items when inventory is full
 * - fix bug when crafting a recipe with 5 ingredients
 * - adjust ui spacing
 * - support for PlanterboxDefender
 * 
 **/

namespace Oxide.Plugins
{
    [Info(nameof(Ganja), "The_Kiiiing", "2.0.5")]
    public class Ganja : RustPlugin
    {
        #region Fields

        #region Constants

        private const float JOINT_USE_COOLDOWN = 2f;

        private const string PERM_GATHER = "ganja.gather";
        private const string PERM_CRAFT = "ganja.craft";

        private const string CMD_TOGGLE_UI = "ganja.toggleui";
        private const string CMD_CRAFT = "ganja.craft";
        private const string CMD_CRAFT_PAGE = "ganja.craft.page";

        private const string SHAKE_EFFECT = "assets/bundled/prefabs/fx/screen_land.prefab";
        private const string SHAKE2_EFECT = "assets/bundled/prefabs/fx/takedamage_generic.prefab";
        private const string VOMIT_EFFECT = "assets/bundled/prefabs/fx/gestures/drink_vomit.prefab";
        private const string LICK_EFFECT = "assets/bundled/prefabs/fx/gestures/lick.prefab";
        private const string BREATHE_EFFECT = "assets/prefabs/npc/bear/sound/breathe.prefab";
        private const string SMOKE_EFFECT = "assets/bundled/prefabs/fx/door/barricade_spawn.prefab";

        #endregion

        [PluginReference]
        private Plugin CustomSkinsStacksFix, StackModifier, Loottable, DeployableNature, PlanterboxDefender;

        private static Ganja _instance;

        private readonly Dictionary<ulong, float> lastUsed = new Dictionary<ulong, float>();
        private readonly Dictionary<Item, Timer> jointTimers = new Dictionary<Item, Timer>();

        private readonly List<ulong> openUis = new List<ulong>();

        #endregion

        #region Configuration

        private static Configuration _config;

        private class Configuration
        {
            [JsonProperty("Weed configuration", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<WeedConfig> weedConfig = new List<WeedConfig>
            {
                new WeedConfig
                {
                    shortName = "sticks",
                    skinId = 2661029427,
                    displayName = "Low Quality Weed",
                    dropChance = 0.4f,
                    dropAmount = new MinMaxInt(1, 3),
                    biomeMask = 6,
                    minHGenesChance = 1,
                    minHGenesGuaranteed = 3,
                    disableCollGathering = false
                },
                new WeedConfig
                {
                    shortName = "sticks",
                    skinId = 2661031542,
                    displayName = "Medium Quality Weed",
                    dropChance = 0.3f,
                    dropAmount = new MinMaxInt(1, 3),
                    biomeMask = 1,
                    minHGenesChance = 1,
                    minHGenesGuaranteed = 3,
                    disableCollGathering = false
                },
                new WeedConfig
                {
                    shortName = "sticks",
                    skinId = 2660588149,
                    displayName = "High Quality Weed",
                    dropChance = 0.1f,
                    dropAmount = new MinMaxInt(1, 2),
                    biomeMask = 8,
                    minHGenesChance = 1,
                    minHGenesGuaranteed = 3,
                    disableCollGathering = false
                }
            };

#if COCA
            [JsonProperty("Berry configuration", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<WeedConfig> berryConfig = new List<WeedConfig>
            {
                new WeedConfig
                {
                    shortName = "sticks",
                    skinId = 2946165766,
                    displayName = "Coca Leaves",
                    dropChance = 0.8f,
                    dropAmount = new MinMaxInt(1, 3),
                    biomeMask = 6,
                    minHGenesChance = 1,
                    minHGenesGuaranteed = 3,
                    disableCollGathering = false,
                    disableGrowableGathering = false
                }
            };
#endif

            [JsonProperty("Crafting Recipes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<CustomRecipe> recipes = new List<CustomRecipe>
            {
                new CustomRecipe
                {
                    ingredientSlots = new Dictionary<int, Ingredient>
                    {
                        [0] = new Ingredient
                        {
                            shortName = "note",
                            skinId = 0,
                            amount = 1
                        },
                        [1] = new Ingredient
                        {
                            shortName = "sticks",
                            skinId = 2661029427,
                            amount = 1
                        },
                        [2] = new Ingredient
                        {
                            shortName = "sticks",
                            skinId = 2661029427,
                            amount = 1
                        }
                    },

                    producedItem = new ProducedItem
                    {
                        shortName = "horse.shoes.basic",
                        skinId = 2894101592,
                        displayName = "Low Quality Joint",
                        amount = 1
                    },
                    
                    isJoint = true,
                    boosts = new BoostCollection
                    {
                        woodPercentage = 0.4f,
                        woodDuration = 20f,
                        orePercentage = 0f,
                        oreDuration = 0f,
                        scrapPercentage = 0f,
                        scrapDuration = 0f,
                        maxHealthPercentage = 0f,
                        maxHealthDuration = 0f,
                        healingPerUse = 1f
                    }
                },

                new CustomRecipe
                {
                    ingredientSlots = new Dictionary<int, Ingredient>
                    {
                        [0] = new Ingredient
                        {
                            shortName = "note",
                            skinId = 0,
                            amount = 1
                        },
                        [1] = new Ingredient
                        {
                            shortName = "sticks",
                            skinId = 2661031542,
                            amount = 1
                        },
                        [2] = new Ingredient
                        {
                            shortName = "sticks",
                            skinId = 2661031542,
                            amount = 1
                        }
                    },

                    producedItem = new ProducedItem
                    {
                        shortName = "horse.shoes.basic",
                        skinId = 2894101290,
                        displayName = "Medium Quality Joint",
                        amount = 1
                    },

                    isJoint = true,
                    boosts = new BoostCollection
                    {
                        woodPercentage = 0f,
                        woodDuration = 0f,
                        orePercentage = 0.8f,
                        oreDuration = 20f,
                        scrapPercentage = 0f,
                        scrapDuration = 0f,
                        maxHealthPercentage = 0f,
                        maxHealthDuration = 0f,
                        healingPerUse = 4f
                    }
                },

                new CustomRecipe
                {
                    ingredientSlots = new Dictionary<int, Ingredient>
                    {
                        [0] = new Ingredient
                        {
                            shortName = "note",
                            skinId = 0,
                            amount = 1
                        },
                        [1] = new Ingredient
                        {
                            shortName = "sticks",
                            skinId = 2660588149,
                            amount = 1
                        },
                        [2] = new Ingredient
                        {
                            shortName = "sticks",
                            skinId = 2660588149,
                            amount = 1
                        }
                    },

                    producedItem = new ProducedItem
                    {
                        shortName = "horse.shoes.basic",
                        skinId = 2893700325,
                        displayName = "High Quality Joint",
                        amount = 1
                    },

                    isJoint = true,
                    boosts = new BoostCollection
                    {
                        woodPercentage = 0f,
                        woodDuration = 0f,
                        orePercentage = 0f,
                        oreDuration = 0f,
                        scrapPercentage = 1f,
                        scrapDuration = 30f,
                        maxHealthPercentage = 0.3f,
                        maxHealthDuration = 30f,
                        healingPerUse = 8f
                    }
                },
            };

            [JsonProperty("Require permission for crafting")]
            public bool enableCraftPerm = true;

            [JsonProperty("Require permission for gathering")]
            public bool enableGatherPerm = true;

            [JsonProperty("Disable built-in stack fix (set to true if you have problems with item stacking/splitting)")]
            public bool disableStackFix = false;

            [JsonProperty("Automatically extinguish joint when unequiping it")]
            public bool extinguishOnUnequip = true;

            [JsonIgnore]
            private HashSet<ulong> jointSkins;

            [JsonIgnore]
            private HashSet<ulong> weedSkins;

            public CustomRecipe GetJointRecipe(Item joint)
            {
                foreach (var recipe in recipes)
                {
                    if (!recipe.isJoint)
                    {
                        continue;
                    }

                    if (recipe.producedItem.skinId == joint.skin &&
                        recipe.producedItem.shortName == joint.info.shortname)
                    {
                        return recipe;
                    }
                }

                return null;
            }

            public bool IsJointSkin(ulong skin)
            {
                if (jointSkins == null)
                {
                    jointSkins = new HashSet<ulong>(recipes.Where(x => x.isJoint).Select(x => x.producedItem.skinId));
                }

                return jointSkins.Contains(skin);
            }

            public bool IsWeedSkin(ulong skin)
            {
                if (weedSkins == null)
                {
                    weedSkins = new HashSet<ulong>(weedConfig.Select(x => x.skinId));
                }

                return weedSkins.Contains(skin);
            }

        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _config = Config.ReadObject<Configuration>();
                if (_config == null) throw new Exception();
                SaveConfig();
            }
            catch
            {
                PrintError("Your configuration file contains an error.Using default configuration values.");
                LoadDefaultConfig();
            }
        }

        protected override void SaveConfig() => Config.WriteObject(_config);

        protected override void LoadDefaultConfig() => _config = new Configuration();

#endregion

        #region Config Classes

        private class CustomRecipe
        {
            [JsonProperty("Ingredient Slots")]
            public Dictionary<int, Ingredient> ingredientSlots;

            [JsonProperty("Produced Item")]
            public ProducedItem producedItem;

            [JsonProperty("Is joint")]
            public bool isJoint;
            [JsonProperty("Boosts (only works for joints)")]
            public BoostCollection boosts;

            /// <summary>
            /// Attempt to craft the current recipe
            /// </summary>
            /// <param name="overflow">Contains excess items when crafting was successful</param>
            /// <returns>true when crafting was successful</returns>
            public bool TryCraft(MixingTable mixingTable, List<Item> overflow)
            {
                if (ingredientSlots.Count < 1)
                {
                    _instance.PrintError("Invalid Recipe! Recipe needs to have at least 1 ingredient");
                    return false;
                }

                List<Item> collect = Pool.GetList<Item>();
                List<int> collectAmount = new List<int>();

                // Check if mixing table contains correct ingredients
                Ingredient ing;
                for (int slot = 0; slot < mixingTable.inventory.capacity; slot++)
                {
                    try
                    {
                        ing = ingredientSlots[slot];
                    }
                    catch (KeyNotFoundException)
                    {
                        continue;
                    }

                    Item item = mixingTable.inventory.GetSlot(slot);

                    if (item == null || 
                        item.info.shortname != ing.shortName || 
                        item.skin != ing.skinId ||  
                        item.amount < ing.amount)
                    {
                        Pool.FreeList(ref collect);
                        return false;
                    }

                    collect.Add(item);
                    collectAmount.Add(ing.amount);

                    ing = null;
                }

                // Collect ingredients
                for (int i = 0; i < collect.Count; i++)
                {
                    var item = collect[i];
                    int amount = collectAmount[i];

                    if (item.amount == amount)
                    {
                        item.Remove();
                    }
                    else
                    {
                        item.amount -= amount;
                        item.RemoveFromContainer();
                        overflow.Add(item);
                    }
                }

                // Free resources
                Pool.FreeList(ref collect);
                ItemManager.DoRemoves();

                // Create new item
                Item result = producedItem.CreateItem();
                if (result == null)
                {
                    _instance.PrintError($"Crafting failed: failed to create item {producedItem.shortName}[{producedItem.skinId}] x{producedItem.amount} - make sure the item you specified in the config exists");
                    return false;
                }

                if (!result.MoveToContainer(mixingTable.inventory))
                {
                    _instance.PrintError("Crafting failed: failed to move item");
                }

                return true;
            }
        }

        private class WeedConfig : CustomItem
        {
            [JsonProperty("Drop chance when harvesting (1 = 100%)")]
            public float dropChance;
            [JsonProperty("Drop amount when harvesting")]
            public MinMaxInt dropAmount;
            [JsonProperty("Biome mask (see description for details)")]
            public ushort biomeMask;

            [JsonProperty("Minimum amount of H genes for a chance to yield weed")]
            public int minHGenesChance;
            [JsonProperty("Minimum amount of H genes for guaranteed yield")]
            public int minHGenesGuaranteed;

            [JsonProperty("Disable gathering from collectable hemp")]
            public bool disableCollGathering;
            [JsonProperty("Disable gathering from growable hemp")]
            public bool disableGrowableGathering;

            public bool IsValidGatherLocation(ushort locationMask)
            {
                //BitArray maskBits = new BitArray(BitConverter.GetBytes(biomeMask));
                //BitArray locationBits = new BitArray(BitConverter.GetBytes(locationMask));

                //return maskBits.And(locationBits).Cast<bool>().Contains(true);

                return (biomeMask & locationMask) > 0;
            }

            public bool MaybeGather(ushort locationMask, bool isCollectable, int hGenes, out Item weed)
            {
                weed = null;

                if (!IsValidGatherLocation(locationMask) || (isCollectable && disableCollGathering) || (!isCollectable && disableGrowableGathering))
                {
                    return false;
                }

                float chance = dropChance;

                if (!isCollectable)
                {
                    if (hGenes < minHGenesChance)
                    {
                        // Not enough h genes
                        return false;
                    }
                    if (hGenes >= minHGenesGuaranteed)
                    {
                        // enough h genes for 100% drop chance
                        chance = 1f;
                    }
                }

                if (Random.Range(0f, 1f) > chance)
                {
                    return false;
                }

                int amount = dropAmount.Random();
                if (amount > 0)
                {
                    weed = CreateItem(amount);
                    if (weed == null)
                    {
                        _instance.PrintError("Failed to create weed item, check if your config is correct");
                    }
                }
                
                return weed != null;
            }
        }

        private class BoostCollection
        {
            [JsonProperty("Wood boost percentage (1 = 100%)")]
            public float woodPercentage;
            [JsonProperty("Wood boost duration (seconds)")]
            public float woodDuration;

            [JsonProperty("Ore boost percentage (1 = 100%)")]
            public float orePercentage;
            [JsonProperty("Ore boost duration (seconds)")]
            public float oreDuration;

            [JsonProperty("Scrap boost percentage (1 = 100%)")]
            public float scrapPercentage;
            [JsonProperty("Scrap boost duration (seconds)")]
            public float scrapDuration;

            [JsonProperty("Max Health percentage (1 = 100%)")]
            public float maxHealthPercentage;
            [JsonProperty("Max Health duration (seconds)")]
            public float maxHealthDuration;

            [JsonProperty("Healing per use")]
            public float healingPerUse;

            [JsonProperty("Joint durability (seconds)")]
            public float jointDurability = 120f;
            [JsonProperty("Joint durability loss per hit (seconds)")]
            public float jointDurabilityLossPerHit = 10f;
            [JsonIgnore]
            public float JointDurabilityLossPerSecond => 100f / jointDurability;

            public void ApplyToPlayer(BasePlayer player)
            {
                List<ModifierDefintion> mods = Pool.GetList<ModifierDefintion>();

                if (scrapDuration > 0 && scrapPercentage > 0)
                {
                    mods.Add(new ModifierDefintion
                    {
                        source = Modifier.ModifierSource.Tea,
                        type = Modifier.ModifierType.Scrap_Yield,
                        duration = scrapDuration,
                        value = scrapPercentage
                    });
                }

                if (woodDuration > 0 && woodPercentage > 0)
                {
                    mods.Add(new ModifierDefintion
                    {
                        source = Modifier.ModifierSource.Tea,
                        type = Modifier.ModifierType.Wood_Yield,
                        duration = woodDuration,
                        value = woodPercentage
                    });
                }

                if (oreDuration > 0 && orePercentage > 0)
                {
                    mods.Add(new ModifierDefintion
                    {
                        source = Modifier.ModifierSource.Tea,
                        type = Modifier.ModifierType.Ore_Yield,
                        duration = oreDuration,
                        value = orePercentage
                    });
                }

                if (maxHealthDuration > 0 && maxHealthPercentage > 0)
                {
                    mods.Add(new ModifierDefintion
                    {
                        source = Modifier.ModifierSource.Tea,
                        type = Modifier.ModifierType.Max_Health,
                        duration = maxHealthDuration,
                        value = maxHealthPercentage
                    });
                }

                player.modifiers.Add(mods);
                player.Heal(healingPerUse);
                Pool.FreeList(ref mods);
            }

            public override string ToString()
            {
                return $"wood {woodPercentage:N2} {woodDuration}s; scrap  {scrapPercentage:N2} {scrapDuration}s; ore {orePercentage:N2} {oreDuration}s; maxHealth {maxHealthPercentage:N2} {maxHealthDuration}s; heal {healingPerUse}";
            }
        }

        private class Ingredient : SkinnedItem
        {
            [JsonProperty("Amount")]
            public int amount;
        }

        private class ProducedItem : CustomItem
        {
            [JsonProperty("Amount")]
            public int amount = 1;

            public Item CreateItem()
            {
                return base.CreateItem(amount);
            }
        }

        private class CustomItem : SkinnedItem
        {
            [JsonProperty("Custom item name (null = default name)")]
            public string displayName;

            [JsonIgnore]
            public string UiDisplayName => GetDisplayName(false);

            public string GetDisplayName(bool nullIfNotCustom)
            {
                if (displayName == null || displayName == string.Empty)
                {
                    if (nullIfNotCustom)
                    {
                        return null;
                    }
                    else
                    {
                        return ItemDefinition.displayName.english;
                    }
                }

                return displayName;
            }

            public override Item CreateItem(int amount)
            {
                var itm = ItemManager.Create(ItemDefinition, amount, skinId);
                string name = GetDisplayName(true);
                if (itm != null && name != null)
                {
                    itm.name = name;
                }

                return itm;
            }
        }

        private class SkinnedItem
        {
            [JsonProperty("Item short name")]
            public string shortName;
            [JsonProperty("Item skin id")]
            public ulong skinId;

            [JsonIgnore]
            public ItemDefinition ItemDefinition => ItemManager.FindItemDefinition(shortName);

            public virtual Item CreateItem(int amount)
            {
                return ItemManager.Create(ItemDefinition, amount, skinId);
            }
        }

        private struct MinMaxInt
        {
            public int min;
            public int max;

            public MinMaxInt(int min, int max)
            {
                this.min = min;
                this.max = max;
            }

            public int Random()
            {
                return UnityEngine.Random.Range(min, max + 1);
            }
        }

        #endregion

        #region Data

        private class PlayerData
        {
            [JsonProperty]
            private Dictionary<ulong, bool> craftingUiState = new Dictionary<ulong, bool>();

            private static PlayerData _ins;

            public static void Load()
            {
                _ins = Interface.Oxide.DataFileSystem.ReadObject<PlayerData>(_instance.Name);
            }

            public static void Save()
            {
                if (_ins != null)
                {
                    Interface.Oxide.DataFileSystem.WriteObject(_instance.Name, _ins);
                }
            }

            public static bool IsCraftingUiOpen(BasePlayer player)
            {
                if (!_ins.craftingUiState.ContainsKey(player.userID))
                {
                    _ins.craftingUiState[player.userID] = false;
                    return false;
                }

                return _ins.craftingUiState[player.userID];
            }

            public static bool ToggleCraftingUi(BasePlayer player)
            {
                if (!_ins.craftingUiState.ContainsKey(player.userID))
                {
                    _ins.craftingUiState[player.userID] = true;
                    return true;
                }

                return _ins.craftingUiState[player.userID] = !_ins.craftingUiState[player.userID];
            }
        }

        #endregion

        #region Helpers

        private bool DisableStackFix()
        {
            if (_config.disableStackFix)
            {
                return true;
            }

            return StackModifier != null || Loottable != null || CustomSkinsStacksFix != null;
        }

        private bool AllowedToCraft(BasePlayer player)
        {
            bool permEnabled = _config.enableCraftPerm;
            bool hasPermission = permission.UserHasPermission(player.UserIDString, PERM_CRAFT);
            bool result = !permEnabled || hasPermission;

            return result;
        }

        private bool AllowedToGather(BasePlayer player) => AllowedToGather(player.UserIDString);
        private bool AllowedToGather(string playerId)
        {
            bool permEnabled = _config.enableGatherPerm;
            bool hasPermission = permission.UserHasPermission(playerId, PERM_GATHER);
            bool result = !permEnabled || hasPermission;

            return result;
        }

        private void SendNote(BasePlayer player, Item item, int amount = 0)
        {
            player.Command("note.inv", item.info.itemid, amount == 0 ? item.amount.ToString() : amount.ToString(), item.name);
        }

        private bool IsDeployableNature(BaseEntity entity)
        {
            if (DeployableNature == null)
            {
                return false;
            }

            var b = DeployableNature.Call("STCanGainXP", null, entity) as Boolean?;
            if (b == false)
            {
                return true;
            }
            
            return false;
        }

        #endregion

        #region Functions

        private bool IsJointOrWeed(Item item)
        {
            if (item == null)
            {
                return false;
            }

            return _config.IsWeedSkin(item.skin) || _config.IsJointSkin(item.skin);
        }

        private bool IsJoint(Item item)
        {
            if (item == null)
            {
                return false;
            }

            return _config.IsJointSkin(item.skin);
        }

        private void UseJoint(BasePlayer player, Item joint)
        {
            if (!joint.HasFlag(global::Item.Flag.OnFire) || joint.isBroken)
            {
                return;
            }

            var def = _config.GetJointRecipe(joint);
            if (def == null)
            {
                _instance.PrintError($"Failed to find joint definition for joint with skin id '{joint.skin}'");
                return;
            }

            def.boosts.ApplyToPlayer(player);
            RunEffects(player, 2.5f);
            JointLoseCondition(joint, def.boosts.JointDurabilityLossPerSecond * def.boosts.jointDurabilityLossPerHit);
        }

        private void IgniteJoint(BasePlayer player, Item joint)
        {
            if (joint.HasFlag(global::Item.Flag.OnFire))
            {
                return;
            }

            var def = _config.GetJointRecipe(joint);
            if (def == null)
            {
                _instance.PrintError($"Failed to find joint definition for joint with skin id '{joint.skin}'");
                return;
            }

            joint.SetFlag(global::Item.Flag.OnFire, true);

            RunEffect("assets/prefabs/weapons/torch/effects/ignite.prefab", player);

            // Start decay timer
            jointTimers[joint] = timer.Every(1, () => JointLoseCondition(joint, def.boosts.JointDurabilityLossPerSecond));
        }

        private void ExtinguishJoint(BasePlayer player, Item joint)
        {
            if (!joint.HasFlag(global::Item.Flag.OnFire))
            {
                return;
            }

            joint.SetFlag(global::Item.Flag.OnFire, false);
            joint.MarkDirty();

            if (player != null)
            {
                RunEffect("assets/prefabs/weapons/torch/effects/extinguish.prefab", player);
            }

            if (jointTimers.ContainsKey(joint))
            {
                jointTimers[joint].Destroy();
                jointTimers.Remove(joint);
            }
        }

        private void JointLoseCondition(Item joint, float loss)
        {
            if (joint.condition - loss < 1f)
            {
                jointTimers[joint].Destroy();
                jointTimers.Remove(joint);
                joint.Remove();

                var player = joint.GetOwnerPlayer();
                if (player != null)
                {
                    RunEffect("assets/bundled/prefabs/fx/impacts/additive/fire.prefab", player);
                }
            }
            else
            {
                joint.condition -= loss;
            }
        }

        private void OnHempGather(BasePlayer player, Vector3 plantPositon, bool isCollectable, int hGenes = 0)
        {
            ushort locationMask = (ushort)TerrainMeta.BiomeMap.GetBiomeMaxType(plantPositon);

            foreach(var cfg in _config.weedConfig)
            {
                if (cfg.MaybeGather(locationMask, isCollectable, hGenes, out var weed))
                {
                    if (player.inventory.containerMain.IsFull() &&
                        player.inventory.containerBelt.IsFull())
                    {
                        weed.Drop(plantPositon, Vector3.up * 3f);
                        SendNote(player, weed);
                        SendNote(player, weed, -weed.amount);
                    }
                    else
                    {
                        int amt = weed.amount;
                        player.inventory.GiveItem(weed);
                        SendNote(player, weed, amt);
                    }
                }
            }
        }

#if COCA
        private void OnBerryGather(BasePlayer player, Vector3 plantPositon, bool isCollectable, int hGenes = 0)
        {
            ushort locationMask = (ushort)TerrainMeta.BiomeMap.GetBiomeMaxType(plantPositon);

            foreach (var cfg in _config.berryConfig)
            {
                if (cfg.MaybeGather(locationMask, isCollectable, hGenes, out var coca))
                {
                    if (player.inventory.containerMain.IsFull() &&
                        player.inventory.containerBelt.IsFull())
                    {
                        coca.Drop(plantPositon, Vector3.up * 3f);
                        SendNote(player, coca);
                        SendNote(player, coca, -coca.amount);
                    }
                    else
                    {
                        int amt = coca.amount;
                        player.inventory.GiveItem(coca);
                        SendNote(player, coca, amt);
                    }
                }
            }
        }
#endif

        private bool OnCooldown(BasePlayer player)
        {
            ulong userId = player.userID;
            if (!lastUsed.ContainsKey(userId) || Time.time - lastUsed[userId] > JOINT_USE_COOLDOWN)
            {
                lastUsed[player.userID] = Time.time;
                return false;
            }

            return true;
        }

#endregion

        #region Default Hooks

        private void Init()
        {
            _instance = this;

            permission.RegisterPermission(PERM_CRAFT, this);
            permission.RegisterPermission(PERM_GATHER, this);

            AddCovalenceCommand(CMD_CRAFT, nameof(CmdCraft));
            AddCovalenceCommand(CMD_TOGGLE_UI, nameof(CmdToggleUi));
            AddCovalenceCommand(CMD_CRAFT_PAGE, nameof(CmdChangePage));

            PlayerData.Load();

            timer.In(1f, () =>
            {
                if (DisableStackFix())
                {
                    Unsubscribe(nameof(CanStackItem));
                    Unsubscribe(nameof(CanCombineDroppedItem));
                    Unsubscribe(nameof(OnItemSplit));
                }
                else
                {
                    Subscribe(nameof(CanStackItem));
                    Subscribe(nameof(CanCombineDroppedItem));
                    Subscribe(nameof(OnItemSplit));
                }

                if (!_config.extinguishOnUnequip)
                {
                    Unsubscribe(nameof(OnActiveItemChanged));
                }
                else
                {
                    Subscribe(nameof(OnActiveItemChanged));
                }

                foreach (var item in _config.weedConfig)
                {
                    Loottable?.Call("AddCustomItem", this, item.ItemDefinition.itemid, item.skinId, item.displayName);
                }
            });
        }

        private void Unload()
        {
            RemoveEffectsGlobal();

            foreach (var id in openUis.ToArray())
            {
                BasePlayer player = BasePlayer.FindByID(id);
                DestroyUi(player);
            }

            foreach (var joint in jointTimers.Keys.ToList())
            {
                ExtinguishJoint(null, joint);
            }

            PlayerData.Save();

            _config = null;
            _instance = null;
        }

        #endregion

        #region Hooks

        #region Stack Fix

        object CanStackItem(Item item, Item target)
        {
            if (DisableStackFix()) return null;
            if (!IsJointOrWeed(item)) return null;

            if (item.info.itemid != target.info.itemid) return false;
            if (item.skin != target.skin) return false;
            if (item.name != target.name) return false;

            return null;
        }

        Item OnItemSplit(Item item, int amount)
        {
            if (DisableStackFix()) return null;
            if (!IsJointOrWeed(item)) return null;

            item.amount -= amount;
            Item split = ItemManager.Create(item.info, amount, item.skin);
            split.name = item.name;
            split.MarkDirty();
            item.MarkDirty();

            return split;
        }

        object CanCombineDroppedItem(WorldItem witem1, WorldItem witem2) => CanStackItem(witem1.item, witem2.item);

        #endregion

        #region Joint controls

        void OnPlayerInput(BasePlayer player, InputState input)
        {
            Item joint = player.GetActiveItem();

            if (!IsJoint(joint))
            {
                return;
            }

            // Ignite or extinguish
            if (input.IsDown(BUTTON.FIRE_SECONDARY))
            {
                if (!OnCooldown(player))
                {
                    if (joint.HasFlag(global::Item.Flag.OnFire))
                    {
                        ExtinguishJoint(player, joint);
                    }
                    else
                    {
                        IgniteJoint(player, joint);
                    }
                }
            }

            // Use
            if (input.IsDown(BUTTON.FIRE_PRIMARY))
            {
                if (!OnCooldown(player))
                {
                    UseJoint(player, joint);
                }
            }
        }

        void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem)
        {
            if (IsJoint(oldItem) && _config.extinguishOnUnequip)
            {
                ExtinguishJoint(player, oldItem);
            }
        }

        #endregion

        #region Gathering

        void OnGrowableGather(GrowableEntity plant, BasePlayer player)
        {
            if (PlanterboxDefender != null && PlanterboxDefender.Call("CanLootGrowableEntity", plant, player) != null)
            {
                return;
            }

            if (plant.prefabID == 3587624038 && plant.State == PlantProperties.State.Ripe && AllowedToGather(player))
            {
                OnHempGather(player, plant.transform.position, false, plant.Genes.GetGeneTypeCount(GrowableGenetics.GeneType.Hardiness));
            }

#if COCA
            if (plant.prefabID == 4038822397 && plant.State == PlantProperties.State.Ripe && AllowedToGather(player))
            {
                OnBerryGather(player, plant.transform.position, false, plant.Genes.GetGeneTypeCount(GrowableGenetics.GeneType.Hardiness));
            }
#endif
        }

        void OnCollectiblePickup(CollectibleEntity entity, BasePlayer player)
        {
            if (player == null)
            {
                return;
            }

            if (entity.prefabID == 3006540952 && AllowedToGather(player) && !IsDeployableNature(entity) && !entity.IsDestroyed)
            {
                OnHempGather(player, entity.transform.position, true);
            }

#if COCA
            if (entity.prefabID == 1989241797 && AllowedToGather(player) && !IsDeployableNature(entity) && !entity.IsDestroyed)
            {
                OnBerryGather(player, entity.transform.position, true);
            }
#endif
        }

        // Support AutoFarm
        void OnAutoFarmGather(string userID, StorageContainer container, Vector3 plantPositon, bool isCollectable, int hGenes = 0)
        {
            if (!AllowedToGather(userID))
            {
                return;
            }

            ushort locationMask = (ushort)TerrainMeta.BiomeMap.GetBiomeMaxType(plantPositon);
            foreach (var cfg in _config.weedConfig)
            {
                if (cfg.MaybeGather(locationMask, isCollectable, hGenes, out var weed))
                {
                    if (container.inventory.IsFull() || !weed.MoveToContainer(container.inventory))
                    {
                        weed.Drop(plantPositon, Vector3.up * 3f);
                    }
                }
            }
        }

        #endregion

        #region Crafting Ui

        private void OnLootEntity(BasePlayer player, MixingTable entity)
        {
            if (entity == null) return;

            if (AllowedToCraft(player))
            {
                entity.OnlyAcceptValidIngredients = false;
                CreateToggleUi(player);
                if (PlayerData.IsCraftingUiOpen(player))
                {
                    CreateRecipeUi(player);
                    CreateCraftButtonUi(player);
                }
            }
        }

        private void OnLootEntityEnd(BasePlayer player, MixingTable entity)
        {
            if (entity == null) return;

            entity.OnlyAcceptValidIngredients = true;
            DestroyUi(player);
        }

        #endregion

        #region Misc

        ItemContainer.CanAcceptResult? CanAcceptItem(ItemContainer container, Item item, int targetPos)
        {
            // Prevent joints form bein put in repair bench
            if (container.entityOwner?.prefabID == 3846783416u && IsJoint(item))
            {
                return ItemContainer.CanAcceptResult.CannotAccept;
            }

            return null;
        }

        void OnPlayerDeath(BasePlayer p, HitInfo hitInfo) => OnPlayerDisconnected(p, null);

        void OnPlayerDisconnected(BasePlayer p, string reason)
        {
            RemoveEffects(p);
            DestroyUi(p);
        }

        #endregion

        #endregion

        #region UI

        private static class UI
        {
            public const string LAYER_RECIPE = "ganja.ui.craft";
            public const string LAYER_TOGGLE = "ganja.ui.toggle";
            public const string LAYER_CRAFT_BUTTON = "ganja.ui.craftbutton";

            public const string LAYER_BLUR = "ganja.ui.effects.blur";
            public const string LAYER_COLOR = "ganja.ui.effects.color";

            public const string greenButtonColor = "0.415 0.5 0.258 0.7";
            public const string redButtonColor = "0.8 0.28 0.2 1";
            public const string blueButtonColor = "0.13 0.52 0.82 0.8";
            public const string greyButtonColor = "0.4 0.4 0.4 0.4";

            public const string buttonColor = "0.75 0.75 0.75 0.3";
            public const string buttonTextColor2 = "0.68 0.68 0.68 1";

            public const string textColor = "0.745 0.709 0.674 1";

            public const string itemTileColor = "0.2 0.2 0.19 1";

            public const int RECIPES_PER_PAGE = 4;
        }

        private void CreateToggleUi(BasePlayer player)
        {
            CuiElementContainer result = new CuiElementContainer();

            string rootPanel = result.Add(new CuiPanel
            {
                Image = new CuiImageComponent
                {
                    Color = "0 0 0 0"
                },
                RectTransform =
                {
                    AnchorMin = "0.5 0",
                    AnchorMax = "0.5 0",
                    OffsetMin = "430 476",
                    OffsetMax = "572 501"
                }
            }, "Hud.Menu", UI.LAYER_TOGGLE);

            result.Add(new CuiButton
            {
                RectTransform =
                {
                    AnchorMin = "0 0",
                    AnchorMax = "1 1",
                },
                Button =
                {
                    Command = CMD_TOGGLE_UI,
                    Color = UI.buttonColor
                },
                Text =
                {
                    Align = TextAnchor.MiddleCenter,
                    Text = !PlayerData.IsCraftingUiOpen(player) ? "SHOW CUSTOM RECIPES" : "HIDE CUSTOM RECIPES",
                    Color = UI.buttonTextColor2,
                    FontSize = 12
                }
            }, rootPanel);

            openUis.Add(player.userID);
            CuiHelper.AddUi(player, result);
        }

        private void CreateRecipeUi(BasePlayer player, int page = 0)
        {
            
            int itemPanelSize = 14;
            CuiElementContainer result = new CuiElementContainer();

            #region Root Panel

            string rootPanelName = UI.LAYER_RECIPE;
                
            result.Add(new CuiElement
            {
                Components =
                {
                    new CuiImageComponent
                    {
                        Color = "0.33 0.314 0.29 1",
                        //Color = "0.32 0.31 0.30 1",
                        Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat",
                    },
                    new CuiRectTransformComponent
                    {
                        AnchorMin = "0.5 0",
                        AnchorMax = "0.5 0",
                        OffsetMin = "192 302",
                        OffsetMax = "572 451"
                    }
                },
                Parent = "Hud.Menu",
                Name = rootPanelName
            });

            #endregion

            #region Page Nav

            if (_config.recipes.Count > UI.RECIPES_PER_PAGE)
            {
                result.Add(new CuiButton
                {
                    RectTransform =
                    {
                        AnchorMin = "0.88 0.01",
                        AnchorMax = "0.98 0.08",
                    },
                    Button =
                    {
                        Command = $"{CMD_CRAFT_PAGE} {page+1}",
                        Color = _config.recipes.Count > (page+1) * UI.RECIPES_PER_PAGE ? UI.blueButtonColor : UI.greyButtonColor
                    },
                    Text =
                    {
                        Align = TextAnchor.MiddleCenter,
                        Text = "->",
                        Color = UI.buttonTextColor2,
                        FontSize = 9
                    }
                }, rootPanelName);

                result.Add(new CuiButton
                {
                    RectTransform =
                    {
                        AnchorMin = "0.76 0.01",
                        AnchorMax = "0.86 0.08",
                    },
                    Button =
                    {
                        Command = $"{CMD_CRAFT_PAGE} {page-1}",
                        Color = page-1 >= 0 ? UI.blueButtonColor : UI.greyButtonColor
                    },
                    Text =
                    {
                        Align = TextAnchor.MiddleCenter,
                        Text = "<-",
                        Color = UI.buttonTextColor2,
                        FontSize = 9
                    }
                }, rootPanelName);
            }

            #endregion

            #region Recipe List

            int offset = page * UI.RECIPES_PER_PAGE;

            for(int i = 0; i < UI.RECIPES_PER_PAGE; i++)
            {
                CustomRecipe recipe;
                try
                {
                    recipe = _config.recipes[i + offset];
                }
                catch (ArgumentOutOfRangeException)
                {
                    break;
                }

                string recipePanel = result.Add(new CuiPanel
                {
                    Image = new CuiImag
// ... [Code preview truncated due to length. Full source in attached file] ...

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