use the following 2 scripts as an example:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemPickup : MonoBehaviour
{
	public enum ItemType
	{
		Health,
		Armor,
		Weapon,
		Bullets,
		Shells,
		Grenades,
		Rockets,
		Lightning,
		Slugs,
		Cells,
		Bfgammo,
	}

	public ItemType itemType;

	public int amount;
	public bool bonus;
	public int givesWeapon = -1;

	public string PickupSound;
	public string PickupIcon;
	public string PickupText;

	string pickUpText = "";

	void OnTriggerEnter(Collider other)
	{
		PlayerThing player = other.GetComponent<PlayerThing>();

		if (player == null)
			return;

		//Dead player don't pick up stuff
		if (player.Dead)
			return;

		bool disable = false;

		switch (itemType)
		{
			default:
				break;

			case ItemType.Bullets:
				if (player.playerInfo.Ammo[0] == player.playerInfo.MaxAmmo[0])
					break;
				player.playerInfo.Ammo[0] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[0] > player.playerInfo.MaxAmmo[0])
					player.playerInfo.Ammo[0] = player.playerInfo.MaxAmmo[0];
				disable = true;
				break;

			case ItemType.Shells:
				if (player.playerInfo.Ammo[1] == player.playerInfo.MaxAmmo[1])
					break;
				player.playerInfo.Ammo[1] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[1] > player.playerInfo.MaxAmmo[1])
					player.playerInfo.Ammo[1] = player.playerInfo.MaxAmmo[1];
				disable = true;
				break;

			case ItemType.Grenades:
				if (player.playerInfo.Ammo[2] == player.playerInfo.MaxAmmo[2])
					break;
				player.playerInfo.Ammo[2] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[2] > player.playerInfo.MaxAmmo[2])
					player.playerInfo.Ammo[2] = player.playerInfo.MaxAmmo[2];
				disable = true;
				break;

			case ItemType.Rockets:
				if (player.playerInfo.Ammo[3] == player.playerInfo.MaxAmmo[3])
					break;
				player.playerInfo.Ammo[3] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[3] > player.playerInfo.MaxAmmo[3])
					player.playerInfo.Ammo[3] = player.playerInfo.MaxAmmo[3];
				disable = true;
				break;

			case ItemType.Lightning:
				if (player.playerInfo.Ammo[4] == player.playerInfo.MaxAmmo[4])
					break;
				player.playerInfo.Ammo[4] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[4] > player.playerInfo.MaxAmmo[4])
					player.playerInfo.Ammo[4] = player.playerInfo.MaxAmmo[4];
				disable = true;
				break;

			case ItemType.Slugs:
				if (player.playerInfo.Ammo[5] == player.playerInfo.MaxAmmo[5])
					break;
				player.playerInfo.Ammo[5] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[5] > player.playerInfo.MaxAmmo[5])
					player.playerInfo.Ammo[5] = player.playerInfo.MaxAmmo[5];
				disable = true;
				break;

			case ItemType.Cells:
				if (player.playerInfo.Ammo[6] == player.playerInfo.MaxAmmo[6])
					break;
				player.playerInfo.Ammo[6] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[6] > player.playerInfo.MaxAmmo[6])
					player.playerInfo.Ammo[6] = player.playerInfo.MaxAmmo[6];
				disable = true;
				break;

			case ItemType.Bfgammo:
				if (player.playerInfo.Ammo[7] == player.playerInfo.MaxAmmo[7])
					break;
				player.playerInfo.Ammo[7] += (amount * GameManager.Instance.PlayerAmmoReceive);
				if (player.playerInfo.Ammo[7] > player.playerInfo.MaxAmmo[7])
					player.playerInfo.Ammo[7] = player.playerInfo.MaxAmmo[7];
				disable = true;
				break;

			case ItemType.Health:
				if (bonus)
				{
					if (player.hitpoints == player.playerInfo.MaxBonusHealth)
						break;

					player.hitpoints += amount;
					if (player.hitpoints > player.playerInfo.MaxBonusHealth)
						player.hitpoints = player.playerInfo.MaxBonusHealth;
				}
				else
				{
					if (player.hitpoints >= player.playerInfo.MaxHealth)
						break;

					player.hitpoints += amount;
					if (player.hitpoints > player.playerInfo.MaxHealth)
						player.hitpoints = player.playerInfo.MaxHealth;
				}
				disable = true;
				break;

			case ItemType.Armor:
				if (bonus)
				{
					if (player.armor == player.playerInfo.MaxBonusArmor)
						break;

					player.armor += amount;
					if (player.armor > player.playerInfo.MaxBonusArmor)
						player.armor = player.playerInfo.MaxBonusArmor;
				}
				else
				{
					if (player.armor >= player.playerInfo.MaxArmor)
						break;

					player.armor += amount;
					if (player.armor > player.playerInfo.MaxArmor)
						player.armor = player.playerInfo.MaxArmor;
				}

				disable = true;
				break;
		}

		if (givesWeapon != -1)
		{
			if (givesWeapon >= 0 && givesWeapon < player.playerInfo.Weapon.Length)
			{
				if (!player.playerInfo.Weapon[givesWeapon])
				{
					player.playerInfo.Weapon[givesWeapon] = true;
					player.playerControls.TrySwapWeapon(givesWeapon);
					player.lookTime = 1.5f;
					disable = true;
				}
			}
		}

		if (disable)
		{
			player.playerInfo.playerHUD.pickupFlashTime = .5f;

			if (!string.IsNullOrEmpty(PickupSound))
				AudioManager.Create3DSound(transform.position, PickupSound, 1f);

			player.playerInfo.playerHUD.HUDUpdateAmmoNum();
			player.playerInfo.playerHUD.HUDUpdateHealthNum();
			player.playerInfo.playerHUD.HUDUpdateArmorNum();
			gameObject.SetActive(false);
		}
	}
}


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ModelAnimation : MonoBehaviour
{
	public string modelName;
	public bool isTransparent = false;
	public bool castShadow = true;

	private MD3 md3Model;

	private MD3UnityConverted unityModel;

	[SerializeField]
	public AnimData modelAnimation;
	[SerializeField]
	public AnimData textureAnimation;
	[SerializeField]
	public PosAnim localAnimation;

	private List<int> modelAnim = new List<int>();
	private List<int> textureAnim = new List<int>();
	private Dictionary<int, Texture[]> textures = new Dictionary<int, Texture[]>();

	private int modelCurrentFrame;
	private List<int> textureCurrentFrame = new List<int>();

	private Vector3 currentOrigin;
	private float height;
	private float timer = 0;
	private Transform cTransform;

	[System.Serializable]
	public struct AnimData
	{
		public float fps;
		public float lerpTime;
		public float currentLerpTime;
	}

	[System.Serializable]
	public struct PosAnim
	{
		public bool rotEnable;
		public float rotFPS;
		public bool rotClockwise;

		public bool posEnable;
		public float posAmplitude;
		public float posFPS;
	}
	void Awake()
	{
		if (string.IsNullOrEmpty(modelName))
		{
			Destroy(gameObject);
			return;
		}

		md3Model = ModelsManager.GetModel(modelName, isTransparent);
		if (md3Model == null)
		{
			Debug.LogWarning("Model not found: " + modelName);
			enabled = false;
			return;
		}
		GameObject currentObject;
		//If There are other ModelAnimation, add this as a child
		if (GetComponent<MeshRenderer>() != null)
		{
			currentObject = new GameObject(modelName);
			currentObject.transform.SetParent(transform);
			currentObject.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
		}
		else
			currentObject = gameObject;

		if (md3Model.readyMeshes.Count == 0)
			unityModel = Mesher.GenerateModelFromMeshes(md3Model, currentObject, isTransparent, null, (modelAnim.Count > 0));
		else
			unityModel = Mesher.FillModelFromProcessedData(md3Model, currentObject);

		for (int i = 0; i < md3Model.meshes.Count; i++)
		{
			var modelMesh = md3Model.meshes[i];
			if (modelMesh.numFrames > 1)
				modelAnim.Add(i);
			if (modelMesh.numSkins > 1)
			{
				Texture[] frames = new Texture[modelMesh.numSkins];
				for (int j = 0; j < modelMesh.numSkins; j++)
				{
					string texName = modelMesh.skins[j].name;
					if (TextureLoader.HasTexture(texName))
						frames[j] = TextureLoader.Instance.GetTexture(texName);
					else
					{
						TextureLoader.AddNewTexture(texName, isTransparent);
						frames[j] = TextureLoader.Instance.GetTexture(texName);
					}
				}
				textureAnim.Add(i);
				textureCurrentFrame.Add(0);
				textures.Add(i, frames);
			}
		}
		cTransform = currentObject.transform;
	}

	void Start()
	{
		//If no vertex animation nor texture animation, disable the animation
		if ((modelAnim.Count == 0) && (textureAnim.Count == 0) && (!localAnimation.rotEnable) && (!localAnimation.posEnable))
			enabled = false;

		//Transparent models never cast shadow
		if ((isTransparent) || (!castShadow))
			for (int i = 0; i < md3Model.readyMeshes.Count; i++)
				unityModel.data[i].meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

		currentOrigin = cTransform.position;
		height = currentOrigin.y;
	}

	void OnEnable()
	{
		modelCurrentFrame = 0;
		modelAnimation.currentLerpTime = 0;
		for(int i = 0; i < textureCurrentFrame.Count; i++)
			textureCurrentFrame[i] = 0;
		textureAnimation.currentLerpTime = 0;
	}

	void AnimateModel()
	{
		int currentFrame = modelCurrentFrame;
		int nextFrame = currentFrame + 1;
		float t = modelAnimation.currentLerpTime;
		if (nextFrame >= md3Model.numFrames)
			nextFrame = 0;

		for (int i = 0; i < modelAnim.Count; i++)
		{
			MD3Mesh currentMesh = md3Model.meshes[modelAnim[i]];
			List<Vector3> lerpVertex = new List<Vector3>(currentMesh.numVertices);
			for (int j = 0; j < currentMesh.numVertices; j++)
			{
				Vector3 newVertex = Vector3.Lerp(currentMesh.verts[currentFrame][j], currentMesh.verts[nextFrame][j], t);
				lerpVertex.Add(newVertex);
			}
			unityModel.data[i].meshFilter.mesh.SetVertices(lerpVertex);
			unityModel.data[i].meshFilter.mesh.RecalculateNormals();
		}

		modelAnimation.lerpTime = modelAnimation.fps * Time.deltaTime;
		modelAnimation.currentLerpTime += modelAnimation.lerpTime;

		if (modelAnimation.currentLerpTime >= 1.0f)
		{
			modelAnimation.currentLerpTime -= 1.0f;
			modelCurrentFrame = nextFrame;
		}
	}

	void AnimateTexture()
	{
		textureAnimation.lerpTime = textureAnimation.fps * Time.deltaTime;
		textureAnimation.currentLerpTime += textureAnimation.lerpTime;

		for (int i = 0; i < textureAnim.Count; i++)
		{
			MD3Mesh currentMesh = md3Model.meshes[textureAnim[i]];

			int currentFrame = textureCurrentFrame[i];
			int nextFrame = currentFrame + 1;
			if (nextFrame >= currentMesh.numSkins)
				nextFrame = 0;
			if (textureAnimation.currentLerpTime >= 1.0f)
			{
				unityModel.data[currentMesh.meshNum].meshRenderer.material.mainTexture = textures[i][nextFrame];
				textureCurrentFrame[i] = nextFrame;
			}
		}

		if (textureAnimation.currentLerpTime >= 1.0f)
			textureAnimation.currentLerpTime -= 1.0f;
	}

	void RotateModel()
	{
		if (!localAnimation.rotEnable)
			return;

		cTransform.RotateAround(currentOrigin, localAnimation.rotClockwise? Vector3.up : Vector3.down, Time.deltaTime * localAnimation.rotFPS);
	}

	void MoveModel()
	{
		if (!localAnimation.posEnable)
			return;

		timer += Time.deltaTime * localAnimation.posFPS;
		float offSet = localAnimation.posAmplitude * Mathf.Sin(timer) + height;
		currentOrigin = new Vector3(currentOrigin.x, offSet, currentOrigin.z);
		cTransform.position = currentOrigin;
	}
	void Update()
	{
		if (GameManager.Paused)
			return;

		RotateModel();
		MoveModel();
		AnimateModel();
		AnimateTexture();
	}
}


but make it compatible with MMFPSE for this project:

using UnityEngine;
using Mirror;
using MarsFPSKit; // For Kit_PlayerBehaviour
using MarsFPSKit.Weapons;

namespace MarsFPSKit.Weapons
{
    [RequireComponent(typeof(Collider))]
    [RequireComponent(typeof(NetworkIdentity))]
    public class Kit_WeaponPickup : NetworkBehaviour
    {
        [Tooltip("Weapon prefab or script to give player or bot when picked up")]
        public Kit_ModernWeaponScript weaponScript;

        void Reset()
        {
            var col = GetComponent<Collider>();
            if (col) col.isTrigger = true;
        }

        private void OnTriggerEnter(Collider other)
        {
            Debug.Log($"[{(isServer ? "Server" : "Client")}] {gameObject.name} triggered by {other.gameObject.name} (Layer: {LayerMask.LayerToName(other.gameObject.layer)})");

            // Check that triggering object is on PlayerRoot layer (players and bots)
            int playerRootLayer = LayerMask.NameToLayer("PlayerRoot");
            if (other.gameObject.layer != playerRootLayer)
            {
                Debug.Log($"Ignoring trigger by non-PlayerRoot layer: {LayerMask.LayerToName(other.gameObject.layer)}");
                return;
            }

            // Attempt to find Kit_PlayerBehaviour on collider, parents, or children
            Kit_PlayerBehaviour pb = other.GetComponent<Kit_PlayerBehaviour>()
                                 ?? other.GetComponentInParent<Kit_PlayerBehaviour>()
                                 ?? other.GetComponentInChildren<Kit_PlayerBehaviour>();

            if (pb == null)
            {
                Debug.LogWarning($"No Kit_PlayerBehaviour found on '{other.gameObject.name}' or its parents/children.");
                return;
            }

            Debug.Log($"Player/Bot detected: {pb.name} (IsBot: {pb.isBot})");

            if (!isServer)
            {
                Debug.Log("Not server, skipping pickup logic.");
                return;
            }

            var weaponManager = pb.weaponManager as Kit_ModernWeaponManager;
            if (weaponManager == null)
            {
                Debug.LogWarning($"Player/Bot {pb.name} weapon manager is null or not Kit_ModernWeaponManager.");
                return;
            }

            // Check whether the player or bot already has this weapon; if not, find empty slot or use slot 0
            int slot = weaponManager.HasWeapon(pb, weaponScript.gameGunID);
            if (slot < 0)
            {
                int[] emptySlots = weaponManager.GetSlotsWithEmptyWeapon(pb);
                slot = emptySlots.Length > 0 ? emptySlots[0] : 0;
            }

            Debug.Log($"Assigning weapon ID {weaponScript.gameGunID} to {(pb.isBot ? "bot" : "player")} {pb.name} in slot {slot}.");

            // Replace weapon in slot with full ammo, no attachments by default
            weaponManager.NetworkReplaceWeapon(
                pb,
                slot,
                weaponScript.gameGunID,
                weaponScript.bulletsPerMag,
                weaponScript.bulletsPerMag,
                new int[weaponScript.attachmentSlots.Length]);

            weaponManager.PluginSelectWeapon(pb, slot);

            // Remove the pickup from the world
            NetworkServer.Destroy(gameObject);

            Debug.Log("Weapon pickup consumed and destroyed.");
        }
    }
}

here are additional mmfpse scripts:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Mirror;
using MarsFPSKit.Networking;
using System.Linq;

namespace MarsFPSKit
{
    /// <summary>
    /// All input for the player (e.g. LMB, W,A,S,D, etc) should be stored here, so that bots may use the same scripts.
    /// </summary>
    public class Kit_PlayerInput
    {
        public float hor;
        public float ver;
        public bool crouch;
        public bool sprint;
        public bool jump;
        public bool interact;
        public bool lmb;
        public bool rmb;
        public bool aiming;
        public bool reload;
        public float mouseX;
        public float mouseY;
        public bool leanLeft;
        public bool leanRight;
        public bool thirdPerson;
        public bool flashlight;
        public bool laser;
        public bool[] weaponSlotUses;

        public bool nextWeapon;
        public bool previousWeapon;

        public Vector3 clientCamPos;
        public Vector3 clientCamForward;
    }

