﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Oxide.Core;
using Oxide.Core.Plugins;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Facepunch;
using Rust;
using Rust.Ai;
using Rust.Ai.Gen2;
using HarmonyLib;

namespace Oxide.Plugins
{
    [Info("Barrelless" , "Krungh Crow" , "3.1.6")]
    [Description("various events after barrel kills")]
    class Barrelless : RustPlugin
    {
        [PluginReference] Plugin Kits;

        #region Variables

        #region Plugin
        bool BlockSpawn;
        bool BlockOutside;
        bool IsSpawned;
        bool fireallreadyspawned = false;

        bool Signaldropped;
        ulong chaticon;
        string prefix;
        System.Random rnd = new System.Random();
        private ConfigData configData;
        public static Barrelless instance;

        #endregion

        #region Permissions

        const string Exclude_Perm = "barrelless.exclude";
        const string CanToggle_Perm = "barrelless.cantoggle";
        const string SciTrigger_Perm = "barrelless.scientist";
        const string ScareTrigger_Perm = "barrelless.scarecrow";
        const string BearTrigger_Perm = "barrelless.bear";
        const string PBearTrigger_Perm = "barrelless.polarbear";
        const string BoarTrigger_Perm = "barrelless.boar";
        const string ChickenTrigger_Perm = "barrelless.chicken";
        //const string WolfTrigger_Perm = "barrelless.wolf";
        const string ExploTrigger_Perm = "barrelless.explosive";
        const string FireTrigger_Perm = "barrelless.fires";
        const string AirdropTrigger_Perm = "barrelless.airdrop";
        const string HackCrateTrigger_Perm = "barrelless.hackcrate";
        const string TigerTrigger_Perm = "barrelless.tiger";
        const string PantherTrigger_Perm = "barrelless.panther";
        const string SnakeTrigger_Perm = "barrelless.snake";

        private readonly List<string> _permissions = new List<string>
        {
            Exclude_Perm,
            CanToggle_Perm,
            SciTrigger_Perm,
            ScareTrigger_Perm,
            BearTrigger_Perm,
            PBearTrigger_Perm,
            BoarTrigger_Perm,
            ChickenTrigger_Perm,
            //WolfTrigger_Perm,
            TigerTrigger_Perm,
            PantherTrigger_Perm,
            SnakeTrigger_Perm,
            ExploTrigger_Perm,
            FireTrigger_Perm,
            AirdropTrigger_Perm,
            HackCrateTrigger_Perm
        };

        #endregion

        #region NPC
        const string zombie = "assets/prefabs/npc/scarecrow/scarecrow.prefab";
        const string _scientist = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab";
        float SciDamageScale;
        int SciHealth;
        List<string> SciKit;
        float SciLifetime;
        string SciName;

        bool _UseKit;
        bool _FromHell;

        private readonly List<ScarecrowNPC> spawnedZombies = new List<ScarecrowNPC>();
        private readonly List<NPCPlayer> spawnedScientists = new List<NPCPlayer>();

        #endregion

        #region Animals
        const string bearString = "assets/rust.ai/agents/bear/bear.prefab";
        const string PBearString = "assets/rust.ai/agents/bear/polarbear.prefab";
        const string boarstring = "assets/rust.ai/agents/boar/boar.prefab";
        const string chickenString = "assets/rust.ai/agents/chicken/chicken.prefab";
        //const string wolfString = "assets/rust.ai/agents/wolf/wolf.prefab";
        string tigerString = "assets/rust.ai/agents/tiger/tiger.prefab";
        string pantherString = "assets/rust.ai/agents/panther/panther.prefab";
        string snakeString = "assets/rust.ai/agents/snake/snake.entity.prefab";

        int _AnimalHealth;
        string _AnimalString;
        float _AnimalLife;

        private List<BaseEntity> spawnedAnimals = new List<BaseEntity>();

        #endregion

        #region Fires
        const string oilfire = "assets/bundled/prefabs/oilfireballsmall.prefab";//oil and diesel barrels
        const string fireball = "assets/bundled/prefabs/fireball.prefab";//OnFire
        #endregion

        #region Drops
        const string airdropString = "assets/prefabs/misc/supply drop/supply_drop.prefab";
        const string hackcrateString = "assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate.prefab";
        const string beancanString = "assets/prefabs/weapons/beancan grenade/grenade.beancan.deployed.prefab";
        const string grenadeString = "assets/prefabs/weapons/f1 grenade/grenade.f1.deployed.prefab";
        const string satchelString = "assets/prefabs/weapons/satchelcharge/explosive.satchel.deployed.prefab";
        const string beenadelString = "assets/prefabs/weapons/bee grenade/grenade.bee.deployed.prefab";
        #endregion

        #region Data
        const string file_main = "barrelless_players/";
        #endregion

        #endregion

        #region Behaviours

        #region Scarecrow
        private class Zombies : FacepunchBehaviour
        {
            private ScarecrowNPC npc;
            public bool ReturningToHome = false;
            public bool isRoaming = true;
            public Vector3 SpawnPoint;

            private void Awake()
            {
                npc = GetComponent<ScarecrowNPC>();
                Invoke(nameof(_UseBrain) , 0.1f);
                InvokeRepeating(Attack , 0.1f , 1.5f);
                InvokeRepeating(GoHome , 2.0f , 4.5f);
            }

            private void Attack()
            {
                BaseEntity entity = npc.Brain.Senses.GetNearestThreat(40);
                Chainsaw heldEntity = npc.GetHeldEntity() as Chainsaw;
                if (entity == null || Vector3.Distance(entity.transform.position , npc.transform.position) > 40.0f)
                {
                    npc.Brain.Navigator.ClearFacingDirectionOverride();
                    GoHome();
                }
                if (entity != null && Vector3.Distance(entity.transform.position , npc.transform.position) < 2.0f)
                {
                    npc.StartAttacking(entity);
                    npc.Brain.Navigator.SetFacingDirectionEntity(entity);
                    if (heldEntity is Chainsaw)
                    {
                        if (!(heldEntity as Chainsaw).EngineOn())
                        {
                            (heldEntity as Chainsaw).ServerNPCStart();
                        }
                        heldEntity.SetFlag(BaseEntity.Flags.Busy , true , false , true);
                        heldEntity.SetFlag(BaseEntity.Flags.Reserved8 , true , false , true);
                    }
                }

                if (entity != null && Vector3.Distance(entity.transform.position , npc.transform.position) > 1.5f)
                {
                    if (heldEntity is Chainsaw)
                    {
                        if (!(heldEntity as Chainsaw).EngineOn())
                        {
                            (heldEntity as Chainsaw).ServerNPCStart();
                        }
                        heldEntity.SetFlag(BaseEntity.Flags.Busy , false , false , true);
                        heldEntity.SetFlag(BaseEntity.Flags.Reserved8 , false , false , true);
                    }
                }

                if (entity != null && Vector3.Distance(entity.transform.position , npc.transform.position) > 2.0f)
                {
                    npc.Brain.Navigator.SetFacingDirectionEntity(entity);
                }
            }

