/// <summary>
        /// Similar to GetExcessThing though narrower in scope.  If there is NOT a loadout which covers the equipped item, it should be dropped.
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="dropEquipment">Thing which should be unequiped.</param>
        /// <returns>bool, true if there is equipment that should be unequipped.</returns>
        static public bool GetExcessEquipment(this Pawn pawn, out ThingWithComps dropEquipment)
        {
            Loadout loadout = pawn.GetLoadout();

            dropEquipment = null;
            if (loadout == null || (loadout != null && loadout.Slots.NullOrEmpty()) || pawn.equipment?.Primary == null)
            {
                return(false);
            }

            if (pawn.IsItemQuestLocked(pawn.equipment?.Primary))
            {
                return(false);
            }

            //Check if equipment is part of the loadout
            LoadoutSlot eqSlot = loadout.Slots.FirstOrDefault(s => s.count >= 1 && ((s.thingDef != null && s.thingDef == pawn.equipment.Primary.def) ||
                                                                                    (s.genericDef != null && s.genericDef.lambda(pawn.equipment.Primary.def))));

            //Check if equipment is in the forced pick-up items list
            HoldRecord eqRecord = pawn.GetHoldRecords()?.FirstOrDefault(s => s.count >= 1 && s.thingDef != null && s.thingDef == pawn.equipment.Primary.def);

            if (eqSlot == null && eqRecord == null)
            {
                dropEquipment = pawn.equipment.Primary;
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// Called when trying to find something to drop (ie coming back from a caravan).  This is useful even on pawns without a loadout.
        /// </summary>
        /// <param name="dropThing">Thing to be dropped from inventory.</param>
        /// <param name="dropCount">Amount to drop from inventory.</param>
        /// <returns></returns>
        static public bool GetAnythingForDrop(this Pawn pawn, out Thing dropThing, out int dropCount)
        {
            dropThing = null;
            dropCount = 0;

            if (pawn.inventory == null || pawn.inventory.innerContainer == null)
            {
                return(false);
            }

            Loadout loadout = pawn.GetLoadout();

            if (loadout == null || loadout.Slots.NullOrEmpty())
            {
                List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);
                if (recs != null)
                {
                    // hand out any inventory item not covered by a HoldRecord.
                    foreach (Thing thing in pawn.inventory.innerContainer)
                    {
                        if (pawn.IsItemQuestLocked(thing))
                        {
                            continue;   //quest requirements prevent this from being dropped, skip to next thing in inventory
                        }
                        int        numContained = pawn.inventory.innerContainer.TotalStackCountOfDef(thing.def);
                        HoldRecord rec          = recs.FirstOrDefault(hr => hr.thingDef == thing.def);
                        if (rec == null)
                        {
                            // we don't have a HoldRecord for this thing, drop it.
                            dropThing = thing;
                            dropCount = numContained > dropThing.stackCount ? dropThing.stackCount : numContained;
                            return(true);
                        }

                        if (numContained > rec.count)
                        {
                            dropThing = thing;
                            dropCount = numContained - rec.count;
                            dropCount = dropCount > dropThing.stackCount ? dropThing.stackCount : dropCount;
                            return(true);
                        }
                    }
                }
                else
                {
                    // we have nither a HoldTracker nor a Loadout that we can ask, so just pick a random non-quest item from Inventory.
                    dropThing = pawn.inventory.innerContainer.Where(inventoryItem => !pawn.IsItemQuestLocked(inventoryItem))?.RandomElement();
                    dropCount = dropThing?.stackCount ?? 0;
                }
            }
            else
            {
                // hand out an item from GetExcessThing...
                return(GetExcessThing(pawn, out dropThing, out dropCount));
            }

            return(false);
        }
示例#3
0
        /// <summary>
        /// Used when a pawn is about to be ordered to pickup a Thing.
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="job"></param>
        static public void Notify_HoldTrackerJob(this Pawn pawn, Job job)
        {
            // make sure it's the right kind of job.
            if (job.def != JobDefOf.TakeInventory)
            {
                throw new ArgumentException();
            }

            // if the pawn doesn't have a normal loadout, nothing to do...
            if (pawn.GetLoadout().defaultLoadout)
            {
                return;
            }

            // find out if we are already remembering this thing on this pawn...
            List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);

            if (recs == null)
            {
                recs = new List <HoldRecord>();
                LoadoutManager.AddHoldRecords(pawn, recs);
            }

            // could check isHeld but that tells us if there is a record AND it's been picked up.
            HoldRecord rec = recs.FirstOrDefault(hr => hr.thingDef == job.targetA.Thing.def);

            if (rec != null)
            {
                if (rec.pickedUp)
                {
                    // modifying a record for which the pawn should have some of that thing in their inventory.
                    CompInventory inventory = pawn.TryGetComp <CompInventory>();
                    if (inventory != null)
                    {
                        rec.count = inventory.container.TotalStackCountOfDef(rec.thingDef) + job.count;
                    }
                    else
                    {
                        rec.count += job.count; // probably won't generally follow this code path but good not to throw an error if possible.
                    }
                }
                else
                {
                    // modifying a record that hasn't been picked up... do it blind.
                    rec.count += job.count;
                }
                // useful debug message...
                //Log.Message(string.Concat("Job was issued to pickup items for this existing record: ", rec));
                return;
            }
            // if we got this far we know that there isn't a record being stored for this thingDef...
            rec = new HoldRecord(job.targetA.Thing.def, job.count);
            recs.Add(rec);
            // useful debug message...
            //Log.Message(string.Concat("Job was issued to pickup for this new record: ", rec));
        }