    public class Kit_PlayerBehaviour : NetworkBehaviour
    {
        #region Game Information
        [Header("Internal Game Information")]
        [Tooltip("This object contains all game information such as Maps, Game Modes and Weapons")]
        public Kit_GameInformation gameInformation;
        #endregion

        //This section contains everything for the local camera control
        #region Camera Control
        [Header("Camera Control")]
        public Transform playerCameraTransform;
        /// <summary>
        /// Transform that should be used for camera animations from weapons
        /// </summary>
        public Transform playerCameraAnimationTransform;
        /// <summary>
        /// Fall effects should be applied here
        /// </summary>
        public Transform playerCameraFallDownTransform;
        /// <summary>
        /// Hit reactions for camera should be applied here
        /// </summary>
        public Transform playerCameraHitReactionsTransform;
        #endregion

        //This section contains everything for the movement
        #region Movement
        [Header("Movement")]
        public Kit_MovementBase movement; //The system used for movement
        [System.NonSerialized]
        /// <summary>
        /// Unsynced data for movement module.
        /// </summary>
        public Kit_MovementNetworkBase movementNetworkData;

        /// <summary>
        /// Our Character Controller, assign it here
        /// </summary>
        public CharacterController cc;
        /// <summary>
        /// Our footstep audio source
        /// </summary>
        public AudioSource footstepSource;
        /// <summary>
        /// An audio source to play sounds from movement
        /// </summary>
        public AudioSource movementSoundSource;
        #endregion

        //This section contains everything for the Mouse Look
        #region Looking
        [Header("Mouse Look")]
        public Kit_MouseLookBase looking; //The system used for looking
        public Transform mouseLookObject; //The transform used for looking around
        [HideInInspector]
        /// <summary>
        /// This is used by the mouse looking script to apply the recoil and by the weapon script to set the recoil
        /// </summary>
        public Quaternion recoilApplyRotation = Quaternion.identity;
        [HideInInspector]
        public Kit_MouseLookNetworkBase customMouseLookData; //Used to store custom mouse look data
        #endregion

        //This section contains everything for the weapons
        #region Weapons
        [Header("Weapons")]
        public Weapons.Kit_WeaponManagerBase weaponManager; //The system used for weapon management
        [System.NonSerialized]
        /// <summary>
        /// Synced data for weapon manager
        /// </summary>
        public Kit_WeaponManagerNetworkBase weaponManagerNetworkData;
        public Transform weaponsGo;
        /// <summary>
        /// Hit reactions for weapons should be applied here
        /// </summary>
        public Transform weaponsHitReactions;
        #endregion

        #region Player Vitals
        [Header("Player Vitals")]
        public Kit_VitalsBase vitalsManager;
        [System.NonSerialized]
        /// <summary>
        /// Network Data of vitals module
        /// </summary>
        public Kit_VitalsNetworkBase vitalsNetworkData;
        #endregion

        #region Player Name UI
        [Header("Player Name UI")]
        public Kit_PlayerNameUIBase nameManager;
        public object customNameData;
        #endregion

        #region Spawn Protection
        [Header("Spawn Protection")]
        public Kit_SpawnProtectionBase spawnProtection;
        public object customSpawnProtectionData;
        #endregion

        #region Bots
        [Header("Bot Controls")]
        /// <summary>
        /// This module will control the behaviour of the bot
        /// </summary>
        public Kit_PlayerBotControlBase botControls;
        /// <summary>
        /// Use this to store runtime data for bot control
        /// </summary>
        public Kit_PlayerBotControlDataBase botControlsRuntimeData;
        #endregion

        #region Voice
        [Header("Voice Manager")]
        /// <summary>
        /// If this is assigned, your characters can talk!
        /// </summary>
        public Kit_VoiceManagerBase voiceManager;

        /// <summary>
        /// use this  to store runtime data for the voice manager
        /// </summary>
        public object voiceManagerData;
        #endregion

        #region Input Manager
        [Header("Input Manager")]
        public Kit_InputManagerBase inputManager;
        /// <summary>
        /// Use this to store input manager runtime data
        /// </summary>
        public object inputManagerData;
        #endregion

        //This section contains internal variables
        #region Internal Variables
        [SyncVar]
        //Team
        public int myTeam = -1;

        /// <summary>
        /// True if first person view is active on this player. Does not mean that this is our local player. To determine actual perspective call looking.GetPerspective()
        /// </summary>
        public bool isFirstPersonActive
        {
            get
            {
                if (isLocalPlayer || isBeingSpectated)
                {
                    return true;
                }

                return false;
            }
        }