            public void _UseBrain()
            {
                #region navigation
                npc.Brain.Navigator.Agent.agentTypeID = -1372625422;
                npc.Brain.Navigator.DefaultArea = "Walkable";
                npc.Brain.Navigator.enabled = true;
                npc.Brain.Navigator.CanUseNavMesh = true;
                npc.Brain.Navigator.BestRoamPointMaxDistance = instance.configData.CrowData.NPCRoamMax;
                npc.Brain.Navigator.MaxRoamDistanceFromHome = instance.configData.CrowData.NPCRoamMax;
                npc.Brain.Navigator.Init(npc , npc.Brain.Navigator.Agent);
                npc.Brain.Navigator.SetDestination(SpawnPoint , BaseNavigator.NavigationSpeed.Slow , 0f , 0f);
                #endregion

                #region senses & Targeting
                npc.Brain.ForceSetAge(0);
                npc.Brain.AllowedToSleep = false;
                npc.Brain.sleeping = false;
                npc.Brain.SenseRange = 30f;
                npc.Brain.ListenRange = 40f;
                npc.Brain.Senses.Init(npc , npc.Brain , 5f , 30 , 40f , 135f , true , true , true , 60f , false , false , true , EntityType.Player , true);
                npc.Brain.TargetLostRange = 20f;
                npc.Brain.HostileTargetsOnly = false;
                npc.Brain.IgnoreSafeZonePlayers = true;
                #endregion
            }

            void GoHome()
            {
                if (npc == null || npc.IsDestroyed || npc.isMounted)
                    return;

                if (!npc.HasBrain)
                    return;
                if (npc.Brain.Senses.Memory.Targets.Count > 0)
                {
                    for (var i = 0; i < npc.Brain.Senses.Memory.Targets.Count; i++)
                    {
                        BaseEntity target = npc.Brain.Senses.Memory.Targets[i];
                        BasePlayer player = target as BasePlayer;

                        if (target == null || player == null || !player.IsAlive() || player.IsSleeping() || player.IsFlying)
                        {
                            WipeMemory();
                            ReturningToHome = true;
                            isRoaming = false;
                            return;
                        }
                        if (npc.Distance(player.transform.position) > 25f)
                        {
                            WipeMemory();
                            ReturningToHome = true;
                            isRoaming = false;
                            return;
                        }
                    }
                }

                var distanceHome = Vector3.Distance(npc.transform.position , SpawnPoint);
                if (ReturningToHome == false)
                {
                    if (isRoaming == true && distanceHome > instance.configData.CrowData.NPCRoamMax)
                    {
                        ReturningToHome = true;
                        isRoaming = false;
                        return;
                    }
                    if (isRoaming == true && distanceHome < instance.configData.CrowData.NPCRoamMax)
                    {
                        Vector3 random = UnityEngine.Random.insideUnitCircle.normalized * instance.configData.CrowData.NPCRoamMax;
                        Vector3 newPos = instance.GetNavPoint(SpawnPoint + new Vector3(random.x , 0f , random.y));
                        SettargetDestination(newPos);
                        return;
                    }
                }
                if (ReturningToHome && distanceHome > 2)
                {
                    if (npc.Brain.Navigator.Destination == SpawnPoint)
                    {
                        return;
                    }

                    WipeMemory();
                    SettargetDestination(SpawnPoint);
                    return;
                }
                ReturningToHome = false;
                isRoaming = true;
            }

            private void SettargetDestination(Vector3 position)
            {
                npc.Brain.Navigator.Destination = position;
                npc.Brain.Navigator.SetDestination(position , BaseNavigator.NavigationSpeed.Slow , 0f , 0f);
            }

            void WipeMemory()
            {
                if (!npc.HasBrain)
                {
                    return;
                }

                npc.Brain.Senses.Players.Clear();
                npc.Brain.Senses.Memory.Players.Clear();
                npc.Brain.Senses.Memory.Targets.Clear();
                npc.Brain.Senses.Memory.Threats.Clear();
                npc.Brain.Senses.Memory.LOS.Clear();
                npc.Brain.Senses.Memory.All.Clear();
            }

            void OnDestroy()
            {
                if (npc != null && !npc.IsDestroyed)
                {
                    npc.Kill();
                }
                CancelInvoke(GoHome);
                CancelInvoke(Attack);
                CancelInvoke(nameof(_UseBrain));
            }
        }
        #endregion

        #region Scientist
        public class Scientists : FacepunchBehaviour
        {
            public global::HumanNPC npc;
            public bool ReturningToHome = false;
            public bool isRoaming = true;
            public Vector3 SpawnPoint;

            void Start()
            {
                npc = GetComponent<global::HumanNPC>();

                InvokeRepeating("GoHome" , 2.0f , 4.5f);
                Invoke(nameof(_UseBrain) , 0.1f);
            }

            public void _UseBrain()
            {
                #region navigation
                npc.Brain.Navigator.Agent.agentTypeID = -1372625422;
                npc.Brain.Navigator.DefaultArea = "Walkable";
                npc.Brain.Navigator.enabled = true;
                npc.Brain.Navigator.CanUseNavMesh = true;
                npc.Brain.Navigator.BestRoamPointMaxDistance = instance.configData.SCIData.NPCRoamMax;
                npc.Brain.Navigator.MaxRoamDistanceFromHome = instance.configData.SCIData.NPCRoamMax;
                npc.Brain.Navigator.Init(npc , npc.Brain.Navigator.Agent);
                npc.Brain.Navigator.SetDestination(SpawnPoint , BaseNavigator.NavigationSpeed.Slow , 0f , 0f);
                #endregion

                #region senses & Targeting
                npc.Brain.ForceSetAge(0);
                npc.Brain.AllowedToSleep = false;
                npc.Brain.sleeping = false;
                npc.Brain.SenseRange = 30f;
                npc.Brain.ListenRange = 40f;
                npc.Brain.Senses.Init(npc , npc.Brain , 5f , 50f , 50f , -1f , true , false , true , 60f , false , false , false , EntityType.Player , false);
                npc.Brain.TargetLostRange = 25f;
                npc.Brain.HostileTargetsOnly = false;
                npc.Brain.IgnoreSafeZonePlayers = true;
                #endregion
            }

