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); }
/// <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); } }
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; }
/// <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); } }
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; } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
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(); }
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()); }
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); }
/// <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); }
/// <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); }
/// <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); }
public void AddSlot(LoadoutSlot slot) { _slots.Add(slot); }
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); }
/// <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; } } } }
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()); }