        /// <summary>
        /// Is input enabled?
        /// </summary>
        public bool enableInput
        {
            get
            {
                if (isOwned || isServer && isBot)
                {
                    if (isBot)
                    {
                        return canControlPlayer;
                    }
                    else
                    {
                        return MarsScreen.lockCursor && canControlPlayer;
                    }
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Runtime data from the game mode can be stored here
        /// </summary>
        public object gameModeCustomRuntimeData;

        /// <summary>
        /// Input for the player, only assigned if we are controlling this player or we are the master client
        /// </summary>
        public Kit_PlayerInput input;
        [SyncVar]
        /// <summary>
        /// Is this player being controlled by AI?
        /// </summary>
        public bool isBot;
        [SyncVar]
        /// <summary>
        /// This is either the player or bot id, depending on <see cref="isBot"/>
        /// </summary>
        public uint id;
        /// <summary>
        /// User name fetched in sync setup
        /// </summary>
        public string userName;

        [HideInInspector]
        //Position and rotation are synced by photon transform view
        public bool syncSetup;

        //We cache this value to avoid to calculate it many times
        [HideInInspector]
        public bool canControlPlayer = true;

        /// <summary>
        /// Currently active third person model
        /// </summary>
        public Kit_ThirdPersonPlayerModel thirdPersonPlayerModel;
        [SyncVar]
        /// <summary>
        /// Id of the player model we are currently using! Only assigned in PvP game modes.
        /// </summary>
        public int thirdPersonPlayerModelID;
        /// <summary>
        /// Active customizations
        /// </summary>
        public readonly SyncList<int> thirdPersonPlayerModelCustomizations = new SyncList<int>();
        [SyncVar]
        /// <summary>
        /// Last forward vector from where we were shot
        /// </summary>
        public Vector3 ragdollForward;
        [SyncVar]
        /// <summary>
        /// Last force which we were shot with
        /// </summary>
        public float ragdollForce;
        [SyncVar]
        /// <summary>
        /// Last point from where we were shot
        /// </summary>
        public Vector3 ragdollPoint;
        [SyncVar]
        /// <summary>
        /// Which collider should the force be applied to?
        /// </summary>
        public int ragdollId;
        [HideInInspector]
        /// <summary>
        /// The category to play
        /// </summary>
        public int deathSoundCategory;
        [HideInInspector]
        /// <summary>
        /// The specific sound to play
        /// </summary>
        public int deathSoundID;
        [HideInInspector]
        /// <summary>
        /// Who damaged us? For the assist manager.
        /// </summary>
        public List<AssistedKillData> damagedBy = new List<AssistedKillData>();
        [HideInInspector]
        /// <summary>
        /// Are we  currently being spectated?
        /// </summary>
        public bool isBeingSpectated;
        /// <summary>
        /// Cached mirror variable for use after destroy
        /// </summary>
        private bool wasLocalPlayer;
        #endregion

        public IEnumerator LocalPlayerRoutine()
        {
            while (!thirdPersonPlayerModel) yield return null;

            //Make sure we're not spectating anymore
            if (Kit_IngameMain.instance.spectatorManager)
            {
                if (Kit_IngameMain.instance.spectatorManager.IsCurrentlySpectating())
                {
                    Kit_IngameMain.instance.spectatorManager.EndSpectating();
                }
            }

            Kit_IngameMain.instance.hud.PlayerStart(this);
            //Disable our own name hitbox
            thirdPersonPlayerModel.enemyNameAboveHeadTrigger.enabled = false;
            // Setup voice
            if (voiceManager)
            {
                voiceManager.SetupOwner(this);
            }

            //Setup marker
            if (nameManager)
            {
                nameManager.LocalPlayerGainedControl(this);
            }

            //Assign ourselves
            Kit_IngameMain.instance.myPlayer = this;

            //Lock the cursor
            MarsScreen.lockCursor = true;
            //Close pause menu
            Kit_IngameMain.isPauseMenuOpen = false;
            Kit_IngameMain.instance.SwitchMenu(Kit_IngameMain.instance.ingameFadeId, true);
            yield return new WaitForSeconds(Kit_IngameMain.instance.menuScreens[Kit_IngameMain.instance.currentScreen].fadeOutLength);
            //Move camera to the right position
            Kit_IngameMain.instance.activeCameraTransform = playerCameraTransform;
            //Setup third person model
            thirdPersonPlayerModel.FirstPerson();
            //Tell Minimap we spawned
            if (Kit_IngameMain.instance.minimap)
            {
                Kit_IngameMain.instance.minimap.LocalPlayerSpawned(this);
            }
            //Tell touchscreen
            if (Kit_IngameMain.instance.touchScreenCurrent)
            {
                Kit_IngameMain.instance.touchScreenCurrent.LocalPlayerSpawned(this);
            }
            //Auto spawn system callack
            if (Kit_IngameMain.instance.autoSpawnSystem && Kit_IngameMain.instance.currentPvPGameModeBehaviour)
            {
                Kit_IngameMain.instance.autoSpawnSystem.LocalPlayerSpawned();
            }
            if (Kit_IngameMain.instance.currentPvPGameModeBehaviour)
            {
                //Tell Game Mode
                Kit_IngameMain.instance.currentPvPGameModeBehaviour.OnLocalPlayerSpawned(this);
            }
            else if (Kit_IngameMain.instance.currentPvEGameModeBehaviour)
            {
                //Tell Game Mode
                Kit_IngameMain.instance.currentPvEGameModeBehaviour.OnLocalPlayerSpawned(this);
            }
            //Show HUD
            Kit_IngameMain.instance.hud.SetVisibility(true);
            Kit_IngameMain.instance.pluginOnForceClose.Invoke();
            //Call Plugin
            for (int i = 0; i < gameInformation.plugins.Length; i++)
            {
                gameInformation.plugins[i].LocalPlayerSpawned(this);
            }

            //Call loadout
            Kit_IngameMain.instance.loadoutMenu.LocalPlayerSpawned(this);

            //Take control in modules
            looking.TakeControl(this);
            weaponManager.TakeControl(this);

            double waitCur = 0f;
            double waitTarget = Time.fixedDeltaTime;

            while (true)
            {
                //Fetch input
                inputManager.WriteToPlayerInput(this);
                waitCur += Time.deltaTime;

                if (waitCur >= waitTarget)
                {
                    if (NetworkClient.ready)
                        CmdInput(input, NetworkTime.rtt * 0.75f);

                    waitCur = 0f;
                }

                PredictionInput(input);

                //Send CMD every frame to server with all inputs.
                yield return null;
            }
        }

        IEnumerator ServerRoutine()
        {
            var wait = new WaitForFixedUpdate();

            while (true)
            {
                if (isBot)
                {
                    botControls.WriteToPlayerInput(this);
                }

                if (isServer && !isLocalPlayer && input != null)
                {
                    //We are only executing in this coroutine so that if we receive more commands than expected, it doesn't matter. Execution rate will stay the same.
                    try
                    {
                        movement.AuthorativeInput(this, input, Time.fixedDeltaTime, lastRevertTime);
                        looking.AuthorativeInput(this, input, Time.fixedDeltaTime, lastRevertTime);
                        weaponManager.AuthorativeInput(this, input, Time.fixedDeltaTime, lastRevertTime);
                    }
                    catch (System.Exception e)
                    {
                        Debug.Log("Input execution exception: " + e.ToString());
                    }
                }
                yield return wait;
            }
        }

        #region Mirror Calls
        private bool localOnlyCallsDone;
        void LocalOnlyCalls()
        {
            if (!localOnlyCallsDone)
            {
                if (Kit_IngameMain.instance.currentGameModeType == 2)
                {
                    //Start Spawn Protection
                    if (spawnProtection)
                    {
                        spawnProtection.CustomStart(this);
                    }
                }
                else
                {
                    //Spawn protection not used in SP / Coop
                    spawnProtection = null;
                }

                //Setup marker
                if (nameManager)
                {
                    nameManager.StartRelay(this);
                }

                // Setup voice
                if (voiceManager)
                {
                    voiceManager.SetupOthers(this);
                }

                //Tell Minimap we spawned
                if (Kit_IngameMain.instance.minimap)
                {
                    Kit_IngameMain.instance.minimap.PlayerSpawned(this);
                }

                //Call Plugin
                for (int i = 0; i < gameInformation.plugins.Length; i++)
                {
                    gameInformation.plugins[i].PlayerSpawned(this);
                }

                if (Kit_IngameMain.instance.currentPvPGameModeBehaviour)
                {
                    //Tell Game Mode
                    Kit_IngameMain.instance.currentPvPGameModeBehaviour.OnPlayerSpawned(this);
                }
                else if (Kit_IngameMain.instance.currentPvEGameModeBehaviour)
                {
                    //Tell Game Mode
                    Kit_IngameMain.instance.currentPvEGameModeBehaviour.OnPlayerSpawned(this);
                }

                //Call event system
                Kit_Events.onPlayerSpawned.Invoke(this);

                //Spectator call. Do this last so that this player is completely setup.
                if (Kit_IngameMain.instance.spectatorManager)
                {
                    Kit_IngameMain.instance.spectatorManager.PlayerWasSpawned(this);
                }

                localOnlyCallsDone = true;
            }
        }

        public override void OnStartServer()
        {
            if (isBot)
            {
                //Fetch bot's username
                userName = Kit_IngameMain.instance.currentBotManager.GetBotWithID(id).name;
            }
            else
            {
                //Fetch player's username
                userName = Kit_NetworkPlayerManager.instance.GetPlayerById(id).name;
            }

            //Set name
            gameObject.name = userName;

            if (isBot)
            {
                //Check for game mode override
                if (Kit_IngameMain.instance.currentPvEGameModeBehaviour && Kit_IngameMain.instance.currentPvEGameModeBehaviour.botControlOverride)
                {
                    botControls = Kit_IngameMain.instance.currentPvEGameModeBehaviour.botControlOverride;
                }
                if (Kit_IngameMain.instance.currentPvPGameModeBehaviour && Kit_IngameMain.instance.currentPvPGameModeBehaviour.botControlOverride)
                {
                    botControls = Kit_IngameMain.instance.currentPvPGameModeBehaviour.botControlOverride;
                }
                input = new Kit_PlayerInput();
                Kit_BotManager manager = FindAnyObjectByType<Kit_BotManager>();
                manager.AddActiveBot(this);
                //Initialize bot input
                botControls.InitializeControls(this);
            }

            if (!thirdPersonPlayerModel) CreatePlayerModel();

            //Add us to player list
            if (!Kit_IngameMain.instance.allActivePlayers.Contains(this)) Kit_IngameMain.instance.allActivePlayers.Add(this);

            //Initialize input module
            input = new Kit_PlayerInput();
            inputManager.InitializeServer(this);
            //Initialize looking
            looking.InitializeServer(this);
            //Initialize movement
            movement.InitializeServer(this);
            //Setup Vitals
            vitalsManager.InitializeServer(this);
            //Setup weapon manager
            weaponManager.InitializeServer(this);

            LocalOnlyCalls();

            syncSetup = true;

            StartCoroutine(ServerRoutine());
        }

        public override void OnStartClient()
        {
            StartCoroutine(OnStartClientWaitForModules());
        }

        IEnumerator OnStartClientWaitForModules()
        {
            while (!Kit_IngameMain.instance) yield return null;

            //Wait for these modules to arrive before we try to setup our client. The server spawns them simultaneously(-ish) with the player, they may arrive out of order or one frame later
            while (!movementNetworkData || !vitalsNetworkData || !weaponManagerNetworkData)
            {
                if (!movementNetworkData) movementNetworkData = FindObjectsOfType<Kit_MovementNetworkBase>().Where(x => x.ownerPlayerNetworkId == netId).FirstOrDefault();
                if (!customMouseLookData) customMouseLookData = FindObjectsOfType<Kit_MouseLookNetworkBase>().Where(x => x.ownerPlayerNetworkId == netId).FirstOrDefault();
                if (!vitalsNetworkData) vitalsNetworkData = FindObjectsOfType<Kit_VitalsNetworkBase>().Where(x => x.ownerPlayerNetworkId == netId).FirstOrDefault();
                if (!weaponManagerNetworkData)
                {
                    weaponManagerNetworkData = FindObjectsOfType<Kit_WeaponManagerNetworkBase>().Where(x => x.ownerPlayerNetworkId == netId).FirstOrDefault();

                    if (weaponManagerNetworkData)
                    {
                        weaponManagerNetworkData.ownerPlayer = this;
                    }
                }

                yield return null;
            }

            if (isBot)
            {
                while (!Kit_IngameMain.instance.currentBotManager) yield return null;
                while (Kit_IngameMain.instance.currentBotManager.GetBotWithID(id) == null) yield return null;

                //Fetch bot's username
                userName = Kit_IngameMain.instance.currentBotManager.GetBotWithID(id).name;
            }
            else
            {
                //Fetch player's username
                userName = Kit_NetworkPlayerManager.instance.GetPlayerById(id).name;
            }

            //Set name
            gameObject.name = userName;

            if (!thirdPersonPlayerModel) CreatePlayerModel();

            //Add us to player list
            if (!Kit_IngameMain.instance.allActivePlayers.Contains(this)) Kit_IngameMain.instance.allActivePlayers.Add(this);

            //Initialize input module
            input = new Kit_PlayerInput();
            inputManager.InitializeClient(this);
            //Initialize looking
            looking.InitializeClient(this);
            //Movement
            movement.InitializeClient(this);
            //Setup Vitals
            vitalsManager.InitializeClient(this);
            //Setup weapon manager
            weaponManager.InitializeClient(this);

            LocalOnlyCalls();

            syncSetup = true;
        }

        public override void OnStartLocalPlayer()
        {
            StartCoroutine(LocalPlayerRoutine());

            wasLocalPlayer = true;
        }

        public override void OnStopLocalPlayer()
        {
            base.OnStopLocalPlayer();
        }
        #endregion

        #region Authorative Gameplay
        public void PredictionInput(Kit_PlayerInput input)
        {
            movement.PredictionInput(this, input, Time.deltaTime);
            looking.PredictionInput(this, input, Time.deltaTime);
            weaponManager.PredictionInput(this, input, Time.deltaTime);
        }

        private double lastRevertTime;

        [Command]
        public void CmdInput(Kit_PlayerInput input, double revertTime, NetworkConnectionToClient sender = null)
        {
            /*
            var revertTime = connectionToClient.remoteTimeline - connectionToClient.remoteTimeStamp;

            if (!isOwned)
                Debug.Log(revertTime);

            if (revertTime < 0) revertTime = 0;
            */

            this.input = input;
            lastRevertTime = revertTime;
        }
        #endregion

        #region Unity Calls
        void Start()
        {

        }

        private bool wasOnDestroyCalled;

        void OnDestroy()
        {
            wasOnDestroyCalled = true;

            if (Kit_IngameMain.executeDestroyActions)
            {
                //Clean up our additional data
                if (isServer)
                {
                    if (movementNetworkData) NetworkServer.Destroy(movementNetworkData.gameObject);
                    if (weaponManagerNetworkData) NetworkServer.Destroy(weaponManagerNetworkData.gameObject);
                    if (vitalsNetworkData) NetworkServer.Destroy(vitalsNetworkData.gameObject);
                    if (customMouseLookData) NetworkServer.Destroy(customMouseLookData.gameObject);
                    if (isBot) botControls.OnDestroyRelay(this);
                }

                //Hide HUD if we were killed
                if (isLocalPlayer)
                {
                    Kit_IngameMain.instance.hud.SetVisibility(false);
                }

                //Release marker
                if (nameManager)
                {
                    nameManager.OnDestroyRelay(this);
                }

                if (botControlsRuntimeData)
                {
                    Destroy(botControlsRuntimeData.gameObject);
                }

                if (wasLocalPlayer)
                {
                    //Tell minimap
                    if (Kit_IngameMain.instance.minimap)
                    {
                        Kit_IngameMain.instance.minimap.LocalPlayerDied(this);
                    }
                    //Tell touchscreen
                    if (Kit_IngameMain.instance.touchScreenCurrent)
                    {
                        Kit_IngameMain.instance.touchScreenCurrent.LocalPlayerDied(this);
                    }
                    //Auto spawn system callack
                    if (Kit_IngameMain.instance.autoSpawnSystem && Kit_IngameMain.instance.currentPvPGameModeBehaviour)
                    {
                        Kit_IngameMain.instance.autoSpawnSystem.LocalPlayerDied();
                    }
                    if (Kit_IngameMain.instance.currentPvPGameModeBehaviour)
                    {
                        //Tell Game Mode
                        Kit_IngameMain.instance.currentPvPGameModeBehaviour.OnLocalPlayerDestroyed(this);
                    }
                    else if (Kit_IngameMain.instance.currentPvEGameModeBehaviour)
                    {
                        //Tell Game Mode
                        Kit_IngameMain.instance.currentPvEGameModeBehaviour.OnLocalPlayerDestroyed(this);
                    }
                    //Tell HUD
                    Kit_IngameMain.instance.hud.PlayerEnd(this);
                    //Call Plugin
                    for (int i = 0; i < gameInformation.plugins.Length; i++)
                    {
                        gameInformation.plugins[i].LocalPlayerDied(this);
                    }
                }
                else
                {
                    //Tell minimap
                    if (Kit_IngameMain.instance.minimap)
                    {
                        Kit_IngameMain.instance.minimap.PlayerDied(this);
                    }
                    if (Kit_IngameMain.instance.currentPvPGameModeBehaviour)
                    {
                        //Tell Game Mode
                        Kit_IngameMain.instance.currentPvPGameModeBehaviour.OnPlayerDestroyed(this);
                    }
                    else if (Kit_IngameMain.instance.currentPvEGameModeBehaviour)
                    {
                        //Tell Game Mode
                        Kit_IngameMain.instance.currentPvEGameModeBehaviour.OnPlayerDestroyed(this);
                    }
                    //Call Plugin
                    for (int i = 0; i < gameInformation.plugins.Length; i++)
                    {
                        gameInformation.plugins[i].PlayerDied(this);
                    }
                }

                //Spectator call
                if (Kit_IngameMain.instance.spectatorManager)
                {
                    Kit_IngameMain.instance.spectatorManager.PlayerWasKilled(this);
                }

                //Make sure the camera never gets destroyed
                if (Kit_IngameMain.instance.activeCameraTransform == playerCameraTransform)
                {
                    Kit_IngameMain.instance.activeCameraTransform = Kit_IngameMain.instance.spawnCameraPosition;
                    //Set Fov
                    Kit_IngameMain.instance.mainCamera.fieldOfView = Kit_GameSettings.baseFov;
                }

                if (canControlPlayer)
                {
                    if (thirdPersonPlayerModel)
                    {
                        //Unparent sounds
                        thirdPersonPlayerModel.soundFire.transform.parent = null;
                        if (thirdPersonPlayerModel.soundFire.clip)
                        {
                            Destroy(thirdPersonPlayerModel.soundFire.gameObject, thirdPersonPlayerModel.soundFire.clip.length);
                        }
                        else
                        {
                            Destroy(thirdPersonPlayerModel.soundFire.gameObject, 1f);
                        }

                        //Setup ragdoll
                        thirdPersonPlayerModel.CreateRagdoll();
                    }
                }

                //Call event system
                Kit_Events.onPlayerDied.Invoke(this);

                //Remove us from list
                Kit_IngameMain.instance.allActivePlayers.Remove(this);
            }
        }

        void Update()
        {
            if (syncSetup && thirdPersonPlayerModel)
            {
                //Everyone update dcalls
                //Weapon manager update
                weaponManager.CustomUpdate(this, lastRevertTime);
                //looking.CalculateLookUpdate(this);
                //movement.CalculateMovementUpdate(this);
                vitalsManager.CustomUpdate(this);


                //Not controller update calls
                if (!isServer && !isLocalPlayer)
                {
                    movement.NotControllerUpdate(this);
                    looking.NotControllerUpdate(this);
                }
                else
                {

                }

                movement.CalculateFootstepsUpdate(this);

                if (isLocalPlayer)
                {
                    //Call Plugin
                    for (int i = 0; i < gameInformation.plugins.Length; i++)
                    {
                        gameInformation.plugins[i].LocalPlayerUpdate(this);
                    }

                    Kit_IngameMain.instance.hud.PlayerUpdate(this);
                }

                //Call Plugin
                for (int i = 0; i < gameInformation.plugins.Length; i++)
                {
                    gameInformation.plugins[i].PlayerUpdate(this);
                }

                if (isFirstPersonActive)
                {
                    movement.Visuals(this);
                    looking.Visuals(this);
                }

                if (isOwned || isServer)
                {
                    if (Kit_IngameMain.instance.currentPvPGameModeBehaviour)
                    {
                        //Update control value
                        canControlPlayer = Kit_IngameMain.instance.currentPvPGameModeBehaviour.CanControlPlayer();
                    }
                    else if (Kit_IngameMain.instance.currentPvEGameModeBehaviour)
                    {
                        //Update control value
                        canControlPlayer = Kit_IngameMain.instance.currentPvEGameModeBehaviour.CanControlPlayer();
                    }
                }

                if (isServer || isFirstPersonActive)
                {
                    //Update spawn protection
                    if (spawnProtection)
                    {
                        spawnProtection.CustomUpdate(this);
                    }
                }

                if (isServer)
                {
                    //Update voice scanner
                    if (voiceManager)
                    {
                        voiceManager.ScanForEnemies(this);
                    }
                }
            }
        }

        void LateUpdate()
        {
            if (syncSetup)
            {
                //Weapon manager update
                weaponManager.CustomLateUpdate(this, lastRevertTime);

                if (!isFirstPersonActive)
                {
                    if (Kit_IngameMain.instance.currentGameModeType == 2 && Kit_IngameMain.instance.currentPvPGameModeBehaviour.AreWeEnemies(isBot, id))
                    {
                        if (nameManager)
                        {
                            nameManager.UpdateEnemy(this);
                        }
                    }
                    else
                    {
                        if (nameManager)
                        {
                            nameManager.UpdateFriendly(this);
                        }
                        if (Kit_IngameMain.instance.minimap)
                        {
                            Kit_IngameMain.instance.minimap.PlayerFriendlyUpdate(this);
                        }
                    }
                }
            }

            //If we are the controller, update everything
            if (isServer || isLocalPlayer)
            {
                looking.CalculateLookLateUpdate(this);
            }
        }

        private void FixedUpdate()
        {
            if (isLocalPlayer && !isServer)
            {
                movement.CreateSnapshot(this);
            }

            if (syncSetup)
            {
                //Weapon manager update
                weaponManager.CustomFixedUpdate(this, lastRevertTime);
                movement.OnFixedUpdate(this);
            }
        }

        void OnControllerColliderHit(ControllerColliderHit hit)
        {
            //Relay to movement script
            movement.OnControllerColliderHitRelay(this, hit);
            //Relay to mouse look script
            looking.OnControllerColliderHitRelay(this, hit);
            //Relay to weapon manager
            weaponManager.OnControllerColliderHitRelay(this, hit);
        }

        void OnTriggerEnter(Collider col)
        {
            if (syncSetup)
            {
                if (isServer)
                {
                    //Check for amo
                    if (col.transform.root.GetComponent<Kit_AmmoPickup>())
                    {
                        Kit_AmmoPickup pickup = col.transform.root.GetComponent<Kit_AmmoPickup>();
                        //Relay to weapon manager
                        weaponManager.OnAmmoPickup(this, pickup);
                        //Destroy
                        pickup.PickedUp();
                    }
                    else if (col.transform.root.GetComponent<Kit_HealthPickup>())
                    {
                        Kit_HealthPickup pickup = col.transform.root.GetComponent<Kit_HealthPickup>();
                        //Relay to health
                        vitalsManager.ApplyHeal(this, pickup.healthRestored);
                        //Destroy
                        pickup.PickedUp();
                    }
                }

                //Relay to weapon manager
                weaponManager.OnTriggerEnterRelay(this, col);

                //Relay to movement
                movement.OnTriggerEnterRelay(this, col);
            }
        }

        void OnTriggerExit(Collider col)
        {
            if (syncSetup)
            {
                //Relay to weapon manager
                weaponManager.OnTriggerExitRelay(this, col);

                //Relay to movement
                movement.OnTriggerExitRelay(this, col);
            }
        }
        #endregion

        #region Custom Calls
        private void CreatePlayerModel()
        {
            if (!thirdPersonPlayerModel)
            {
                int[] playerModelCustomizations = new int[0];
                GameObject go = null;

                switch (Kit_IngameMain.instance.currentGameModeType)
                {
                    case 0:
                        //Get player model
                        PlayerModelConfig pmcSp = Kit_IngameMain.instance.currentPvEGameModeBehaviour.GetPlayerModel(this);
                        playerModelCustomizations = pmcSp.customization;

                        //Set up player model
                        //Instantiate one random player model for chosen team
                        go = Instantiate(pmcSp.information.prefab, transform, false);
                        //Assign
                        thirdPersonPlayerModel = go.GetComponent<Kit_ThirdPersonPlayerModel>();
                        //Set information
                        thirdPersonPlayerModel.information = pmcSp.information;
                        break;
                    case 1:
                        //Get player model
                        PlayerModelConfig pmcCoop = Kit_IngameMain.instance.currentPvEGameModeBehaviour.GetPlayerModel(this);
                        playerModelCustomizations = pmcCoop.customization;

                        //Set up player model
                        //Instantiate one random player model for chosen team
                        go = Instantiate(pmcCoop.information.prefab, transform, false);
                        //Assign
                        thirdPersonPlayerModel = go.GetComponent<Kit_ThirdPersonPlayerModel>();
                        //Set information
                        thirdPersonPlayerModel.information = pmcCoop.information;
                        break;
                    case 2:
                        //Set up player model
                        //Instantiate one random player model for chosen team
                        go = Instantiate(Kit_IngameMain.instance.gameInformation.allPvpTeams[myTeam].playerModels[thirdPersonPlayerModelID].prefab, transform, false);
                        //Assign
                        thirdPersonPlayerModel = go.GetComponent<Kit_ThirdPersonPlayerModel>();
                        //And cache information
                        thirdPersonPlayerModel.information = Kit_IngameMain.instance.gameInformation.allPvpTeams[myTeam].playerModels[thirdPersonPlayerModelID];
                        break;
                }

                //Reset scale
                go.transform.localScale = Vector3.one;
                //Setup
                thirdPersonPlayerModel.SetupModel(this);
                //Setup Customization
                thirdPersonPlayerModel.SetCustomizations(playerModelCustomizations, this);
                //Make it third person initially
                thirdPersonPlayerModel.ThirdPerson();

                if (thirdPersonPlayerModel.firstPersonArmsPrefab.Count == 0)
                {
                    Debug.LogWarning("WARNING: Player Model does not have ANY first person arms prefabs assigned! Game might be broken! To fix, assign first person arms prefabs to this player model with your key (Default Key: Kit)", thirdPersonPlayerModel.gameObject);
                }
            }
        }

        public void ServerDamage(float dmg, int gunID, Vector3 shotPos, Vector3 forward, float force, Vector3 hitPos, int id, bool botShot, uint idWhoShot)
        {
            //Damage is server authorative now
            if (isServer)
            {
                if (Kit_IngameMain.instance.assistManager)
                {
                    Kit_IngameMain.instance.assistManager.PlayerDamaged(botShot, idWhoShot, this, dmg);
                }

                ragdollForce = force;
                ragdollForward = forward;
                ragdollPoint = hitPos;
                ragdollId = id;
                deathSoundCategory = gameInformation.allWeapons[gunID].deathSoundCategory;
                if (voiceManager)
                {
                    deathSoundID = voiceManager.GetDeathSoundID(this, deathSoundCategory);
                }

                //Relay to the assigned manager
                vitalsManager.ApplyDamage(this, dmg, botShot, idWhoShot, gunID, shotPos);

                RpcDamageVisuals(dmg, shotPos);
            }
        }

        public void ServerDamage(float dmg, string deathCause, Vector3 shotPos, Vector3 forward, float force, Vector3 hitPos, int id, bool botShot, uint idWhoShot)
        {
            if (isServer)
            {
                if (Kit_IngameMain.instance.assistManager)
                {
                    Kit_IngameMain.instance.assistManager.PlayerDamaged(botShot, idWhoShot, this, dmg);
                }

                //Apply damage
                vitalsManager.ApplyDamage(this, dmg, botShot, idWhoShot, deathCause, shotPos);

                ragdollForce = force;
                ragdollForward = forward;
                ragdollId = id;
                ragdollPoint = hitPos;
                deathSoundCategory = gameInformation.allWeapons[0].deathSoundCategory;

                RpcDamageVisuals(dmg, shotPos);
            }
        }

        public void ServerBlind(float time, int gunID, Vector3 shotPos, bool botShot, uint idWhoShot)
        {
            if (isFirstPersonActive)
            {
                RpcBlind(time, gunID, shotPos, botShot, idWhoShot);
            }
        }

        public void ApplyFallDamage(float dmg)
        {
            if (isServer)
            {
                vitalsManager.ApplyFallDamage(this, dmg);
            }
        }

        [Command]
        public void CmdSuicide()
        {
            if (isServer)
            {
                vitalsManager.Suicide(this);
            }
        }

        /// <summary>
        /// Kill the player by cause.
        /// </summary>
        /// <param name="cause"></param>
        public void Die(int cause)
        {
            if (isServer || isLocalPlayer)
            {
                //Tell weapon manager
                weaponManager.PlayerDead(this);
            }

            if (isServer)
            {
                //Tell clients for the killfeed
                Kit_IngameMain.instance.ServerDeathInfo(isBot, id, isBot, id, cause, thirdPersonPlayerModelID, ragdollId);
                Kit_IngameMain.instance.RpcDeathInfo(isBot, id, isBot, id, cause, thirdPersonPlayerModelID, ragdollId);

                if (Kit_IngameMain.instance.assistManager)
                {
                    Kit_IngameMain.instance.assistManager.PlayerKilled(isBot, id, this);
                }

                NetworkServer.Destroy(gameObject);
            }
        }

        public void Die(bool botShot, uint killer, int gunID)
        {
            if (isServer || isLocalPlayer)
            {
                //Tell weapon manager
                weaponManager.PlayerDead(this);
            }

            if (isServer)
            {
                //Tell clients for the killfeed
                Kit_IngameMain.instance.ServerDeathInfo(botShot, killer, isBot, id, gunID, thirdPersonPlayerModelID, ragdollId);
                Kit_IngameMain.instance.RpcDeathInfo(botShot, killer, isBot, id, gunID, thirdPersonPlayerModelID, ragdollId);

                if (Kit_IngameMain.instance.assistManager)
                {
                    Kit_IngameMain.instance.assistManager.PlayerKilled(botShot, killer, this);
                }

                NetworkServer.Destroy(gameObject);
            }
        }

        public void Die(bool botShot, uint killer, string cause)
        {
            if (isServer || isLocalPlayer)
            {
                //Tell weapon manager
                weaponManager.PlayerDead(this);
            }

            if (isServer)
            {
                //Tell clients for the killfeed
                Kit_IngameMain.instance.ServerDeathInfo(botShot, killer, isBot, id, cause, thirdPersonPlayerModelID, ragdollId);
                Kit_IngameMain.instance.RpcDeathInfo(botShot, killer, isBot, id, cause, thirdPersonPlayerModelID, ragdollId);

                if (Kit_IngameMain.instance.assistManager)
                {
                    Kit_IngameMain.instance.assistManager.PlayerKilled(botShot, killer, this);
                }

                NetworkServer.Destroy(gameObject);
            }
        }
        #endregion

        #region Server Calls
        /// <summary>
        /// Server tells us that we hit something.
        /// 0 = Normal, 1 = Spawn protection
        /// Chose byte so you can add more types.
        /// </summary>
        /// <param name="type"></param>
        [TargetRpc]
        public void TargetHitMarker(byte type)
        {
            if (isFirstPersonActive)
            {
                switch (type)
                {
                    case 0:
                        Kit_IngameMain.instance.hud.DisplayHitmarker();
                        break;
                    case 1:
                        Kit_IngameMain.instance.hud.DisplayHitmarkerSpawnProtected();
                        break;
                }
            }
        }

        [ClientRpc(includeOwner = true)]
        public void ClientImpactProcess(Vector3 pos, Vector3 normal, string material)
        {
            //Relay to impact processor
            Kit_IngameMain.instance.impactProcessor.ProcessImpact(pos, normal, material);
        }
        #endregion

        #region RPCs
        [ClientRpc] //Client RPC for spectating purposes
        public void RpcBlind(float time, int gunID, Vector3 shotPos, bool botShot, uint idWhoShot)
        {
            if (isFirstPersonActive)
            {
                Kit_IngameMain.instance.hud.DisplayBlind(time);
                //Tell HUD
                Kit_IngameMain.instance.hud.DisplayShot(shotPos);
            }
        }

        [ClientRpc] //Client RPC for spectating purposes
        public void RpcDamageVisuals(float dmg, Vector3 shotPos)
        {
            if (isFirstPersonActive)
            {
                //Tell HUD
                Kit_IngameMain.instance.hud.DisplayShot(shotPos);
            }
        }

        //If we fire using a semi auto weapon, this is called

        [ClientRpc]
        public void RpcWeaponSemiFireNetwork()
        {
            //Relay to weapon manager
            weaponManager.NetworkSemiRPCReceived(this);
        }

        [ClientRpc]
        //If we fire using a bolt action weapon, this is called
        public void RpcWeaponBoltActionFireNetwork(int state)
        {
            //Relay to weapon manager
            weaponManager.NetworkBoltActionRPCReceived(this, state);
        }

        [ClientRpc]
        public void RpcWeaponBurstFireNetwork(int burstLength)
        {
            //Relay to weapon manager
            weaponManager.NetworkBurstRPCReceived(this, burstLength);
        }

        [ClientRpc]
        public void RpcWeaponFirePhysicalBulletOthers(Vector3 pos, Vector3 dir)
        {
            //Relay to weapon manager
            weaponManager.NetworkPhysicalBulletFired(this, pos, dir);
        }

        //When we reload, this is called
        [ClientRpc]
        public void RpcWeaponReloadNetwork(bool empty)
        {
            //Reload to weapon manager
            weaponManager.NetworkReloadRPCReceived(this, empty);
        }

        //When a procedural reload occurs, this will be called with the correct stage
        [ClientRpc]
        public void RpcWeaponProceduralReloadNetwork(int stage)
        {
            //Relay to weapon manager
            weaponManager.NetworkProceduralReloadRPCReceived(this, stage);
        }

        [ClientRpc]
        public void RpcMeleeStabNetwork(int state, int slot)
        {
            //Send to player model
            thirdPersonPlayerModel.PlayMeleeAnimation(0, state);
            //Weapon Manager
            weaponManager.NetworkMeleeStabRPCReceived(this, state, slot);
        }

        [ClientRpc]
        public void RpcMeleeChargeNetwork(int id, int slot)
        {
            //Send to player model
            thirdPersonPlayerModel.PlayMeleeAnimation(1, id);
            //Weapon Manager
            weaponManager.NetworkMeleeChargeRPCReceived(this, id, slot);
        }

        [ClientRpc]
        public void RpcMeleeHealNetwork(int id)
        {
            //Send to playyer model
            thirdPersonPlayerModel.PlayMeleeAnimation(2, id);
            //Weapon Manager
            weaponManager.NetworkMeleeHealRPCReceived(this, id);
        }

        [ClientRpc]
        public void RpcGrenadePullPinNetwork()
        {
            //Relay
            weaponManager.NetworkGrenadePullPinRPCReceived(this);
        }

        [ClientRpc]
        public void RpcGrenadeThrowNetwork()
        {
            //Relay
            weaponManager.NetworkGrenadeThrowRPCReceived(this);
        }

        public void WeaponRestockAll(bool allWeapons)
        {
            if (isServer)
            {
                //Relay
                weaponManager.RestockAmmo(this, allWeapons);
            }
        }


        public void ReplaceWeapon(int slot, int weapon, int bulletsLeft, int bulletsLeftToReload, int[] attachments)
        {
            if (isServer)
            {
                //Relay to weapon manager
                weaponManager.NetworkReplaceWeapon(this, slot, weapon, bulletsLeft, bulletsLeftToReload, attachments);
            }
            else
            {
                Debug.LogError("Server only function called on non-server!");
            }
        }


        [ClientRpc]
        public void RpcPlayVoiceLine(int catId, int id)
        {
            if (voiceManager)
            {
                voiceManager.PlayVoiceRpcReceived(this, catId, id);
            }
        }


        [ClientRpc]
        public void RpcPlayVoiceLine(int catId, int id, int idTwo)
        {
            if (voiceManager)
            {
                voiceManager.PlayVoiceRpcReceived(this, catId, id, idTwo);
            }
        }

        [ClientRpc]
        public void RpcMovementPlaySound(int id, int id2, int arrayID)
        {
            //Relay to movement
            movement.PlaySound(this, id, id2, arrayID);
        }

        [ClientRpc]
        public void RpcMovementPlayAnimation(int id, int id2)
        {
            //Relay to movement
            movement.PlayAnimation(this, id, id2);
        }
        #endregion

        #region Spectating
        /// <summary>
        /// Begin spectating this player
        /// </summary>
        public void OnSpectatingStart()
        {
            //Move camera to the right position
            Kit_IngameMain.instance.activeCameraTransform = playerCameraTransform;
            //Set bool
            isBeingSpectated = true;

            if (looking.GetPerspective(this) == Kit_GameInformation.Perspective.FirstPerson)
            {
                //Hide third person object
                thirdPersonPlayerModel.FirstPerson();
            }
            else
            {

            }

            if (nameManager)
            {
                nameManager.OnDestroyRelay(this);
            }

            //Call Weapon Manager
            weaponManager.BeginSpectating(this);

            //HUD
            Kit_IngameMain.instance.hud.SetVisibility(true);

            //Minimap
            if (Kit_IngameMain.instance.minimap)
            {
                Kit_IngameMain.instance.minimap.LocalPlayerSpawned(this);
            }
        }

        /// <summary>
        /// End spectating this player
        /// </summary>
        public void OnSpectatingEnd()
        {
            //Set bool
            isBeingSpectated = false;
            //Third Person
            thirdPersonPlayerModel.ThirdPerson();

            //Weapon Manager
            weaponManager.EndSpectating(this);

            if (nameManager && !wasOnDestroyCalled)
            {
                nameManager.StartRelay(this);
            }

            //HUD
            Kit_IngameMain.instance.hud.SetVisibility(false);

            //Minimap
            if (Kit_IngameMain.instance.minimap)
            {
                Kit_IngameMain.instance.minimap.LocalPlayerDied(this);
            }
        }
        #endregion
    }
}
using Mirror;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine;

namespace MarsFPSKit
{
    namespace Weapons
    {
        /// <summary>
        /// Input that is allowed for the given weapon slot
        /// </summary>
        [System.Serializable]
        public class WeaponManagerSlot
        {
            /// <summary>
            /// Can weapons in this slot be equipped?
            /// </summary>
            public bool enableEquipping = true;
            /// <summary>
            /// ID of <see cref="Kit_PlayerInput.weaponSlotUses"/>
            /// </summary>
            public int equippingInputID;
            /// <summary>
            /// Can weapons in this slot be quick used (e.g. quick grenades, quick knife)
            /// </summary>
            public bool enableQuickUse;
            /// <summary>
            /// ID of <see cref="Kit_PlayerInput.weaponSlotUses"/>
            /// </summary>
            public int quickUseInputID;
        }

        public enum DeadDrop { None, Selected, All }

        /// <summary>
        /// This is a modern, generic weapon manager. Weapons are put away and then taken out, like in COD or Battlefield. Supports an "infinite" amount of weapons
        /// </summary>
        [CreateAssetMenu(menuName = ("MarsFPSKit/Weapons/Modern Weapon Manager"))]
        public class Kit_ModernWeaponManager : Kit_WeaponManagerBase
        {
            public WeaponManagerSlot[] slotConfiguration;
            /// <summary>
            /// Main drop prefab!
            /// </summary>
            public GameObject dropPrefab;
            /// <summary>
            /// Layers that will be hit by the pickup raycast
            /// </summary>
            public LayerMask pickupLayers;
            /// <summary>
            /// Distance for the pickup raycast
            /// </summary>
            public float pickupDistance = 3f;
            /// <summary>
            /// Which weapons should be dropped upon death?
            /// </summary>
            public DeadDrop uponDeathDrop;
            /// <summary>
            /// How fast does the weapon position change?
            /// </summary>
            public float weaponPositionChangeSpeed = 5f;
            /// <summary>
            /// Can we switch while we are running?
            /// </summary>
            public bool allowSwitchingWhileRunning;
            /// <summary>
            /// Can we do quick use while running?
            /// </summary>
            public bool allowQuickUseWhileRunning;

            public override void OnWeaponsInUseSyncCallback(Kit_PlayerBehaviour pb, SyncList<uint>.Operation op, int itemIndex, uint item)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                switch (op)
                {
                    case SyncList<uint>.Operation.OP_SET:
                        //Cleanup old data first
                        for (int i = 0; i < runtimeData.weaponsInUseDataObjects[itemIndex].instantiatedObjects.Count; i++)
                        {
                            if (runtimeData.weaponsInUseDataObjects[itemIndex].instantiatedObjects[i])
                            {
                                NetworkIdentity nid = runtimeData.weaponsInUseDataObjects[itemIndex].instantiatedObjects[i].GetComponentInChildren<NetworkIdentity>();

                                if (nid)
                                {
                                    if (NetworkServer.active)
                                    {
                                        NetworkServer.Destroy(nid.gameObject);
                                    }
                                }
                                else
                                {
                                    Destroy(runtimeData.weaponsInUseDataObjects[itemIndex].instantiatedObjects[i]);
                                }
                            }
                        }

                        runtimeData.weaponsInUseDataObjects[itemIndex].instantiatedObjects.Clear();

                        //We may need to wait a bit for the network
                        Kit_ScriptableObjectCoroutineHelper.instance.StartCoroutine(Kit_ScriptableObjectCoroutineHelper.instance.NetworkReplaceWeapon(pb, runtimeData, itemIndex, runtimeData.weaponsInUseSync[itemIndex]));
                        break;
                }
            }

            public override void SetupSpawnData(Kit_PlayerBehaviour pb, Loadout loadout, NetworkConnectionToClient sender)
            {
                GameObject data = Instantiate(networkData, Vector3.zero, Quaternion.identity);
                Kit_ModernWeaponManagerNetworkData runtimeData = data.GetComponent<Kit_ModernWeaponManagerNetworkData>();
                pb.weaponManagerNetworkData = runtimeData;

                for (int i = 0; i < loadout.loadoutWeapons.Length; i++)
                {
                    GameObject spawnObject = Kit_IngameMain.instance.gameInformation.allWeapons[loadout.loadoutWeapons[i].weaponID].SetupSpawnData(pb, i, loadout.loadoutWeapons[i]);
                    NetworkServer.Spawn(spawnObject, sender);
                    NetworkIdentity id = spawnObject.GetComponent<NetworkIdentity>();

                    //Add to our weapon list
                    runtimeData.weaponsInUseSync.Add(id.netId);
                }
            }

            public override void SetupSpawnData(Kit_PlayerBehaviour pb, Loadout loadout)
            {
                GameObject data = Instantiate(networkData, Vector3.zero, Quaternion.identity);
                Kit_ModernWeaponManagerNetworkData runtimeData = data.GetComponent<Kit_ModernWeaponManagerNetworkData>();
                pb.weaponManagerNetworkData = runtimeData;

                for (int i = 0; i < loadout.loadoutWeapons.Length; i++)
                {
                    GameObject spawnObject = Kit_IngameMain.instance.gameInformation.allWeapons[loadout.loadoutWeapons[i].weaponID].SetupSpawnData(pb, i, loadout.loadoutWeapons[i]);
                    NetworkServer.Spawn(spawnObject);
                    NetworkIdentity id = spawnObject.GetComponent<NetworkIdentity>();

                    //Add to our weapon list
                    runtimeData.weaponsInUseSync.Add(id.netId);
                }
            }

            public override void InitializeServer(Kit_PlayerBehaviour pb)
            {
                //Is created in SetupSpawnData
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                runtimeData.ownerPlayerNetworkId = pb.netId;
                //Spawn it here
                if (pb.isBot)
                    NetworkServer.Spawn(runtimeData.gameObject);
                else
                    NetworkServer.Spawn(runtimeData.gameObject, pb.gameObject);

                //Setup input IDs
                runtimeData.lastInputIDs = new bool[pb.input.weaponSlotUses.Length];

                //Initially build list
                runtimeData.weaponsInUseDataObjects = new Kit_WeaponRuntimeDataBase[runtimeData.weaponsInUseSync.Count];

                for (int i = 0; i < runtimeData.weaponsInUseSync.Count; i++)
                {
                    NetworkIdentity id = null;

                    if (NetworkServer.active)
                    {
                        id = NetworkServer.spawned[runtimeData.weaponsInUseSync[i]];
                    }
                    else
                    {
                        id = NetworkClient.spawned[runtimeData.weaponsInUseSync[i]];
                    }

                    if (id)
                    {
                        runtimeData.weaponsInUseDataObjects[i] = id.GetComponent<Kit_WeaponRuntimeDataBase>();
                    }
                    else
                    {
                        //Didn't find it. This shouldn't happen!
                        runtimeData.weaponsInUseDataObjects[i] = null;
                    }
                }

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    int slot = i;
                    int id = runtimeData.weaponsInUseDataObjects[slot].id;
                    //Get their behaviour modules
                    Kit_WeaponBase weaponBehaviour = pb.gameInformation.allWeapons[id];
                    //Assign Behaviour
                    runtimeData.weaponsInUseDataObjects[slot].behaviour = weaponBehaviour;
                    runtimeData.weaponsInUseDataObjects[slot].pb = pb;
                    //Setup FP
                    weaponBehaviour.SetupFirstPerson(pb, runtimeData.weaponsInUseDataObjects[slot]);
                    //Setup TP
                    weaponBehaviour.SetupThirdPerson(pb, runtimeData.weaponsInUseDataObjects[slot]);
                }

                SelectDefaultWeapon(pb, runtimeData);

                //Set time
                runtimeData.switchNextEnd = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.drawTime;
                //Set phase
                runtimeData.switchPhase = 1;
                //Set switching
                runtimeData.switchInProgress = true;
            }

            public override void InitializeClient(Kit_PlayerBehaviour pb)
            {
                if (!pb.weaponManagerNetworkData)
                {
                    //Find ours
                    pb.weaponManagerNetworkData = FindObjectsOfType<Kit_ModernWeaponManagerNetworkData>().Where(x => x.ownerPlayerNetworkId == pb.netId).FirstOrDefault();
                }

                if (pb.isClientOnly)
                {
                    Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                    //Setup input IDs
                    runtimeData.lastInputIDs = new bool[pb.input.weaponSlotUses.Length];

                    //Initially build list
                    runtimeData.weaponsInUseDataObjects = new Kit_WeaponRuntimeDataBase[runtimeData.weaponsInUseSync.Count];

                    for (int i = 0; i < runtimeData.weaponsInUseSync.Count; i++)
                    {
                        NetworkIdentity id = null;

                        if (NetworkServer.active)
                        {
                            id = NetworkServer.spawned[runtimeData.weaponsInUseSync[i]];
                        }
                        else
                        {
                            id = NetworkClient.spawned[runtimeData.weaponsInUseSync[i]];
                        }

                        if (id)
                        {
                            runtimeData.weaponsInUseDataObjects[i] = id.GetComponent<Kit_WeaponRuntimeDataBase>();
                        }
                        else
                        {
                            //Didn't find it. This shouldn't happen!
                            runtimeData.weaponsInUseDataObjects[i] = null;
                        }
                    }

                    for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                    {
                        int slot = i;
                        int id = runtimeData.weaponsInUseDataObjects[slot].id;
                        //Get their behaviour modules
                        Kit_WeaponBase weaponBehaviour = pb.gameInformation.allWeapons[id];
                        //Assign Behaviour
                        runtimeData.weaponsInUseDataObjects[slot].behaviour = weaponBehaviour;
                        runtimeData.weaponsInUseDataObjects[slot].pb = pb;
                        //Setup FP
                        weaponBehaviour.SetupFirstPerson(pb, runtimeData.weaponsInUseDataObjects[slot]);
                        //Setup TP
                        weaponBehaviour.SetupThirdPerson(pb, runtimeData.weaponsInUseDataObjects[slot]);
                    }

                    SelectDefaultWeapon(pb, runtimeData);

                    //Set time
                    runtimeData.switchNextEnd = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.drawTime;
                    //Set phase
                    runtimeData.switchPhase = 1;
                    //Set switching
                    runtimeData.switchInProgress = true;
                }
            }

            /// <summary>
            /// Selects the first weapon
            /// </summary>
            /// <param name="pb"></param>
            /// <param name="runtimeData"></param>
            void SelectDefaultWeapon(Kit_PlayerBehaviour pb, Kit_ModernWeaponManagerNetworkData runtimeData)
            {
                //Select default weapon
                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (i < slotConfiguration.Length && slotConfiguration[i].enableEquipping)
                    {
                        if (runtimeData.weaponsInUseDataObjects[i].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[i]))
                        {
                            int id = i;
                            //Select current weapon
                            runtimeData.weaponsInUseDataObjects[i].behaviour.DrawWeapon(pb, runtimeData.weaponsInUseDataObjects[i]);
                            //Play Third person animation
                            pb.thirdPersonPlayerModel.PlayWeaponChangeAnimation(runtimeData.weaponsInUseDataObjects[i].behaviour.thirdPersonAnimType, true, runtimeData.weaponsInUseDataObjects[i].behaviour.drawTime);
                            //Set current weapon
                            runtimeData.desiredWeapon = runtimeData.currentWeapon = id;
                            return;
                        }

                    }
                }
            }