            void GoHome()
            {
                if (npc == null || npc.IsDestroyed || npc.isMounted)
                    return;

                if (!npc.HasBrain)
                    return;
                if (npc.Brain.Senses.Memory.Targets.Count > 0)
                {
                    for (var i = 0; i < npc.Brain.Senses.Memory.Targets.Count; i++)
                    {
                        BaseEntity target = npc.Brain.Senses.Memory.Targets[i];
                        BasePlayer player = target as BasePlayer;

                        if (target == null || player == null || !player.IsAlive())
                        {
                            WipeMemory();
                            ReturningToHome = true;
                            isRoaming = false;
                            return;
                        }
                        if (npc.Distance(player.transform.position) > 40f)
                        {
                            WipeMemory();
                            ReturningToHome = true;
                            isRoaming = false;
                            return;
                        }
                    }
                }

                var distanceHome = Vector3.Distance(npc.transform.position , SpawnPoint);
                if (ReturningToHome == false)
                {
                    if (isRoaming == true && distanceHome > instance.configData.SCIData.NPCRoamMax)
                    {
                        ReturningToHome = true;
                        isRoaming = false;
                        return;
                    }
                    if (isRoaming == true && distanceHome < instance.configData.SCIData.NPCRoamMax)
                    {
                        Vector3 random = UnityEngine.Random.insideUnitCircle.normalized * instance.configData.SCIData.NPCRoamMax;
                        Vector3 newPos = instance.GetNavPoint(SpawnPoint + new Vector3(random.x , 0f , random.y));
                        SettargetDestination(newPos);
                        return;
                    }
                }
                if (ReturningToHome && distanceHome > 2)
                {
                    if (npc.Brain.Navigator.Destination == SpawnPoint)
                    {
                        return;
                    }

                    WipeMemory();
                    SettargetDestination(SpawnPoint);
                    return;
                }
                ReturningToHome = false;
                isRoaming = true;
            }

            private void SettargetDestination(Vector3 position)
            {
                npc.Brain.Navigator.Destination = position;
                npc.Brain.Navigator.SetDestination(position , BaseNavigator.NavigationSpeed.Slow , 0f , 0f);
            }

            void WipeMemory()
            {
                if (!npc.HasBrain)
                {
                    return;
                }

                npc.Brain.Senses.Players.Clear();
                npc.Brain.Senses.Memory.Players.Clear();
                npc.Brain.Senses.Memory.Targets.Clear();
                npc.Brain.Senses.Memory.Threats.Clear();
                npc.Brain.Senses.Memory.LOS.Clear();
                npc.Brain.Senses.Memory.All.Clear();
            }

            void OnDestroy()
            {
                if (npc != null && !npc.IsDestroyed)
                {
                    npc.Kill();
                }
                CancelInvoke("GoHome");
                CancelInvoke(nameof(_UseBrain));
            }
        }
        #endregion

        #endregion

        #region Configuration

        class ConfigData
        {
            [JsonProperty(PropertyName = "Plugin Settings")]
            public SettingsDrop DropData = new SettingsDrop();
            [JsonProperty(PropertyName = "Airdrop Settings")]
            public SettingsAirdrop AirdropData = new SettingsAirdrop();
            [JsonProperty(PropertyName = "Hack crate Settings")]
            public SettingsHack HackData = new SettingsHack();
            [JsonProperty(PropertyName = "Scarecrow Settings")]
            public NPCSettings CrowData = new NPCSettings();
            [JsonProperty(PropertyName = "Scientist Settings")]
            public SCISettings SCIData = new SCISettings();
            [JsonProperty(PropertyName = "Animal Settings")]
            public SettingsAnimal AnimalData = new SettingsAnimal();
            [JsonProperty(PropertyName = "Explosives Settings")]
            public SettingsExplosives ExplosivesData = new SettingsExplosives();
            [JsonProperty(PropertyName = "Fire Settings")]
            public SettingsFire FireData = new SettingsFire();
        }

        #region Plugin settings
        class SettingsDrop
        {
            [JsonProperty(PropertyName = "Chat Prefix")]
            public string Prefix = "[<color=green>Barrelles</color>] ";
            [JsonProperty(PropertyName = "Drop : Count random per x barrels")]
            public int Barrelcountdrop { get; set; } = 1;
            [JsonProperty(PropertyName = "Drop : Max range to trigger events")]
            public int RangeTrigger { get; set; } = 10;
            [JsonProperty(PropertyName = "Drop : Spawn only 1 entity on trigger")]
            public bool Trigger = false;
            [JsonProperty(PropertyName = "Only allow spawning outside")]
            public bool TriggerOut = true;
            [JsonProperty(PropertyName = "Show messages")]
            public bool ShowMsg = true;
            [JsonProperty(PropertyName = "FX on trigger")]
            public string FX = "assets/prefabs/misc/halloween/lootbag/effects/gold_open.prefab";
        }
        #endregion

        #region Hackable locked crate
        class SettingsHack
        {
            [JsonProperty(PropertyName = "Spawn chance (0-100)")]
            public int HackdropRate { get; set; } = 1;
            [JsonProperty(PropertyName = "Drop height")]
            public int HackdropHeight { get; set; } = 120;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = true;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
        }
        #endregion

        #region Airdrop
        class SettingsAirdrop
        {
            [JsonProperty(PropertyName = "Spawn chance (0-100)")]
            public int AirdropRate { get; set; } = 1;
            [JsonProperty(PropertyName = "Drop height")]
            public int AirdropHeight { get; set; } = 120;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = true;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
        }
        #endregion

        #region Scarecrow
        class NPCSettings
        {
            [JsonProperty(PropertyName = "Spawn chance (0-100)")]
            public int SpawnRate = 10;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = true;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
            [JsonProperty(PropertyName = "Spawn Amount")]
            public int NPCAmount = 1;
            [JsonProperty(PropertyName = "NPC Spawns on fire")]
            public bool FromHell = false;
            [JsonProperty(PropertyName = "Max Roam Distance")]
            public int NPCRoamMax = 15;
            [JsonProperty(PropertyName = "Prefix (Title)")]
            public string NPCName = "Scarecrow";
            [JsonProperty(PropertyName = "Prefix (Title) if chainsaw equiped")]
            public string NPCName2 = "Chainsaw Murderer";
            [JsonProperty(PropertyName = "Health (HP)")]
            public int NPCHealth = 250;
            [JsonProperty(PropertyName = "Life Duration (minutes)")]
            public float NPCLife = 5f;
            [JsonProperty(PropertyName = "Damage multiplier")]
            public float NPCDamageScale = 0.6f;
            [JsonProperty(PropertyName = "Use kit (clothing)")]
            public bool UseKit = false;
            [JsonProperty(PropertyName = "Kit ID")]
            public List<string> KitName = new List<string>();
        }
        #endregion

