/// <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);
        }
Example #2
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));
        }
        /// <summary>
        /// Simply reports back if the pawn is tracking an item by ThingDef if it's been picked up.
        /// </summary>
        /// <param name="pawn">Pawn to check tracking on.</param>
        /// <param name="thing">Thing who's def should be checked if being held.</param>
        /// <returns></returns>
        public static bool HoldTrackerIsHeld(this Pawn pawn, Thing thing)
        {
            List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);

            if (recs != null && recs.Any(hr => hr.thingDef == thing.def))
            {
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// Is there any hold tracker records for this pawn?  This doesn't care if an item is actually present in the pawn's inventory.
        /// </summary>
        /// <param name="pawn">Pawn who's HoldTracker is to be polled</param>
        /// <returns>bool indicating if the pawn has any HoldTracker records.</returns>
        public static bool HoldTrackerAnythingHeld(this Pawn pawn)
        {
            List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);

            if (recs == null || recs.NullOrEmpty())
            {
                return(false);
            }
            return(recs.Any(r => r.pickedUp));
        }
Example #5
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>
        /// This should be called periodically so that HoldTracker can remove items that are no longer in the inventory via a method which isn't being watched.
        /// </summary>
        /// <param name="pawn">The pawn who's tracker should be checked.</param>
        public static void HoldTrackerCleanUp(this Pawn pawn)
        {
            if (_tickLastPurge <= GenTicks.TicksAbs)
            {
                LoadoutManager.PurgeHoldTrackerRolls();
                _tickLastPurge = GenTicks.TicksAbs + GenDate.TicksPerDay;
            }
            List <HoldRecord> recs      = LoadoutManager.GetHoldRecords(pawn);
            CompInventory     inventory = pawn.TryGetComp <CompInventory>();

            if (recs == null || inventory == null)
            {
                return;
            }

            for (int i = recs.Count - 1; i > 0; i--)
            {
                if (recs[i].pickedUp && inventory.container.TotalStackCountOfDef(recs[i].thingDef) <= 0)
                {
                    recs.RemoveAt(i);
                }
            }
        }
        /// <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);
        }
 /// <summary>
 /// Makes it convenient to fetch a pawn's holdTracker (List of HoldRecords).
 /// </summary>
 /// <param name="pawn">Pawn to fetch the records for.</param>
 /// <returns>List of HoldRecords otherwise known as the Pawn's holdTracker.</returns>
 public static List <HoldRecord> GetHoldRecords(this Pawn pawn)
 {
     return(LoadoutManager.GetHoldRecords(pawn));
 }
        public static void HoldTrackerClear(this Pawn pawn)
        {
            List <HoldRecord> recs = LoadoutManager.GetHoldRecords(pawn);

            recs.Clear();
        }