            public override void TakeControl(Kit_PlayerBehaviour pb)
            {
                //Hide crosshair
                Kit_IngameMain.instance.hud.DisplayCrosshair(0f, false);

            }

            public override void ForceUnselectCurrentWeapon(Kit_PlayerBehaviour pb)
            {
                Debug.Log("[Weapon Manager] Forcing unselect of current weapon!");
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                bool foundWeapon = false;
                //Try to find next weapon
                int next = -1;
                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (foundWeapon) break;


                    if (!runtimeData.weaponsInUseDataObjects[i].isInjectedFromPlugin)
                    {
                        //Check if this one works!
                        if (runtimeData.weaponsInUseDataObjects[i].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[i]))
                        {
                            int id = i;
                            next = id;
                            //We found one
                            foundWeapon = true;
                            break;
                        }
                    }

                }

                //This should ALWAYS be true!
                if (next >= 0)
                {
                    runtimeData.desiredWeapon = next;
                    //Begin switch and skip putaway
                    runtimeData.switchInProgress = true;
                    //Set time (Because here we cannot use a coroutine)
                    runtimeData.switchNextEnd = 0f;
                    //Set phase
                    runtimeData.switchPhase = 0;
                    if (pb.isFirstPersonActive)
                    {
                        //Hide crosshair
                        Kit_IngameMain.instance.hud.DisplayCrosshair(0f, false);
                    }
                    //Set current one too!
                    runtimeData.currentWeapon = next;
                }
                else
                {
                    Debug.LogError("Could not find next weapon! This is not allowed!");
                }
            }

            public override void AuthorativeInput(Kit_PlayerBehaviour pb, Kit_PlayerInput input, float delta, double revertTime)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                if (!runtimeData.isDesiredWeaponLocked)
                {
                    for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                    {
                        if (!runtimeData.weaponsInUseDataObjects[i].isInjectedFromPlugin)
                        {
                            if (slotConfiguration.Length > i)
                            {
                                if (slotConfiguration[i].enableEquipping && slotConfiguration[i].equippingInputID >= 0 && !runtimeData.quickUseInProgress)
                                {
                                    if (runtimeData.lastInputIDs[slotConfiguration[i].equippingInputID] != input.weaponSlotUses[slotConfiguration[i].equippingInputID])
                                    {
                                        runtimeData.lastInputIDs[slotConfiguration[i].equippingInputID] = input.weaponSlotUses[slotConfiguration[i].equippingInputID];
                                        //Check for input
                                        if (input.weaponSlotUses[slotConfiguration[i].equippingInputID] && (allowSwitchingWhileRunning || !pb.movement.IsRunning(pb)))
                                        {
                                            int id = i;
                                            if (runtimeData.desiredWeapon != id)
                                            {
                                                if (runtimeData.weaponsInUseDataObjects[i].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[i]))
                                                {
                                                    runtimeData.desiredWeapon = id;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (input.nextWeapon)
                    {
                        int nextWeapon = runtimeData.desiredWeapon + 1;
                        if (nextWeapon >= runtimeData.weaponsInUseSync.Count) nextWeapon = 0;
                        while (runtimeData.weaponsInUseDataObjects[nextWeapon].isInjectedFromPlugin || !runtimeData.weaponsInUseDataObjects[nextWeapon].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[nextWeapon]))
                        {
                            nextWeapon++;
                            if (nextWeapon >= runtimeData.weaponsInUseSync.Count) nextWeapon = 0;
                        }

                        runtimeData.desiredWeapon = nextWeapon;
                    }
                    else if (input.previousWeapon)
                    {
                        int previousWeapon = runtimeData.desiredWeapon - 1;
                        if (previousWeapon < 0) previousWeapon = runtimeData.weaponsInUseDataObjects.Length - 1;
                        while (runtimeData.weaponsInUseDataObjects[previousWeapon].isInjectedFromPlugin || !runtimeData.weaponsInUseDataObjects[previousWeapon].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[previousWeapon]))
                        {
                            previousWeapon--;
                            if (previousWeapon < 0) previousWeapon = runtimeData.weaponsInUseDataObjects.Length - 1;
                        }

                        runtimeData.desiredWeapon = previousWeapon;
                    }

                    //Check if we can do a quick use!
                    if (runtimeData.currentWeapon == runtimeData.desiredWeapon && !runtimeData.switchInProgress)
                    {
                        if (!runtimeData.quickUseInProgress)
                        {
                            for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                            {
                                if (!runtimeData.weaponsInUseDataObjects[i].isInjectedFromPlugin)
                                {
                                    int slot = i;
                                    if (slotConfiguration.Length > slot)
                                    {
                                        if (slotConfiguration[slot].enableQuickUse)
                                        {
                                            if (slotConfiguration[slot].quickUseInputID >= 0)
                                            {
                                                if (runtimeData.lastInputIDs[slotConfiguration[slot].quickUseInputID] != input.weaponSlotUses[slotConfiguration[slot].quickUseInputID])
                                                {
                                                    runtimeData.lastInputIDs[slotConfiguration[slot].quickUseInputID] = input.weaponSlotUses[slotConfiguration[slot].quickUseInputID];
                                                    //Check for input
                                                    if (input.weaponSlotUses[slotConfiguration[slot].quickUseInputID] && (allowQuickUseWhileRunning || !pb.movement.IsRunning(pb)))
                                                    {
                                                        if (runtimeData.weaponsInUseDataObjects[slot].behaviour.SupportsQuickUse(pb, runtimeData.weaponsInUseDataObjects[slot]))
                                                        {
                                                            runtimeData.desiredQuickUse = slot;
                                                            runtimeData.quickUseInProgress = true;
                                                            //Also reset these!
                                                            runtimeData.quickUseState = 0;
                                                            runtimeData.quickUseOverAt = Time.time;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (runtimeData.quickUseState == 2)
                            {
                                //Check if quick use button needs to be released
                                if (runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.WaitForQuickUseButtonRelease(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse]) && (!pb.input.weaponSlotUses[slotConfiguration[runtimeData.desiredQuickUse].quickUseInputID]))
                                {
                                    runtimeData.quickUseSyncButtonWaitOver = true;
                                }
                            }
                        }
                    }
                }

                if (!runtimeData.switchInProgress && !runtimeData.quickUseInProgress)
                {
                    runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.AuthorativeInput(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], input, delta, revertTime);
                }
            }

            public override void PredictionInput(Kit_PlayerBehaviour pb, Kit_PlayerInput input, float delta)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                AuthorativeInput(pb, input, delta, 0f);


                if (Physics.Raycast(pb.playerCameraTransform.position, pb.playerCameraTransform.forward, out runtimeData.hit, pickupDistance, pickupLayers.value))
                {
                    if (runtimeData.hit.transform.root.GetComponent<Kit_DropBehaviour>())
                    {
                        Kit_DropBehaviour drop = runtimeData.hit.transform.root.GetComponent<Kit_DropBehaviour>();
                        if (pb.isFirstPersonActive)
                        {
                            Kit_IngameMain.instance.hud.DisplayWeaponPickup(true, drop.weaponID);
                            Kit_IngameMain.instance.hud.DisplayInteraction(false);
                        }

                        if (runtimeData.lastInteract != input.interact)
                        {
                            runtimeData.lastInteract = input.interact;
                            if (input.interact && (allowSwitchingWhileRunning || !pb.movement.IsRunning(pb)))
                            {
                                drop.CmdTryPickUp();
                            }
                        }
                    }
                    else if (runtimeData.hit.transform.GetComponentInParent<Kit_InteractableObject>())
                    {
                        Kit_InteractableObject io = runtimeData.hit.transform.GetComponentInParent<Kit_InteractableObject>();

                        if (io.CanInteract(pb))
                        {
                            if (pb.isFirstPersonActive)
                            {
                                Kit_IngameMain.instance.hud.DisplayWeaponPickup(false);
                                Kit_IngameMain.instance.hud.DisplayInteraction(true, io.interactionText);
                            }

                            if (io.IsHold())
                            {
                                if (input.interact)
                                {
                                    //Store object
                                    runtimeData.holdingInteractableObject = io;

                                    //Tell object we want to interact
                                    io.Interact(pb);
                                }
                                else
                                {
                                    if (runtimeData.holdingInteractableObject)
                                    {
                                        runtimeData.holdingInteractableObject.InteractHoldEnd(pb);
                                        runtimeData.holdingInteractableObject = null;
                                    }
                                }
                            }
                            else
                            {
                                if (runtimeData.lastInteract != input.interact)
                                {
                                    runtimeData.lastInteract = input.interact;
                                    if (input.interact)
                                    {
                                        //Tell object we want to interact
                                        io.Interact(pb);
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (pb.isFirstPersonActive)
                            {
                                Kit_IngameMain.instance.hud.DisplayWeaponPickup(false);
                                Kit_IngameMain.instance.hud.DisplayInteraction(false);
                            }

                            if (runtimeData.holdingInteractableObject)
                            {
                                runtimeData.holdingInteractableObject.InteractHoldEnd(pb);
                                runtimeData.holdingInteractableObject = null;
                            }
                        }
                    }
                    else
                    {
                        if (pb.isFirstPersonActive)
                        {
                            Kit_IngameMain.instance.hud.DisplayWeaponPickup(false);
                            Kit_IngameMain.instance.hud.DisplayInteraction(false);
                        }

                        if (runtimeData.holdingInteractableObject)
                        {
                            runtimeData.holdingInteractableObject.InteractHoldEnd(pb);
                            runtimeData.holdingInteractableObject = null;
                        }
                    }
                }
                else
                {
                    if (pb.isFirstPersonActive)
                    {
                        Kit_IngameMain.instance.hud.DisplayWeaponPickup(false);
                        Kit_IngameMain.instance.hud.DisplayInteraction(false);
                    }

                    if (runtimeData.holdingInteractableObject)
                    {
                        runtimeData.holdingInteractableObject.InteractHoldEnd(pb);
                        runtimeData.holdingInteractableObject = null;
                    }
                }
            }

            public override void CustomUpdate(Kit_PlayerBehaviour pb, double revertBy)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                //Quick use has priority!
                if ((runtimeData.quickUseInProgress || runtimeData.quickUseState > 0) && runtimeData.desiredQuickUse >= 0)
                {
                    //Update weapon animation
                    runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.AnimateWeapon(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse], pb.movement.GetCurrentWeaponMoveAnimation(pb), pb.movement.GetCurrentWalkAnimationSpeed(pb));

                    if (Time.time >= runtimeData.quickUseOverAt)
                    {
                        //First, put away current weapon!
                        if (runtimeData.quickUseState == 0)
                        {
                            if (!runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.QuickUseSkipsPutaway(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse]))
                            {
                                //Set time (Because here we cannot use a coroutine)
                                runtimeData.quickUseOverAt = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.putawayTime;
                                //Set phase
                                runtimeData.quickUseState = 1;
                                //Start putaway
                                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.PutawayWeapon(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                                //Play Third person animation
                                pb.thirdPersonPlayerModel.PlayWeaponChangeAnimation(runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.thirdPersonAnimType, false, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.putawayTime);
                                if (pb.isFirstPersonActive)
                                {
                                    //Hide crosshair
                                    Kit_IngameMain.instance.hud.DisplayCrosshair(0f, false);
                                }

                                if (pb.isLocalPlayer)
                                {
                                    pb.inputManager.ResetAiming(pb);
                                }
                            }
                            else
                            {
                                //Set phase
                                runtimeData.quickUseState = 1;
                                if (pb.isFirstPersonActive)
                                {
                                    //Hide crosshair
                                    Kit_IngameMain.instance.hud.DisplayCrosshair(0f, false);
                                }
                            }
                        }
                        else if (runtimeData.quickUseState == 1)
                        {
                            //Weapon has been put away, hide weapon
                            runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.PutawayWeaponHide(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                            runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.QuickUseOnOtherWeaponBegin(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);

                            //Set state
                            runtimeData.quickUseState = 2;

                            //Begin quick use....
                            runtimeData.quickUseOverAt = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.BeginQuickUse(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse], (float)revertBy);
                        }
                        else if (runtimeData.quickUseState == 2)
                        {
                            //Check if weapon wants to abort quick use
                            if (!runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.SupportsQuickUse(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse]) ||
                                //Check if we don't need to wait for the button
                                (!runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.WaitForQuickUseButtonRelease(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse])) ||
                                //Or button was released via sync
                                (runtimeData.quickUseSyncButtonWaitOver))
                            {
                                runtimeData.quickUseSyncButtonWaitOver = true;
                                //Set State
                                runtimeData.quickUseState = 3;
                                //End quick use...
                                runtimeData.quickUseOverAt = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.EndQuickUse(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse], (float)revertBy);
                            }
                        }
                        else if (runtimeData.quickUseState == 3)
                        {
                            //Hide Quick Use!
                            runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.EndQuickUseAfter(pb, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse], (float)revertBy);
                            //Check if currently selected  weapon is valid.
                            if (runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]))
                            {
                                //Set weapon
                                runtimeData.currentWeapon = runtimeData.desiredWeapon;
                            }
                            else
                            {
                                //Its not, find a new one
                                int next = 0;
                                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                                {
                                    //Check if this one works!
                                    if (runtimeData.weaponsInUseDataObjects[i].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[i]))
                                    {
                                        int id = i;
                                        next = id;
                                        //We found one
                                        break;
                                    }
                                }

                                //This should ALWAYS be true!
                                if (next >= 0)
                                {
                                    runtimeData.desiredWeapon = next;
                                    //Set current one too!
                                    runtimeData.currentWeapon = next;
                                }
                                else
                                {
                                    Debug.LogError("Could not find next weapon! This is not allowed!");
                                }
                            }

                            //Draw that weapon
                            runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.DrawWeapon(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                            //Play Third person animation
                            pb.thirdPersonPlayerModel.PlayWeaponChangeAnimation(runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.thirdPersonAnimType, true, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.drawTime);
                            //Set phase
                            runtimeData.quickUseState = 4;
                            //Set time
                            runtimeData.quickUseOverAt = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.drawTime;
                            //Done, now wait
                        }
                        else if (runtimeData.quickUseState == 4)
                        {
                            //End quick use
                            runtimeData.quickUseInProgress = false;
                            runtimeData.desiredQuickUse = -1;
                            runtimeData.quickUseSyncButtonWaitOver = false;
                            runtimeData.quickUseState = 0;
                            runtimeData.quickUseOverAt = Time.time;

                            //Also reset switching just to be sure!
                            runtimeData.switchPhase = 0;
                            runtimeData.switchNextEnd = 0f;
                            runtimeData.switchInProgress = false;
                        }
                    }
                }
                else
                {
                    //Update weapon animation
                    runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.AnimateWeapon(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], pb.movement.GetCurrentWeaponMoveAnimation(pb), pb.movement.GetCurrentWalkAnimationSpeed(pb));

                    if (!runtimeData.switchInProgress)
                    {
                        //If we aren't switching weapons, update weapon behaviour
                        runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.CalculateWeaponUpdate(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);

                        //Check if we want to select a different weapon
                        if (runtimeData.desiredWeapon != runtimeData.currentWeapon)
                        {
                            //If not, start to switch
                            runtimeData.switchInProgress = true;
                            //Set time (Because here we cannot use a coroutine)
                            runtimeData.switchNextEnd = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.putawayTime;
                            //Set phase
                            runtimeData.switchPhase = 0;
                            //Start putaway
                            runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.PutawayWeapon(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                            //Play Third person animation
                            pb.thirdPersonPlayerModel.PlayWeaponChangeAnimation(runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.thirdPersonAnimType, false, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.putawayTime);
                            if (pb.isFirstPersonActive)
                            {
                                //Hide crosshair
                                Kit_IngameMain.instance.hud.DisplayCrosshair(0f, false);
                            }
                        }
                    }
                    else
                    {
                        //Switching, courtine less
                        #region Switching
                        //Check for time
                        if (Time.time >= runtimeData.switchNextEnd)
                        {
                            //Time is over, check which phase is next
                            if (runtimeData.switchPhase == 0)
                            {
                                //Weapon has been put away, hide weapon
                                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.PutawayWeaponHide(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);

                                runtimeData.currentWeapon = runtimeData.desiredWeapon;

                                //Draw that weapon
                                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.DrawWeapon(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                                //Set phase
                                runtimeData.switchPhase = 1;
                                //Set time
                                runtimeData.switchNextEnd = Time.time + runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.drawTime;
                                //Play Third person animation
                                pb.thirdPersonPlayerModel.PlayWeaponChangeAnimation(runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.thirdPersonAnimType, true, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.drawTime);
                                //Done, now wait
                            }
                            else if (runtimeData.switchPhase == 1)
                            {
                                //Switching is over
                                runtimeData.switchPhase = 0;
                                runtimeData.switchNextEnd = 0f;
                                runtimeData.switchInProgress = false;
                            }
                        }
                        #endregion
                    }
                }

                //Move weapons transform
                pb.weaponsGo.localPosition = Vector3.Lerp(pb.weaponsGo.localPosition, Vector3.zero + pb.looking.GetWeaponOffset(pb), Time.deltaTime * weaponPositionChangeSpeed);

                //Move weapons transform
                pb.weaponsGo.localRotation = Quaternion.Slerp(pb.weaponsGo.localRotation, pb.looking.GetWeaponRotationOffset(pb), Time.deltaTime * weaponPositionChangeSpeed);

                if (pb.isFirstPersonActive)
                {
                    Kit_IngameMain.instance.hud.DisplayWeaponsAndQuickUses(pb, runtimeData);
                }
            }

            public override void CustomLateUpdate(Kit_PlayerBehaviour pb, double revertTime)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                //Update weapon animation
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.CalculateWeaponLateUpdate(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void CustomFixedUpdate(Kit_PlayerBehaviour pb, double revertTime)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                //Update weapon animation
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.CalculateWeaponFixedUpdate(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void PlayerDead(Kit_PlayerBehaviour pb)
            {
                if (NetworkServer.active && (Kit_IngameMain.instance.currentPvPGameModeBehaviour && Kit_IngameMain.instance.currentPvPGameModeBehaviour.CanDropWeapons()) || (Kit_IngameMain.instance.currentPvEGameModeBehaviour && Kit_IngameMain.instance.currentPvEGameModeBehaviour.CanDropWeapons()))
                {
                    Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                    if (uponDeathDrop == DeadDrop.Selected)
                    {
                        DropWeaponDead(pb, runtimeData.currentWeapon);
                    }
                    else if (uponDeathDrop == DeadDrop.All)
                    {
                        for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                        {
                            if (!runtimeData.weaponsInUseDataObjects[i].isInjectedFromPlugin)
                            {
                                DropWeaponDead(pb, i);
                            }
                        }
                    }

                }
            }

            public override void OnAnimatorIKCallback(Kit_PlayerBehaviour pb, Animator anim)
            {
                //Get runtime data
                if (pb.isLocalPlayer)
                {
                    Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                    if (anim)
                    {
                        //Get Weapon IK
                        WeaponIKValues ikv = null;

                        if (runtimeData.quickUseInProgress && runtimeData.quickUseState > 0 && runtimeData.quickUseState < 4)
                        {
                            ikv = runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.GetIK(pb, anim, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse]);
                        }
                        else
                        {
                            ikv = runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.GetIK(pb, anim, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                        }

                        if (ikv != null)
                        {
                            if (ikv.leftHandIK)
                            {
                                anim.SetIKPosition(AvatarIKGoal.LeftHand, ikv.leftHandIK.position);
                                anim.SetIKRotation(AvatarIKGoal.LeftHand, ikv.leftHandIK.rotation);
                            }
                            if (!runtimeData.switchInProgress && ikv.canUseIK && ikv.leftHandIK)
                            {
                                runtimeData.leftHandIKWeight = Mathf.Lerp(runtimeData.leftHandIKWeight, 1f, Time.deltaTime * 3);
                            }
                            else
                            {
                                runtimeData.leftHandIKWeight = Mathf.Lerp(runtimeData.leftHandIKWeight, 0f, Time.deltaTime * 20);
                            }
                            anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                            anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                        }
                        else
                        {
                            runtimeData.leftHandIKWeight = Mathf.Lerp(runtimeData.leftHandIKWeight, 0f, Time.deltaTime * 20);
                            anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                            anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                        }
                    }

                }
                else
                {
                    Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                    if (anim)
                    {
                        //Get Weapon IK
                        WeaponIKValues ikv = null;

                        if (runtimeData.quickUseInProgress && runtimeData.quickUseState > 0 && runtimeData.quickUseState < 4)
                        {
                            ikv = runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.GetIK(pb, anim, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse]);
                        }
                        else
                        {
                            ikv = runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.GetIK(pb, anim, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                        }

                        if (ikv != null)
                        {
                            if (ikv.leftHandIK)
                            {
                                anim.SetIKPosition(AvatarIKGoal.LeftHand, ikv.leftHandIK.position);
                                anim.SetIKRotation(AvatarIKGoal.LeftHand, ikv.leftHandIK.rotation);
                            }
                            if (!runtimeData.switchInProgress && ikv.canUseIK && ikv.leftHandIK)
                            {
                                runtimeData.leftHandIKWeight = Mathf.Lerp(runtimeData.leftHandIKWeight, 1f, Time.deltaTime * 3);
                            }
                            else
                            {
                                runtimeData.leftHandIKWeight = Mathf.Lerp(runtimeData.leftHandIKWeight, 0f, Time.deltaTime * 20);
                            }
                            anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                            anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                        }
                        else
                        {
                            runtimeData.leftHandIKWeight = Mathf.Lerp(runtimeData.leftHandIKWeight, 0f, Time.deltaTime * 20);
                            anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                            anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, runtimeData.leftHandIKWeight);
                        }
                    }

                }
            }

            public override void FallDownEffect(Kit_PlayerBehaviour pb, bool wasFallDamageApplied)
            {
                if (pb.isBot) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.FallDownEffect(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], wasFallDamageApplied);

            }

            public override void OnControllerColliderHitRelay(Kit_PlayerBehaviour pb, ControllerColliderHit hit)
            {
                if (!pb.weaponManagerNetworkData) return;
                //Get runtime data
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.OnControllerColliderHitCallback(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], hit);
            }

            public override void NetworkSemiRPCReceived(Kit_PlayerBehaviour pb)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkSemiRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void NetworkBoltActionRPCReceived(Kit_PlayerBehaviour pb, int state)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkBoltActionRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], state);
            }

            public override void NetworkBurstRPCReceived(Kit_PlayerBehaviour pb, int burstLength)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkBurstRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], burstLength);
            }

            public override void NetworkReloadRPCReceived(Kit_PlayerBehaviour pb, bool isEmpty)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkReloadRPCReceived(pb, isEmpty, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);

            }

            public override void NetworkProceduralReloadRPCReceived(Kit_PlayerBehaviour pb, int stage)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkProceduralReloadRPCReceived(pb, stage, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);

            }

            public override void NetworkMeleeChargeRPCReceived(Kit_PlayerBehaviour pb, int state, int slot)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkMeleeChargeRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], state, slot);

            }

            public override void NetworkMeleeHealRPCReceived(Kit_PlayerBehaviour pb, int slot)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkMeleeHealRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], slot);
            }

            public override void NetworkMeleeStabRPCReceived(Kit_PlayerBehaviour pb, int state, int slot)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkMeleeStabRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon], state, slot);
            }

            public override void NetworkGrenadePullPinRPCReceived(Kit_PlayerBehaviour pb)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkGrenadePullPinRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void NetworkGrenadeThrowRPCReceived(Kit_PlayerBehaviour pb)
            {
                if (!pb.weaponManagerNetworkData) return;
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length <= runtimeData.currentWeapon) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]) return;
                if (!runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour) return;
                //Relay to weapon script
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkGrenadeThrowRPCReceived(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override float GetAimingPercentage(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.GetAimingPercentage(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]); //Relay to weapon script
            }

            public override bool IsAiming(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.IsWeaponAiming(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]); //Relay to weapon script
            }

            public override float AimInTime(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.AimInTime(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]); //Relay to weapon script
            }

            public override bool ForceIntoFirstPerson(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.ForceIntoFirstPerson(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]); //Relay to weapon script
            }

            public override bool CanRun(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (allowSwitchingWhileRunning) return true;
                else return !runtimeData.switchInProgress;
            }

            public override float CurrentMovementMultiplier(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.SpeedMultiplier(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]); //Relay to weapon script
            }

            public override float CurrentSensitivity(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.Sensitivity(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]); //Relay to weapon script
            }

            public override int GetCurrentWeapon(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                //Just return ID
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].id;
            }

            public override int HasWeapon(Kit_PlayerBehaviour pb, int id)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (runtimeData.weaponsInUseDataObjects[i].id == id)
                    {
                        return i;
                    }
                }

                return -1;
            }

            public override Kit_WeaponRuntimeDataBase GetRuntimeDataOfWeapon(Kit_PlayerBehaviour pb, int slot)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                //Just return ID
                return runtimeData.weaponsInUseDataObjects[slot];
            }

            public override bool CanBuyWeapon(Kit_PlayerBehaviour pb, int id)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (runtimeData.weaponsInUseDataObjects[i].id == id)
                    {
                        //Special case for grenades - let us buy more grenades if we are below that #
                        if (runtimeData.weaponsInUseDataObjects[i] != null && runtimeData.weaponsInUseDataObjects[i].GetType() == typeof(Kit_ModernGrenadeScriptRuntimeData))
                        {
                            Kit_ModernGrenadeScriptRuntimeData gcrd = runtimeData.weaponsInUseDataObjects[i] as Kit_ModernGrenadeScriptRuntimeData;
                            Kit_ModernGrenadeScript grenadeData = runtimeData.weaponsInUseDataObjects[i].behaviour as Kit_ModernGrenadeScript;

                            if (gcrd.amountOfGrenadesLeft < grenadeData.amountOfGrenadesAtStart)
                            {
                                return true;
                            }

                        }

                        return false;
                    }

                }

                //Can buy
                return true;
            }

            public override void NetworkReplaceWeapon(Kit_PlayerBehaviour pb, int slot, int weapon, int bulletsLeft, int bulletsLeftToReload, int[] attachments)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                GameObject spawnObject = Kit_IngameMain.instance.gameInformation.allWeapons[weapon].SetupSpawnData(pb, slot, new LoadoutWeapon { weaponID = weapon, attachments = attachments });

                Kit_WeaponRuntimeDataBase wrdb = spawnObject.GetComponent<Kit_WeaponRuntimeDataBase>();

                if (wrdb is Kit_ModernWeaponScriptRuntimeData)
                {
                    Kit_ModernWeaponScriptRuntimeData mwsrd = wrdb as Kit_ModernWeaponScriptRuntimeData;
                    mwsrd.bulletsLeft = bulletsLeft;
                    mwsrd.bulletsLeftToReload = bulletsLeftToReload;
                }

                NetworkServer.Spawn(spawnObject, pb.gameObject);
                NetworkIdentity id = spawnObject.GetComponent<NetworkIdentity>();

                NetworkIdentity oldIdentity = NetworkServer.spawned[runtimeData.weaponsInUseSync[slot]];

                if (oldIdentity)
                {
                    NetworkServer.Destroy(oldIdentity.gameObject);
                }

                runtimeData.weaponsInUseSync[slot] = id.netId;
            }

            public override void NetworkPhysicalBulletFired(Kit_PlayerBehaviour pb, Vector3 pos, Vector3 dir)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects.Length > runtimeData.currentWeapon)
                    //Relay to weapon script
                    runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.NetworkPhysicalBulletFired(pb, pos, dir, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }


            public override void DropWeapon(Kit_PlayerBehaviour pb, int slot)
            {
                if ((Kit_IngameMain.instance.currentPvPGameModeBehaviour && Kit_IngameMain.instance.currentPvPGameModeBehaviour.CanDropWeapons()) || (Kit_IngameMain.instance.currentPvEGameModeBehaviour && Kit_IngameMain.instance.currentPvEGameModeBehaviour.CanDropWeapons()))
                {
                    if (pb.weaponManagerNetworkData != null && pb.weaponManagerNetworkData.GetType() == typeof(Kit_ModernWeaponManagerNetworkData))
                    {
                        //Get the manager's runtime data
                        Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                        if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernWeaponScriptRuntimeData))
                        {
                            //Get the weapon's runtime data
                            Kit_ModernWeaponScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernWeaponScriptRuntimeData;
                            //Get the Scriptable object
                            Kit_ModernWeaponScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernWeaponScript;
                            //Instantiate
                            GameObject go = Instantiate(dropPrefab, pb.playerCameraTransform.position, pb.playerCameraTransform.rotation);
                            Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                            dp.isSceneOwned = true;
                            dp.weaponID = wepScript.gameGunID;
                            dp.bulletsLeft = wepData.bulletsLeft;
                            dp.bulletsLeftToReload = wepData.bulletsLeftToReload;

                            for (int i = 0; i < wepData.attachments.Count; i++)
                            {
                                dp.attachments.Add(wepData.attachments[i]);
                            }

                            NetworkServer.Spawn(go);
                        }
                        else if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernMeleeScriptRuntimeData))
                        {
                            //Get the weapon's runtime data
                            Kit_ModernMeleeScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernMeleeScriptRuntimeData;
                            //Get the Scriptable object
                            Kit_ModernMeleeScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernMeleeScript;
                            //Instantiate
                            GameObject go = Instantiate(dropPrefab, pb.playerCameraTransform.position, pb.playerCameraTransform.rotation);
                            Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                            dp.isSceneOwned = true;
                            dp.weaponID = wepScript.gameGunID;
                            dp.bulletsLeft = 0;
                            dp.bulletsLeftToReload = 0;

                            for (int i = 0; i < wepData.attachments.Count; i++)
                            {
                                dp.attachments.Add(wepData.attachments[i]);
                            }

                            NetworkServer.Spawn(go);
                        }
                        else if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernGrenadeScriptRuntimeData))
                        {
                            //Get the weapon's runtime data
                            Kit_ModernGrenadeScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernGrenadeScriptRuntimeData;
                            if (wepData.amountOfGrenadesLeft <= 0) return;
                            //Get the Scriptable object
                            Kit_ModernGrenadeScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernGrenadeScript;
                            //Instantiate
                            GameObject go = Instantiate(dropPrefab, pb.playerCameraTransform.position, pb.playerCameraTransform.rotation);
                            Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                            dp.isSceneOwned = true;
                            dp.weaponID = wepScript.gameGunID;
                            dp.bulletsLeft = 0;
                            dp.bulletsLeftToReload = 0;

                            for (int i = 0; i < wepData.attachments.Count; i++)
                            {
                                dp.attachments.Add(wepData.attachments[i]);
                            }

                            NetworkServer.Spawn(go);
                        }
                    }
                }
            }

            /// <summary>
            /// Drops a weapon and applies the ragdoll force!
            /// </summary>
            /// <param name="pb"></param>
            /// <param name="slot"></param>
            public void DropWeaponDead(Kit_PlayerBehaviour pb, int slot)
            {
                if ((Kit_IngameMain.instance.currentPvPGameModeBehaviour && Kit_IngameMain.instance.currentPvPGameModeBehaviour.CanDropWeapons()) || (Kit_IngameMain.instance.currentPvEGameModeBehaviour && Kit_IngameMain.instance.currentPvEGameModeBehaviour.CanDropWeapons()))
                {
                    if (pb.weaponManagerNetworkData != null && pb.weaponManagerNetworkData.GetType() == typeof(Kit_ModernWeaponManagerNetworkData))
                    {
                        //Get the manager's runtime data
                        Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                        if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernWeaponScriptRuntimeData))
                        {
                            //Get the weapon's runtime data
                            Kit_ModernWeaponScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernWeaponScriptRuntimeData;
                            //Get the Scriptable object
                            Kit_ModernWeaponScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernWeaponScript;
                            //Instantiate
                            GameObject go = Instantiate(dropPrefab, pb.playerCameraTransform.position, pb.playerCameraTransform.rotation);
                            Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                            dp.isSceneOwned = true;
                            dp.weaponID = wepScript.gameGunID;
                            dp.bulletsLeft = wepData.bulletsLeft;
                            dp.bulletsLeftToReload = wepData.bulletsLeftToReload;

                            for (int i = 0; i < wepData.attachments.Count; i++)
                            {
                                dp.attachments.Add(wepData.attachments[i]);
                            }

                            NetworkServer.Spawn(go);
                            Rigidbody body = go.GetComponent<Rigidbody>();
                            if (body)
                            {
                                body.velocity = pb.movement.GetVelocity(pb);
                                body.AddForceNextFrame(pb.ragdollForward * pb.ragdollForce / 10);
                            }
                        }
                        else if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernMeleeScriptRuntimeData))
                        {
                            //Get the weapon's runtime data
                            Kit_ModernMeleeScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernMeleeScriptRuntimeData;
                            //Get the Scriptable object
                            Kit_ModernMeleeScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernMeleeScript;
                            //Instantiate
                            GameObject go = Instantiate(dropPrefab, pb.playerCameraTransform.position, pb.playerCameraTransform.rotation);
                            Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                            dp.isSceneOwned = true;
                            dp.weaponID = wepScript.gameGunID;
                            dp.bulletsLeft = 0;
                            dp.bulletsLeftToReload = 0;

                            for (int i = 0; i < wepData.attachments.Count; i++)
                            {
                                dp.attachments.Add(wepData.attachments[i]);
                            }

                            NetworkServer.Spawn(go);
                            Rigidbody body = go.GetComponent<Rigidbody>();
                            if (body)
                            {
                                body.velocity = pb.movement.GetVelocity(pb);
                                body.AddForceNextFrame(pb.ragdollForward * pb.ragdollForce / 10);
                            }
                        }
                        else if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernGrenadeScriptRuntimeData))
                        {
                            //Get the weapon's runtime data
                            Kit_ModernGrenadeScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernGrenadeScriptRuntimeData;
                            if (wepData.amountOfGrenadesLeft <= 0) return;
                            //Get the Scriptable object
                            Kit_ModernGrenadeScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernGrenadeScript;
                            //Instantiate
                            GameObject go = Instantiate(dropPrefab, pb.playerCameraTransform.position, pb.playerCameraTransform.rotation);
                            Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                            dp.isSceneOwned = true;
                            dp.weaponID = wepScript.gameGunID;
                            dp.bulletsLeft = 0;
                            dp.bulletsLeftToReload = 0;

                            for (int i = 0; i < wepData.attachments.Count; i++)
                            {
                                dp.attachments.Add(wepData.attachments[i]);
                            }

                            NetworkServer.Spawn(go);
                            Rigidbody body = go.GetComponent<Rigidbody>();
                            if (body)
                            {
                                body.velocity = pb.movement.GetVelocity(pb);
                                body.AddForceNextFrame(pb.ragdollForward * pb.ragdollForce / 10);
                            }
                        }
                    }
                }
            }

            public override void DropWeapon(Kit_PlayerBehaviour pb, int slot, Transform replace)
            {
                if (pb.weaponManagerNetworkData != null && pb.weaponManagerNetworkData.GetType() == typeof(Kit_ModernWeaponManagerNetworkData))
                {
                    //Get the manager's runtime data
                    Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                    if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernWeaponScriptRuntimeData))
                    {
                        //Get the weapon's runtime data
                        Kit_ModernWeaponScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernWeaponScriptRuntimeData;
                        //Get the Scriptable object
                        Kit_ModernWeaponScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernWeaponScript;
                        //Instantiate
                        GameObject go = Instantiate(dropPrefab, replace.position, replace.rotation);
                        Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                        dp.isSceneOwned = true;
                        dp.weaponID = wepScript.gameGunID;
                        dp.bulletsLeft = wepData.bulletsLeft;
                        dp.bulletsLeftToReload = wepData.bulletsLeftToReload;

                        for (int i = 0; i < wepData.attachments.Count; i++)
                        {
                            dp.attachments.Add(wepData.attachments[i]);
                        }

                        NetworkServer.Spawn(go);
                    }
                    else if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernMeleeScriptRuntimeData))
                    {
                        //Get the weapon's runtime data
                        Kit_ModernMeleeScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernMeleeScriptRuntimeData;
                        //Get the Scriptable object
                        Kit_ModernMeleeScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernMeleeScript;
                        //Instantiate
                        GameObject go = Instantiate(dropPrefab, replace.position, replace.rotation);
                        Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                        dp.isSceneOwned = true;
                        dp.weaponID = wepScript.gameGunID;
                        dp.bulletsLeft = 0;
                        dp.bulletsLeftToReload = 0;

                        for (int i = 0; i < wepData.attachments.Count; i++)
                        {
                            dp.attachments.Add(wepData.attachments[i]);
                        }

                        NetworkServer.Spawn(go);
                    }
                    else if (runtimeData.weaponsInUseDataObjects[slot] != null && runtimeData.weaponsInUseDataObjects[slot].GetType() == typeof(Kit_ModernGrenadeScriptRuntimeData))
                    {
                        //Get the weapon's runtime data
                        Kit_ModernGrenadeScriptRuntimeData wepData = runtimeData.weaponsInUseDataObjects[slot] as Kit_ModernGrenadeScriptRuntimeData;
                        if (wepData.amountOfGrenadesLeft <= 0) return;
                        //Get the Scriptable object
                        Kit_ModernGrenadeScript wepScript = runtimeData.weaponsInUseDataObjects[slot].behaviour as Kit_ModernGrenadeScript;
                        //Instantiate
                        GameObject go = Instantiate(dropPrefab, replace.position, replace.rotation);
                        Kit_DropBehaviour dp = go.GetComponent<Kit_DropBehaviour>();
                        dp.isSceneOwned = true;
                        dp.weaponID = wepScript.gameGunID;
                        dp.bulletsLeft = 0;
                        dp.bulletsLeftToReload = 0;

                        for (int i = 0; i < wepData.attachments.Count; i++)
                        {
                            dp.attachments.Add(wepData.attachments[i]);
                        }

                        NetworkServer.Spawn(go);
                    }
                }
            }

            public override int WeaponState(Kit_PlayerBehaviour pb)
            {
                //Get the manager's runtime data
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.WeaponState(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override int WeaponType(Kit_PlayerBehaviour pb)
            {
                //Get the manager's runtime data
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.GetWeaponType(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void FirstThirdPersonChanged(Kit_PlayerBehaviour pb, Kit_GameInformation.Perspective perspective)
            {
                if (pb.syncSetup)
                {
                    //Forward to currently selected weapon
                    Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                    if (runtimeData.quickUseInProgress && runtimeData.quickUseState > 0 && runtimeData.quickUseState < 4)
                    {
                        runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse].behaviour.FirstThirdPersonChanged(pb, perspective, runtimeData.weaponsInUseDataObjects[runtimeData.desiredQuickUse]);
                    }
                    else
                    {
                        runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.FirstThirdPersonChanged(pb, perspective, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                    }
                }
            }

            public override void OnAmmoPickup(Kit_PlayerBehaviour pb, Kit_AmmoPickup pickup)
            {
                //Forward to currently selected weapon
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.OnAmmoPickup(pb, pickup, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void RestockAmmo(Kit_PlayerBehaviour pb, bool allWeapons)
            {
                if (pb.isServer)
                {
                    if (pb.weaponManagerNetworkData != null && pb.weaponManagerNetworkData.GetType() == typeof(Kit_ModernWeaponManagerNetworkData))
                    {
                        if (allWeapons)
                        {
                            Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                            for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                            {
                                runtimeData.weaponsInUseDataObjects[i].behaviour.RestockAmmo(pb, runtimeData.weaponsInUseDataObjects[i]);
                            }
                        }
                        else
                        {
                            //Forward to currently selected weapon
                            Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                            runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.RestockAmmo(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
                        }
                    }
                }
            }

            public override bool IsCurrentWeaponFull(Kit_PlayerBehaviour pb)
            {
                //Forward to currently selected weapon
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon].behaviour.IsWeaponFull(pb, runtimeData.weaponsInUseDataObjects[runtimeData.currentWeapon]);
            }

            public override void PluginSelectWeapon(Kit_PlayerBehaviour pb, int slot, bool locked = true)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                //Just set, thats all!
                runtimeData.desiredWeapon = slot;
                runtimeData.isDesiredWeaponLocked = locked;
            }

            public override int GetCurrentlyDesiredWeapon(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.desiredWeapon;
            }

            public override int GetCurrentlyDesiredQuickUse(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.desiredQuickUse;
            }

            public override int GetCurrentlySelectedWeapon(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                return runtimeData.currentWeapon;
            }

            public override void SetDesiredWeapon(Kit_PlayerBehaviour pb, int desiredWeapon)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;
                if (runtimeData.weaponsInUseDataObjects[desiredWeapon].behaviour.CanBeSelected(pb, runtimeData.weaponsInUseDataObjects[desiredWeapon]))
                {
                    runtimeData.desiredWeapon = desiredWeapon;

                }
            }

            public override int[] GetSlotsWithEmptyWeapon(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                List<int> emptySlots = new List<int>();

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    //Check if that slot contains a placehodler weapon
                    if (runtimeData.weaponsInUseDataObjects[i].behaviour.GetType() == typeof(Kit_WeaponUnselectable))
                    {
                        int id = i;
                        //Add it to list of empty slots
                        emptySlots.Add(id);
                    }
                }

                return emptySlots.ToArray();
            }

            public override void BeginSpectating(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    //Relay
                    runtimeData.weaponsInUseDataObjects[i].behaviour.BeginSpectating(pb, runtimeData.weaponsInUseDataObjects[i], runtimeData.weaponsInUseDataObjects[i].attachments.ToArray());
                }
            }

            public override void EndSpectating(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    //Relay
                    runtimeData.weaponsInUseDataObjects[i].behaviour.EndSpectating(pb, runtimeData.weaponsInUseDataObjects[i]);
                }
            }

            public override void OnTriggerEnterRelay(Kit_PlayerBehaviour pb, Collider col)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (runtimeData.weaponsInUseDataObjects[i].behaviour)
                    {
                        //Relay
                        runtimeData.weaponsInUseDataObjects[i].behaviour.OnTriggerEnterRelay(pb, runtimeData.weaponsInUseDataObjects[i], col);
                    }
                }
            }

            public override void OnTriggerExitRelay(Kit_PlayerBehaviour pb, Collider col)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (runtimeData.weaponsInUseDataObjects[i].behaviour)
                    {
                        //Relay
                        runtimeData.weaponsInUseDataObjects[i].behaviour.OnTriggerExitRelay(pb, runtimeData.weaponsInUseDataObjects[i], col);
                    }
                }
            }

            public override void OnDestroyRelay(Kit_PlayerBehaviour pb)
            {
                Kit_ModernWeaponManagerNetworkData runtimeData = pb.weaponManagerNetworkData as Kit_ModernWeaponManagerNetworkData;

                for (int i = 0; i < runtimeData.weaponsInUseDataObjects.Length; i++)
                {
                    if (runtimeData.weaponsInUseDataObjects[i].behaviour)
                    {
                        //Relay
                        runtimeData.weaponsInUseDataObjects[i].behaviour.OnDestroyRelay(pb, runtimeData.weaponsInUseDataObjects[i]);
                    }
                }
            }
        }
    }
}
using System.Collections.Generic;
using Mirror;
using UnityEngine;
using static System.Net.WebRequestMethods;