示例#4
0
        /// <summary>
        /// Called when trying to find something to drop (ie coming back from a caravan).  This is useful even on pawns without a loadout.
        /// </summary>
        /// <param name="dropThing">Thing to be dropped from inventory.</param>
        /// <param name="dropCount">Amount to drop from inventory.</param>
        /// <returns></returns>
        static public bool GetAnythingForDrop(this Pawn pawn, out Thing dropThing, out int dropCount)
        {
            dropThing = null;
            dropCount = 0;

            if (pawn.inventory == null || pawn.inventory.innerContainer == null)
            {
                return(false);
            }

            Loadout loadout = pawn.GetLoadout();

            if (loadout == null || loadout.Slots.NullOrEmpty())
            {
                List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);
                if (recs != null)
                {
                    // hand out any inventory item not covered by a HoldRecord.
                    foreach (Thing thing in pawn.inventory.innerContainer)
                    {
                        int        numContained = pawn.inventory.innerContainer.TotalStackCountOfDef(thing.def);
                        HoldRecord rec          = recs.FirstOrDefault(hr => hr.thingDef == thing.def);
                        if (rec == null)
                        {
                            // we don't have a HoldRecord for this thing, drop it.
                            dropThing = thing;
                            dropCount = numContained > dropThing.stackCount ? dropThing.stackCount : numContained;
                            return(true);
                        }

                        if (numContained > rec.count)
                        {
                            dropThing = thing;
                            dropCount = numContained - rec.count;
                            dropCount = dropCount > dropThing.stackCount ? dropThing.stackCount : dropCount;
                            return(true);
                        }
                    }
                }
                else
                {
                    // we have nither a HoldTracker nor a Loadout that we can ask, so just pick stuff at random from Inventory.
                    dropThing = pawn.inventory.innerContainer.RandomElement <Thing>();
                    dropCount = dropThing.stackCount;
                }
            }
            else
            {
                // hand out an item from GetExcessThing...
                return(GetExcessThing(pawn, out dropThing, out dropCount));
            }

            return(false);
        }
        /// <summary>
        /// Called when a pawn is instructed to drop something as well as if the user explicitly specifies the item should no longer be held onto.
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="thing">Thing who's def should be forgotten.</param>
        public static void HoldTrackerForget(this Pawn pawn, Thing thing)
        {
            List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);

            if (recs == null)
            {
                Log.Error(string.Concat(pawn.Name, " wasn't being tracked by HoldTracker and tried to forget a ThingDef ", thing.def, "."));
                return;
            }
            HoldRecord rec = recs.FirstOrDefault(hr => hr.thingDef == thing.def);

            if (rec != null)
            {
                recs.RemoveAt(recs.IndexOf(rec));
            }
        }
        /// <summary>
        /// Used when a pawn is about to be ordered to pickup a Thing.
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="item"></param>
        /// <param name="count"></param>
        static public void Notify_HoldTrackerItem(this Pawn pawn, Thing item, int count)
        {
            // if the pawn doesn't have a normal loadout, nothing to do...
            if (pawn.GetLoadout().defaultLoadout)
            {
                return;
            }

            // find out if we are already remembering this thing on this pawn...
            List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);

            if (recs == null)
            {
                recs = new List <HoldRecord>();
                LoadoutManager.AddHoldRecords(pawn, recs);
            }

            // could check isHeld but that tells us if there is a record AND it's been picked up.
            HoldRecord rec = recs.FirstOrDefault(hr => hr.thingDef == item.def);

            if (rec != null)
            {
                if (rec.pickedUp)
                {
                    // modifying a record for which the pawn should have some of that thing in their inventory.
                    rec.count = GetMagazineAwareStackCount(pawn, rec.thingDef) + count;
                }
                else
                {
                    // modifying a record that hasn't been picked up... do it blind.
                    rec.count += count;
                }
                // useful debug message...
                //Log.Message(string.Concat("Job was issued to pickup items for this existing record: ", rec));
                return;
            }
            // if we got this far we know that there isn't a record being stored for this thingDef...
            rec = new HoldRecord(item.def, count);
            recs.Add(rec);
            // useful debug message...
            //Log.Message(string.Concat("Job was issued to pickup for this new record: ", rec));
        }
        /// <summary>
        /// Find an item that should be dropped from the pawn's inventory and how much to drop.
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="dropThing">The thing which should be dropped.</param>
        /// <param name="dropCount">The amount to drop.</param>
        /// <returns>bool, true indicates that the out variables are filled with something to do work on (drop).</returns>
        // NOTE (ProfoundDarkness): Ended up doing this by nibbling away at the pawn's inventory (or dictionary representation of ThingDefs/Count).
        //  Probably not efficient but was easier to handle atm.
        static public bool GetExcessThing(this Pawn pawn, out Thing dropThing, out int dropCount)
        {
            //(ProfoundDarkness) Thanks to erdelf on the RimWorldMod discord for helping me figure out some dictionary stuff and C# concepts related to 'Primitives' (pass by Value).
            CompInventory     inventory = pawn.TryGetComp <CompInventory>();
            Loadout           loadout   = pawn.GetLoadout();
            List <HoldRecord> records   = LoadoutManager.GetHoldRecords(pawn);

            dropThing = null;
            dropCount = 0;

            if (inventory == null || inventory.container == null || loadout == null || loadout.Slots.NullOrEmpty())
            {
                return(false);
            }

            Dictionary <ThingDef, Integer> listing = GetStorageByThingDef(pawn);

            // iterate over specifics and generics and Chip away at the dictionary.
            foreach (LoadoutSlot slot in loadout.Slots)
            {
                if (slot.thingDef != null && listing.ContainsKey(slot.thingDef))
                {
                    listing[slot.thingDef].value -= slot.count;
                    if (listing[slot.thingDef].value <= 0)
                    {
                        listing.Remove(slot.thingDef);
                    }
                }
                if (slot.genericDef != null)
                {
                    List <ThingDef> killKeys     = new List <ThingDef>();
                    int             desiredCount = slot.count;
                    // find dictionary entries which corespond to covered slot.
                    foreach (ThingDef def in listing.Keys.Where(td => slot.genericDef.lambda(td)))
                    {
                        listing[def].value -= desiredCount;
                        if (listing[def].value <= 0)
                        {
                            desiredCount = 0 - listing[def].value;
                            killKeys.Add(def); // the thing in inventory is exausted, forget about it.
                        }
                        else
                        {
                            break; // we have satisifed this loadout so no need to keep enumerating.
                        }
                    }
                    // cleanup dictionary.
                    foreach (ThingDef def in killKeys)
                    {
                        listing.Remove(def);
                    }
                }
            }

            // if there is something left in the dictionary, that is what is to be dropped.
            // Complicated by the fact that we now consider ammo in guns as part of the inventory...
            if (listing.Any())
            {
                if (records != null && !records.NullOrEmpty())
                {
                    // look at each remaining 'uneaten' thingdef in pawn's inventory.
                    foreach (ThingDef def in listing.Keys)
                    {
                        HoldRecord rec = records.FirstOrDefault(r => r.thingDef == def);
                        if (rec == null)
                        {
                            // the item we have extra of has no HoldRecord, drop it.
                            dropThing = inventory.container.FirstOrDefault(t => t.def == def && !pawn.IsItemQuestLocked(t));
                            if (dropThing != null)
                            {
                                dropCount = listing[def].value > dropThing.stackCount ? dropThing.stackCount : listing[def].value;
                                return(true);
                            }
                        }
                        else if (rec.count < listing[def].value)
                        {
                            // the item we have extra of HAS a HoldRecord but the amount carried is above the limit of the HoldRecord, drop extra.
                            dropThing = pawn.inventory.innerContainer.FirstOrDefault(t => t.def == def && !pawn.IsItemQuestLocked(t));
                            if (dropThing != null)
                            {
                                dropCount = listing[def].value - rec.count;
                                dropCount = dropCount > dropThing.stackCount ? dropThing.stackCount : dropCount;
                                return(true);
                            }
                        }
                    }
                }
                else
                {
                    foreach (ThingDef def in listing.Keys)
                    {
                        dropThing = inventory.container.FirstOrDefault(t => t.GetInnerIfMinified().def == def && !pawn.IsItemQuestLocked(t));
                        if (dropThing != null)
                        {
                            dropCount = listing[def].value > dropThing.stackCount ? dropThing.stackCount : listing[def].value;
                            return(true);
                        }
                    }
                }
            } // else
            return(false);
        }
