/// <summary>
    /// initializes the collider, id, amount and vp_Toss component of a pickup
    /// (vp_Toss is what moves the pickup in a nice bouncy arc when dropped).
    /// </summary>
    protected virtual vp_Toss InitPickup(GameObject pickup, int units, int id)
    {
        Collider[] cs = pickup.GetComponentsInChildren <Collider>();
        foreach (Collider c in cs)
        {
            if (c.isTrigger)
            {
                Collider cc = c;
                vp_Timer.In(1, delegate()
                {
                    if (cc != null)
                    {
                        cc.enabled = true;
                    }
                });
            }
            else
            {
                c.enabled = false;
            }
        }

        // NOTE: in the case of throwing weapons (e.g. grenades), 2 vp_ItemPickup
        // components may be present on the pickup gameobject. the first is for
        // the grenade unit (ammo) type and the second is for the 'grenade thrower'
        // weapon. to account for this case, there's some special logic to initing
        // the components of a pickup prefab:

        // fetch all pickup components on the prefab
        vp_ItemPickup[] ips = pickup.GetComponentsInChildren <vp_ItemPickup>();
        if (ips.Length == 0)
        {
            return(null);               // abort if no pickup components
        }
        // iterate through all pickup components on this prefab (usually only one)
        for (int v = 0; v < ips.Length; v++)
        {
            if (ips[v] == null)
            {
                if (v == 0)
                {
                    return(null);                       // abort if first pickup component is null
                }
                continue;                               // skip if the second or later component is null
            }
            ips[v].ID = id + v;                         // id of first component will be 'id', the rest will be incremented from that
            if (v == 0)
            {
                ips[v].Amount = units;                  // only the first pickup component can have a count, the rest will be zero.
            }
            // IMPORTANT: for grenades, the first vp_ItemPickup component must be for the unit, and the second for the throwing weapon
            else
            {
                ips[v].Amount = 0;
            }

            // handle edge cases where ammo pickups end up empty - by canceling the item drop
            if (ips[v].ItemTypeObject is vp_UnitType && (units == 0))
            {
                return(null);
            }

            vp_GlobalEvent <vp_ItemPickup> .Send("RegisterPickup", ips[v]);             // this only has effect in multiplayer. NOTE: we must register all pickup

            // components on the transform (grenade throwers as well as grenade units)
        }

        // make sure the pickup has a toss component and return it
        vp_Toss toss = pickup.GetComponent <vp_Toss>();

        if (toss == null)
        {
            toss = pickup.AddComponent <vp_Toss>();
        }

        return(toss);
    }
    /// <summary>
    /// internal method for handling all aspects of dropping any item type.
    /// NOTE: in multiplayer, this only gets run on the master, which in turn
    /// triggers 'MPClientDropItem' on clients
    /// </summary>
    protected virtual bool TryDropItemInternal(vp_ItemType itemType, Vector3 direction, int units = 0, int id = 0)
    {
        //Debug.Log("TryDropItemInternal: " + itemType + ", " + units);

        if (!vp_Gameplay.IsMaster)
        {
            return(false);
        }

        // fetch pickup prefab of the item type
        GameObject prefab = GetPickupPrefab(itemType, units);

        if (prefab == null)
        {
            return(false);
        }

        // try to remove item records or units from the inventory
        if (!TryRemoveItem(itemType, units, id))
        {
            return(false);
        }

        // keep track of the number of item drop attempts for circular distribution and max cap
        UpdateDropCount();

        // abort if we can't drop any more items at once
        if (m_ItemDropsThisFrame >= MaxDrops)
        {
            return(false);
        }


        // spawn a pickup
        GameObject pickup = SpawnPickup(
            prefab,
            transform.position                                  // position of inventory owner
            + (Vector3.up * SpawnHeight)                        // plus a height offset
            + (direction * SpawnDistance)                       // plus a little bit away from body of inventory holder
            );

        if (pickup == null)
        {
            return(false);
        }

        // in the UFPS multiplayer add-on, every pickup must be assigned a unique id on
        // spawn / startup (+ the id feature is reserved for the vp_MPPickupManager system)
        if (vp_Gameplay.IsMultiplayer)
        {
            id = vp_Utility.UniqueID;
        }

        // initialize the pickup with a collision timer, units, id and a toss component
        vp_Toss toss = InitPickup(pickup, units, id);

        if (toss == null)
        {
            Object.Destroy(pickup);
            return(false);
        }

        // item drop success! play drop sound
        TryPlayDropSound();

        // offset direction based on how many items we have thrown this frame, so as
        // to not throw items inside each other
        direction = GetCircleDirection(direction);

        // toss the pickup away from the owner
        // the first 8 items will be tossed to 'm_DestDistance'. after that,
        // 75% of the initial distance will be added every 8 items, causing
        // large amounts of items to be arranged in concentric rings
        float   additionalDistance = (((int)((m_ItemDropsThisFrame - 1) / 8)) * (TossDistance * 0.75f));
        float   targetYaw          = Random.Range(-180, 180);
        Vector3 finalPosition      = toss.Toss(pickup.transform.position, targetYaw, direction, (TossDistance + additionalDistance), !DisableBob, !DisableSpin);

        //Debug.Log("item drops: " + m_ItemDropsThisFrame + ", additional: " + additionalDistance);

        // notify master object
        vp_ItemPickup itemPickup = pickup.GetComponentInChildren <vp_ItemPickup>();

        if (itemPickup != null)
        {
            vp_GlobalEvent <object[]> .Send("TransmitDropItem", new object[] { itemType.name, finalPosition, targetYaw, transform.root, itemPickup.ID, itemPickup.Amount });
        }

        return(true);
    }
    /// <summary>
    /// forces a multiplayer client to drop an item (remotely triggered by
    /// the master in multiplayer)
    /// </summary>
    public virtual void MPClientDropItem(string itemTypeName, Vector3 targetPosition, float targetYaw, int id, int units)
    {
        vp_ItemType itemType;

        if (!m_SceneItemTypesByName.TryGetValue(itemTypeName, out itemType))
        {
            return;
        }

        if (itemType == null)
        {
            return;
        }

        if (itemType is vp_UnitType)
        {
            TryRemoveItem(itemType, units);
        }
        else
        {
            TryRemoveItem(itemType);
        }

        // fetch pickup prefab of the item type
        GameObject prefab = GetPickupPrefab(itemType, units);

        if (prefab == null)
        {
            return;
        }

        // spawn a pickup
        GameObject pickup = SpawnPickup(
            prefab,
            transform.position                                  // position of inventory owner
            + (Vector3.up * SpawnHeight)                        // plus a height offset
            // plus offset in the direction of the target position (but at the original height)
            + vp_3DUtility.HorizontalVector((targetPosition - transform.position).normalized * SpawnDistance)
            );

        if (pickup == null)
        {
            return;
        }

        // initialize the pickup with a collision timer, a toss component,
        // and the units and id as specified by master
        vp_Toss toss = InitPickup(pickup, units, id);

        if (toss == null)
        {
            Object.Destroy(pickup);
            return;
        }

        // toss the pickup to the specified position - without local collision
        // detection - which is assumed to have been already taken care of on
        // the master
        toss.Toss(pickup.transform.position, targetPosition, targetYaw, !DisableBob, !DisableSpin);

        // play sound
        TryPlayDropSound();
    }