namespace MarsFPSKit
{
    namespace Weapons
    {
        public class Kit_ModernWeaponScriptRuntimeData : Kit_WeaponRuntimeDataBase
        {
            [SyncVar]
            /// <summary>
            /// Is this weapon out and ready to shoot?
            /// </summary>
            public bool isSelectedAndReady = false;
            [SyncVar]
            /// <summary>
            /// Is the weapon selected?
            /// </summary>
            public bool isSelected;
            [SyncVar]
            /// <summary>
            /// How many bullets are left in the magazine?
            /// </summary>
            public int bulletsLeft;
            [SyncVar]
            /// <summary>
            /// How many bullets do we have left to reload
            /// </summary>
            public int bulletsLeftToReload;

            [SyncVar]
            /// <summary>
            /// Are we currently reloading?
            /// </summary>
            public bool reloadInProgress;

            public Kit_WeaponRenderer weaponRenderer;
            public Kit_ThirdPersonWeaponRenderer tpWeaponRenderer;

            public Animator genericAnimator;

            #region Stats
            [SyncVar]
            /// <summary>
            /// Are we currently firing full auto?
            /// </summary>
            public bool isFiring;
            /// <summary>
            /// When did we fire for the last time? Compare with <see cref="Time.time"/>
            /// </summary>
            public float lastFire;