示例#8
0
        /// <summary>
        /// Refreshes the cached bulk and weight. Call this whenever items are added/removed from inventory
        /// </summary>
        public void UpdateInventory()
        {
            if (parentPawn == null)
            {
                Log.Error("CompInventory on non-pawn " + parent.ToString());
                return;
            }
            float newBulk   = 0f;
            float newWeight = 0f;

            // Add equipped weapon
            if (parentPawn.equipment != null && parentPawn.equipment.Primary != null)
            {
                GetEquipmentStats(parentPawn.equipment.Primary, out newWeight, out newBulk);
            }

            // Add apparel
            if (parentPawn.apparel != null && parentPawn.apparel.WornApparelCount > 0)
            {
                foreach (Thing apparel in parentPawn.apparel.WornApparel)
                {
                    float apparelBulk   = apparel.GetStatValue(CE_StatDefOf.WornBulk);
                    float apparelWeight = apparel.GetStatValue(StatDefOf.Mass);
                    newBulk   += apparelBulk;
                    newWeight += apparelWeight;
                    if (apparelBulk > 0 && parentPawn != null && parentPawn.IsColonist && parentPawn.Spawned)
                    {
                        LessonAutoActivator.TeachOpportunity(CE_ConceptDefOf.CE_WornBulk, OpportunityType.GoodToKnow);
                    }
                }
            }

            // Add inventory items
            if (parentPawn.inventory != null && parentPawn.inventory.innerContainer != null)
            {
                ammoListCached.Clear();
                meleeWeaponListCached.Clear();
                rangedWeaponListCached.Clear();

                List <HoldRecord> recs = LoadoutManager.GetHoldRecords(parentPawn);
                foreach (Thing thing in parentPawn.inventory.innerContainer)
                {
                    // Check for weapons
                    ThingWithComps eq     = thing as ThingWithComps;
                    CompEquippable compEq = thing.TryGetComp <CompEquippable>();
                    if (eq != null && compEq != null)
                    {
                        if (eq.def.IsRangedWeapon)
                        {
                            rangedWeaponListCached.Add(eq);
                        }
                        else
                        {
                            meleeWeaponListCached.Add(eq);
                        }
                        // Calculate equipment weight
                        float eqWeight;
                        float eqBulk;
                        GetEquipmentStats(eq, out eqWeight, out eqBulk);
                        newWeight += eqWeight * thing.stackCount;
                        newBulk   += eqBulk * thing.stackCount;
                    }
                    else
                    {
                        // Add item weight
                        newBulk   += thing.GetStatValue(CE_StatDefOf.Bulk) * thing.stackCount;
                        newWeight += thing.GetStatValue(StatDefOf.Mass) * thing.stackCount;
                    }
                    // Update ammo list
                    if (thing.def is AmmoDef)
                    {
                        ammoListCached.Add(thing);
                    }
                    if (recs != null)
                    {
                        HoldRecord rec = recs.FirstOrDefault(hr => hr.thingDef == thing.def);
                        if (rec != null && !rec.pickedUp)
                        {
                            rec.pickedUp = true;
                        }
                    }
                }
            }
            currentBulkCached   = newBulk;
            currentWeightCached = newWeight;
        }