Exemplo n.º 1
0
        private bool CheckForExcessItems(Pawn pawn)
        {
            //if (pawn.CurJob != null && pawn.CurJob.def == JobDefOf.Tame) return false;
            CompInventory inventory = pawn.TryGetComp <CompInventory>();
            Loadout       loadout   = pawn.GetLoadout();

            if (inventory == null || inventory.container == null || loadout == null || loadout.Slots.NullOrEmpty())
            {
                return(false);
            }
            if (inventory.container.Count > loadout.SlotCount + 1)
            {
                return(true);
            }
            // Check to see if there is at least one loadout slot specifying currently equipped weapon
            ThingWithComps equipment = ((pawn.equipment == null) ? null : pawn.equipment.Primary) ?? null;

            if (equipment != null && !loadout.Slots.Any(slot => slot.Def == equipment.def && slot.Count >= 1))
            {
                return(true);
            }

            // Go through each item in the inventory and see if its part of our loadout
            bool allowDropRaw = Find.TickManager.TicksGame > pawn.mindState?.lastInventoryRawFoodUseTick + ticksBeforeDropRaw;

            foreach (Thing thing in inventory.container)
            {
                if (allowDropRaw || !thing.def.IsNutritionGivingIngestible || thing.def.ingestible.preferability > FoodPreferability.RawTasty)
                {
                    LoadoutSlot slot = loadout.Slots.FirstOrDefault(x => x.Def == thing.def);
                    if (slot == null)
                    {
                        return(true);
                    }
                    int numContained = inventory.container.TotalStackCountOfDef(thing.def);

                    // Add currently equipped gun
                    if (pawn.equipment != null && pawn.equipment.Primary != null)
                    {
                        if (pawn.equipment.Primary.def == slot.Def)
                        {
                            numContained++;
                        }
                    }
                    if (slot.Count < numContained)
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Handles adding a new LoadoutSlot to self.  If self already has the same slot (based on Def) then increment the already present slot.count.
        /// </summary>
        /// <param name="slot">LoadoutSlot to add to this Loadout.</param>
        public void AddSlot(LoadoutSlot slot)
        {
            LoadoutSlot old = _slots.FirstOrDefault(slot.isSameDef);

            if (old != null)
            {
                old.count += slot.count;
            }
            else
            {
                _slots.Add(slot);
            }
        }
Exemplo n.º 3
0
        private void DrawCountField(Rect canvas, LoadoutSlot slot)
        {
            if (slot == null)
            {
                return;
            }

            int    countInt = slot.count;
            string buffer   = countInt.ToString();

            Widgets.TextFieldNumeric <int>(canvas, ref countInt, ref buffer);
            TooltipHandler.TipRegion(canvas, "CE_CountFieldTip".Translate(slot.count));
            slot.count = countInt;
        }
Exemplo n.º 4
0
        /// <summary>
        /// Handles adding a new LoadoutSlot to self.  If self already has the same slot (based on Def) then increment the already present slot.count.
        /// </summary>
        /// <param name="slot">LoadoutSlot to add to this Loadout.</param>
        public void AddSlot(LoadoutSlot slot)
        {
            LoadoutSlot old = _slots.FirstOrDefault(slot.isSameDef);

            Log.Message(string.Concat("Adding ThingDef ", slot.thingDef == null ? "Null" : (string)slot.thingDef.LabelCap, " added, count: ", slot.count));
            if (old != null)
            {
                old.count += slot.count;
            }
            else
            {
                _slots.Add(slot);
            }
        }
Exemplo n.º 5
0
        private void DrawCountField(Rect canvas, LoadoutSlot slot)
        {
            if (slot == null)
            {
                return;
            }
            string count = GUI.TextField(canvas, slot.count.ToString());

            TooltipHandler.TipRegion(canvas, "CE_CountFieldTip".Translate(slot.count));
            int countInt;

            if (int.TryParse(count, out countInt))
            {
                slot.count = countInt;
            }
        }
Exemplo n.º 6
0
        /// <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);
            }

            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))));

            if (eqSlot == null)
            {
                dropEquipment = pawn.equipment.Primary;
                return(true);
            }
            return(false);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Gets a priority value of how important it is for a pawn to do pickup/drop activities.
        /// </summary>
        /// <param name="pawn">Pawn to fetch priority for</param>
        /// <returns>float indicating priority of pickup/drop job</returns>
        public override float GetPriority(Pawn pawn)
        {
            CompInventory comp = pawn.TryGetComp <CompInventory>();

            if (comp == null)
            {
                return(0f);
            }
            else if (comp.ForcedLoadoutUpdate)
            {
                return(35f);
            }
            else if (comp.SkipUpdateLoadout)
            {
                return(0f);
            }
            if (pawn.HasExcessThing())
            {
                return(9.2f);
            }
            LoadoutSlot slot = GetPrioritySlot(pawn, out ItemPriority priority, out _, out _, out _);

            if (slot == null)
            {
                return(0f);
            }
            if (priority == ItemPriority.Low)
            {
                return(1f);
            }
            TimeAssignmentDef assignment = (pawn.timetable != null) ? pawn.timetable.CurrentAssignment : TimeAssignmentDefOf.Anything;

            if (assignment == TimeAssignmentDefOf.Sleep)
            {
                return(1f);
            }
            return(2.8f);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Used to move one LoadoutSlot into a different position in this Loadout's List.  Generally connected to drag and drop activities by user.
        /// </summary>
        /// <param name="fromIndex">int index (source) in List to move from.</param>
        /// <param name="toIndex">int index (target) in List to move to.</param>
        /// <returns></returns>
        private int MoveTo(int fromIndex, int toIndex)
        {
            if (fromIndex < 0 || fromIndex >= _slots.Count || toIndex < 0 || toIndex >= _slots.Count)
            {
                throw new Exception("Attempted to move i " + fromIndex + " to " + toIndex + ", bounds are [0," + (_slots.Count - 1) + "].");
            }

            // fetch the filter we're moving
            LoadoutSlot temp = _slots[fromIndex];

            // remove from old location
            _slots.RemoveAt(fromIndex);

            // this may have changed the toIndex
            if (fromIndex + 1 < toIndex)
            {
                toIndex--;
            }

            // insert at new location
            _slots.Insert(toIndex, temp);
            return(toIndex);
        }
Exemplo n.º 9
0
        /// <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);
            }

            //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);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Used to move a slot in this Loadout to a different position in the List.
        /// </summary>
        /// <param name="slot">LoadoutSlot being moved.</param>
        /// <param name="toIndex">int position (index) to move slot to.</param>
        public void MoveSlot(LoadoutSlot slot, int toIndex)
        {
            int fromIndex = _slots.IndexOf(slot);

            MoveTo(fromIndex, toIndex);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Used to determine if two slots are referring to the same def without exposing the internal def directly.
        /// </summary>
        /// <param name="slot">The other slot to compare to.</param>
        /// <returns>bool true if both LoadoutSlots refer to the same def.</returns>
        public bool isSameDef(LoadoutSlot slot)
        {
            Def def = (slot.thingDef as Def) ?? (slot.genericDef as Def);

            return(_def == def);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Generates a loadout from a pawn's current equipment and inventory.  Attempts to put items which fit in Generics that are default/DropExcess into said Generic.
        /// </summary>
        /// <param name="pawn">Pawn to check equipment/inventory on and generate a Loadout from.</param>
        /// <returns>Loadout which was generated based on Pawn's inventory.</returns>
        public static Loadout GenerateLoadoutFromPawn(this Pawn pawn)
        {
            // generate the name for this new pawn based loadout.
            string newName = string.Concat(pawn.Name.ToStringShort, " ", "CE_DefaultLoadoutName".Translate());
            Regex  reNum   = new Regex(@"^(.*?)\d+$");

            if (reNum.IsMatch(newName))
            {
                newName = reNum.Replace(newName, @"$1");
            }
            newName = LoadoutManager.GetUniqueLabel(newName);

            // set basic loadout properties.
            Loadout loadout = new Loadout(newName);

            loadout.defaultLoadout = false;
            loadout.canBeDeleted   = true;

            LoadoutSlot slot = null;

            // grab the pawn's current equipment as a loadoutslot.
            if (pawn.equipment?.Primary != null)
            {
                slot = new LoadoutSlot(pawn.equipment.Primary.def);
                loadout.AddSlot(slot);
            }

            // get a list of generics which are drop only.  Assumes that anything that doesn't fit here is a Specific slot later.
            IEnumerable <LoadoutGenericDef> generics = DefDatabase <LoadoutGenericDef> .AllDefs.Where(gd => gd.defaultCountType == LoadoutCountType.dropExcess);

            // enumerate each item in the pawn's inventory and add appropriate slots.
            foreach (Thing thing in pawn.inventory.innerContainer)
            {
                LoadoutGenericDef foundGeneric = null;
                // first check if it's a generic-able item...
                foreach (LoadoutGenericDef generic in generics)
                {
                    if (generic.lambda(thing.def))
                    {
                        foundGeneric = generic;
                        break;
                    }
                }

                // assign a loadout slot that fits the thing.
                if (foundGeneric != null)
                {
                    slot = new LoadoutSlot(foundGeneric, thing.stackCount);
                }
                else
                {
                    slot = new LoadoutSlot(thing.def, thing.stackCount);
                }

                // add the slot (this also takes care of adding to existing slots)
                loadout.AddSlot(slot);
            }

            // finally check the loadout and make sure that it has sufficient generics like what happens with a new loadout in the management UI.
            foreach (LoadoutGenericDef generic in generics.Where(gd => gd.isBasic))
            {
                slot = loadout.Slots.FirstOrDefault(s => s.genericDef == generic);
                if (slot != null)
                {
                    if (slot.count < slot.genericDef.defaultCount)
                    {
                        slot.count = slot.genericDef.defaultCount;
                    }
                }
                else
                {
                    slot = new LoadoutSlot(generic);
                    loadout.AddSlot(slot);
                }
            }

            return(loadout);
        }
Exemplo n.º 13
0
        private void DrawSlotSelection(Rect canvas)
        {
            int count = _sourceType == SourceSelection.Generic ? _sourceGeneric.Count : _source.Count;

            GUI.DrawTexture(canvas, _darkBackground);

            if ((_sourceType != SourceSelection.Generic && _source.NullOrEmpty()) || (_sourceType == SourceSelection.Generic && _sourceGeneric.NullOrEmpty()))
            {
                return;
            }

            Rect viewRect = new Rect(canvas);

            viewRect.width -= 16f;
            viewRect.height = count * _rowHeight;

            Widgets.BeginScrollView(canvas, ref _availableScrollPosition, viewRect.AtZero());
            int startRow = (int)Math.Floor((decimal)(_availableScrollPosition.y / _rowHeight));

            startRow = (startRow < 0) ? 0 : startRow;
            int endRow = startRow + (int)(Math.Ceiling((decimal)(canvas.height / _rowHeight)));

            endRow = (endRow > count) ? count : endRow;
            for (int i = startRow; i < endRow; i++)
            {
                // gray out weapons not in stock
                Color baseColor = GUI.color;
                if (_sourceType == SourceSelection.Generic)
                {
                    if (GetVisibleGeneric(_sourceGeneric[i]))
                    {
                        GUI.color = Color.gray;
                    }
                }
                else
                {
                    //if (Find.CurrentMap.listerThings.AllThings.FindAll(x => x.GetInnerIfMinified().def == _source[i] && !x.def.Minifiable).Count <= 0)
                    if (Find.CurrentMap.listerThings.AllThings.Find(x => x.GetInnerIfMinified().def == _source[i] && !x.def.Minifiable) == null)
                    {
                        GUI.color = Color.gray;
                    }
                }

                Rect row       = new Rect(0f, i * _rowHeight, canvas.width, _rowHeight);
                Rect labelRect = new Rect(row);
                if (_sourceType == SourceSelection.Generic)
                {
                    TooltipHandler.TipRegion(row, _sourceGeneric[i].GetWeightAndBulkTip());
                }
                else
                {
                    TooltipHandler.TipRegion(row, _source[i].GetWeightAndBulkTip());
                }

                labelRect.xMin += _margin;
                if (i % 2 == 0)
                {
                    GUI.DrawTexture(row, _darkBackground);
                }

                Text.Anchor = TextAnchor.MiddleLeft;
                if (_sourceType == SourceSelection.Generic)
                {
                    Widgets.Label(labelRect, _sourceGeneric[i].LabelCap);
                }
                else
                {
                    Widgets.Label(labelRect, _source[i].LabelCap);
                }
                Text.Anchor = TextAnchor.UpperLeft;

                Widgets.DrawHighlightIfMouseover(row);
                if (Widgets.ButtonInvisible(row))
                {
                    LoadoutSlot slot;
                    if (_sourceType == SourceSelection.Generic)
                    {
                        slot = new LoadoutSlot(_sourceGeneric[i]);
                    }
                    else
                    {
                        slot = new LoadoutSlot(_source[i]);
                    }
                    CurrentLoadout.AddSlot(slot);
                }
                // revert to original color
                GUI.color = baseColor;
            }
            Widgets.EndScrollView();
        }
Exemplo n.º 14
0
        private void DrawSlot(Rect row, LoadoutSlot slot, bool slotDraggable = true)
        {
            // set up rects
            // dragging handle (square) | label (fill) | count (50px) | delete (iconSize)
            Rect draggingHandle = new Rect(row);

            draggingHandle.width = row.height;

            Rect labelRect = new Rect(row);

            if (slotDraggable)
            {
                labelRect.xMin = draggingHandle.xMax;
            }
            labelRect.xMax = row.xMax - _countFieldSize.x - _iconSize - 2 * _margin;

            Rect countRect = new Rect(
                row.xMax - _countFieldSize.x - _iconSize - 2 * _margin,
                row.yMin + (row.height - _countFieldSize.y) / 2f,
                _countFieldSize.x,
                _countFieldSize.y);

            Rect ammoRect = new Rect(
                countRect.xMin - _iconSize - _margin,
                row.yMin + (row.height - _iconSize) / 2f,
                _iconSize, _iconSize);

            Rect countModeRect = new Rect(
                ammoRect.xMin - _iconSize - _margin,
                row.yMin + (row.height - _iconSize) / 2f,
                _iconSize, _iconSize);

            Rect deleteRect = new Rect(countRect.xMax + _margin, row.yMin + (row.height - _iconSize) / 2f, _iconSize, _iconSize);

            // dragging on dragHandle
            if (slotDraggable)
            {
                TooltipHandler.TipRegion(draggingHandle, "CE_DragToReorder".Translate());
                GUI.DrawTexture(draggingHandle, _iconMove);

                if (Mouse.IsOver(draggingHandle) && Input.GetMouseButtonDown(0))
                {
                    Dragging = slot;
                }
            }

            // interactions (main row rect)
            if (!Mouse.IsOver(deleteRect))
            {
                Widgets.DrawHighlightIfMouseover(row);
                TooltipHandler.TipRegion(row, slot.genericDef != null ? slot.genericDef.GetWeightAndBulkTip(slot.count) : slot.thingDef.GetWeightAndBulkTip(slot.count));
            }

            // label
            Text.Anchor = TextAnchor.MiddleLeft;
            Widgets.Label(labelRect, slot.LabelCap);
            Text.Anchor = TextAnchor.UpperLeft;

            // easy ammo adder, ranged weapons only
            if (slot.thingDef != null && slot.thingDef.IsRangedWeapon)
            {
                // make sure there's an ammoset defined
                AmmoSetDef ammoSet = ((slot.thingDef.GetCompProperties <CompProperties_AmmoUser>() == null) ? null : slot.thingDef.GetCompProperties <CompProperties_AmmoUser>().ammoSet);

                bool?temp = !((((ammoSet == null) ? null : ammoSet.ammoTypes)).NullOrEmpty());

                if (temp ?? false)
                {
                    if (Widgets.ButtonImage(ammoRect, _iconAmmoAdd))
                    {
                        List <FloatMenuOption> options = new List <FloatMenuOption>();
                        int magazineSize = (slot.thingDef.GetCompProperties <CompProperties_AmmoUser>() == null) ? 0 : slot.thingDef.GetCompProperties <CompProperties_AmmoUser>().magazineSize;

                        foreach (AmmoLink link in ((ammoSet == null) ? null : ammoSet.ammoTypes))
                        {
                            options.Add(new FloatMenuOption(link.ammo.LabelCap, delegate
                            {
                                CurrentLoadout.AddSlot(new LoadoutSlot(link.ammo, (magazineSize <= 1 ? link.ammo.defaultAmmoCount : magazineSize)));
                            }));
                        }
                        // Add in the generic for this gun.
                        LoadoutGenericDef generic = DefDatabase <LoadoutGenericDef> .GetNamed("GenericAmmo-" + slot.thingDef.defName);

                        if (generic != null)
                        {
                            options.Add(new FloatMenuOption(generic.LabelCap, delegate
                            {
                                CurrentLoadout.AddSlot(new LoadoutSlot(generic));
                            }));
                        }

                        Find.WindowStack.Add(new FloatMenu(options, "CE_AddAmmoFor".Translate(slot.thingDef.LabelCap)));
                    }
                }
            }

            // count
            DrawCountField(countRect, slot);

            // toggle count mode
            if (slot.genericDef != null)
            {
                Texture2D curModeIcon = slot.countType == LoadoutCountType.dropExcess ? _iconDropExcess : _iconPickupDrop;
                string    tipString   = slot.countType == LoadoutCountType.dropExcess ? "Drop Excess" : "Pickup Missing and Drop Excess";
                if (Widgets.ButtonImage(countModeRect, curModeIcon))
                {
                    slot.countType = slot.countType == LoadoutCountType.dropExcess ? LoadoutCountType.pickupDrop : LoadoutCountType.dropExcess;
                }
                TooltipHandler.TipRegion(countModeRect, tipString);
            }

            // delete
            if (Mouse.IsOver(deleteRect))
            {
                GUI.DrawTexture(row, TexUI.HighlightTex);
            }
            if (Widgets.ButtonImage(deleteRect, _iconClear))
            {
                CurrentLoadout.RemoveSlot(slot);
            }
            TooltipHandler.TipRegion(deleteRect, "CE_DeleteFilter".Translate());
        }
Exemplo n.º 15
0
        protected override Job TryGiveJob(Pawn pawn)
        {
            // Get inventory
            CompInventory inventory = pawn.TryGetComp <CompInventory>();

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

            Loadout loadout = pawn.GetLoadout();

            if (loadout != null)
            {
                // Find and drop excess items
                foreach (LoadoutSlot slot in loadout.Slots)
                {
                    int numContained = inventory.container.TotalStackCountOfDef(slot.Def);

                    // Add currently equipped gun
                    if (pawn.equipment != null && pawn.equipment.Primary != null)
                    {
                        if (pawn.equipment.Primary.def == slot.Def)
                        {
                            numContained++;
                        }
                    }
                    // Drop excess items
                    if (numContained > slot.Count)
                    {
                        Thing thing = inventory.container.FirstOrDefault(x => x.def == slot.Def);
                        if (thing != null)
                        {
                            Thing droppedThing;
                            if (inventory.container.TryDrop(thing, pawn.Position, pawn.Map, ThingPlaceMode.Near, numContained - slot.Count, out droppedThing))
                            {
                                if (droppedThing != null)
                                {
                                    return(HaulAIUtility.HaulToStorageJob(pawn, droppedThing));
                                }
                                Log.Error(pawn + " tried dropping " + thing + " from loadout but resulting thing is null");
                            }
                        }
                    }
                }

                // Try drop currently equipped weapon
                if (pawn.equipment != null && pawn.equipment.Primary != null && !loadout.Slots.Any(slot => slot.Def == pawn.equipment.Primary.def && slot.Count >= 1))
                {
                    ThingWithComps droppedEq;
                    if (pawn.equipment.TryDropEquipment(pawn.equipment.Primary, out droppedEq, pawn.Position, false))
                    {
                        return(HaulAIUtility.HaulToStorageJob(pawn, droppedEq));
                    }
                }

                // Find excess items in inventory that are not part of our loadout
                bool  allowDropRaw  = Find.TickManager.TicksGame > pawn.mindState?.lastInventoryRawFoodUseTick + ticksBeforeDropRaw;
                Thing thingToRemove = inventory.container.FirstOrDefault(t =>
                                                                         (allowDropRaw || !t.def.IsNutritionGivingIngestible || t.def.ingestible.preferability > FoodPreferability.RawTasty) &&
                                                                         !loadout.Slots.Any(s => s.Def == t.def));
                if (thingToRemove != null)
                {
                    Thing droppedThing;
                    if (inventory.container.TryDrop(thingToRemove, pawn.Position, pawn.Map, ThingPlaceMode.Near, thingToRemove.stackCount, out droppedThing))
                    {
                        return(HaulAIUtility.HaulToStorageJob(pawn, droppedThing));
                    }
                    Log.Error(pawn + " tried dropping " + thingToRemove + " from inventory but resulting thing is null");
                }

                // Find missing items
                ItemPriority priority;
                Thing        closestThing;
                int          count;
                LoadoutSlot  prioritySlot = GetPrioritySlot(pawn, out priority, out closestThing, out count);
                if (closestThing != null)
                {
                    // Equip gun if unarmed or current gun is not in loadout
                    if (closestThing.TryGetComp <CompEquippable>() != null &&
                        (pawn.health != null && pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation)) &&
                        (pawn.equipment == null || pawn.equipment.Primary == null || !loadout.Slots.Any(s => s.Def == pawn.equipment.Primary.def)))
                    {
                        return(new Job(JobDefOf.Equip, closestThing));
                    }
                    // Take items into inventory if needed
                    int numContained = inventory.container.TotalStackCountOfDef(prioritySlot.Def);
                    return(new Job(JobDefOf.TakeInventory, closestThing)
                    {
                        count = Mathf.Min(closestThing.stackCount, prioritySlot.Count - numContained, count)
                    });
                }
            }
            return(null);
        }
Exemplo n.º 16
0
        /// <summary>
        /// This starts the work of finding something lacking that the pawn should pickup.
        /// </summary>
        /// <param name="pawn">Pawn who's inventory and loadout should be considered.</param>
        /// <param name="priority">Priority value for how important doing this job is.</param>
        /// <param name="closestThing">The thing found to be picked up.</param>
        /// <param name="count">The amount of closestThing to pickup.  Already checked if inventory can hold it.</param>
        /// <param name="carriedBy">If unable to find something on the ground to pickup, the pawn (pack animal/prisoner) which has the item to take.</param>
        /// <returns>LoadoutSlot which has something that can be picked up.</returns>
        private LoadoutSlot GetPrioritySlot(Pawn pawn, out ItemPriority priority, out Thing closestThing, out int count, out Pawn carriedBy)
        {
            priority = ItemPriority.None;
            LoadoutSlot slot = null;

            closestThing = null;
            count        = 0;
            carriedBy    = null;

            CompInventory inventory = pawn.TryGetComp <CompInventory>();

            if (inventory != null && inventory.container != null)
            {
                Loadout loadout = pawn.GetLoadout();
                if (loadout != null && !loadout.Slots.NullOrEmpty())
                {
                    // Need to generate a dictionary and nibble like when dropping in order to allow for conflicting loadouts to work properly.
                    Dictionary <ThingDef, Integer> listing = pawn.GetStorageByThingDef();

                    // process each loadout slot... (While the LoadoutSlot.countType property only really makes sense in the context of genericDef != null, it should be the correct value (pickupDrop) on .thingDef != null.)
                    foreach (LoadoutSlot curSlot in loadout.Slots.Where(s => s.countType != LoadoutCountType.dropExcess))
                    {
                        Thing        curThing    = null;
                        ItemPriority curPriority = ItemPriority.None;
                        Pawn         curCarrier  = null;
                        int          wantCount   = curSlot.count;

                        if (curSlot.thingDef != null)
                        {
                            if (listing.ContainsKey(curSlot.thingDef))
                            {
                                int amount = listing[curSlot.thingDef].value >= wantCount ? wantCount : listing[curSlot.thingDef].value;
                                listing[curSlot.thingDef].value -= amount;
                                wantCount -= amount;
                                if (listing[curSlot.thingDef].value <= 0)
                                {
                                    listing.Remove(curSlot.thingDef);
                                }
                            }
                        }
                        if (curSlot.genericDef != null)
                        {
                            List <ThingDef> killKeys = new List <ThingDef>();
                            int             amount;
                            foreach (ThingDef def in listing.Keys.Where(td => curSlot.genericDef.lambda(td)))
                            {
                                amount              = listing[def].value >= wantCount ? wantCount : listing[def].value;
                                listing[def].value -= amount;
                                wantCount          -= amount;
                                if (listing[def].value <= 0)
                                {
                                    killKeys.Add(def);
                                }
                                if (wantCount <= 0)
                                {
                                    break;
                                }
                            }
                            foreach (ThingDef def in killKeys) // oddly enough removing a dictionary entry while enumerating hasn't caused a problem but this is the 'correct' way based on reading.
                            {
                                listing.Remove(def);
                            }
                        }
                        if (wantCount > 0)
                        {
                            FindPickup(pawn, curSlot, wantCount, out curPriority, out curThing, out curCarrier);

                            if (curPriority > priority && curThing != null && inventory.CanFitInInventory(curThing, out count))
                            {
                                priority     = curPriority;
                                slot         = curSlot;
                                count        = count >= wantCount ? wantCount : count;
                                closestThing = curThing;
                                if (curCarrier != null)
                                {
                                    carriedBy = curCarrier;
                                }
                            }
                            if (priority >= ItemPriority.LowStock)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            return(slot);
        }
Exemplo n.º 17
0
        /// <summary>
        /// Tries to give the pawn a job related to picking up or dropping an item from their inventory.
        /// </summary>
        /// <param name="pawn">Pawn to which the job is given.</param>
        /// <returns>Job that the pawn was instructed to do, be it hauling a dropped Thing or going and getting a Thing.</returns>
        protected override Job TryGiveJob(Pawn pawn)
        {
            // Get inventory
            CompInventory inventory = pawn.TryGetComp <CompInventory>();

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

            Loadout loadout = pawn.GetLoadout();

            if (loadout != null)
            {
                ThingWithComps dropEq;
                if (pawn.GetExcessEquipment(out dropEq))
                {
                    ThingWithComps droppedEq;
                    if (pawn.equipment.TryDropEquipment(pawn.equipment.Primary, out droppedEq, pawn.Position, false))
                    {
                        if (droppedEq != null)
                        {
                            return(HaulAIUtility.HaulToStorageJob(pawn, droppedEq));
                        }
                        Log.Error(string.Concat(pawn, " tried dropping ", dropEq, " from loadout but resulting thing is null"));
                    }
                }
                Thing dropThing;
                int   dropCount;
                if (pawn.GetExcessThing(out dropThing, out dropCount))
                {
                    Thing droppedThing;
                    if (inventory.container.TryDrop(dropThing, pawn.Position, pawn.Map, ThingPlaceMode.Near, dropCount, out droppedThing))
                    {
                        if (droppedThing != null)
                        {
                            return(HaulAIUtility.HaulToStorageJob(pawn, droppedThing));
                        }
                        Log.Error(string.Concat(pawn, " tried dropping ", dropThing, " from loadout but resulting thing is null"));
                    }
                }

                // Find missing items
                ItemPriority priority;
                Thing        closestThing;
                int          count;
                Pawn         carriedBy;
                bool         doEquip      = false;
                LoadoutSlot  prioritySlot = GetPrioritySlot(pawn, out priority, out closestThing, out count, out carriedBy);
                // moved logic to detect if should equip vs put in inventory here...
                if (closestThing != null)
                {
                    if (closestThing.TryGetComp <CompEquippable>() != null &&
                        !(pawn.story != null && pawn.WorkTagIsDisabled(WorkTags.Violent)) &&
                        (pawn.health != null && pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation)) &&
                        (pawn.equipment == null || pawn.equipment.Primary == null || !loadout.Slots.Any(s => s.thingDef == pawn.equipment.Primary.def ||
                                                                                                        (s.genericDef != null && s.countType == LoadoutCountType.pickupDrop &&
                                                                                                         s.genericDef.lambda(pawn.equipment.Primary.def)))))
                    {
                        doEquip = true;
                    }
                    if (carriedBy == null)
                    {
                        // Equip gun if unarmed or current gun is not in loadout
                        if (doEquip)
                        {
                            return(new Job(JobDefOf.Equip, closestThing));
                        }
                        return(new Job(JobDefOf.TakeInventory, closestThing)
                        {
                            count = Mathf.Min(closestThing.stackCount, count)
                        });
                    }
                    else
                    {
                        return(new Job(CE_JobDefOf.TakeFromOther, closestThing, carriedBy, doEquip ? pawn : null)
                        {
                            count = doEquip ? 1 : Mathf.Min(closestThing.stackCount, count)
                        });
                    }
                }
            }
            return(null);
        }
Exemplo n.º 18
0
 /// <summary>
 /// Used to remove a LoadoutSlot from this Loadout.
 /// </summary>
 /// <param name="slot">LoadoutSlot to remove.</param>
 public void RemoveSlot(LoadoutSlot slot)
 {
     _slots.Remove(slot);
 }
Exemplo n.º 19
0
 public void AddSlot(LoadoutSlot slot)
 {
     _slots.Add(slot);
 }
Exemplo n.º 20
0
        private LoadoutSlot GetPrioritySlot(Pawn pawn, out ItemPriority priority, out Thing closestThing, out int count)
        {
            priority = ItemPriority.None;
            LoadoutSlot slot = null;

            closestThing = null;
            count        = 0;

            CompInventory inventory = pawn.TryGetComp <CompInventory>();

            if (inventory != null && inventory.container != null)
            {
                Loadout loadout = pawn.GetLoadout();
                if (loadout != null && !loadout.Slots.NullOrEmpty())
                {
                    foreach (LoadoutSlot curSlot in loadout.Slots)
                    {
                        ItemPriority curPriority = ItemPriority.None;
                        Thing        curThing    = null;
                        int          numCarried  = inventory.container.TotalStackCountOfDef(curSlot.Def);

                        // Add currently equipped gun
                        if (pawn.equipment != null && pawn.equipment.Primary != null)
                        {
                            if (pawn.equipment.Primary.def == curSlot.Def)
                            {
                                numCarried++;
                            }
                        }
                        if (numCarried < curSlot.Count)
                        {
                            curThing = GenClosest.ClosestThingReachable(
                                pawn.Position,
                                pawn.Map,
                                ThingRequest.ForDef(curSlot.Def),
                                PathEndMode.ClosestTouch,
                                TraverseParms.For(pawn, Danger.None, TraverseMode.ByPawn),
                                proximitySearchRadius,
                                x => !x.IsForbidden(pawn) && pawn.CanReserve(x));
                            if (curThing != null)
                            {
                                curPriority = ItemPriority.Proximity;
                            }
                            else
                            {
                                curThing = GenClosest.ClosestThingReachable(
                                    pawn.Position,
                                    pawn.Map,
                                    ThingRequest.ForDef(curSlot.Def),
                                    PathEndMode.ClosestTouch,
                                    TraverseParms.For(pawn, Danger.None, TraverseMode.ByPawn),
                                    maximumSearchRadius,
                                    x => !x.IsForbidden(pawn) && pawn.CanReserve(x));
                                if (curThing != null)
                                {
                                    if (!curSlot.Def.IsNutritionGivingIngestible && numCarried / curSlot.Count <= 0.5f)
                                    {
                                        curPriority = ItemPriority.LowStock;
                                    }
                                    else
                                    {
                                        curPriority = ItemPriority.Low;
                                    }
                                }
                            }
                        }
                        if (curPriority > priority && curThing != null && inventory.CanFitInInventory(curThing, out count))
                        {
                            priority     = curPriority;
                            slot         = curSlot;
                            closestThing = curThing;
                        }
                        if (priority >= ItemPriority.LowStock)
                        {
                            break;
                        }
                    }
                }
            }

            return(slot);
        }
Exemplo n.º 21
0
        /// <summary>
        /// Used by GetPrioritySlot, actually finds a requested thing.
        /// </summary>
        /// <param name="pawn">Pawn to be considered.  Used in checking equipment and position when looking for nearby things.</param>
        /// <param name="curSlot">Pawn's LoadoutSlot being considered.</param>
        /// <param name="findCount">Amount of Thing of ThingDef to try and pickup.</param>
        /// <param name="curPriority">Priority of the job.</param>
        /// <param name="curThing">Thing found near pawn for potential pickup.</param>
        /// <param name="curCarrier">Pawn that is holding the curThing that 'pawn' wants.</param>
        /// <remarks>Was split off into a sepearate method so the code could be run from multiple places in caller but that is no longer needed.</remarks>
        private void FindPickup(Pawn pawn, LoadoutSlot curSlot, int findCount, out ItemPriority curPriority, out Thing curThing, out Pawn curCarrier)
        {
            curPriority = ItemPriority.None;
            curThing    = null;
            curCarrier  = null;

            Predicate <Thing> isFoodInPrison = (Thing t) => (t.GetRoom()?.isPrisonCell ?? false) && t.def.IsNutritionGivingIngestible && pawn.Faction.IsPlayer;
            // Hint: The following block defines how to find items... pay special attention to the Predicates below.
            ThingRequest req;

            if (curSlot.genericDef != null)
            {
                req = ThingRequest.ForGroup(curSlot.genericDef.thingRequestGroup);
            }
            else
            {
                req = curSlot.thingDef.Minifiable ? ThingRequest.ForGroup(ThingRequestGroup.MinifiedThing) : ThingRequest.ForDef(curSlot.thingDef);
            }
            Predicate <Thing> findItem;

            if (curSlot.genericDef != null)
            {
                findItem = t => curSlot.genericDef.lambda(t.GetInnerIfMinified().def);
            }
            else
            {
                findItem = t => t.GetInnerIfMinified().def == curSlot.thingDef;
            }
            Predicate <Thing> search = t => findItem(t) && !t.IsForbidden(pawn) && pawn.CanReserve(t) && !isFoodInPrison(t);

            // look for a thing near the pawn.
            curThing = GenClosest.ClosestThingReachable(
                pawn.Position,
                pawn.Map,
                req,
                PathEndMode.ClosestTouch,
                TraverseParms.For(pawn, Danger.None, TraverseMode.ByPawn),
                ProximitySearchRadius,
                search);
            if (curThing != null)
            {
                curPriority = ItemPriority.Proximity;
            }
            else
            {
                // look for a thing basically anywhere on the map.
                curThing = GenClosest.ClosestThingReachable(
                    pawn.Position,
                    pawn.Map,
                    req,
                    PathEndMode.ClosestTouch,
                    TraverseParms.For(pawn, Danger.None, TraverseMode.ByPawn),
                    MaximumSearchRadius,
                    search);
                if (curThing == null && pawn.Map != null)
                {
                    // look for a thing inside caravan pack animals and prisoners.  EXCLUDE other colonists to avoid looping state.
                    List <Pawn> carriers = pawn.Map.mapPawns.AllPawns.Where(
                        p => (p.inventory?.innerContainer?.InnerListForReading?.Any() ?? false) && (p.RaceProps.packAnimal && p.Faction == pawn.Faction || p.IsPrisoner && p.HostFaction == pawn.Faction) &&
                        pawn.CanReserveAndReach(p, PathEndMode.ClosestTouch, Danger.Deadly, int.MaxValue, 0)).ToList();
                    foreach (Pawn carrier in carriers)
                    {
                        Thing thing = carrier.inventory.innerContainer.FirstOrDefault(t => findItem(t));
                        if (thing != null)
                        {
                            curThing   = thing;
                            curCarrier = carrier;
                            break;
                        }
                    }
                }
                if (curThing != null)
                {
                    if (!curThing.def.IsNutritionGivingIngestible && (float)findCount / curSlot.count >= 0.5f)
                    {
                        curPriority = ItemPriority.LowStock;
                    }
                    else
                    {
                        curPriority = ItemPriority.Low;
                    }
                }
            }
        }
Exemplo n.º 22
0
        private void DrawSlot(Rect row, LoadoutSlot slot, bool slotDraggable = true)
        {
            // set up rects
            // dragging handle (square) | label (fill) | count (50px) | delete (iconSize)
            Rect draggingHandle = new Rect(row);

            draggingHandle.width = row.height;

            Rect labelRect = new Rect(row);

            if (slotDraggable)
            {
                labelRect.xMin = draggingHandle.xMax;
            }
            labelRect.xMax = row.xMax - _countFieldSize.x - _iconSize - 2 * _margin;

            Rect countRect = new Rect(
                row.xMax - _countFieldSize.x - _iconSize - 2 * _margin,
                row.yMin + (row.height - _countFieldSize.y) / 2f,
                _countFieldSize.x,
                _countFieldSize.y);

            Rect ammoRect = new Rect(
                countRect.xMin - _iconSize - _margin,
                row.yMin + (row.height - _iconSize) / 2f,
                _iconSize, _iconSize);

            Rect deleteRect = new Rect(countRect.xMax + _margin, row.yMin + (row.height - _iconSize) / 2f, _iconSize, _iconSize);

            // dragging on dragHandle
            if (slotDraggable)
            {
                TooltipHandler.TipRegion(draggingHandle, "CE_DragToReorder".Translate());
                GUI.DrawTexture(draggingHandle, _iconMove);

                if (Mouse.IsOver(draggingHandle) && Input.GetMouseButtonDown(0))
                {
                    Dragging = slot;
                }
            }

            // interactions (main row rect)
            if (!Mouse.IsOver(deleteRect))
            {
                Widgets.DrawHighlightIfMouseover(row);
                TooltipHandler.TipRegion(row, slot.Def.GetWeightAndBulkTip(slot.Count));
            }

            // label
            Text.Anchor = TextAnchor.MiddleLeft;
            Widgets.Label(labelRect, slot.Def.LabelCap);
            Text.Anchor = TextAnchor.UpperLeft;

            // easy ammo adder, ranged weapons only
            if (slot.Def.IsRangedWeapon)
            {
                // make sure there's an ammoset defined
                AmmoSetDef ammoSet = ((slot.Def.GetCompProperties <CompProperties_AmmoUser>() == null) ? null : slot.Def.GetCompProperties <CompProperties_AmmoUser>().ammoSet);

                bool?temp = !((((ammoSet == null) ? null : ammoSet.ammoTypes)).NullOrEmpty());

                if (temp ?? false)
                {
                    if (Widgets.ButtonImage(ammoRect, _iconAmmoAdd))
                    {
                        List <FloatMenuOption> options = new List <FloatMenuOption>();

                        foreach (ThingDef ammo in ((ammoSet == null) ? null : ammoSet.ammoTypes))
                        {
                            options.Add(new FloatMenuOption(ammo.LabelCap, delegate
                            {
                                CurrentLoadout.AddSlot(new LoadoutSlot(ammo));
                            }));
                        }

                        Find.WindowStack.Add(new FloatMenu(options, "CE_AddAmmoFor".Translate(slot.Def.LabelCap)));
                    }
                }
            }

            // count
            DrawCountField(countRect, slot);

            // delete
            if (Mouse.IsOver(deleteRect))
            {
                GUI.DrawTexture(row, TexUI.HighlightTex);
            }
            if (Widgets.ButtonImage(deleteRect, _iconClear))
            {
                CurrentLoadout.RemoveSlot(slot);
            }
            TooltipHandler.TipRegion(deleteRect, "CE_DeleteFilter".Translate());
        }