            /// <summary>
            /// When did we ran the last time (using this weapon)
            /// </summary>
            public float lastRun;
            #endregion

            #region Reload
            /// <summary>
            /// When is the next reloading phase over?
            /// </summary>
            public float reloadNextEnd; //This is only so we don't have to use a coroutine
            /// <summary>
            /// The current phase of reloading
            /// </summary>
            public int reloadPhase;
            #endregion

            #region Procedural Reload
            /// <summary>
            /// Set to true if the player attemps to fire during the reload
            /// </summary>
            public bool cancelProceduralReload = false;
            #endregion

            #region Aiming
            /// <summary>
            /// Are we currently aiming?
            /// </summary>
            public bool isAiming;

            /// <summary>
            /// So we can see if it changed, based on bools only
            /// </summary>
            public bool lastIsAiming;

            /// <summary>
            /// Float between 0 and 1 that indicates how far we have aimed in
            /// </summary>
            public float aimingProgress;

            /// <summary>
            /// Are we aimed in?
            /// </summary>
            public bool isAimedIn
            {
                get
                {
                    return Mathf.Approximately(aimingProgress, 1f);
                }
            }

            /// <summary>
            /// If we are using the sniper scope, this helps us to only hide the weapon once
            /// </summary>
            public bool sniperWeaponHidden;