Example #12
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;
        }
        /* (ProfoundDarkness) I've intentionally left some code remarked in the following code because it's a useful guide on how to create
         * and maintain the transpilers that will do nearly identical changes to RimWorld's code for the other 2 PawnColumnWorkers.
         */
        public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
        {
            if (pawn.outfits == null)
            {
                return;
            }
            //changed: int num = Mathf.FloorToInt((rect.width - 4f) * 0.714285731f);
            int num = Mathf.FloorToInt((rect.width - 4f) - IconSize);
            //changed: int num2 = Mathf.FloorToInt((rect.width - 4f) * 0.2857143f);
            int   num2 = Mathf.FloorToInt(IconSize);
            float num3 = rect.x;
            //added:
            float num4 = rect.y + ((rect.height - IconSize) / 2);

            // Reduce width if we're adding a clear forced button
            bool somethingIsForced = pawn.HoldTrackerAnythingHeld();
            Rect loadoutButtonRect = new Rect(num3, rect.y + 2f, (float)num, rect.height - 4f);

            if (somethingIsForced)
            {
                loadoutButtonRect.width -= 4f + (float)num2;
            }

            // Main loadout button
            string label = pawn.GetLoadout().label.Truncate(loadoutButtonRect.width, null);

            Widgets.Dropdown <Pawn, Loadout>(loadoutButtonRect, pawn, (Pawn p) => p.GetLoadout(), new Func <Pawn, IEnumerable <Widgets.DropdownMenuElement <Loadout> > >(Button_GenerateMenu), label, null, null, null, null, true);

            // Clear forced button
            num3 += loadoutButtonRect.width;
            num3 += 4f;
            //changed: Rect forcedHoldRect = new Rect(num3, rect.y + 2f, (float)num2, rect.height - 4f);
            Rect forcedHoldRect = new Rect(num3, num4, (float)num2, (float)num2);

            if (somethingIsForced)
            {
                if (Widgets.ButtonImage(forcedHoldRect, ClearImage))
                {
                    pawn.HoldTrackerClear(); // yes this will also delete records that haven't been picked up and thus not shown to the player...
                }
                TooltipHandler.TipRegion(forcedHoldRect, new TipSignal(delegate
                {
                    string text = "CE_ForcedHold".Translate() + ":\n";
                    foreach (HoldRecord rec in LoadoutManager.GetHoldRecords(pawn))
                    {
                        if (!rec.pickedUp)
                        {
                            continue;
                        }
                        text = text + "\n   " + rec.thingDef.LabelCap + " x" + rec.count;
                    }
                    return(text);
                }, pawn.GetHashCode() * 613));
                num3 += (float)num2;
                num3 += 4f;
            }

            //changed: Rect assignTabRect = new Rect(num3, rect.y + 2f, (float)num2, rect.height - 4f);
            Rect assignTabRect = new Rect(num3, num4, (float)num2, (float)num2);

            //changed: if (Widgets.ButtonText(assignTabRect, "AssignTabEdit".Translate(), true, false, true))
            if (Widgets.ButtonImage(assignTabRect, EditImage))
            {
                Find.WindowStack.Add(new Dialog_ManageLoadouts(pawn.GetLoadout()));
            }
            // Added this next line.
            TooltipHandler.TipRegion(assignTabRect, new TipSignal(textGetter("CE_Loadouts"), pawn.GetHashCode() * 613));
            num3 += (float)num2;
        }
Example #14
0
        /* (ProfoundDarkness) I've intentionally left some code remarked in the following code because it's a useful guide on how to create
         * and maintain the transpilers that will do nearly identical changes to RimWorld's code for the other 2 PawnColumnWorkers.
         */
        public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
        {
            if (pawn.outfits == null)
            {
                return;
            }
            //changed: int num = Mathf.FloorToInt((rect.width - 4f) * 0.714285731f);
            int num = Mathf.FloorToInt((rect.width - 4f) - IconSize);
            //changed: int num2 = Mathf.FloorToInt((rect.width - 4f) * 0.2857143f);
            int   num2 = Mathf.FloorToInt(IconSize);
            float num3 = rect.x;
            //added:
            float num4 = rect.y + ((rect.height - IconSize) / 2);

            // Reduce width if we're adding a clear forced button
            bool somethingIsForced = pawn.HoldTrackerAnythingHeld();
            Rect loadoutButtonRect = new Rect(num3, rect.y + 2f, (float)num, rect.height - 4f);

            if (somethingIsForced)
            {
                loadoutButtonRect.width -= 4f + (float)num2;
            }

            // Main loadout button
            string label = pawn.GetLoadout().label.Truncate(loadoutButtonRect.width, null);

            if (Widgets.ButtonText(loadoutButtonRect, label, true, false, true))
            {
                LoadoutManager.SortLoadouts();
                List <FloatMenuOption> options = new List <FloatMenuOption>();
                foreach (Loadout loadout in LoadoutManager.Loadouts)
                {
                    // need to create a local copy for delegate
                    Loadout localLoadout = loadout;
                    options.Add(new FloatMenuOption(localLoadout.LabelCap, delegate
                    {
                        pawn.SetLoadout(localLoadout);
                    }, MenuOptionPriority.Default, null, null));
                }
                Find.WindowStack.Add(new FloatMenu(options));
            }

            // Clear forced button
            num3 += loadoutButtonRect.width;
            num3 += 4f;
            //changed: Rect forcedHoldRect = new Rect(num3, rect.y + 2f, (float)num2, rect.height - 4f);
            Rect forcedHoldRect = new Rect(num3, num4, (float)num2, (float)num2);

            if (somethingIsForced)
            {
                //changed: if (Widgets.ButtonText(forcedHoldRect, "ClearForcedApparel".Translate(), true, false, true)) // "Clear forced" is sufficient and that's what this is at the moment.
                if (Widgets.ButtonImage(forcedHoldRect, ClearImage))
                {
                    pawn.HoldTrackerClear(); // yes this will also delete records that haven't been picked up and thus not shown to the player...
                }
                TooltipHandler.TipRegion(forcedHoldRect, new TipSignal(delegate
                {
                    string text = "CE_ForcedHold".Translate() + ":\n";
                    foreach (HoldRecord rec in LoadoutManager.GetHoldRecords(pawn))
                    {
                        if (!rec.pickedUp)
                        {
                            continue;
                        }
                        text = text + "\n   " + rec.thingDef.LabelCap + " x" + rec.count;
                    }
                    return(text);
                }, pawn.GetHashCode() * 613));
                num3 += (float)num2;
                num3 += 4f;
            }

            //changed: Rect assignTabRect = new Rect(num3, rect.y + 2f, (float)num2, rect.height - 4f);
            Rect assignTabRect = new Rect(num3, num4, (float)num2, (float)num2);

            //changed: if (Widgets.ButtonText(assignTabRect, "AssignTabEdit".Translate(), true, false, true))
            if (Widgets.ButtonImage(assignTabRect, EditImage))
            {
                Find.WindowStack.Add(new Dialog_ManageLoadouts(pawn.GetLoadout()));
            }
            // Added this next line.
            TooltipHandler.TipRegion(assignTabRect, new TipSignal(textGetter("CE_Loadouts"), pawn.GetHashCode() * 613));
            num3 += (float)num2;
        }