        #region Scientist
        class SCISettings
        {
            [JsonProperty(PropertyName = "Spawn chance (0-100)")]
            public int SpawnRate = 10;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = true;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
            [JsonProperty(PropertyName = "Spawn Amount")]
            public int NPCAmount = 1;
            [JsonProperty(PropertyName = "NPC Spawns on fire")]
            public bool FromHell = false;
            [JsonProperty(PropertyName = "Max Roam Distance")]
            public int NPCRoamMax = 15;
            [JsonProperty(PropertyName = "Prefix (Title)")]
            public string NPCName = "BarrelScientist";
            [JsonProperty(PropertyName = "Health (HP)")]
            public int NPCHealth = 250;
            [JsonProperty(PropertyName = "Life Duration (minutes)")]
            public float NPCLife = 5f;
            [JsonProperty(PropertyName = "Damage multiplier")]
            public float NPCDamageScale = 0.6f;
            [JsonProperty(PropertyName = "Use kit (clothing)")]
            public bool UseKit = false;
            [JsonProperty(PropertyName = "Kit ID")]
            public List<string> KitName = new List<string>();
        }
        #endregion

        #region Animals
        class SettingsAnimal
        {
            [JsonProperty(PropertyName = "Animals")]
            public Dictionary<string , AnimalSettings> Animals = new Dictionary<string , AnimalSettings>
            {
                { "bear", new AnimalSettings { Health = 450, SpawnChance = 5 } },
                { "polarbear", new AnimalSettings { Health = 450, SpawnChance = 5 } },
                { "boar", new AnimalSettings { Health = 250, SpawnChance = 10 } },
                { "chicken", new AnimalSettings { Health = 40, SpawnChance = 10 } },
                //{ "wolf", new AnimalSettings { Health = 250, SpawnChance = 10 } },
                { "tiger", new AnimalSettings { Health = 500, SpawnChance = 3 } },
                { "panther", new AnimalSettings { Health = 400, SpawnChance = 4 } },
                { "snake", new AnimalSettings { Health = 80, SpawnChance = 6 } }
            };
        }

        class AnimalSettings
        {
            [JsonProperty(PropertyName = "Chance on spawn (0-100)")]
            public int SpawnChance = 5;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = true;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
            [JsonProperty(PropertyName = "Amount")]
            public int Amount = 1;
            [JsonProperty(PropertyName = "Health")]
            public int Health = 250;
            [JsonProperty(PropertyName = "Life Duration (minutes)")]
            public float Life = 5f;
        }

        #endregion

        #region Explosives
        class SettingsExplosives
        {
            [JsonProperty(PropertyName = "Bee grenade : Chance on spawn (0-100)")]
            public int BeeNadeRate = 5;
            [JsonProperty(PropertyName = "Beancan : Chance on spawn (0-100)")]
            public int BeancanRate = 5;
            [JsonProperty(PropertyName = "F1 Grenade : Chance on spawn (0-100)")]
            public int GrenadeRate = 3;
            [JsonProperty(PropertyName = "Satchel Charge : Chance on spawn (0-100)")]
            public int SatchelRate = 3;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = true;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
        }
        #endregion

        #region Fires
        class SettingsFire
        {
            [JsonProperty(PropertyName = "Small fire : Chance on spawn (0-100)")]
            public int SmallfireRate = 5;
            [JsonProperty(PropertyName = "Oil fire : Chance on spawn (0-100)")]
            public int OilfireRate = 3;
            [JsonProperty(PropertyName = "Oil fire : Duration (max 20s)")]
            public float Duration = 10f;
            [JsonProperty(PropertyName = "Normal Barrels")]
            public bool Normal = false;
            [JsonProperty(PropertyName = "Diesel Barrels (diesel_barrel_world)")]
            public bool Diesel = true;
            [JsonProperty(PropertyName = "Oil Barrels")]
            public bool Oil = true;
        }
        #endregion

        #region cfg save/load
        private bool LoadConfigVariables()
        {
            try
            {
                configData = Config.ReadObject<ConfigData>();
            }
            catch
            {
                return false;
            }
            SaveConf();
            return true;
        }

        protected override void LoadDefaultConfig()
        {
            Puts("Fresh install detected Creating a new config file.");
            configData = new ConfigData();
            SaveConf();
        }

        void SaveConf() => Config.WriteObject(configData , true);
        #endregion

        #endregion