            /// <summary>
            /// Transform used to move the weapon to aiming position
            /// </summary>
            public Transform aimingTransform;
            #endregion

            #region Weapon Delay
            /// <summary>
            /// The transform to apply our delay effect to
            /// </summary>
            public Transform weaponDelayTransform;
            /// <summary>
            /// Current weapon delay target
            /// </summary>
            public Vector3 weaponDelayCur;
            /// <summary>
            /// Current Mouse X input for weapon delay
            /// </summary>
            public float weaponDelayCurrentX;
            /// <summary>
            /// Current Mouse Y input for weapon delay
            /// </summary>
            public float weaponDelayCurrentY;
            /// <summary>
            /// This is the difference from last frame
            /// </summary>
            public Quaternion weaponDelayLastDifference;
            /// <summary>
            /// To get difference, we use quaternion of look root
            /// </summary>
            public Quaternion weaponDelayLastRotation;
            #endregion

            #region Weapon Fall
            public Transform weaponFallTransform;
            #endregion

            #region Shell Ejection
            /// <summary>
            /// Should we check if we should eject a shell?
            /// </summary>
            public bool shellEjectEnabled = false;
            /// <summary>
            /// At which point in time are we going to eject the shell?
            /// </summary>
            public float shellEjectNext;
            #endregion