Example #15
0
        protected override void DrawPawnRow(Rect rect, Pawn p)
        {
            // available space for row
            Rect rowRect = new Rect(rect.x + 165f, rect.y, rect.width - 165f, rect.height);

            // response button rect
            Vector2 responsePos = new Vector2(rowRect.xMin, rowRect.yMin + (rowRect.height - 24f) / 2f);

            // offset rest of row for that button, so we don't have to mess with all the other rect calculations
            rowRect.xMin += 24f + _margin;

            // label + buttons for outfit
            Rect outfitRect = new Rect(rowRect.xMin,
                                       rowRect.yMin,
                                       rowRect.width * (1f / 4f) + (_margin + _buttonSize) / 2f,
                                       rowRect.height);

            Rect labelOutfitRect = new Rect(outfitRect.xMin,
                                            outfitRect.yMin,
                                            outfitRect.width - _margin * 3 - _buttonSize * 2,
                                            outfitRect.height)
                                   .ContractedBy(_margin / 2f);
            Rect editOutfitRect = new Rect(labelOutfitRect.xMax + _margin,
                                           outfitRect.yMin + ((outfitRect.height - _buttonSize) / 2),
                                           _buttonSize,
                                           _buttonSize);
            Rect forcedOutfitRect = new Rect(labelOutfitRect.xMax + _buttonSize + _margin * 2,
                                             outfitRect.yMin + ((outfitRect.height - _buttonSize) / 2),
                                             _buttonSize,
                                             _buttonSize);

            // drucg policy
            Rect drugRect = new Rect(outfitRect.xMax,
                                     rowRect.yMin,
                                     rowRect.width * (1f / 4f) - (_margin + _buttonSize) / 2f,
                                     rowRect.height);
            Rect labelDrugRect = new Rect(drugRect.xMin,
                                          drugRect.yMin,
                                          drugRect.width - _margin * 2 - _buttonSize,
                                          drugRect.height)
                                 .ContractedBy(_margin / 2f);
            Rect editDrugRect = new Rect(labelDrugRect.xMax + _margin,
                                         drugRect.yMin + ((drugRect.height - _buttonSize) / 2),
                                         _buttonSize,
                                         _buttonSize);

            // label + button for loadout
            Rect loadoutRect = new Rect(drugRect.xMax,
                                        rowRect.yMin,
                                        rowRect.width * (1f / 4f) - (_margin + _buttonSize) / 2f,
                                        rowRect.height);
            Rect labelLoadoutRect = new Rect(loadoutRect.xMin,
                                             loadoutRect.yMin,
                                             loadoutRect.width - _margin * 3 - _buttonSize * 2,
                                             loadoutRect.height)
                                    .ContractedBy(_margin / 2f);
            Rect editLoadoutRect = new Rect(labelLoadoutRect.xMax + _margin,
                                            loadoutRect.yMin + ((loadoutRect.height - _buttonSize) / 2),
                                            _buttonSize,
                                            _buttonSize);
            Rect forcedHoldRect = new Rect(labelLoadoutRect.xMax + _buttonSize + _margin * 2,
                                           loadoutRect.yMin + ((loadoutRect.height - _buttonSize) / 2),
                                           _buttonSize,
                                           _buttonSize);

            // fight or flight button
            HostilityResponseModeUtility.DrawResponseButton(responsePos, p);

            // weight + bulk indicators
            Rect weightRect = new Rect(loadoutRect.xMax, rowRect.yMin, rowRect.width * (1f / 8f) - _margin, rowRect.height).ContractedBy(_margin / 2f);
            Rect bulkRect   = new Rect(weightRect.xMax + _margin, rowRect.yMin, rowRect.width * (1f / 8f) - _margin, rowRect.height).ContractedBy(_margin / 2f);

            // OUTFITS
            // main button
            if (Widgets.ButtonText(labelOutfitRect, p.outfits.CurrentOutfit.label, true, false))
            {
                List <FloatMenuOption> options = new List <FloatMenuOption>();
                foreach (Outfit current in Current.Game.outfitDatabase.AllOutfits)
                {
                    // need to create a local copy for delegate
                    Outfit localOut = current;
                    options.Add(new FloatMenuOption(localOut.label, delegate
                    {
                        p.outfits.CurrentOutfit = localOut;
                    }, MenuOptionPriority.Default, null, null));
                }
                Find.WindowStack.Add(new FloatMenu(options, optionalTitle, false));
            }

            // edit button
            TooltipHandler.TipRegion(editOutfitRect, "CE_EditX".Translate("CE_outfit".Translate() + " " + p.outfits.CurrentOutfit.label));
            if (Widgets.ButtonImage(editOutfitRect, _iconEdit))
            {
                Text.Font = GameFont.Small;
                Find.WindowStack.Add(new Dialog_ManageOutfits(p.outfits.CurrentOutfit));
            }

            // clear forced button
            if (p.outfits.forcedHandler.SomethingIsForced)
            {
                TooltipHandler.TipRegion(forcedOutfitRect, "ClearForcedApparel".Translate());
                if (Widgets.ButtonImage(forcedOutfitRect, _iconClearForced))
                {
                    p.outfits.forcedHandler.Reset();
                }
                TooltipHandler.TipRegion(forcedOutfitRect, new TipSignal(delegate
                {
                    string text = "ForcedApparel".Translate() + ":\n";
                    foreach (Apparel current2 in p.outfits.forcedHandler.ForcedApparel)
                    {
                        text = text + "\n   " + current2.LabelCap;
                    }
                    return(text);
                }, p.GetHashCode() * 612));
            }

            // DRUG POLICY
            // main button
            string textDrug = p.drugs.CurrentPolicy.label;

            if (p.story != null && p.story.traits != null)
            {
                Trait trait = p.story.traits.GetTrait(TraitDefOf.DrugDesire);
                if (trait != null)
                {
                    textDrug = textDrug + " (" + trait.Label + ")";
                }
            }
            if (Widgets.ButtonText(labelDrugRect, textDrug, true, false, true))
            {
                List <FloatMenuOption> list = new List <FloatMenuOption>();
                foreach (DrugPolicy current in Current.Game.drugPolicyDatabase.AllPolicies)
                {
                    DrugPolicy localAssignedDrugs = current;
                    list.Add(new FloatMenuOption(current.label, delegate
                    {
                        p.drugs.CurrentPolicy = localAssignedDrugs;
                    }, MenuOptionPriority.Default, null, null, 0f, null));
                }
                Find.WindowStack.Add(new FloatMenu(list));
                PlayerKnowledgeDatabase.KnowledgeDemonstrated(ConceptDefOf.DrugPolicies, KnowledgeAmount.Total);
            }


            // edit button
            TooltipHandler.TipRegion(editDrugRect, "CE_EditX".Translate("CE_drugs".Translate() + " " + p.drugs.CurrentPolicy.label));
            if (Widgets.ButtonImage(editDrugRect, _iconEdit))
            {
                Text.Font = GameFont.Small;
                Find.WindowStack.Add(new Dialog_ManageDrugPolicies(p.drugs.CurrentPolicy));
                PlayerKnowledgeDatabase.KnowledgeDemonstrated(ConceptDefOf.DrugPolicies, KnowledgeAmount.Total);
            }

            // LOADOUTS
            // main button
            if (Widgets.ButtonText(labelLoadoutRect, p.GetLoadout().LabelCap, true, false))
            {
                LoadoutManager.SortLoadouts();
                List <FloatMenuOption> options = new List <FloatMenuOption>();
                foreach (Loadout loadout in LoadoutManager.Loadouts)
                {
                    // need to create a local copy for delegate
                    Loadout localLoadout = loadout;
                    options.Add(new FloatMenuOption(localLoadout.LabelCap, delegate
                    {
                        p.SetLoadout(localLoadout);
                    }, MenuOptionPriority.Default, null, null));
                }
                Find.WindowStack.Add(new FloatMenu(options, optionalTitle, false));
            }

            // edit button
            TooltipHandler.TipRegion(editLoadoutRect, "CE_EditX".Translate("CE_loadout".Translate() + " " + p.GetLoadout().LabelCap));
            if (Widgets.ButtonImage(editLoadoutRect, _iconEdit))
            {
                Find.WindowStack.Add(new Dialog_ManageLoadouts(p.GetLoadout()));
            }

            // clear forced held button
            if (p.HoldTrackerAnythingHeld())
            {
                TooltipHandler.TipRegion(forcedHoldRect, "ClearForcedApparel".Translate()); // "Clear forced" is sufficient and that's what this is at the moment.
                if (Widgets.ButtonImage(forcedHoldRect, _iconClearForced))                  // also can re-use the icon for clearing forced at the moment.
                {
                    p.HoldTrackerClear();                                                   // yes this will also delete records that haven't been picked up and thus not shown to the player...
                }
                TooltipHandler.TipRegion(forcedHoldRect, new TipSignal(delegate
                {
                    string text = "CE_ForcedHold".Translate() + ":\n";
                    foreach (HoldRecord rec in LoadoutManager.GetHoldRecords(p))
                    {
                        if (!rec.pickedUp)
                        {
                            continue;
                        }
                        text = text + "\n   " + rec.thingDef.LabelCap + " x" + rec.count;
                    }
                    return(text);
                }, p.GetHashCode() * 613));
            }

            // STATUS BARS
            // fetch the comp
            CompInventory comp = p.TryGetComp <CompInventory>();

            if (comp != null)
            {
                Utility_Loadouts.DrawBar(bulkRect, comp.currentBulk, comp.capacityBulk, "", p.GetBulkTip());
                Utility_Loadouts.DrawBar(weightRect, comp.currentWeight, comp.capacityWeight, "", p.GetWeightTip());
            }
        }
        /* (ProfoundDarkness) I've intentionally left some code remarked in the following code because it's a useful guide on how to create
         * and maintain the transpilers that will do nearly identical changes to RimWorld's code for the other 2 PawnColumnWorkers.
         */
        public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
        {
            if (pawn.outfits == null)
            {
                return;
            }
            //changed: int num = Mathf.FloorToInt((rect.width - 4f) * 0.714285731f);
            int num = Mathf.FloorToInt((rect.width - 4f) - IconSize);
            //changed: int num2 = Mathf.FloorToInt((rect.width - 4f) * 0.2857143f);
            int   num2 = Mathf.FloorToInt(IconSize);
            float num3 = rect.x;
            //added:
            float num4          = rect.y + ((rect.height - IconSize) / 2);
            float equipNowWidth = "CE_UpdateLoadoutNow".Translate().GetWidthCached();

            // Reduce width if we're adding a clear forced button
            bool somethingIsForced = pawn.HoldTrackerAnythingHeld();
            Rect loadoutRect       = new Rect(num3, rect.y + 2f, (float)num, rect.height - 4f);

            if (somethingIsForced)
            {
                loadoutRect.width -= 4f + (float)num2;
            }

            // Main loadout button
            Rect mainLoadoutButton = pawn.Spawned ? loadoutRect.LeftPartPixels(loadoutRect.width - equipNowWidth - 16) : loadoutRect;

            mainLoadoutButton.xMax -= 2;
            string label = pawn.GetLoadout().label.Truncate(mainLoadoutButton.width, null);

            Widgets.Dropdown <Pawn, Loadout>(mainLoadoutButton, pawn, (Pawn p) => p.GetLoadout(), new Func <Pawn, IEnumerable <Widgets.DropdownMenuElement <Loadout> > >(Button_GenerateMenu), label, null, null, null, null, true);

            if (pawn.Spawned)
            {
                Rect forceEquipNow = loadoutRect.RightPartPixels(equipNowWidth + 12);
                if (Widgets.ButtonText(forceEquipNow, "CE_UpdateLoadoutNow".Translate()))
                {
                    Job           job       = JobGiver_UpdateLoadout.GetUpdateLoadoutJob(pawn);
                    CompInventory inventory = pawn.TryGetComp <CompInventory>();
                    if (inventory != null)
                    {
                        inventory.ForcedLoadoutUpdate = job != null;
                    }
                    if (job != null)
                    {
                        pawn.jobs.StopAll(false, true);
                        pawn.jobs.StartJob(job, JobCondition.InterruptForced, tag: JobTag.ChangingApparel);
                    }
                }
            }

            // Clear forced button
            num3 += loadoutRect.width;
            num3 += 4f;
            //changed: Rect forcedHoldRect = new Rect(num3, rect.y + 2f, (float)num2, rect.height - 4f);
            Rect forcedHoldRect = new Rect(num3, num4, (float)num2, (float)num2);

            if (somethingIsForced)
            {
                if (Widgets.ButtonImage(forcedHoldRect, ClearImage))
                {
                    pawn.HoldTrackerClear(); // yes this will also delete records that haven't been picked up and thus not shown to the player...
                }
                TooltipHandler.TipRegion(forcedHoldRect, new TipSignal(delegate
                {
                    string text = "CE_ForcedHold".Translate() + ":\n";
                    foreach (HoldRecord rec in LoadoutManager.GetHoldRecords(pawn))
                    {
                        if (!rec.pickedUp)
                        {
                            continue;
                        }
                        text = text + "\n   " + rec.thingDef.LabelCap + " x" + rec.count;
                    }
                    return(text);
                }, pawn.GetHashCode() * 613));
                num3 += (float)num2;
                num3 += 4f;
            }

            //changed: Rect assignTabRect = new Rect(num3, rect.y + 2f, (float)num2, rect.height - 4f);
            Rect assignTabRect = new Rect(num3, num4, (float)num2, (float)num2);

            //changed: if (Widgets.ButtonText(assignTabRect, "AssignTabEdit".Translate(), true, false, true))
            if (Widgets.ButtonImage(assignTabRect, EditImage))
            {
                Find.WindowStack.Add(new Dialog_ManageLoadouts(pawn.GetLoadout()));
            }
            // Added this next line.
            TooltipHandler.TipRegion(assignTabRect, new TipSignal(textGetter("CE_Loadouts"), pawn.GetHashCode() * 613));
            num3 += (float)num2;
        }