        #region LanguageApi

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string , string>
            {
                ["AirdropSpawned"] = "You lucky bastard a Airdrop is comming to your location" ,
                ["Barrelless_toggle_disabled"] = "Barrel exclusion disabled." ,
                ["Barrelless_toggle_enabled"] = "Barrel exclusion enabled." ,
                ["Barrelless_toggle_noperm"] = "You do not have permission to toggle this." ,
                ["Bearspawned"] = "A wild bear just apeared" ,
                ["Beancanspawned"] = "A small explosive fell out of the barrel" ,
                ["Beenadespawned"] = "Angry swarm of bees is attacking you." ,
                ["Boarspawned"] = "Oink..." ,
                ["Chickenspawned"] = "tok tok..." ,
                ["Firespawned"] = "Carefull a spark set the barrel on fire" ,
                ["Grenadespawned"] = "A Grenade fell out of the barrel" ,
                ["HackCrateSpawned"] = "You lucky bastard a Hackable Crate is comming to your location" ,
                ["MoreBearspawned"] = "Runnn... a sleuth of Bears apeared" ,
                ["MoreBoarspawned"] = "Oinks...A Sounder of boars apeared" ,
                ["MoreChickenspawned"] = "tok...toktoktok!" ,
                ["MorePanthersspawned"] = "Panthers are stalking the area!" ,
                ["MorePBearspawned"] = "Runnn... they hungry" ,
                ["MoreScientistsSpawned"] = "Multiple Scientists where freed from their barrel prison" ,
                ["MoreSnakesspawned"] = "Multiple snakes are crawling around!" ,
                ["MoreTigersspawned"] = "A group of tigers have arrived!" ,
                ["MoreWolvesspawned"] = "Runnn... it is a pack of wolves" ,
                ["MoreZombiesSpawned"] = "Multiple Zombies where freed from their barrel prison" ,
                ["OilFirespawned"] = "Ouch a spark had the barrel lit up" ,
                ["Pantherspawned"] = "A panther emerges from the shadows!" ,
                ["PBearspawned"] = "A wild PolarBear just apeared" ,
                ["Satchelspawned"] = "Run !!! A hidden satchelcharge was activated" ,
                ["Scientistspawned"] = "A Scientist was freed from his barrel prison" ,
                ["SignalDropped"] = "You found a supply signal but you dropped it on the floor" ,
                ["SignalRecieved"] = "Your recieved a supply signal in your inventory" ,
                ["Snakespawned"] = "A snake slithers into the open..." ,
                ["Tigerspawned"] = "A tiger has been unleashed!" ,
                //["Wolfspawned"] = "A wild wolf just apeared" ,
                ["Zombiespawned"] = "A Zombie was freed from his barrel prison"
            } , this);
        }

        #endregion

        #region Hooks

        void OnServerInitialized()
        {
            instance = this;
        }

        void Unload()
        {
            foreach (var npc in spawnedZombies)
            {
                if (npc != null && !npc.IsDestroyed)
                    npc.Kill();
            }
            spawnedZombies.Clear();

            foreach (var npc in spawnedScientists)
            {
                if (npc != null && !npc.IsDestroyed)
                    npc.Kill();
            }
            spawnedScientists.Clear();

            foreach (BaseEntity entity in spawnedAnimals)
            {
                if (entity != null && !entity.IsDestroyed)
                    entity.Kill();
            }
            spawnedAnimals.Clear();
        }

        void Init()
        {
            if (!LoadConfigVariables())
            {
                Puts("Config file issue detected. Please delete file, or check syntax and fix.");
                return;
            }

            prefix = configData.DropData.Prefix;
            chaticon = 76561199200662608;
            BlockOutside = configData.DropData.TriggerOut;

            RegisterPermissions();
        }

        void OnEntityDeath(LootContainer entity , HitInfo info)
        {
            if (info?.InitiatorPlayer == null) return;
            if (!IsLootBarrel(entity)) return;
            if (!CheckPlayer(info)) return;
            if (!entity.IsOutside() && BlockOutside) return;

            BasePlayer player = info.InitiatorPlayer;

            if (HasPerm(player , Exclude_Perm)) return;

            var BarrelDistance = Vector3.Distance(entity.transform.position , player.transform.position);
            if (BarrelDistance > configData.DropData.RangeTrigger) return;

            bool eventTriggered = false;
            bool isSpawned = false;
            bool fireAlreadySpawned = false;
            bool signalDropped = false;

            Playerinfo user = get_user(player);
            if (user.barrelCount < configData.DropData.Barrelcountdrop)
            {
                user.barrelCount += 1;
                update_user(player , user);
                return;
            }

            user.barrelCount = 0;
            update_user(player , user);

            #region Scarecrow
            if (SpawnRate(configData.CrowData.SpawnRate) && HasPerm(player , ScareTrigger_Perm))
            {
                isSpawned = false;
                for (int i = 0; i < configData.CrowData.NPCAmount; i++)
                {
                    if (!IsValidCrowBarrelType(entity)) continue;
                    Spawnnpc(entity.transform.position);
                    isSpawned = true;
                    RunFX(player);
                }

                if (isSpawned && configData.DropData.ShowMsg)
                {
                    string msgKey = configData.CrowData.NPCAmount == 1 ? "Zombiespawned" : "MoreZombiesSpawned";
                    Player.Message(player , prefix + string.Format(msg(msgKey , player.UserIDString)) , chaticon);
                }

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }
            #endregion

            #region Scientists
            if (SpawnRate(configData.SCIData.SpawnRate) && HasPerm(player , SciTrigger_Perm))
            {
                isSpawned = false;
                for (int i = 0; i < configData.SCIData.NPCAmount; i++)
                {
                    if (!IsValidScientistBarrelType(entity)) continue;
                    _FromHell = configData.SCIData.FromHell;
                    SciHealth = configData.SCIData.NPCHealth;
                    SciDamageScale = configData.SCIData.NPCDamageScale;
                    SciLifetime = configData.SCIData.NPCLife;
                    SciName = configData.SCIData.NPCName;
                    _UseKit = configData.SCIData.UseKit;
                    SciKit = configData.SCIData.KitName;
                    SpawnScientist(entity.transform.position);
                    isSpawned = true;
                    RunFX(player);
                }

                if (isSpawned && configData.DropData.ShowMsg)
                {
                    string msgKey = configData.SCIData.NPCAmount == 1 ? "Scientistspawned" : "MoreScientistsSpawned";
                    Player.Message(player , prefix + string.Format(msg(msgKey , player.UserIDString)) , chaticon);
                }

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }
            #endregion

            #region Airdrop
            if (SpawnRate(configData.AirdropData.AirdropRate) && HasPerm(player , AirdropTrigger_Perm))
            {
                isSpawned = false;

                if (entity.IsOutside())
                {
                    SpawnSupplyCrate(airdropString , entity.transform.position);
                    isSpawned = true;
                    RunFX(player);
                    if (isSpawned && configData.DropData.ShowMsg)
                        Player.Message(player , prefix + string.Format(msg("AirdropSpawned" , player.UserIDString)) , chaticon);
                }
                else
                {
                    var signal = ItemManager.CreateByName("supply.signal" , 1 , 0);
                    player.inventory.GiveItem(signal , null);
                    isSpawned = true;
                    RunFX(player);

                    if (player.inventory.containerMain.IsFull())
                    {
                        signal.DropAndTossUpwards(player.transform.position);
                        signalDropped = true;
                    }

                    if (isSpawned && configData.DropData.ShowMsg)
                    {
                        string msgKey = signalDropped ? "SignalDropped" : "SignalRecieved";
                        Player.Message(player , prefix + string.Format(msg(msgKey , player.UserIDString)) , chaticon);
                    }
                }

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }
            #endregion

            #region HackCrate
            if (SpawnRate(configData.HackData.HackdropRate) && HasPerm(player , HackCrateTrigger_Perm))
            {
                isSpawned = false;
                SpawnHackCrate(hackcrateString , entity.transform.position);
                isSpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("HackCrateSpawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }
            #endregion

            #region Animals
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "bear" , bearString , BearTrigger_Perm , "Bearspawned" , "MoreBearspawned" , ref eventTriggered);
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "polarbear" , PBearString , PBearTrigger_Perm , "PBearspawned" , "MorePBearspawned" , ref eventTriggered);
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "boar" , boarstring , BoarTrigger_Perm , "Boarspawned" , "MoreBoarspawned" , ref eventTriggered);
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "chicken" , chickenString , ChickenTrigger_Perm , "Chickenspawned" , "MoreChickenspawned" , ref eventTriggered);
            //if (!eventTriggered) HandleAnimalSpawn(player , entity , "wolf" , wolfString , WolfTrigger_Perm , "Wolfspawned" , "MoreWolvesspawned" , ref eventTriggered);
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "tiger" , tigerString , TigerTrigger_Perm , "Tigerspawned" , "MoreTigersspawned" , ref eventTriggered);
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "panther" , pantherString , PantherTrigger_Perm , "Pantherspawned" , "MorePanthersspawned" , ref eventTriggered);
            if (!eventTriggered) HandleAnimalSpawn(player , entity , "snake" , snakeString , SnakeTrigger_Perm , "Snakespawned" , "MoreSnakesspawned" , ref eventTriggered);
            #endregion

            #region Explosives
            if (SpawnRate(configData.ExplosivesData.BeancanRate) && HasPerm(player , ExploTrigger_Perm))
            {
                isSpawned = false;
                SpawnThrowable(beancanString , entity.transform.position);
                isSpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("Beancanspawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }

            if (SpawnRate(configData.ExplosivesData.GrenadeRate) && HasPerm(player , ExploTrigger_Perm))
            {
                isSpawned = false;
                SpawnThrowable(grenadeString , entity.transform.position);
                isSpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("Grenadespawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }

            if (SpawnRate(configData.ExplosivesData.SatchelRate) && HasPerm(player , ExploTrigger_Perm))
            {
                isSpawned = false;
                SpawnThrowable(satchelString , entity.transform.position);
                isSpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("Satchelspawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }

            if (SpawnRate(configData.ExplosivesData.BeeNadeRate) && HasPerm(player , ExploTrigger_Perm))
            {
                isSpawned = false;
                SpawnThrowable(beenadelString , entity.transform.position);
                isSpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("Beenadespawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }
            #endregion

            #region Fires
            if (SpawnRate(configData.FireData.SmallfireRate) && HasPerm(player , FireTrigger_Perm))
            {
                isSpawned = false;
                fireAlreadySpawned = false;
                SpawnFire(fireball , entity.transform.position , configData.FireData.Duration);
                isSpawned = true;
                fireAlreadySpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("Firespawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }

            if (SpawnRate(configData.FireData.OilfireRate) && HasPerm(player , FireTrigger_Perm) && !fireAlreadySpawned)
            {
                isSpawned = false;
                SpawnFire(oilfire , entity.transform.position , configData.FireData.Duration);
                isSpawned = true;
                RunFX(player);

                if (isSpawned && configData.DropData.ShowMsg)
                    Player.Message(player , prefix + string.Format(msg("OilFirespawned" , player.UserIDString)) , chaticon);

                if (isSpawned && configData.DropData.Trigger) eventTriggered = true;
            }
            #endregion
        }

        object OnNpcKits(BasePlayer player)
        {
            if (player?.gameObject?.GetComponent<Zombies>() != null) return true;
            if (player?.gameObject?.GetComponent<Scientists>() != null) return true;
            return null;
        }

        private void HandleAnimalSpawn(BasePlayer player , LootContainer entity , string animalKey , string prefabName , string perm , string msgSingle , string msgMulti , ref bool eventTriggered)
        {
            AnimalSettings settings;
            if (!configData.AnimalData.Animals.TryGetValue(animalKey , out settings)) return;
            if (!HasPerm(player , perm)) return;
            if (!SpawnRate(settings.SpawnChance)) return;

            bool isSpawned = false;

            for (int i = 0; i < settings.Amount; i++)
            {
                if (IsNormalBarrel(entity) && !settings.Normal) continue;
                if (IsDiesel(entity) && !settings.Diesel) continue;
                if (IsOilBarrel(entity) && !settings.Oil) continue;

                _AnimalHealth = settings.Health;
                _AnimalLife = settings.Life;
                _AnimalString = prefabName;
                SpawnAnimal(entity.transform.position , _AnimalString , _AnimalHealth , _AnimalLife);
                isSpawned = true;
                RunFX(player);
            }

            if (!isSpawned || !configData.DropData.ShowMsg) return;

            string msgKey = settings.Amount == 1 ? msgSingle : msgMulti;
            Player.Message(player , prefix + string.Format(msg(msgKey , player.UserIDString)) , chaticon);

            if (isSpawned && configData.DropData.Trigger)
            {
                eventTriggered = true;
            }
        }

        private object OnNpcTarget(BaseEntity attacker , BaseEntity target)
        {
            if (attacker?.gameObject?.GetComponent<Zombies>())
            {
                if (target is ScarecrowNPC || target is BaseNpc)
                {
                    return true;
                }

                if (target is TunnelDweller || target is UnderwaterDweller)
                {
                    return true;
                }
                return null;
            }

            if (target?.gameObject?.GetComponent<Zombies>())
            {
                return true;
            }
            return null;
        }

        private object OnNpcTarget(BaseEntity attacker , BasePlayer target)
        {
            if (attacker?.gameObject?.GetComponent<Zombies>())
            {
                if (target.IsSleeping() || target.IsFlying || !(target.userID.IsSteamId()))
                    return false;
            }
            return null;
        }

        private void OnFireBallDamage(FireBall fire , ScarecrowNPC npc , HitInfo info)
        {
            if (!(npc?.gameObject?.GetComponent<Zombies>() != null && info.Initiator is FireBall))
            {
                return;
            }

            info.damageTypes = new DamageTypeList();
            info.DoHitEffects = false;
            info.damageTypes.ScaleAll(0f);
        }

        #endregion

        #region Event Helpers

        private void RunFX(BasePlayer player)
        {
            if (configData.DropData.FX == string.Empty) return;
            Effect.server.Run(configData.DropData.FX , player.transform.position + new Vector3(0f , 2.0f , 0f));
        }

        private void RegisterPermissions()
        {
            for (int i = 0; i < _permissions.Count; i++)
            {
                permission.RegisterPermission(_permissions[i] , this);
            }
        }

        #region Nav & Checks
        public Vector3 GetNavPoint(Vector3 position)
        {
            NavMeshHit hit;
            if (!NavMesh.SamplePosition(position , out hit , 5 , -1))
            {
                return position;
            }
            else if (Physics.RaycastAll(hit.position + new Vector3(0 , 100 , 0) , Vector3.down , 99f , 1235288065).Length > 0)
            {
                return position;
            }
            else if (hit.position.y < TerrainMeta.WaterMap.GetHeight(hit.position))
            {
                return position;
            }
            position = hit.position;
            return position;
        }

        private bool SpawnRate(int npcRate)
        {
            if (rnd.Next(1 , 101) <= npcRate)
            {
                return true;
            }
            return false;
        }

        private bool CheckPlayer(HitInfo info)
        {
            if (info == null) return false;

            BasePlayer player = info.InitiatorPlayer;
            if (player == null) return false;
            if (!player.IsValid()) return false;
            if (info.Initiator == null) return false;

            return true;
        }

        private bool IsLootBarrel(BaseCombatEntity entity)
        {
            if (entity.ShortPrefabName.StartsWith("loot-barrel")) return true;
            if (entity.ShortPrefabName.StartsWith("loot_barrel")) return true;
            if (entity.ShortPrefabName.StartsWith("oil_barrel")) return true;
            if (entity.ShortPrefabName.StartsWith("diesel_barrel_world")) return true;
            return false;
        }

        private bool IsOilBarrel(BaseCombatEntity entity)
        {
            if (entity.ShortPrefabName.StartsWith("oil_barrel")) return true;
            return false;
        }

        private bool IsDiesel(BaseCombatEntity entity)
        {
            if (entity.ShortPrefabName.StartsWith("diesel_barrel_world")) return true;
            return false;
        }

        private bool IsNormalBarrel(BaseCombatEntity entity)
        {
            if (entity.ShortPrefabName.StartsWith("loot-barrel")) return true;
            if (entity.ShortPrefabName.StartsWith("loot_barrel")) return true;
            return false;
        }

        private bool IsValidCrowBarrelType(BaseCombatEntity entity)
        {
            if (IsNormalBarrel(entity) && configData.CrowData.Normal) return true;
            if (IsDiesel(entity) && configData.CrowData.Diesel) return true;
            if (IsOilBarrel(entity) && configData.CrowData.Oil) return true;
            return false;
        }

        private bool IsValidScientistBarrelType(BaseCombatEntity entity)
        {
            if (IsNormalBarrel(entity) && configData.SCIData.Normal) return true;
            if (IsDiesel(entity) && configData.SCIData.Diesel) return true;
            if (IsOilBarrel(entity) && configData.SCIData.Oil) return true;
            return false;
        }

        #endregion

        #region Scarecrow
        private void Spawnnpc(Vector3 position)
        {
            Vector3 pos = position + UnityEngine.Random.onUnitSphere * 1.5f;
            pos = GetNavPoint(pos);

            ScarecrowNPC npc = GameManager.server.CreateEntity(zombie , pos , new Quaternion() , true) as ScarecrowNPC;
            if (npc == null) return;

            npc.Spawn();

            NextTick(() =>
            {
                if (npc == null || npc.IsDestroyed)
                    return;

                spawnedZombies.Add(npc);
                var mono = npc.gameObject.AddComponent<Zombies>();
                mono.SpawnPoint = pos;

                npc.startHealth = configData.CrowData.NPCHealth;
                npc.InitializeHealth(configData.CrowData.NPCHealth , configData.CrowData.NPCHealth);
                npc.CanAttack();

                timer.Once(0.5f , () =>
                {
                    if (npc == null || npc.IsDestroyed) return;

                    Chainsaw heldEntity = npc.GetHeldEntity() as Chainsaw;
                    if (heldEntity != null)
                    {
                        npc.displayName = configData.CrowData.NPCName2 + " " + RandomUsernames.Get(npc.userID.Get());
                        heldEntity.SetFlag(Chainsaw.Flags.On , true);
                        heldEntity.SendNetworkUpdateImmediate();
                    }
                    else
                    {
                        npc.displayName = configData.CrowData.NPCName + " " + RandomUsernames.Get(npc.userID.Get());
                    }
                });

                if (configData.CrowData.FromHell)
                {
                    var fire = GameManager.server.CreateEntity(fireball , new Vector3(0 , 1 , 0) , Quaternion.Euler(0 , 0 , 0));
                    if (fire != null)
                    {
                        fire.gameObject.Identity();
                        fire.SetParent(npc);
                        fire.Spawn();
                        timer.Once(1f , () =>
                        {
                            if (fire != null && !fire.IsDestroyed) fire.Kill();
                            Puts($"{npc} spawned From Hell (on Fire)");
                        });
                    }
                }

                npc.damageScale = configData.CrowData.NPCDamageScale;

                if (configData.CrowData.UseKit && configData.CrowData.KitName.Count > 0)
                {
                    int randomKitIndex = UnityEngine.Random.Range(0 , configData.CrowData.KitName.Count);
                    string kitName = configData.CrowData.KitName[randomKitIndex];
                    object checkKit = Kits?.CallHook("GetKitInfo" , kitName);

                    if (checkKit == null)
                    {
                        NextTick(() =>
                        {
                            if (npc == null || npc.IsDestroyed) return;

                            var invWear = npc.inventory.containerWear;
                            Item eyes = ItemManager.CreateByName("gloweyes" , 1 , 0);
                            if (eyes != null) eyes.MoveToContainer(invWear);
                            PrintWarning($"Kit for {npc} does not exist - Using a default outfit.");
                        });
                    }
                    else
                    {
                        npc.inventory.Strip();
                        Kits?.Call("GiveKit" , npc , kitName);
                    }
                }
                else
                {
                    var invWear = npc.inventory.containerWear;
                    Item eyes = ItemManager.CreateByName("gloweyes" , 1 , 0);
                    if (eyes != null) eyes.MoveToContainer(invWear);
                }
            });

            if (npc.IsHeadUnderwater())
            {
                npc.Kill();
                Puts($"{npc} got destroyed for being under water!!!");
                return;
            }

            timer.Once(configData.CrowData.NPCLife * 60f , () =>
            {
                if (npc != null && !npc.IsDestroyed)
                {
                    spawnedZombies.Remove(npc);
                    npc.Kill();
                    Puts($"{npc} Died of old age");
                }
            });
        }
        #endregion

        #region Scientist
        private void SpawnScientist(Vector3 position)
        {
            Vector3 pos = position + UnityEngine.Random.onUnitSphere * 1.5f;
            pos = GetNavPoint(pos);

            NPCPlayer npc = GameManager.server.CreateEntity(_scientist , pos , new Quaternion() , true) as NPCPlayer;
            if (npc == null) return;

            npc.Spawn();

            NextTick(() =>
            {
                if (npc == null || npc.IsDestroyed)
                    return;

                spawnedScientists.Add(npc);

                var mono = npc.gameObject.AddComponent<Scientists>();
                mono.SpawnPoint = pos;

                npc.startHealth = SciHealth;
                npc.InitializeHealth(SciHealth , SciHealth);
                npc.CanAttack();

                timer.Once(0.5f , () =>
                {
                    if (npc == null || npc.IsDestroyed) return;
                    npc.displayName = SciName + " " + RandomUsernames.Get(npc.userID.Get());
                });

                if (_FromHell)
                {
                    var fire = GameManager.server.CreateEntity(fireball , new Vector3(0 , 1 , 0) , Quaternion.Euler(0 , 0 , 0));
                    if (fire != null)
                    {
                        fire.gameObject.Identity();
                        fire.SetParent(npc);
                        fire.Spawn();
                        timer.Once(1f , () =>
                        {
                            if (fire != null && !fire.IsDestroyed) fire.Kill();
                            Puts($"{npc} spawned From Hell (on Fire)");
                        });
                    }
                }

                npc.damageScale = SciDamageScale;

                if (_UseKit && SciKit != null && SciKit.Count > 0)
                {
                    int randomKitIndex = UnityEngine.Random.Range(0 , SciKit.Count);
                    string kitName = SciKit[randomKitIndex];
                    object checkKit = Kits?.CallHook("GetKitInfo" , kitName);

                    if (checkKit == null)
                    {
                        timer.Once(1f , () =>
                        {
                            PrintWarning($"Kit for {npc} does not exist - Using a default outfit.");
                        });
                    }
                    else
                    {
                        npc.inventory.Strip();
                        Kits?.Call("GiveKit" , npc , kitName);
                        timer.Once(1f , () =>
                        {
                            PrintWarning($"Kit for {npc} succesfully equiped.");
                        });
                    }
                }
                else
                {
                    timer.Once(1f , () =>
                    {
                        PrintWarning($"{npc} spawned Using a default outfit.");
                    });
                }
            });

            if (npc.IsHeadUnderwater())
            {
                npc.Kill();
                timer.Once(1f , () =>
                {
                    PrintWarning($"{npc} got destroyed for being under water!!!");
                });
                return;
            }

            timer.Once(SciLifetime * 60f , () =>
            {
                if (npc != null && !npc.IsDestroyed)
                {
                    spawnedScientists.Remove(npc);
                    npc.Kill();
                    Puts($"{npc} Died of old age");
                }
            });
        }

        #endregion

        #region Airdrop
        private void SpawnSupplyCrate(string prefab , Vector3 position)
        {
            Vector3 newPosition = position + new Vector3(0 , configData.AirdropData.AirdropHeight , 0);
            BaseEntity supplyCrateEntity = GameManager.server.CreateEntity(prefab , newPosition);
            if (supplyCrateEntity != null)
            {
                SupplyDrop drop = supplyCrateEntity.GetComponent<SupplyDrop>();
                if (drop != null)
                {
                    drop.Spawn();
                }
            }
        }
        #endregion

        #region hack crate
        private void SpawnHackCrate(string prefab , Vector3 position)
        {
            Vector3 newPosition = position + new Vector3(0 , configData.HackData.HackdropHeight , 0);
            BaseEntity hackCrateEntity = GameManager.server.CreateEntity(prefab , newPosition);
            if (hackCrateEntity != null)
            {
                HackableLockedCrate hackCrate = hackCrateEntity.GetComponent<HackableLockedCrate>();
                if (hackCrate != null)
                {
                    hackCrate.Spawn();
                }
            }
        }
        #endregion

        #region Animals
        private void SpawnAnimal(Vector3 position , string prefab , float health , float life)
        {
            Vector3 pos = position + UnityEngine.Random.onUnitSphere * 1f;
            pos = GetNavPoint(pos);

            BaseEntity entity = GameManager.server.CreateEntity(prefab , pos , Quaternion.identity , true);
            if (entity == null) return;

            BaseNpc oldAnimal = entity as BaseNpc;
            if (oldAnimal != null)
            {
                oldAnimal.Spawn();
                oldAnimal.startHealth = health;
                oldAnimal.InitializeHealth(health , health);
                spawnedAnimals.Add(oldAnimal);
            }

            BaseNPC2 gen2Animal = entity as BaseNPC2;
            if (gen2Animal != null)
            {
                gen2Animal.Spawn();
                if (!prefab.Contains("snake"))
                    gen2Animal.InitializeHealth(health , health);

                spawnedAnimals.Add(gen2Animal);
            }

            if (oldAnimal == null && gen2Animal == null)
            {
                entity.Spawn();
                spawnedAnimals.Add(entity);
            }

            timer.Once(life * 60f , () =>
            {
                if (entity != null && !entity.IsDestroyed)
                {
                    spawnedAnimals.Remove(entity);
                    entity.Kill();
                    Puts($"[Barrelless] {entity.ShortPrefabName} despawned after {life} minutes");
                }
            });
        }
        #endregion

        #region Explosives
        private void SpawnThrowable(string prefab , Vector3 position)
        {
            Vector3 newPosition = GetNavPoint(position) + new Vector3(0 , 1.4f , 0);
            BaseEntity throwingItem = GameManager.server.CreateEntity(prefab , newPosition);
            if (throwingItem != null)
            {
                throwingItem.Spawn();
            }
        }

        private void SpawnFire(string prefab , Vector3 position , float killTime)
        {
            Vector3 newPosition = GetNavPoint(position) + new Vector3(0 , 1.4f , 0);
            BaseEntity fire = GameManager.server.CreateEntity(prefab , newPosition);
            if (killTime > 20f || killTime == 0f) killTime = 20f;
            if (fire != null)
            {
                fire.Spawn();
                timer.Once(killTime , () =>
                {
                    if (fire != null && !fire.IsDestroyed)
                        fire.Kill();
                });
            }
        }
        #endregion

        #endregion

        #region Message and helpers

        private string msg(string key , string id = null) => lang.GetMessage(key , this , id);

        bool HasPerm(BasePlayer player , string perm)
        {
            return player != null && permission.UserHasPermission(player.UserIDString , perm);
        }

        #endregion

        #region Data helpers
        Playerinfo get_user(BasePlayer player)
        {
            string filePath = file_main + player.UserIDString;

            if (!Interface.Oxide.DataFileSystem.ExistsDatafile(filePath))
            {
                var user = new Playerinfo
                {
                    userName = player.displayName ,
                    barrelCount = 0
                };
                update_user(player , user);
                return user;
            }

            string rawPlayerFile = Interface.Oxide.DataFileSystem.ReadObject<string>(filePath);

            if (string.IsNullOrEmpty(rawPlayerFile))
            {
                var user = new Playerinfo
                {
                    userName = player.displayName ,
                    barrelCount = 0
                };
                update_user(player , user);
                return user;
            }

            Playerinfo loadedUser = JsonConvert.DeserializeObject<Playerinfo>(rawPlayerFile);
            if (loadedUser == null)
            {
                loadedUser = new Playerinfo
                {
                    userName = player.displayName ,
                    barrelCount = 0
                };
                update_user(player , loadedUser);
            }

            return loadedUser;
        }

        void update_user(BasePlayer player , Playerinfo user)
        {
            if (player == null || user == null) return;
            Interface.Oxide.DataFileSystem.WriteObject<string>(file_main + player.UserIDString , JsonConvert.SerializeObject(user));
        }

        public class Playerinfo
        {
            private string _userName;
            private int _barrelCount;

            public int barrelCount
            {
                get { return _barrelCount; }
                set { _barrelCount = value; }
            }

            public string userName
            {
                get { return _userName; }
                set { _userName = value; }
            }
        }
        #endregion

        #region Chatcommand

        [ChatCommand("togglebarrel")]
        void ToggleBarrelCommand(BasePlayer player , string command , string[] args)
        {
            if (!HasPerm(player , CanToggle_Perm))
            {
                Player.Message(player , prefix + msg("Barrelless_toggle_noperm" , player.UserIDString) , chaticon);
                return;
            }

            if (HasPerm(player , Exclude_Perm))
            {
                permission.RevokeUserPermission(player.UserIDString , Exclude_Perm);
                Player.Message(player , prefix + msg("Barrelless_toggle_disabled" , player.UserIDString) , chaticon);
            }
            else
            {
                permission.GrantUserPermission(player.UserIDString , Exclude_Perm , this);
                Player.Message(player , prefix + msg("Barrelless_toggle_enabled" , player.UserIDString) , chaticon);
            }
        }

        #endregion
    }
}