            #region  Bolt Action
            /// <summary>
            /// 0 = Nothing; 1 = Eject next; 2 = Eject next (last shot)
            /// </summary>
            public int boltActionState;
            /// <summary>
            /// When is the eject definied in <see cref="boltActionState"/> going to happen?
            /// </summary>
            public float boltActionTime;
            #endregion

            /// <summary>
            /// Which animation was played the last time? Used to only call CrossFade once so it transitions correctly.
            /// </summary>
            public int lastWeaponAnimationID;

            #region Spray Pattern
            /// <summary>
            /// Current state of the spray pattern
            /// </summary>
            public float sprayPatternState = 0f;
            #endregion

            #region Run Animation
            /// <summary>
            /// Is the running animation (using non generic mecanim) currently playing?
            /// </summary>
            public bool startedRunAnimation;
            #endregion

            #region Sound
            /// <summary>
            /// Audio source used for fire sounds
            /// </summary>
            public AudioSource soundFire;

            /// <summary>
            /// Audio Source used for reload sounds
            /// </summary>
            public AudioSource soundReload;

            /// <summary>
            /// Audio Source used for other sounds
            /// </summary>
            public AudioSource soundOther;
            #endregion

            #region Input
            public bool lastLmb;
            public bool lastRmb;
            public bool lastReload;
            #endregion

            #region Spring
            /// <summary>
            /// Positional spring
            /// </summary>
            public Kit_Spring springPos;
            /// <summary>
            /// Rotational spring
            /// </summary>
            public Kit_Spring springRot;
            #endregion

            //In order to modify the stats, we copy them here first
            #region Modifyable stats
            #region Settings
            [Header("Settings")]
            public FireMode fireMode = FireMode.Semi;
            public float fireRate = 0.1f;
            /// <summary>
            /// How many bullets do we have per mag
            /// </summary>
            public int bulletsPerMag = 30;
            /// <summary>
            /// With how many <see cref="WeaponControllerRuntimeData.bulletsLeftToReload"/> do we start?
            /// </summary>
            public int bulletsToReloadAtStart = 60;
            /// <summary>
            /// How much damage does this weapon deal?
            /// </summary>
            public float baseDamage = 30f;
            /// <summary>
            /// The max. range of this weapon
            /// </summary>
            public float range = 500f;
            /// <summary>
            /// This is the damage drop off relative to <see cref="range"/>. It should be a value between 0 and 1 that multiplies with <see cref="baseDamage"/>
            /// </summary>
            public AnimationCurve damageDropoff = AnimationCurve.Linear(0, 1, 500f, 0.8f);
            /// <summary>
            /// Should we shoot one bullet at a time or pellets?
            /// </summary>
            public FireTypeMode fireTypeMode = FireTypeMode.Simple;
            /// <summary>
            /// If we are firing using burst, how many bullets should be fired in one burst?
            /// </summary>
            public int burstBulletsPerShot = 3;
            /// <summary>
            /// How many seconds need to pass between individual shots in burst fire mode?
            /// </summary>
            public float burstTimeBetweenShots = 0.1f;
            /// <summary>
            /// If we should shoot pellets, how many should we shoot?
            /// </summary>
            public int amountOfPellets = 12;
            /// <summary>
            /// How much should the weapon slow us down (base)?  Other values are multiplier by this.
            /// </summary>
            public float speedMultiplierBase = 1f;
            /// <summary>
            /// How much force is applied to the ragdoll?
            /// </summary>
            public float ragdollForce = 500f;
            #endregion

            #region Bullets
            /// <summary>
            /// Determines whether we should use raycast or physical bullets
            /// </summary>
            public BulletMode bulletsMode = BulletMode.Raycast;
            /// <summary>
            /// Is bullet penetration enabled?
            /// </summary>
            public bool bulletsPenetrationEnabled = true;
            /// <summary>
            /// This is a value that determines how many objects can be penetrated with this bullet. Think of it as the money you have available to pay the penetration cost here <see cref="Kit_PenetrateableObject.cost"/>
            /// </summary>
            public int bulletsPenetrationForce = 3;
            /// <summary>
            /// If <see cref="bulletsMode"/> is set to <see cref="BulletMode.Physical"/>, this prefab will be instantiated instead of raycast
            /// </summary>
            public GameObject bulletPrefab;
            /// <summary>
            /// Speed of physical bullet in m/s
            /// </summary>
            public float bulletSpeed = 200;
            /// <summary>
            /// How many frames after shooting should the bullet be hidden?
            /// </summary>
            public int bulletHideForFrames = 3;
            /// <summary>
            /// Multiplier of gravity applied to bullet (1 = realistic). Multiplies with <see cref="Physics.gravity"/>
            /// </summary>
            public float bulletGravityMultiplier = 1f;
            /// <summary>
            /// How long will this weapon's bullets exist?
            /// </summary>
            public float bulletLifeTime = 10f;
            /// <summary>
            /// Should the bullet parent itself to its hit thing and stay alive? Useful for things like nails and arrows.
            /// </summary>
            public bool bulletStaysAliveAfterDeath = false;
            /// <summary>
            /// If bullet stays alive after death, this is how long
            /// </summary>
            public float bulletStaysAliveAfterDeathTime = 10f;
            #endregion

            #region Bullet Spread
            /// <summary>
            /// Which spray m ode does this weapon use?
            /// </summary>
            public SpreadMode bulletSpreadMode = SpreadMode.Simple;
            /// <summary>
            /// Base value for hip bullet spread
            /// </summary>
            public float bulletSpreadHipBase = 0.1f;
            /// <summary>
            /// How much spread should be added for our current velocity in reference to <see cref="bulletSpreadHipVelocityReference"/>
            /// </summary>
            public float bulletSpreadHipVelocityAdd = 0.1f;
            /// <summary>
            /// Reference velocity for hip spread
            /// </summary>
            public float bulletSpreadHipVelocityReference = 6f;

            /// <summary>
            /// Base value for aiming bullet spread
            /// </summary>
            public float bulletSpreadAimBase = 0.01f;
            /// <summary>
            /// How much spread should be added for our current velocity in reference to <see cref="bulletSpreadAimVelocityReference"/>
            /// </summary>
            public float bulletSpreadAimVelocityAdd = 0.02f;
            /// <summary>
            /// Reference velocity for aim spread
            /// </summary>
            public float bulletSpreadAimVelocityReference = 6f;

            //SPRAY PATTERN
            /// <summary>
            /// Offset values for the spray pattern mode
            /// </summary>
            public Vector3[] bulletSpreadSprayPattern = new Vector3[30];
            /// <summary>
            /// How fast does the spray pattern recover?
            /// </summary>
            public float bulletSpreadSprayPatternRecoverySpeed = 1f;
            //END
            #endregion

            #region Recoil
            /// <summary>
            /// Minimum amount of recoil per shot
            /// </summary>
            public Vector2 recoilPerShotMin = new Vector2 { x = -0.1f, y = 0.5f };
            /// <summary>
            /// Max amount of recoil per shot
            /// </summary>
            public Vector2 recoilPerShotMax = new Vector2 { x = 0.1f, y = 1f };
            /// <summary>
            /// How fast is our recoil going to be applied
            /// </summary>
            public float recoilApplyTime = 0.1f;
            /// <summary>
            /// How fast are we going to return to our normal position?
            /// </summary>
            public float recoilReturnSpeed = 6f;
            #endregion
            #endregion

            public void FiredWeapon(Kit_PlayerBehaviour target, int damageIndex, Vector3 originPoint, Vector3 hitPoint)
            {
                if (!pb.isBot)
                {
                    CmdFiredWeapon(target, damageIndex, originPoint, hitPoint);
                }
            }

            [Command]
            public void CmdFiredWeapon(Kit_PlayerBehaviour target, int damageIndex, Vector3 originPoint, Vector3 hitPoint)
            {
                FiredWeaponInternal(target, damageIndex, originPoint, hitPoint);
            }

            private void FiredWeaponInternal(Kit_PlayerBehaviour target, int damageIndex, Vector3 originPoint, Vector3 hitPoint)
            {
                if (!pb.isBot)
                {
                    if (target)
                    {
                        // 'this' fired at 'target'.
                        // we need to grab 'target's LagCompensator to find out where it was.
                        LagCompensator compensator = target.GetComponent<LagCompensator>();

                        if (compensator)
                        {
                            RaycastHit hit;

                            Vector3 dir = (hitPoint - originPoint).normalized;

                            // next, we can use either of the compensator's 'Check...' functions.
                            // viewer is the one who fired, so it's 'this' object.
                            // Lag Compensator checks 'this' object's latency and then rewinds 'target's history by that time.
                            if (compensator.RaycastCheck(this.connectionToClient, originPoint, hitPoint, pb.gameInformation.lagCompensationTolerancePercent, target.gameInformation.weaponHitLayersNoPlayers, out hit))
                            {
                                List<Kit_PlayerBehaviour> damagedPlayers = new List<Kit_PlayerBehaviour>();
                                #region Damage
                                //Check if we hit a player
                                //if (hit.collider.GetComponent<Kit_PlayerDamageMultiplier>() && Kit_IngameMain.instance.currentGameModeType == 2)
                                {
                                    Kit_PlayerDamageMultiplier pdm = target.thirdPersonPlayerModel.damageMultipliers[damageIndex];
                                    //if (hit.collider.transform.root.GetComponent<Kit_PlayerBehaviour>())
                                    {
                                        //First check if we can actually damage that player
                                        if (Kit_IngameMain.instance.currentPvPGameModeBehaviour.ArePlayersEnemies(pb, target))
                                        {
                                            //Check if he has spawn protection
                                            if (!target.spawnProtection || target.spawnProtection.CanTakeDamage(target))
                                            {
                                                //Apply local damage, sample damage dropoff via distance
                                                if (pb.isServer)
                                                {
                                                    target.ServerDamage((behaviour as Kit_ModernWeaponScript).GetDamage(this, Vector3.Distance(pb.playerCameraTransform.position, hit.point)) * pdm.damageMultiplier, id, pb.transform.position, dir, ragdollForce, hit.point, /*pdm.ragdollId*/ 0, pb.isBot, pb.id);
                                                    if (!pb.isBot) pb.TargetHitMarker(0);
                                                }
                                            }
                                            else if (pb.isServer)
                                            {
                                                //We hit a player but his spawn protection is active
                                                if (!pb.isBot) pb.TargetHitMarker(1);
                                            }
                                        }
                                    }
                                }
                                #endregion
                            }
                        }
                    }
                }
            }


            public void FiredWeapon(LagCompensator target, Vector3 originPoint, Vector3 hitPoint)
            {
                if (!pb.isBot)
                {
                    CmdFiredWeapon(target, originPoint, hitPoint);
                }
            }

            [Command]
            public void CmdFiredWeapon(LagCompensator target, Vector3 originPoint, Vector3 hitPoint)
            {
                FiredWeaponInternal(target, originPoint, hitPoint);
            }

            private void FiredWeaponInternal(LagCompensator target, Vector3 originPoint, Vector3 hitPoint)
            {
                if (!pb.isBot)
                {
                    if (target)
                    {
                        // 'this' fired at 'target'.
                        // we need to grab 'target's LagCompensator to find out where it was.
                        LagCompensator compensator = target.GetComponent<LagCompensator>();

                        if (compensator)
                        {
                            RaycastHit hit;

                            Vector3 dir = (hitPoint - originPoint).normalized;

                            // next, we can use either of the compensator's 'Check...' functions.
                            // viewer is the one who fired, so it's 'this' object.
                            // Lag Compensator checks 'this' object's latency and then rewinds 'target's history by that time.
                            if (compensator.RaycastCheck(this.connectionToClient, originPoint, hitPoint, pb.gameInformation.lagCompensationTolerancePercent, pb.gameInformation.weaponHitLayersNoPlayers, out hit))
                            {
                                //Apply local damage, sample damage dropoff via distance
                                if (pb.isServer)
                                {
                                    var dmg = target.GetComponentInChildren<IKitDamageable>();
                                    if (dmg != null)
                                    {
                                        float damage = (behaviour as Kit_ModernWeaponScript).GetDamage(this, Vector3.Distance(pb.playerCameraTransform.position, hit.point));
                                        dmg.ServerDamage(damage, id, pb.playerCameraTransform.position, dir, ragdollForce, hit.point, pb.isBot, pb.id);
                                        //Show hitmarker
                                        pb.TargetHitMarker(0);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
using Mirror;
using UnityEngine;

namespace MarsFPSKit
{
    /// <summary>
    /// Upon entering, this will be picked up
    /// </summary>
    public class Kit_AmmoPickup : NetworkBehaviour
    {
        /// <summary>
        /// How many clips will be picked up
        /// </summary>
        public int amountOfClipsToPickup = 2;
        /// <summary>
        /// Rigidbody
        /// </summary>
        public Rigidbody body;
        /// <summary>
        /// Root object of renderer to hide before destroyed
        /// </summary>
        public GameObject renderRoot;
        
        public void PickedUp()
        {
            if (isServer)
            {
                NetworkServer.Destroy(gameObject);
            }
        }
    }
}

I want to place a weapon (and other elements) in the scene and want the player to be able to walk through it. If he walks through a weapon the current weapon should change to the picked up weapon. The weapon should than disappear like in the Quranity example and than appear again after 5 seconds. If the player meanwhile shoots, he should be able to refill his ammo for the weapon with this ID (like in the example above) use that as an inspiration and generate new full scripts 100% compatible with MMFPSE, ready to copy and paste