根据向量相似度分析,与 'get_FirstUnloadableThing, Pawn_InventoryTracker' 最相关的代码定义如下: --- **文件路径 (精确匹配):** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6\Verse\Pawn_InventoryTracker.txt` ```csharp public class Pawn_InventoryTracker : IThingHolder, IExposable { public Pawn pawn; public ThingOwner innerContainer; private bool unloadEverything; private List itemsNotForSale = new List(); private List unpackedCaravanItems = new List(); public static readonly Texture2D DrugTex = ContentFinder.Get("UI/Commands/TakeDrug"); private static List tmpItemsToKeep = new List(); private static readonly List tmpThingList = new List(); private List usableDrugsTmp = new List(); public bool UnloadEverything { get { if (unloadEverything) { return HasAnyUnloadableThing; } return false; } set { if (value && HasAnyUnloadableThing) { unloadEverything = true; } else { unloadEverything = false; } } } public bool HasAnyUnpackedCaravanItems => unpackedCaravanItems.Count > 0; private bool HasAnyUnloadableThing => FirstUnloadableThing != default(ThingCount); public ThingCount FirstUnloadableThing { get { if (innerContainer.Count == 0) { return default(ThingCount); } if (pawn.drugs?.CurrentPolicy != null) { DrugPolicy currentPolicy = pawn.drugs.CurrentPolicy; tmpItemsToKeep.Clear(); for (int i = 0; i < currentPolicy.Count; i++) { if (currentPolicy[i].takeToInventory > 0) { tmpItemsToKeep.Add(new ThingDefCount(currentPolicy[i].drug, currentPolicy[i].takeToInventory)); } } } Pawn_InventoryStockTracker inventoryStock = pawn.inventoryStock; if (inventoryStock != null && inventoryStock.stockEntries?.Count > 0) { foreach (InventoryStockEntry value in pawn.inventoryStock.stockEntries.Values) { tmpItemsToKeep.Add(new ThingDefCount(value.thingDef, value.count)); } } foreach (Thing item in innerContainer) { int num = -1; for (int j = 0; j < tmpItemsToKeep.Count; j++) { if (item.def == tmpItemsToKeep[j].ThingDef) { num = j; break; } } if (pawn.IsColonist && item.def.IsNutritionGivingIngestible && !item.def.IsDrug && JobGiver_PackFood.IsGoodPackableFoodFor(item, pawn, checkMass: false)) { float inventoryPackableFoodNutrition = JobGiver_PackFood.GetInventoryPackableFoodNutrition(pawn); float maxLevel = pawn.needs.food.MaxLevel; if (inventoryPackableFoodNutrition - item.GetStatValue(StatDefOf.Nutrition) * (float)item.stackCount <= maxLevel) { int k; for (k = 0; inventoryPackableFoodNutrition - item.GetStatValue(StatDefOf.Nutrition) * (float)k > maxLevel; k++) { } if (item.stackCount - k > 0) { tmpItemsToKeep.Add(new ThingDefCount(item.def, item.stackCount - k)); num = tmpItemsToKeep.Count - 1; } } } if (num < 0) { return new ThingCount(item, item.stackCount); } if (item.stackCount > tmpItemsToKeep[num].Count) { return new ThingCount(item, item.stackCount - tmpItemsToKeep[num].Count); } tmpItemsToKeep[num] = new ThingDefCount(tmpItemsToKeep[num].ThingDef, tmpItemsToKeep[num].Count - item.stackCount); } return default(ThingCount); } } public IThingHolder ParentHolder => pawn; public Pawn_InventoryTracker(Pawn pawn) { this.pawn = pawn; innerContainer = new ThingOwner(this, oneStackOnly: false); } public void ExposeData() { Scribe_Collections.Look(ref itemsNotForSale, "itemsNotForSale", LookMode.Reference); Scribe_Collections.Look(ref unpackedCaravanItems, "unpackedCaravanItems", LookMode.Reference); Scribe_Deep.Look(ref innerContainer, "innerContainer", this); Scribe_Values.Look(ref unloadEverything, "unloadEverything", defaultValue: false); } public void InventoryTrackerTick() { if (unloadEverything && !HasAnyUnloadableThing) { unloadEverything = false; } } public void DropAllNearPawn(IntVec3 pos, bool forbid = false, bool unforbid = false) { DropAllNearPawnHelper(pos, forbid, unforbid); } private void DropAllNearPawnHelper(IntVec3 pos, bool forbid = false, bool unforbid = false, bool caravanHaulOnly = false) { if (pawn.MapHeld == null) { Log.Error("Tried to drop all inventory near pawn but the pawn is unspawned. pawn=" + pawn); return; } tmpThingList.Clear(); if (caravanHaulOnly) { tmpThingList.AddRange(unpackedCaravanItems); } else { tmpThingList.AddRange(innerContainer); } int i; for (i = 0; i < tmpThingList.Count; i++) { if (caravanHaulOnly && !innerContainer.Contains(tmpThingList[i])) { unpackedCaravanItems.Remove(tmpThingList[i]); Log.Warning("Could not drop unpacked caravan item " + tmpThingList[i].Label + ", inventory no longer contains it"); continue; } innerContainer.TryDrop(tmpThingList[i], pos, pawn.MapHeld, ThingPlaceMode.Near, out var _, delegate(Thing t, int unused) { if (forbid) { t.SetForbiddenIfOutsideHomeArea(); } if (unforbid) { t.SetForbidden(value: false, warnOnFail: false); } if (t.def.IsPleasureDrug) { LessonAutoActivator.TeachOpportunity(ConceptDefOf.DrugBurning, OpportunityType.Important); } LordJob_FormAndSendCaravan lordJob_FormAndSendCaravan = CaravanFormingUtility.GetFormAndSendCaravanLord(pawn)?.LordJob as LordJob_FormAndSendCaravan; if (caravanHaulOnly && lordJob_FormAndSendCaravan != null && lordJob_FormAndSendCaravan.GatheringItemsNow) { CaravanFormingUtility.TryAddItemBackToTransferables(t, lordJob_FormAndSendCaravan.transferables, tmpThingList[i].stackCount); } unpackedCaravanItems.Remove(tmpThingList[i]); }); } } public void DropCount(ThingDef def, int count, bool forbid = false, bool unforbid = false) { if (pawn.MapHeld == null) { Log.Error("Tried to drop a thing near pawn but the pawn is unspawned. pawn=" + pawn); return; } tmpThingList.Clear(); tmpThingList.AddRange(innerContainer); int num = 0; for (int i = 0; i < tmpThingList.Count; i++) { Thing thing = tmpThingList[i]; if (thing.def != def) { continue; } int num2 = Math.Min(thing.stackCount, count); innerContainer.TryDrop(tmpThingList[i], pawn.Position, pawn.MapHeld, ThingPlaceMode.Near, num2, out var _, delegate(Thing t, int unused) { if (forbid) { t.SetForbiddenIfOutsideHomeArea(); } if (unforbid) { t.SetForbidden(value: false, warnOnFail: false); } if (t.def.IsPleasureDrug) { LessonAutoActivator.TeachOpportunity(ConceptDefOf.DrugBurning, OpportunityType.Important); } }); num += num2; if (num >= count) { break; } } } public void RemoveCount(ThingDef def, int count, bool destroy = true) { tmpThingList.Clear(); tmpThingList.AddRange(innerContainer); foreach (Thing tmpThing in tmpThingList) { if (tmpThing.def != def) { continue; } if (tmpThing.stackCount > count) { tmpThing.stackCount -= count; break; } innerContainer.Remove(tmpThing); if (destroy) { tmpThing.Destroy(); } break; } } public void DestroyAll(DestroyMode mode = DestroyMode.Vanish) { innerContainer.ClearAndDestroyContents(mode); } public bool Contains(Thing item) { return innerContainer.Contains(item); } public int Count(ThingDef def) { int num = 0; foreach (Thing item in innerContainer) { if (item.def == def) { num += item.stackCount; } } return num; } public int Count(Func validator) { int num = 0; foreach (Thing item in innerContainer) { if (validator(item)) { num += item.stackCount; } } return num; } public void AddHauledCaravanItem(Thing item) { if (pawn.carryTracker.innerContainer.TryTransferToContainer(item, innerContainer, item.stackCount, out var resultingTransferredItem, canMergeWithExistingStacks: false) > 0) { unpackedCaravanItems.Add(resultingTransferredItem); } CompForbiddable compForbiddable = resultingTransferredItem?.TryGetComp(); if (compForbiddable != null) { compForbiddable.Forbidden = false; } } public void TryAddAndUnforbid(Thing item) { CompForbiddable compForbiddable = item.TryGetComp(); if (innerContainer.TryAdd(item) && compForbiddable != null) { compForbiddable.Forbidden = false; } } public void TransferCaravanItemsToCarrier(Pawn_InventoryTracker carrierInventory) { List list = new List(); list.AddRange(pawn.inventory.unpackedCaravanItems); foreach (Thing item in list) { if (MassUtility.IsOverEncumbered(carrierInventory.pawn)) { break; } if (innerContainer.Contains(item)) { pawn.inventory.innerContainer.TryTransferToContainer(item, carrierInventory.innerContainer, item.stackCount); } unpackedCaravanItems.Remove(item); } } public void DropAllPackingCaravanThings() { if (pawn.Spawned) { DropAllNearPawnHelper(pawn.Position, forbid: false, unforbid: false, caravanHaulOnly: true); ClearHaulingCaravanCache(); } } public void ClearHaulingCaravanCache() { unpackedCaravanItems.Clear(); } public bool NotForSale(Thing item) { return itemsNotForSale.Contains(item); } public void TryAddItemNotForSale(Thing item) { if (innerContainer.TryAdd(item, canMergeWithExistingStacks: false)) { itemsNotForSale.Add(item); } } public void Notify_ItemRemoved(Thing item) { itemsNotForSale.Remove(item); unpackedCaravanItems.Remove(item); if (unloadEverything && !HasAnyUnloadableThing) { unloadEverything = false; } } public ThingOwner GetDirectlyHeldThings() { return innerContainer; } public void GetChildHolders(List outChildren) { ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings()); } public IEnumerable GetDrugs() { foreach (Thing item in innerContainer) { if (item.TryGetComp() != null) { yield return item; } } } public IEnumerable GetCombatEnhancingDrugs() { foreach (Thing item in innerContainer) { CompDrug compDrug = item.TryGetComp(); if (compDrug != null && compDrug.Props.isCombatEnhancingDrug) { yield return item; } } } public Thing FindCombatEnhancingDrug() { return GetCombatEnhancingDrugs().FirstOrDefault(); } public IEnumerable GetGizmos() { if (!pawn.IsColonistPlayerControlled || !pawn.Drafted || Find.Selector.SingleSelectedThing != pawn) { yield break; } usableDrugsTmp.Clear(); foreach (Thing drug2 in GetDrugs()) { if (FoodUtility.WillIngestFromInventoryNow(pawn, drug2)) { usableDrugsTmp.Add(drug2); } } if (usableDrugsTmp.Count == 0) { yield break; } if (usableDrugsTmp.Count == 1) { Thing drug = usableDrugsTmp[0]; string defaultLabel = (drug.def.ingestible.ingestCommandString.NullOrEmpty() ? "ConsumeThing".Translate(drug.LabelNoCount, drug) : drug.def.ingestible.ingestCommandString.Formatted(drug.LabelShort)); Command_Action command_Action = new Command_Action(); command_Action.defaultLabel = defaultLabel; command_Action.defaultDesc = drug.LabelCapNoCount + ": " + drug.def.description.CapitalizeFirst(); command_Action.icon = drug.def.uiIcon; command_Action.iconAngle = drug.def.uiIconAngle; command_Action.iconOffset = drug.def.uiIconOffset; command_Action.action = delegate { FoodUtility.IngestFromInventoryNow(pawn, drug); }; yield return command_Action; yield break; } Command_Action command_Action2 = new Command_Action(); command_Action2.defaultLabel = "TakeDrug".Translate(); command_Action2.defaultDesc = "TakeDrugDesc".Translate(); command_Action2.icon = DrugTex; command_Action2.action = delegate { List list = new List(); foreach (Thing drug in usableDrugsTmp) { string label = (drug.def.ingestible.ingestCommandString.NullOrEmpty() ? "ConsumeThing".Translate(drug.LabelNoCount, drug) : drug.def.ingestible.ingestCommandString.Formatted(drug.LabelShort)); list.Add(new FloatMenuOption(label, delegate { FoodUtility.IngestFromInventoryNow(pawn, drug); })); } Find.WindowStack.Add(new FloatMenu(list)); }; yield return command_Action2; } } ``` --- **文件路径:** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6\Verse\ThingOwnerUtility.txt` **相似度:** 0.6304 ```csharp public static class ThingOwnerUtility { private static readonly Stack tmpStack = new Stack(); private static readonly List tmpHolders = new List(); private static readonly List tmpThings = new List(); private static readonly List tmpMapChildHolders = new List(); public static bool ThisOrAnyCompIsThingHolder(this ThingDef thingDef) { if (typeof(IThingHolder).IsAssignableFrom(thingDef.thingClass)) { return true; } for (int i = 0; i < thingDef.comps.Count; i++) { if (typeof(IThingHolder).IsAssignableFrom(thingDef.comps[i].compClass)) { return true; } } return false; } public static ThingOwner TryGetInnerInteractableThingOwner(this Thing thing) { IThingHolder thingHolder = thing as IThingHolder; ThingWithComps thingWithComps = thing as ThingWithComps; if (thingHolder != null) { ThingOwner directlyHeldThings = thingHolder.GetDirectlyHeldThings(); if (directlyHeldThings != null) { return directlyHeldThings; } } if (thingWithComps != null) { List allComps = thingWithComps.AllComps; for (int i = 0; i < allComps.Count; i++) { if (allComps[i] is IThingHolder thingHolder2) { ThingOwner directlyHeldThings2 = thingHolder2.GetDirectlyHeldThings(); if (directlyHeldThings2 != null) { return directlyHeldThings2; } } } } tmpHolders.Clear(); if (thingHolder != null) { thingHolder.GetChildHolders(tmpHolders); if (tmpHolders.Any()) { ThingOwner directlyHeldThings3 = tmpHolders[0].GetDirectlyHeldThings(); if (directlyHeldThings3 != null) { tmpHolders.Clear(); return directlyHeldThings3; } } } if (thingWithComps != null) { List allComps2 = thingWithComps.AllComps; for (int j = 0; j < allComps2.Count; j++) { if (!(allComps2[j] is IThingHolder thingHolder3)) { continue; } thingHolder3.GetChildHolders(tmpHolders); if (tmpHolders.Any()) { ThingOwner directlyHeldThings4 = tmpHolders[0].GetDirectlyHeldThings(); if (directlyHeldThings4 != null) { tmpHolders.Clear(); return directlyHeldThings4; } } } } tmpHolders.Clear(); return null; } public static bool SpawnedOrAnyParentSpawned(IThingHolder holder) { return SpawnedParentOrMe(holder) != null; } public static Thing SpawnedParentOrMe(IThingHolder holder) { while (holder != null) { if (holder is Thing { Spawned: not false } thing) { return thing; } if (holder is ThingComp thingComp && thingComp.parent.Spawned) { return thingComp.parent; } holder = holder.ParentHolder; } return null; } public static IntVec3 GetRootPosition(IThingHolder holder) { IntVec3 result = IntVec3.Invalid; while (holder != null) { if (holder is Thing { Position: { IsValid: not false } } thing) { result = thing.Position; } else if (holder is ThingComp thingComp && thingComp.parent.Position.IsValid) { result = thingComp.parent.Position; } holder = holder.ParentHolder; } return result; } public static Map GetRootMap(IThingHolder holder) { while (holder != null) { if (holder is Map result) { return result; } holder = holder.ParentHolder; } return null; } public static PlanetTile GetRootTile(IThingHolder holder) { while (holder != null) { if (holder is WorldObject { Tile: { Valid: not false } } worldObject) { return worldObject.Tile; } holder = holder.ParentHolder; } return PlanetTile.Invalid; } public static bool ContentsSuspended(IThingHolder holder) { while (holder != null) { if (holder is Building_CryptosleepCasket || holder is ISuspendableThingHolder { IsContentsSuspended: not false }) { return true; } holder = holder.ParentHolder; } return false; } public static bool ContentsInCryptosleep(IThingHolder holder) { while (holder != null) { if (holder is Building_CryptosleepCasket) { return true; } holder = holder.ParentHolder; } return false; } public static bool IsEnclosingContainer(this IThingHolder holder) { if (holder != null && !(holder is Pawn_CarryTracker) && !(holder is Corpse) && !(holder is Map) && !(holder is Caravan) && !(holder is Settlement_TraderTracker)) { return !(holder is TradeShip); } return false; } public static bool ShouldAutoRemoveDestroyedThings(IThingHolder holder) { if (!(holder is Corpse)) { return !(holder is Caravan); } return false; } public static bool ShouldAutoExtinguishInnerThings(IThingHolder holder) { return !(holder is Map); } public static bool ShouldRemoveDesignationsOnAddedThings(IThingHolder holder) { return holder.IsEnclosingContainer(); } public static void AppendThingHoldersFromThings(List outThingsHolders, IList container) { if (container == null) { return; } int i = 0; for (int count = container.Count; i < count; i++) { if (container[i] is IThingHolder item) { outThingsHolders.Add(item); } if (!(container[i] is ThingWithComps { AllComps: var allComps })) { continue; } for (int j = 0; j < allComps.Count; j++) { if (allComps[j] is IThingHolder item2) { outThingsHolders.Add(item2); } } } } public static bool AnyParentIs(Thing thing) where T : class, IThingHolder { return GetAnyParent(thing) != null; } public static T GetAnyParent(Thing thing) where T : class, IThingHolder { if (thing is T result) { return result; } for (IThingHolder parentHolder = thing.ParentHolder; parentHolder != null; parentHolder = parentHolder.ParentHolder) { if (parentHolder is T result2) { return result2; } } return null; } public static Thing GetFirstParentThing(Thing thing) { for (IThingHolder parentHolder = thing.ParentHolder; parentHolder != null; parentHolder = parentHolder.ParentHolder) { if (parentHolder is Thing result) { return result; } if (parentHolder is ThingComp thingComp) { return thingComp.parent; } } return null; } public static Thing GetFirstSpawnedParentThing(Thing thing) { if (thing.Spawned) { return thing; } for (IThingHolder parentHolder = thing.ParentHolder; parentHolder != null; parentHolder = parentHolder.ParentHolder) { if (parentHolder is Thing { Spawned: not false } thing2) { return thing2; } if (parentHolder is ThingComp thingComp && thingComp.parent.Spawned) { return thingComp.parent; } } return null; } public static void GetAllThingsRecursively(IThingHolder holder, List outThings, bool allowUnreal = true, Predicate passCheck = null) { outThings.Clear(); if (passCheck != null && !passCheck(holder)) { return; } tmpStack.Clear(); tmpStack.Push(holder); while (tmpStack.Count != 0) { IThingHolder thingHolder = tmpStack.Pop(); if (allowUnreal || AreImmediateContentsReal(thingHolder)) { ThingOwner directlyHeldThings = thingHolder.GetDirectlyHeldThings(); if (directlyHeldThings != null) { outThings.AddRange(directlyHeldThings); } } tmpHolders.Clear(); thingHolder.GetChildHolders(tmpHolders); for (int i = 0; i < tmpHolders.Count; i++) { if (passCheck == null || passCheck(tmpHolders[i])) { tmpStack.Push(tmpHolders[i]); } } } tmpStack.Clear(); tmpHolders.Clear(); } public static void GetAllThingsRecursively(Map map, ThingRequest request, List outThings, bool allowUnreal = true, Predicate passCheck = null, bool alsoGetSpawnedThings = true) where T : Thing { outThings.Clear(); if (alsoGetSpawnedThings) { List list = map.listerThings.ThingsMatching(request); for (int i = 0; i < list.Count; i++) { if (list[i] is T item) { outThings.Add(item); } } } tmpMapChildHolders.Clear(); map.GetChildHolders(tmpMapChildHolders); for (int j = 0; j < tmpMapChildHolders.Count; j++) { tmpThings.Clear(); GetAllThingsRecursively(tmpMapChildHolders[j], tmpThings, allowUnreal, passCheck); for (int k = 0; k < tmpThings.Count; k++) { if (tmpThings[k] is T val && request.Accepts(val)) { outThings.Add(val); } } } tmpThings.Clear(); tmpMapChildHolders.Clear(); } public static List GetAllThingsRecursively(IThingHolder holder, bool allowUnreal = true) { List list = new List(); GetAllThingsRecursively(holder, list, allowUnreal); return list; } public static bool AreImmediateContentsReal(IThingHolder holder) { if (!(holder is Corpse)) { return !(holder is MinifiedThing); } return false; } public static bool TryGetFixedTemperature(IThingHolder holder, Thing forThing, out float temperature) { if (holder is Pawn_InventoryTracker && forThing.TryGetComp() != null) { temperature = 14f; return true; } if (holder is CompLaunchable || holder is ActiveTransporterInfo || holder is TravellingTransporters) { temperature = 14f; return true; } if (holder is Settlement_TraderTracker || holder is TradeShip) { temperature = 14f; return true; } if (holder is CompTransporter) { temperature = 14f; return true; } temperature = 21f; return false; } } ``` --- **文件路径:** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6\RimWorld.Planet\CaravanInventoryUtility.txt` **相似度:** 0.6279 ```csharp public static class CaravanInventoryUtility { private static List inventoryItems = new List(); private static List inventoryToMove = new List(); private static List tmpApparel = new List(); private static List tmpEquipment = new List(); public static List AllInventoryItems(Caravan caravan) { inventoryItems.Clear(); List pawnsListForReading = caravan.PawnsListForReading; for (int i = 0; i < pawnsListForReading.Count; i++) { Pawn pawn = pawnsListForReading[i]; for (int num = pawn.inventory.innerContainer.Count - 1; num >= 0; num--) { Thing item = pawn.inventory.innerContainer[num]; inventoryItems.Add(item); } } return inventoryItems; } public static Building_PassengerShuttle FindShuttle(Caravan caravan) { List list = AllInventoryItems(caravan); for (int i = 0; i < list.Count; i++) { if (list[i] is Building_PassengerShuttle result) { return result; } } return null; } public static void CaravanInventoryUtilityStaticUpdate() { inventoryItems.Clear(); } public static Pawn GetOwnerOf(Caravan caravan, Thing item) { IThingHolder parentHolder = item.ParentHolder; if (parentHolder is Pawn_InventoryTracker) { Pawn pawn = (Pawn)parentHolder.ParentHolder; if (caravan.ContainsPawn(pawn)) { return pawn; } } return null; } public static bool TryGetBestFood(Caravan caravan, Pawn forPawn, out Thing food, out Pawn owner) { List list = AllInventoryItems(caravan); Thing thing = null; float num = 0f; for (int i = 0; i < list.Count; i++) { Thing thing2 = list[i]; if (CaravanPawnsNeedsUtility.CanEatForNutritionNow(thing2, forPawn)) { float foodScore = CaravanPawnsNeedsUtility.GetFoodScore(thing2, forPawn); if (thing == null || foodScore > num) { thing = thing2; num = foodScore; } } } if (thing != null) { food = thing; owner = GetOwnerOf(caravan, thing); return true; } food = null; owner = null; return false; } public static bool TryGetDrugToSatisfyChemicalNeed(Caravan caravan, Pawn forPawn, Hediff hediff, out Thing drug, out Pawn owner) { if (hediff == null) { drug = null; owner = null; return false; } List list = AllInventoryItems(caravan); Thing thing = null; for (int i = 0; i < list.Count; i++) { Thing thing2 = list[i]; if (!thing2.IngestibleNow || !thing2.def.IsDrug) { continue; } CompDrug compDrug = thing2.TryGetComp(); if (compDrug != null && compDrug.Props.chemical != null && (!(hediff is Hediff_ChemicalDependency hediff_ChemicalDependency) || compDrug.Props.chemical == hediff_ChemicalDependency.chemical) && (!(hediff is Hediff_Addiction hediff_Addiction) || compDrug.Props.chemical.addictionHediff == hediff_Addiction.def)) { DrugPolicy drugPolicy = forPawn.drugs?.CurrentPolicy; if (drugPolicy == null || drugPolicy[thing2.def].allowedForAddiction || forPawn.story == null || forPawn.story.traits.DegreeOfTrait(TraitDefOf.DrugDesire) > 0) { thing = thing2; break; } } } if (thing != null) { drug = thing; owner = GetOwnerOf(caravan, thing); return true; } drug = null; owner = null; return false; } public static bool TryGetBestMedicine(Caravan caravan, Pawn patient, out Medicine medicine, out Pawn owner) { if (patient.playerSettings == null || (int)patient.playerSettings.medCare <= 1) { medicine = null; owner = null; return false; } List list = AllInventoryItems(caravan); Medicine medicine2 = null; float num = 0f; for (int i = 0; i < list.Count; i++) { Thing thing = list[i]; if (thing.def.IsMedicine && patient.playerSettings.medCare.AllowsMedicine(thing.def)) { float statValue = thing.GetStatValue(StatDefOf.MedicalPotency); if (statValue > num || medicine2 == null) { num = statValue; medicine2 = (Medicine)thing; } } } if (medicine2 != null) { medicine = medicine2; owner = GetOwnerOf(caravan, medicine2); return true; } medicine = null; owner = null; return false; } public static bool TryGetThingOfDef(Caravan caravan, ThingDef thingDef, out Thing thing, out Pawn owner) { List list = AllInventoryItems(caravan); for (int i = 0; i < list.Count; i++) { Thing thing2 = list[i]; if (thing2.def == thingDef) { thing = thing2; owner = GetOwnerOf(caravan, thing2); return true; } } thing = null; owner = null; return false; } public static void MoveAllInventoryToSomeoneElse(Pawn from, List candidates, List ignoreCandidates = null) { inventoryToMove.Clear(); inventoryToMove.AddRange(from.inventory.innerContainer); for (int i = 0; i < inventoryToMove.Count; i++) { MoveInventoryToSomeoneElse(from, inventoryToMove[i], candidates, ignoreCandidates, inventoryToMove[i].stackCount); } inventoryToMove.Clear(); } public static void MoveInventoryToSomeoneElse(Pawn itemOwner, Thing item, List candidates, List ignoreCandidates, int numToMove) { if (numToMove < 0 || numToMove > item.stackCount) { Log.Warning("Tried to move item " + item?.ToString() + " with numToMove=" + numToMove + " (item stack count = " + item.stackCount + ")"); } else { Pawn pawn = FindPawnToMoveInventoryTo(item, candidates, ignoreCandidates, itemOwner); if (pawn != null) { itemOwner.inventory.innerContainer.TryTransferToContainer(item, pawn.inventory.innerContainer, numToMove); } } } public static Pawn FindPawnToMoveInventoryTo(Thing item, List candidates, List ignoreCandidates, Pawn currentItemOwner = null) { if (item is Pawn) { Log.Error("Called FindPawnToMoveInventoryTo but the item is a pawn."); return null; } if (candidates.Where((Pawn x) => CanMoveInventoryTo(x) && (ignoreCandidates == null || !ignoreCandidates.Contains(x)) && x != currentItemOwner && !MassUtility.IsOverEncumbered(x)).TryRandomElement(out var result)) { return result; } if (candidates.Where((Pawn x) => CanMoveInventoryTo(x) && (ignoreCandidates == null || !ignoreCandidates.Contains(x)) && x != currentItemOwner).TryRandomElement(out result)) { return result; } if (candidates.Where((Pawn x) => (ignoreCandidates == null || !ignoreCandidates.Contains(x)) && x != currentItemOwner).TryRandomElement(out result)) { return result; } return null; } public static void MoveAllApparelToSomeonesInventory(Pawn moveFrom, List candidates, bool moveLocked = true) { if (moveFrom.apparel == null) { return; } tmpApparel.Clear(); if (moveLocked) { tmpApparel.AddRange(moveFrom.apparel.WornApparel); } else { for (int i = 0; i < moveFrom.apparel.WornApparel.Count; i++) { Apparel apparel = moveFrom.apparel.WornApparel[i]; if (!moveFrom.apparel.IsLocked(apparel)) { tmpApparel.Add(apparel); } } } for (int j = 0; j < tmpApparel.Count; j++) { moveFrom.apparel.Remove(tmpApparel[j]); FindPawnToMoveInventoryTo(tmpApparel[j], candidates, null, moveFrom)?.inventory.innerContainer.TryAdd(tmpApparel[j]); } tmpApparel.Clear(); } public static void MoveAllEquipmentToSomeonesInventory(Pawn moveFrom, List candidates) { if (moveFrom.equipment != null) { tmpEquipment.Clear(); tmpEquipment.AddRange(moveFrom.equipment.AllEquipmentListForReading); for (int i = 0; i < tmpEquipment.Count; i++) { moveFrom.equipment.Remove(tmpEquipment[i]); FindPawnToMoveInventoryTo(tmpEquipment[i], candidates, null, moveFrom)?.inventory.innerContainer.TryAdd(tmpEquipment[i]); } tmpEquipment.Clear(); } } private static bool CanMoveInventoryTo(Pawn pawn) { return MassUtility.CanEverCarryAnything(pawn); } public static List TakeThings(Caravan caravan, Func takeQuantity) { List list = new List(); foreach (Thing item in AllInventoryItems(caravan).ToList()) { int num = takeQuantity(item); if (num > 0) { list.Add(item.holdingOwner.Take(item, num)); } } return list; } public static void GiveThing(Caravan caravan, Thing thing) { if (AllInventoryItems(caravan).Contains(thing)) { Log.Error("Tried to give the same item twice (" + thing?.ToString() + ") to a caravan (" + caravan?.ToString() + ")."); return; } Pawn pawn = FindPawnToMoveInventoryTo(thing, caravan.PawnsListForReading, null); if (pawn == null) { Log.Error($"Failed to give item {thing} to caravan {caravan}; item was lost"); thing.Destroy(); } else if (!pawn.inventory.innerContainer.TryAdd(thing)) { Log.Error($"Failed to give item {thing} to caravan {caravan}; item was lost"); thing.Destroy(); } } public static bool HasThings(Caravan caravan, ThingDef thingDef, int count, Func validator = null) { int num = 0; List list = AllInventoryItems(caravan); for (int i = 0; i < list.Count; i++) { Thing thing = list[i]; if (thing.def == thingDef && (validator == null || validator(thing))) { num += thing.stackCount; } } return num >= count; } public static IEnumerable GetAllDissolvingThings(Caravan caravan) { ThingRequest group = ThingRequest.ForGroup(ThingRequestGroup.Dissolving); List items = AllInventoryItems(caravan); for (int i = 0; i < items.Count; i++) { if (group.Accepts(items[i])) { yield return items[i]; } } } } ``` --- **文件路径:** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6\Verse\ThingOwner.txt` **相似度:** 0.6188 ```csharp public abstract class ThingOwner : IExposable, IList, ICollection, IEnumerable, IEnumerable { protected IThingHolder owner; protected int maxStacks = 999999; public LookMode contentsLookMode = LookMode.Deep; public bool removeContentsIfDestroyed = true; private const int InfMaxStacks = 999999; public IThingHolder Owner => owner; public abstract int Count { get; } public Thing this[int index] => GetAt(index); public bool Any => Count > 0; public int TotalStackCount { get { int num = 0; int count = Count; for (int i = 0; i < count; i++) { num += GetAt(i).stackCount; } return num; } } public string ContentsString { get { if (Any) { return GenThing.ThingsToCommaList(this); } return "NothingLower".Translate(); } } Thing IList.this[int index] { get { return GetAt(index); } set { throw new InvalidOperationException("ThingOwner doesn't allow setting individual elements."); } } bool ICollection.IsReadOnly => true; public event Action OnContentsChanged; public ThingOwner() { } public ThingOwner(IThingHolder owner) { this.owner = owner; } public ThingOwner(IThingHolder owner, LookMode contentsLookMode = LookMode.Deep, bool removeContentsIfDestroyed = true) { this.owner = owner; this.contentsLookMode = contentsLookMode; this.removeContentsIfDestroyed = removeContentsIfDestroyed; } public ThingOwner(IThingHolder owner, bool oneStackOnly, LookMode contentsLookMode = LookMode.Deep, bool removeContentsIfDestroyed = true) : this(owner) { maxStacks = (oneStackOnly ? 1 : 999999); this.contentsLookMode = contentsLookMode; this.removeContentsIfDestroyed = removeContentsIfDestroyed; } public virtual void ExposeData() { Scribe_Values.Look(ref maxStacks, "maxStacks", 999999); Scribe_Values.Look(ref contentsLookMode, "contentsLookMode", LookMode.Deep); Scribe_Values.Look(ref removeContentsIfDestroyed, "removeContentsIfDestroyed", defaultValue: true); } public void DoTick() { for (int num = Count - 1; num >= 0; num--) { Thing at = GetAt(num); int offset = ((owner is Thing t) ? t.HashOffset() : 0); at.DoTick(); if (at.def.tickerType == TickerType.Rare && GenTicks.IsTickInterval(offset, 250)) { at.TickRare(); } else if (at.def.tickerType == TickerType.Long && GenTicks.IsTickInterval(offset, 2000)) { at.TickLong(); } if (at.Destroyed && removeContentsIfDestroyed) { Remove(at); } } } public void Clear() { for (int num = Count - 1; num >= 0; num--) { Remove(GetAt(num)); } } public void ClearAndDestroyContents(DestroyMode mode = DestroyMode.Vanish) { while (Any) { for (int num = Count - 1; num >= 0; num--) { Thing at = GetAt(num); at.Destroy(mode); Remove(at); } } } public void ClearAndDestroyContentsOrPassToWorld(DestroyMode mode = DestroyMode.Vanish) { while (Any) { for (int num = Count - 1; num >= 0; num--) { Thing at = GetAt(num); at.DestroyOrPassToWorld(mode); Remove(at); } } } public bool CanAcceptAnyOf(Thing item, bool canMergeWithExistingStacks = true) { return GetCountCanAccept(item, canMergeWithExistingStacks) > 0; } public virtual int GetCountCanAccept(Thing item, bool canMergeWithExistingStacks = true) { if (item == null || item.stackCount <= 0) { return 0; } if (maxStacks == 999999) { return item.stackCount; } int num = 0; if (Count < maxStacks) { num += (maxStacks - Count) * item.def.stackLimit; } if (num >= item.stackCount) { return Mathf.Min(num, item.stackCount); } if (canMergeWithExistingStacks) { int i = 0; for (int count = Count; i < count; i++) { Thing at = GetAt(i); if (at.stackCount < at.def.stackLimit && at.CanStackWith(item)) { num += at.def.stackLimit - at.stackCount; if (num >= item.stackCount) { return Mathf.Min(num, item.stackCount); } } } } return Mathf.Min(num, item.stackCount); } public abstract int TryAdd(Thing item, int count, bool canMergeWithExistingStacks = true); public abstract bool TryAdd(Thing item, bool canMergeWithExistingStacks = true); public abstract int IndexOf(Thing item); public abstract bool Remove(Thing item); protected abstract Thing GetAt(int index); public bool Contains(Thing item) { if (item == null) { return false; } return item.holdingOwner == this; } public void RemoveAt(int index) { if (index < 0 || index >= Count) { throw new ArgumentOutOfRangeException("index"); } Remove(GetAt(index)); } public int TryAddOrTransfer(Thing item, int count, bool canMergeWithExistingStacks = true) { if (item == null) { Log.Warning("Tried to add or transfer null item to ThingOwner."); return 0; } if (item.holdingOwner != null) { return item.holdingOwner.TryTransferToContainer(item, this, count, canMergeWithExistingStacks); } return TryAdd(item, count, canMergeWithExistingStacks); } public bool TryAddOrTransfer(Thing item, bool canMergeWithExistingStacks = true) { if (item == null) { Log.Warning("Tried to add or transfer null item to ThingOwner."); return false; } if (item.holdingOwner != null) { return item.holdingOwner.TryTransferToContainer(item, this, canMergeWithExistingStacks); } return TryAdd(item, canMergeWithExistingStacks); } public void TryAddRangeOrTransfer(IEnumerable things, bool canMergeWithExistingStacks = true, bool destroyLeftover = false) { if (things == this) { return; } if (things is ThingOwner thingOwner) { thingOwner.TryTransferAllToContainer(this, canMergeWithExistingStacks); if (destroyLeftover) { thingOwner.ClearAndDestroyContents(); } return; } if (things is IList list) { for (int i = 0; i < list.Count; i++) { if (!TryAddOrTransfer(list[i], canMergeWithExistingStacks) && destroyLeftover) { list[i].Destroy(); } } return; } foreach (Thing thing in things) { if (!TryAddOrTransfer(thing, canMergeWithExistingStacks) && destroyLeftover) { thing.Destroy(); } } } public int RemoveAll(Predicate predicate) { int num = 0; for (int num2 = Count - 1; num2 >= 0; num2--) { if (predicate(GetAt(num2))) { Remove(GetAt(num2)); num++; } } return num; } public bool TryTransferToContainer(Thing item, ThingOwner otherContainer, bool canMergeWithExistingStacks = true) { return TryTransferToContainer(item, otherContainer, item.stackCount, canMergeWithExistingStacks) == item.stackCount; } public int TryTransferToContainer(Thing item, ThingOwner otherContainer, int count, bool canMergeWithExistingStacks = true) { Thing resultingTransferredItem; return TryTransferToContainer(item, otherContainer, count, out resultingTransferredItem, canMergeWithExistingStacks); } public int TryTransferToContainer(Thing item, ThingOwner otherContainer, int count, out Thing resultingTransferredItem, bool canMergeWithExistingStacks = true) { if (!Contains(item)) { Log.Error("Can't transfer item " + item?.ToString() + " because it's not here. owner=" + owner.ToStringSafe()); resultingTransferredItem = null; return 0; } if (otherContainer == this && count > 0) { resultingTransferredItem = item; return item.stackCount; } if (!otherContainer.CanAcceptAnyOf(item, canMergeWithExistingStacks)) { resultingTransferredItem = null; return 0; } if (count <= 0) { resultingTransferredItem = null; return 0; } if (owner is Map || otherContainer.owner is Map) { Log.Warning("Can't transfer items to or from Maps directly. They must be spawned or despawned manually. Use TryAdd(item.SplitOff(count))"); resultingTransferredItem = null; return 0; } int num = Mathf.Min(item.stackCount, count); Thing thing = item.SplitOff(num); if (Contains(thing)) { Remove(thing); } if (otherContainer.TryAdd(thing, canMergeWithExistingStacks)) { resultingTransferredItem = thing; item.MapHeld?.resourceCounter?.CheckUpdateResource(thing); return thing.stackCount; } resultingTransferredItem = null; if (!otherContainer.Contains(thing) && thing.stackCount > 0 && !thing.Destroyed) { int result = num - thing.stackCount; if (item != thing) { item.TryAbsorbStack(thing, respectStackLimit: false); } else { TryAdd(thing, canMergeWithExistingStacks: false); } Map mapHeld = item.MapHeld; if (mapHeld != null) { ResourceCounter resourceCounter = mapHeld.resourceCounter; if (resourceCounter != null) { resourceCounter.CheckUpdateResource(thing); return result; } return result; } return result; } return thing.stackCount; } public void TryTransferAllToContainer(ThingOwner other, bool canMergeWithExistingStacks = true) { for (int num = Count - 1; num >= 0; num--) { TryTransferToContainer(GetAt(num), other, canMergeWithExistingStacks); } } public Thing Take(Thing thing, int count) { if (!Contains(thing)) { Log.Error("Tried to take " + thing.ToStringSafe() + " but it's not here."); return null; } if (count > thing.stackCount) { Log.Error("Tried to get " + count + " of " + thing.ToStringSafe() + " while only having " + thing.stackCount); count = thing.stackCount; } if (count == thing.stackCount) { Remove(thing); return thing; } Thing thing2 = thing.SplitOff(count); thing2.holdingOwner = null; return thing2; } public Thing Take(Thing thing) { return Take(thing, thing.stackCount); } public bool TryDrop(Thing thing, ThingPlaceMode mode, int count, out Thing lastResultingThing, Action placedAction = null, Predicate nearPlaceValidator = null) { Map rootMap = ThingOwnerUtility.GetRootMap(owner); IntVec3 rootPosition = ThingOwnerUtility.GetRootPosition(owner); if (rootMap == null || !rootPosition.IsValid) { Log.Error("Cannot drop " + thing?.ToString() + " without a dropLoc and with an owner whose map is null."); lastResultingThing = null; return false; } return TryDrop(thing, rootPosition, rootMap, mode, count, out lastResultingThing, placedAction, nearPlaceValidator); } public bool TryDrop(Thing thing, IntVec3 dropLoc, Map map, ThingPlaceMode mode, int count, out Thing resultingThing, Action placedAction = null, Predicate nearPlaceValidator = null) { if (!Contains(thing)) { Log.Error("Tried to drop " + thing.ToStringSafe() + " but it's not here."); resultingThing = null; return false; } if (thing.stackCount < count) { Log.Error("Tried to drop " + count + " of " + thing?.ToString() + " while only having " + thing.stackCount); count = thing.stackCount; } if (count == thing.stackCount) { if (GenDrop.TryDropSpawn(thing, dropLoc, map, mode, out resultingThing, placedAction, nearPlaceValidator)) { Remove(thing); return true; } return false; } Thing thing2 = thing.SplitOff(count); if (GenDrop.TryDropSpawn(thing2, dropLoc, map, mode, out resultingThing, placedAction, nearPlaceValidator)) { return true; } thing.TryAbsorbStack(thing2, respectStackLimit: false); return false; } public bool TryDrop(Thing thing, ThingPlaceMode mode, out Thing lastResultingThing, Action placedAction = null, Predicate nearPlaceValidator = null) { Map rootMap = ThingOwnerUtility.GetRootMap(owner); IntVec3 rootPosition = ThingOwnerUtility.GetRootPosition(owner); if (rootMap == null || !rootPosition.IsValid) { Log.Error("Cannot drop " + thing?.ToString() + " without a dropLoc and with an owner whose map is null."); lastResultingThing = null; return false; } return TryDrop(thing, rootPosition, rootMap, mode, out lastResultingThing, placedAction, nearPlaceValidator); } public bool TryDrop(Thing thing, IntVec3 dropLoc, Map map, ThingPlaceMode mode, out Thing lastResultingThing, Action placedAction = null, Predicate nearPlaceValidator = null, bool playDropSound = true) { if (!Contains(thing)) { Log.Error(owner.ToStringSafe() + " container tried to drop " + thing.ToStringSafe() + " which it didn't contain."); lastResultingThing = null; return false; } if (GenDrop.TryDropSpawn(thing, dropLoc, map, mode, out lastResultingThing, placedAction, nearPlaceValidator, playDropSound)) { Remove(thing); return true; } return false; } public bool TryDropAll(IntVec3 dropLoc, Map map, ThingPlaceMode mode, Action placeAction = null, Predicate nearPlaceValidator = null, bool playDropSound = true) { bool result = true; for (int num = Count - 1; num >= 0; num--) { if (!TryDrop(GetAt(num), dropLoc, map, mode, out var _, placeAction, nearPlaceValidator, playDropSound)) { result = false; } } return result; } public bool Contains(ThingDef def) { return Contains(def, 1); } public bool Contains(ThingDef def, int minCount) { if (minCount <= 0) { return true; } int num = 0; int count = Count; for (int i = 0; i < count; i++) { if (GetAt(i).def == def) { num += GetAt(i).stackCount; } if (num >= minCount) { return true; } } return false; } public int TotalStackCountOfDef(ThingDef def) { int num = 0; int count = Count; for (int i = 0; i < count; i++) { if (GetAt(i).def == def) { num += GetAt(i).stackCount; } } return num; } public void Notify_ContainedItemDestroyed(Thing t) { if (ThingOwnerUtility.ShouldAutoRemoveDestroyedThings(owner)) { Remove(t); } } protected virtual void NotifyAdded(Thing item) { if (ThingOwnerUtility.ShouldAutoExtinguishInnerThings(owner) && item.HasAttachment(ThingDefOf.Fire)) { item.GetAttachment(ThingDefOf.Fire).Destroy(); } if (ThingOwnerUtility.ShouldRemoveDesignationsOnAddedThings(owner)) { List maps = Find.Maps; for (int i = 0; i < maps.Count; i++) { maps[i].designationManager.RemoveAllDesignationsOn(item); } } if ((owner is Thing thing && thing.Faction.IsPlayerSafe()) || (owner is WorldObject worldObject && worldObject.Faction.IsPlayerSafe()) || (owner is Pawn_InventoryTracker pawn_InventoryTracker && pawn_InventoryTracker.pawn.Faction.IsPlayerSafe())) { item.EverSeenByPlayer = true; } if (owner is CompTransporter compTransporter) { compTransporter.Notify_ThingAdded(item); } if (owner is Caravan caravan) { caravan.Notify_PawnAdded((Pawn)item); } if (owner is Pawn_ApparelTracker pawn_ApparelTracker) { pawn_ApparelTracker.Notify_ApparelAdded((Apparel)item); if (pawn_ApparelTracker.pawn.Faction.IsPlayerSafe()) { item.EverSeenByPlayer = true; } } if (owner is Pawn_EquipmentTracker pawn_EquipmentTracker) { pawn_EquipmentTracker.Notify_EquipmentAdded((ThingWithComps)item); if (pawn_EquipmentTracker.pawn.Faction.IsPlayerSafe()) { item.EverSeenByPlayer = true; } } NotifyColonistBarIfColonistCorpse(item); this.OnContentsChanged?.Invoke(); } protected void NotifyAddedAndMergedWith(Thing item, int mergedCount) { if (owner is CompTransporter compTransporter) { compTransporter.Notify_ThingAddedAndMergedWith(item, mergedCount); } } protected virtual void NotifyRemoved(Thing item) { if (owner is Pawn_InventoryTracker pawn_InventoryTracker) { pawn_InventoryTracker.Notify_ItemRemoved(item); } if (owner is Pawn_ApparelTracker pawn_ApparelTracker) { pawn_ApparelTracker.Notify_ApparelRemoved((Apparel)item); } if (owner is Pawn_EquipmentTracker pawn_EquipmentTracker) { pawn_EquipmentTracker.Notify_EquipmentRemoved((ThingWithComps)item); } if (owner is Caravan caravan) { caravan.Notify_PawnRemoved((Pawn)item); } NotifyColonistBarIfColonistCorpse(item); this.OnContentsChanged?.Invoke(); } private void NotifyColonistBarIfColonistCorpse(Thing thing) { if (thing is Corpse { Bugged: false } corpse && corpse.InnerPawn.Faction != null && corpse.InnerPawn.Faction.IsPlayer && Current.ProgramState == ProgramState.Playing) { Find.ColonistBar.MarkColonistsDirty(); } } void IList.Insert(int index, Thing item) { throw new InvalidOperationException("ThingOwner doesn't allow inserting individual elements at any position."); } void ICollection.Add(Thing item) { TryAdd(item); } void ICollection.CopyTo(Thing[] array, int arrayIndex) { for (int i = 0; i < Count; i++) { array[i + arrayIndex] = GetAt(i); } } IEnumerator IEnumerable.GetEnumerator() { for (int i = 0; i < Count; i++) { yield return GetAt(i); } } IEnumerator IEnumerable.GetEnumerator() { for (int i = 0; i < Count; i++) { yield return GetAt(i); } } } ``` --- **文件路径:** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6\Verse.AI\Toils_Haul.txt` **相似度:** 0.6124 ```csharp public class Toils_Haul { public static bool ErrorCheckForCarry(Pawn pawn, Thing haulThing, bool canTakeFromInventory = false) { if (!haulThing.SpawnedOrAnyParentSpawned || (!canTakeFromInventory && !haulThing.Spawned)) { Log.Message(pawn?.ToString() + " tried to start carry " + haulThing?.ToString() + " which isn't spawned."); pawn.jobs.EndCurrentJob(JobCondition.Incompletable); return true; } if (haulThing.stackCount == 0) { Log.Message(pawn?.ToString() + " tried to start carry " + haulThing?.ToString() + " which had stackcount 0."); pawn.jobs.EndCurrentJob(JobCondition.Incompletable); return true; } if (pawn.jobs.curJob.count <= 0) { Log.Error("Invalid count: " + pawn.jobs.curJob.count + ", setting to 1. Job was " + pawn.jobs.curJob); pawn.jobs.curJob.count = 1; } return false; } public static Toil StartCarryThing(TargetIndex haulableInd, bool putRemainderInQueue = false, bool subtractNumTakenFromJobCount = false, bool failIfStackCountLessThanJobCount = false, bool reserve = true, bool canTakeFromInventory = false) { Toil toil = ToilMaker.MakeToil("StartCarryThing"); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; Thing thing = curJob.GetTarget(haulableInd).Thing; if (!ErrorCheckForCarry(actor, thing, canTakeFromInventory)) { if (curJob.count == 0) { throw new Exception($"StartCarryThing job had count = {curJob.count}. Job: {curJob}"); } int num = actor.carryTracker.AvailableStackSpace(thing.def); if (num <= 0) { int num2 = actor.carryTracker.MaxStackSpaceEver(thing.def); int num3 = 0; if (actor.carryTracker.CarriedThing != null) { num3 = actor.carryTracker.CarriedThing.stackCount; } throw new Exception($"StartCarryThing got availableStackSpace {num} (haulTarg {thing}, Job: {curJob}, maximum: {num2}, carrying: {num3})"); } if (failIfStackCountLessThanJobCount && thing.stackCount < curJob.count) { actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable); } else { int num4 = Mathf.Min(curJob.count, num, thing.stackCount); if (num4 <= 0) { int num5 = actor.carryTracker.MaxStackSpaceEver(thing.def); int num6 = 0; if (actor.carryTracker.CarriedThing != null) { num6 = actor.carryTracker.CarriedThing.stackCount; } throw new Exception($"StartCarryThing zero or negative desiredNumToTake ({num4}), curJob.count: {curJob.count}, availableStackSpace: {num} (maximum: {num5}, carrying: {num6}), haulTarg.stackCount: {thing.stackCount}"); } int stackCount = thing.stackCount; int num7 = actor.carryTracker.TryStartCarry(thing, num4, reserve); if (num7 == 0) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); } if (num7 < stackCount) { int num8 = curJob.count - num7; if (putRemainderInQueue && num8 > 0) { curJob.GetTargetQueue(haulableInd).Insert(0, thing); Job job = curJob; if (job.countQueue == null) { job.countQueue = new List(); } curJob.countQueue.Insert(0, num8); } else if (actor.Map.reservationManager.ReservedBy(thing, actor, curJob)) { actor.Map.reservationManager.Release(thing, actor, curJob); } } if (subtractNumTakenFromJobCount) { curJob.count -= num7; } curJob.SetTarget(haulableInd, actor.carryTracker.CarriedThing); actor.records.Increment(RecordDefOf.ThingsHauled); } } }; return toil; } public static Toil StoreThingJob(TargetIndex thingIndex) { Toil toil = ToilMaker.MakeToil("StoreThingJob"); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.CurJob; Thing thing = curJob.GetTarget(thingIndex).Thing; Job job = HaulAIUtility.HaulToStorageJob(actor, thing, curJob.playerForced); if (job != null) { actor.jobs.TryTakeOrderedJob(job, JobTag.Misc); } }; return toil; } public static Toil DropCarriedThing() { Toil toil = ToilMaker.MakeToil("DropCarriedThing"); toil.initAction = delegate { Pawn actor = toil.actor; Thing resultingThing; if (actor.carryTracker.CarriedThing == null) { Log.Error(actor?.ToString() + " tried to drop carried thing but is not carrying anything."); } else if (!actor.carryTracker.TryDropCarriedThing(actor.Position, ThingPlaceMode.Direct, out resultingThing)) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); } }; return toil; } public static Toil JumpIfAlsoCollectingNextTargetInQueue(Toil gotoGetTargetToil, TargetIndex ind) { Toil toil = ToilMaker.MakeToil("JumpIfAlsoCollectingNextTargetInQueue"); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; List targetQueue = curJob.GetTargetQueue(ind); if (!targetQueue.NullOrEmpty() && curJob.count > 0) { if (actor.carryTracker.CarriedThing == null) { Log.Error("JumpToAlsoCollectTargetInQueue run on " + actor?.ToString() + " who is not carrying something."); } else if (actor.carryTracker.AvailableStackSpace(actor.carryTracker.CarriedThing.def) > 0) { for (int i = 0; i < targetQueue.Count; i++) { if (!GenAI.CanUseItemForWork(actor, targetQueue[i].Thing)) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); break; } if (targetQueue[i].Thing.def == actor.carryTracker.CarriedThing.def) { curJob.SetTarget(ind, targetQueue[i].Thing); targetQueue.RemoveAt(i); actor.jobs.curDriver.JumpToToil(gotoGetTargetToil); break; } } } } }; return toil; } public static Toil CheckForGetOpportunityDuplicate(Toil getHaulTargetToil, TargetIndex haulableInd, TargetIndex storeCellInd, bool takeFromValidStorage = false, Predicate extraValidator = null) { Toil toil = ToilMaker.MakeToil("CheckForGetOpportunityDuplicate"); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; if (actor.carryTracker.CarriedThing.def.stackLimit != 1 && !actor.carryTracker.Full && curJob.count > 0) { Thing thing = GenClosest.ClosestThingReachable(actor.Position, actor.Map, ThingRequest.ForGroup(ThingRequestGroup.HaulableAlways), PathEndMode.ClosestTouch, TraverseParms.For(actor), 8f, DupeValidator); if (thing != null) { curJob.SetTarget(haulableInd, thing); actor.jobs.curDriver.JumpToToil(getHaulTargetToil); } } bool DupeValidator(Thing t) { if (!t.Spawned) { return false; } if (t.def != actor.carryTracker.CarriedThing.def) { return false; } if (!t.CanStackWith(actor.carryTracker.CarriedThing)) { return false; } if (t.IsForbidden(actor)) { return false; } if (!t.IsSociallyProper(actor, forPrisoner: false, animalsCare: true)) { return false; } if (takeFromValidStorage && storeCellInd != 0 && curJob.GetTarget(storeCellInd).Cell.TryGetSlotGroup(actor.Map, out var group) && t.TryGetValidStoragePriority(out var priority) && (int)priority >= (int)group.Settings.Priority) { return false; } if (storeCellInd != 0 && !curJob.GetTarget(storeCellInd).Cell.IsValidStorageFor(actor.Map, t)) { return false; } if (!actor.CanReserve(t)) { return false; } if (extraValidator != null && !extraValidator(t)) { return false; } return true; } }; return toil; } public static Toil CarryHauledThingToCell(TargetIndex squareIndex, PathEndMode pathEndMode = PathEndMode.ClosestTouch) { Toil toil = ToilMaker.MakeToil("CarryHauledThingToCell"); toil.initAction = delegate { IntVec3 cell3 = toil.actor.jobs.curJob.GetTarget(squareIndex).Cell; toil.actor.pather.StartPath(cell3, pathEndMode); }; toil.defaultCompleteMode = ToilCompleteMode.PatherArrival; toil.AddEndCondition(delegate { Pawn actor2 = toil.actor; IntVec3 cell2 = actor2.jobs.curJob.GetTarget(squareIndex).Cell; CompPushable compPushable2 = actor2.carryTracker.CarriedThing.TryGetComp(); if (compPushable2 != null) { Vector3 v = actor2.Position.ToVector3() + compPushable2.drawPos; if (new IntVec3(v) == cell2) { return JobCondition.Succeeded; } } return JobCondition.Ongoing; }); toil.AddFailCondition(delegate { Pawn actor = toil.actor; IntVec3 cell = actor.jobs.curJob.GetTarget(squareIndex).Cell; if (actor.carryTracker.CarriedThing == null) { return true; } if (actor.jobs.curJob.haulMode == HaulMode.ToCellStorage && !cell.IsValidStorageFor(actor.Map, actor.carryTracker.CarriedThing)) { return true; } CompPushable compPushable = actor.carryTracker.CarriedThing.TryGetComp(); return (compPushable != null && !compPushable.canBePushed) ? true : false; }); return toil; } public static Toil PlaceCarriedThingInCellFacing(TargetIndex facingTargetInd) { Toil toil = ToilMaker.MakeToil("PlaceCarriedThingInCellFacing"); toil.initAction = delegate { Pawn actor = toil.actor; if (actor.carryTracker.CarriedThing == null) { Log.Error(actor?.ToString() + " tried to place hauled thing in facing cell but is not hauling anything."); } else { LocalTargetInfo target = actor.CurJob.GetTarget(facingTargetInd); IntVec3 intVec = ((!target.HasThing) ? target.Cell : target.Thing.OccupiedRect().ClosestCellTo(actor.Position)); IntVec3 dropLoc = actor.Position + Pawn_RotationTracker.RotFromAngleBiased((actor.Position - intVec).AngleFlat).FacingCell; if (!actor.carryTracker.TryDropCarriedThing(dropLoc, ThingPlaceMode.Direct, out var _)) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); } } }; return toil; } public static Toil PlaceHauledThingInCell(TargetIndex cellInd, Toil nextToilOnPlaceFailOrIncomplete, bool storageMode, bool tryStoreInSameStorageIfSpotCantHoldWholeStack = false) { Toil toil = ToilMaker.MakeToil("PlaceHauledThingInCell"); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; IntVec3 cell = curJob.GetTarget(cellInd).Cell; if (actor.carryTracker.CarriedThing == null) { Log.Error(actor?.ToString() + " tried to place hauled thing in cell but is not hauling anything."); } else { SlotGroup slotGroup = actor.Map.haulDestinationManager.SlotGroupAt(cell); if (slotGroup != null && slotGroup.Settings.AllowedToAccept(actor.carryTracker.CarriedThing)) { actor.Map.designationManager.TryRemoveDesignationOn(actor.carryTracker.CarriedThing, DesignationDefOf.Haul); } Action placedAction = null; if (curJob.def == JobDefOf.DoBill || curJob.def == JobDefOf.RecolorApparel || curJob.def == JobDefOf.RefuelAtomic || curJob.def == JobDefOf.RearmTurretAtomic) { placedAction = delegate(Thing th, int added) { HaulAIUtility.UpdateJobWithPlacedThings(curJob, th, added); }; } if (!actor.carryTracker.TryDropCarriedThing(cell, ThingPlaceMode.Direct, out var _, placedAction)) { if (storageMode) { IntVec3 storeCell; if (nextToilOnPlaceFailOrIncomplete != null && ((tryStoreInSameStorageIfSpotCantHoldWholeStack && StoreUtility.TryFindBestBetterStoreCellForIn(actor.carryTracker.CarriedThing, actor, actor.Map, StoragePriority.Unstored, actor.Faction, curJob.bill.GetSlotGroup(), out var foundCell)) || StoreUtility.TryFindBestBetterStoreCellFor(actor.carryTracker.CarriedThing, actor, actor.Map, StoragePriority.Unstored, actor.Faction, out foundCell))) { if (actor.CanReserve(foundCell)) { actor.Reserve(foundCell, actor.CurJob); } actor.CurJob.SetTarget(cellInd, foundCell); actor.jobs.curDriver.JumpToToil(nextToilOnPlaceFailOrIncomplete); } else if (HaulAIUtility.CanHaulAside(actor, actor.carryTracker.CarriedThing, out storeCell)) { curJob.SetTarget(cellInd, storeCell); curJob.count = int.MaxValue; curJob.haulOpportunisticDuplicates = false; curJob.haulMode = HaulMode.ToCellNonStorage; if (nextToilOnPlaceFailOrIncomplete != null) { actor.jobs.curDriver.JumpToToil(nextToilOnPlaceFailOrIncomplete); } } else { Log.Warning($"Incomplete haul for {actor}: Could not find anywhere to put {actor.carryTracker.CarriedThing} near {actor.Position}. Destroying. This should be very uncommon!"); actor.carryTracker.CarriedThing.Destroy(); } } else if (nextToilOnPlaceFailOrIncomplete != null) { actor.jobs.curDriver.JumpToToil(nextToilOnPlaceFailOrIncomplete); } } } }; return toil; } public static Toil CarryHauledThingToContainer() { Toil gotoDest = ToilMaker.MakeToil("CarryHauledThingToContainer"); gotoDest.initAction = delegate { gotoDest.actor.pather.StartPath(gotoDest.actor.jobs.curJob.targetB.Thing, PathEndMode.Touch); }; gotoDest.AddFailCondition(delegate { Thing thing = gotoDest.actor.jobs.curJob.targetB.Thing; if (thing.Destroyed || (!gotoDest.actor.jobs.curJob.ignoreForbidden && thing.IsForbidden(gotoDest.actor))) { return true; } ThingOwner thingOwner = thing.TryGetInnerInteractableThingOwner(); return (thingOwner != null && !thingOwner.CanAcceptAnyOf(gotoDest.actor.carryTracker.CarriedThing)) ? true : false; }); gotoDest.defaultCompleteMode = ToilCompleteMode.PatherArrival; return gotoDest; } public static Toil DepositHauledThingInContainer(TargetIndex containerInd, TargetIndex reserveForContainerInd, Action onDeposited = null) { Toil toil = ToilMaker.MakeToil("DepositHauledThingInContainer"); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; if (actor.carryTracker.CarriedThing == null) { Log.Error(actor?.ToString() + " tried to place hauled thing in container but is not hauling anything."); } else { Thing thing = curJob.GetTarget(containerInd).Thing; ThingOwner thingOwner = thing.TryGetInnerInteractableThingOwner(); if (thingOwner != null) { int num = actor.carryTracker.CarriedThing.stackCount; if (thing is IHaulEnroute haulEnroute) { ThingDef def = actor.carryTracker.CarriedThing.def; num = Mathf.Min(haulEnroute.GetSpaceRemainingWithEnroute(def, actor), num); if (reserveForContainerInd != 0) { Thing thing2 = curJob.GetTarget(reserveForContainerInd).Thing; if (!thing2.DestroyedOrNull() && thing2 != haulEnroute && thing2 is IHaulEnroute enroute) { int spaceRemainingWithEnroute = enroute.GetSpaceRemainingWithEnroute(def, actor); num = Mathf.Min(num, actor.carryTracker.CarriedThing.stackCount - spaceRemainingWithEnroute); } } } Thing carriedThing = actor.carryTracker.CarriedThing; int num2 = actor.carryTracker.innerContainer.TryTransferToContainer(carriedThing, thingOwner, num); if (num2 != 0) { if (thing is IHaulEnroute container) { thing.Map.enrouteManager.ReleaseFor(container, actor); } if (thing is INotifyHauledTo notifyHauledTo) { notifyHauledTo.Notify_HauledTo(actor, carriedThing, num2); } if (thing is ThingWithComps thingWithComps) { foreach (ThingComp allComp in thingWithComps.AllComps) { if (allComp is INotifyHauledTo notifyHauledTo2) { notifyHauledTo2.Notify_HauledTo(actor, carriedThing, num2); } } } if (curJob.def == JobDefOf.DoBill) { HaulAIUtility.UpdateJobWithPlacedThings(curJob, carriedThing, num2); } onDeposited?.Invoke(); } } else if (curJob.GetTarget(containerInd).Thing.def.Minifiable) { actor.carryTracker.innerContainer.ClearAndDestroyContents(); } else { Log.Error("Could not deposit hauled thing in container: " + curJob.GetTarget(containerInd).Thing); } } }; return toil; } public static Toil JumpToCarryToNextContainerIfPossible(Toil carryToContainerToil, TargetIndex primaryTargetInd) { Toil toil = ToilMaker.MakeToil("JumpToCarryToNextContainerIfPossible"); toil.debugName = "Jump carry if possible"; toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; if (actor.carryTracker.CarriedThing != null && curJob.targetQueueB != null && curJob.targetQueueB.Count > 0) { if (TryGetNextDestinationFromQueue(primaryTargetInd, TargetIndex.B, actor.carryTracker.CarriedThing.def, curJob, actor, out var nextTarget)) { curJob.targetQueueB.RemoveAll((LocalTargetInfo target) => target.Thing == nextTarget); curJob.targetB = nextTarget; curJob.targetC = nextTarget; actor.jobs.curDriver.JumpToToil(carryToContainerToil); } } }; return toil; } public static bool TryGetNextDestinationFromQueue(TargetIndex primaryIndex, TargetIndex destIndex, ThingDef stuff, Job job, Pawn actor, out Thing target) { Thing primaryTarget = job.GetTarget(primaryIndex).Thing; target = null; if (actor.carryTracker?.CarriedThing == null) { return false; } bool hasSpareItems = actor.carryTracker.CarriedThing.stackCount > 0; if (primaryTarget != null && primaryTarget.Spawned && primaryTarget is IHaulEnroute enroute) { int spaceRemainingWithEnroute = enroute.GetSpaceRemainingWithEnroute(stuff, actor); hasSpareItems = actor.carryTracker.CarriedThing.stackCount > spaceRemainingWithEnroute; } target = GenClosest.ClosestThing_Global_Reachable(actor.Position, actor.Map, from x in job.GetTargetQueue(destIndex) select x.Thing, PathEndMode.Touch, TraverseParms.For(actor), 99999f, Validator); return target != null; bool Validator(Thing th) { if (!(th is IHaulEnroute enroute2)) { return false; } if (enroute2.GetSpaceRemainingWithEnroute(stuff, actor) <= 0) { return false; } if (th != primaryTarget && !hasSpareItems) { return false; } return true; } } public static Toil TakeToInventory(TargetIndex ind, int count) { return TakeToInventory(ind, count, null, null); } private static Toil TakeToInventory(TargetIndex ind, int? count, Func countGetter, Func countGetterPassingThing) { Toil takeThing = ToilMaker.MakeToil("TakeToInventory"); takeThing.initAction = delegate { Pawn actor = takeThing.actor; Thing thing = actor.CurJob.GetTarget(ind).Thing; if (!ErrorCheckForCarry(actor, thing)) { int num = Mathf.Min(count ?? countGetterPassingThing?.Invoke(thing) ?? countGetter(), thing.stackCount); if (actor.CurJob.checkEncumbrance) { num = Math.Min(num, MassUtility.CountToPickUpUntilOverEncumbered(actor, thing)); } if (num <= 0) { actor.jobs.curDriver.ReadyForNextToil(); } else { actor.inventory.GetDirectlyHeldThings().TryAdd(thing.SplitOff(num)); if (thing.def.ingestible != null && (int)thing.def.ingestible.preferability <= 5) { actor.mindState.lastInventoryRawFoodUseTick = Find.TickManager.TicksGame; } thing.def.soundPickup.PlayOneShot(new TargetInfo(actor.Position, actor.Map)); } } }; return takeThing; } public static Toil TakeToInventory(TargetIndex ind, Func countGetter) { return TakeToInventory(ind, null, countGetter, null); } public static Toil TakeToInventory(TargetIndex ind, Func countGetter) { return TakeToInventory(ind, null, null, countGetter); } public static Toil TakeFromOtherInventory(Thing item, ThingOwner taker, ThingOwner holder, int count = -1, TargetIndex indexToSet = TargetIndex.None) { Toil toil = ToilMaker.MakeToil("TakeFromOtherInventory"); toil.initAction = delegate { if (!holder.Contains(item)) { toil.actor.jobs.EndCurrentJob(JobCondition.Incompletable); } else { count = ((count < 0) ? toil.actor.jobs.curJob.count : count); holder.TryTransferToContainer(item, taker, Mathf.Min(item.stackCount, count), out var resultingTransferredItem); if (resultingTransferredItem == null) { Log.Warning($"Taker {toil.actor.Label} unable to take count {count} of thing {item.Label} from holder's inventory"); toil.actor.jobs.EndCurrentJob(JobCondition.Incompletable); } else if (indexToSet != 0) { toil.actor.jobs.curJob.SetTarget(indexToSet, resultingTransferredItem); } } }; return toil; } public static Toil CheckItemCarriedByOtherPawn(Thing item, TargetIndex targetPawnIfCarried = TargetIndex.None, Toil jumpIfCarriedByOther = null) { Toil toil = ToilMaker.MakeToil("CheckItemCarriedByOtherPawn"); toil.initAction = delegate { Pawn pawn = (item?.ParentHolder as Pawn_InventoryTracker)?.pawn; if (pawn != null && pawn != toil.actor) { if (targetPawnIfCarried != 0) { toil.actor.jobs.curJob.SetTarget(targetPawnIfCarried, pawn); } if (jumpIfCarriedByOther != null) { toil.actor.jobs.curDriver.JumpToToil(jumpIfCarriedByOther); } } }; toil.defaultCompleteMode = ToilCompleteMode.Instant; toil.atomicWithPrevious = true; return toil; } } ``` --- **文件路径:** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6\RimWorld\PawnUtility.txt` **相似度:** 0.5783 ```csharp public static class PawnUtility { private static List tmpPawns = new List(); private static List tmpPawnKindsStr = new List(); private static HashSet tmpAddedPawnKinds = new HashSet(); private static List tmpPawnKinds = new List(); private static List tmpThings = new List(); public static Faction GetFactionLeaderFaction(Pawn pawn) { List allFactionsListForReading = Find.FactionManager.AllFactionsListForReading; for (int i = 0; i < allFactionsListForReading.Count; i++) { if (allFactionsListForReading[i].leader == pawn) { return allFactionsListForReading[i]; } } return null; } public static bool IsFactionLeader(Pawn pawn) { return GetFactionLeaderFaction(pawn) != null; } public static bool IsInteractionBlocked(this Pawn pawn, InteractionDef interaction, bool isInitiator, bool isRandom) { MentalStateDef mentalStateDef = pawn.MentalStateDef; if (mentalStateDef != null) { if (isRandom) { return mentalStateDef.blockRandomInteraction; } if (interaction == null) { return false; } List list = (isInitiator ? mentalStateDef.blockInteractionInitiationExcept : mentalStateDef.blockInteractionRecipientExcept); if (list != null) { return !list.Contains(interaction); } return false; } List hediffs = pawn.health.hediffSet.hediffs; for (int i = 0; i < hediffs.Count; i++) { if (hediffs[i].def.blocksSocialInteraction) { return true; } } Lord lord = pawn.GetLord(); if (lord != null && lord.BlocksSocialInteraction(pawn)) { return true; } return false; } public static bool IsKidnappedPawn(Pawn pawn) { List allFactionsListForReading = Find.FactionManager.AllFactionsListForReading; for (int i = 0; i < allFactionsListForReading.Count; i++) { if (allFactionsListForReading[i].kidnapped.KidnappedPawnsListForReading.Contains(pawn)) { return true; } } return false; } public static bool IsTravelingInTransportPodWorldObject(Pawn pawn) { if (!pawn.IsWorldPawn() || !ThingOwnerUtility.AnyParentIs(pawn)) { return ThingOwnerUtility.AnyParentIs(pawn); } return true; } public static bool ForSaleBySettlement(Pawn pawn) { return pawn.ParentHolder is Settlement_TraderTracker; } public static bool IsCarrying(this Pawn pawn) { if (!pawn.Destroyed && pawn.carryTracker != null) { return pawn.carryTracker.CarriedThing != null; } return false; } public static bool IsCarryingPawn(this Pawn pawn, Pawn carryPawn = null) { if (pawn.carryTracker != null && pawn.carryTracker.CarriedThing is Pawn) { if (carryPawn != null) { return pawn.carryTracker.CarriedThing == carryPawn; } return true; } return false; } public static bool IsCarryingThing(this Pawn pawn, Thing carriedThing) { if (pawn.carryTracker != null && pawn.carryTracker.CarriedThing != null) { return pawn.carryTracker.CarriedThing == carriedThing; } return false; } public static void TryDestroyStartingColonistFamily(Pawn pawn) { if (!pawn.relations.RelatedPawns.Any((Pawn x) => Find.GameInitData.startingAndOptionalPawns.Contains(x))) { DestroyStartingColonistFamily(pawn); } } public static void DestroyStartingColonistFamily(Pawn pawn) { foreach (Pawn item in pawn.relations.RelatedPawns.ToList()) { if (!Find.GameInitData.startingAndOptionalPawns.Contains(item)) { WorldPawnSituation situation = Find.WorldPawns.GetSituation(item); if (situation == WorldPawnSituation.Free || situation == WorldPawnSituation.Dead) { Find.WorldPawns.RemovePawn(item); Find.WorldPawns.PassToWorld(item, PawnDiscardDecideMode.Discard); } } } } public static bool EnemiesAreNearby(Pawn pawn, int regionsToScan = 9, bool passDoors = false, float maxDistance = -1f, int maxCount = 1, bool invisible = false) { TraverseParms tp = (passDoors ? TraverseParms.For(TraverseMode.PassDoors) : TraverseParms.For(pawn)); int count = 0; RegionTraverser.BreadthFirstTraverse(pawn.Position, pawn.Map, (Region from, Region to) => to.Allows(tp, isDestination: false), delegate(Region r) { List list = r.ListerThings.ThingsInGroup(ThingRequestGroup.AttackTarget); for (int i = 0; i < list.Count; i++) { Thing thing = list[i]; if ((maxDistance <= 0f || thing.Position.InHorDistOf(pawn.Position, maxDistance)) && thing.HostileTo(pawn) && (invisible || !(thing is Pawn pawn2) || !pawn2.IsPsychologicallyInvisible())) { count++; } } return count >= maxCount; }, regionsToScan); return count >= maxCount; } public static bool WillSoonHaveBasicNeed(Pawn p, float thresholdOffset = 0.05f) { if (p.needs == null) { return false; } if (p.needs.rest != null && p.needs.rest.CurLevel < 0.28f + thresholdOffset) { return true; } if (p.needs.food != null && p.needs.food.CurLevelPercentage < p.needs.food.PercentageThreshHungry + thresholdOffset) { return true; } return false; } public static bool CanCasuallyInteractNow(this Pawn p, bool twoWayInteraction = false, bool canInteractWhileSleeping = false, bool canInteractWhileRoaming = false, bool canInteractWhileDrafted = false) { if (p.Drafted && !canInteractWhileDrafted) { return false; } if (p.IsPsychologicallyInvisible()) { return false; } if (ThinkNode_ConditionalShouldFollowMaster.ShouldFollowMaster(p)) { return false; } if (p.InAggroMentalState) { return false; } if (p.InMentalState && p.MentalStateDef == MentalStateDefOf.Roaming && !canInteractWhileRoaming) { return false; } if (!p.Awake() && !canInteractWhileSleeping) { return false; } if (p.IsFormingCaravan()) { return false; } Job curJob = p.CurJob; if (curJob != null && twoWayInteraction && (!curJob.def.casualInterruptible || !curJob.playerForced)) { return false; } return true; } public static IEnumerable SpawnedMasteredPawns(Pawn master) { if (Current.ProgramState != ProgramState.Playing || master.Faction == null || !master.RaceProps.Humanlike || !master.Spawned) { yield break; } List pawns = master.Map.mapPawns.SpawnedPawnsInFaction(master.Faction); for (int i = 0; i < pawns.Count; i++) { if (pawns[i].playerSettings != null && pawns[i].playerSettings.Master == master) { yield return pawns[i]; } } } public static bool InValidState(Pawn p) { if (p.health == null) { return false; } if (!p.Dead && (p.stances == null || p.mindState == null || p.needs == null || p.ageTracker == null)) { return false; } return true; } public static PawnPosture GetPosture(this Pawn p) { PawnPosture pawnPosture = (p.Dead ? PawnPosture.LayingOnGroundNormal : ((!ModsConfig.BiotechActive || !p.IsCharging()) ? ((!(p.ParentHolder is IThingHolderWithDrawnPawn thingHolderWithDrawnPawn)) ? (p.Downed ? ((p.jobs == null || !p.jobs.posture.Laying()) ? PawnPosture.LayingOnGroundNormal : p.jobs.posture) : ((p.jobs != null) ? p.jobs.posture : PawnPosture.Standing)) : thingHolderWithDrawnPawn.HeldPawnPosture) : PawnPosture.Standing)); Pawn_MindState mindState = p.mindState; if (mindState != null && mindState.duty?.def?.forceFaceUpPosture == true && pawnPosture != 0) { pawnPosture |= PawnPosture.FaceUpMask; } return pawnPosture; } public static void ForceWait(Pawn pawn, int ticks, Thing faceTarget = null, bool maintainPosture = false, bool maintainSleep = false) { if (ticks <= 0) { Log.ErrorOnce("Forcing a wait for zero ticks", 47045639); } JobDef def = (maintainPosture ? JobDefOf.Wait_MaintainPosture : JobDefOf.Wait); if (pawn.IsDeactivated()) { def = JobDefOf.Deactivated; } if (pawn.IsSelfShutdown()) { def = JobDefOf.SelfShutdown; } else if (pawn.InBed()) { def = (pawn.Awake() ? JobDefOf.LayDownAwake : JobDefOf.LayDown); } else if (!pawn.health.capacities.CanBeAwake) { def = JobDefOf.Wait_Downed; } else if (maintainSleep && !pawn.Awake()) { def = JobDefOf.Wait_Asleep; } Job job = JobMaker.MakeJob(def, faceTarget); if (maintainSleep && !pawn.Awake()) { job.forceSleep = true; job.targetA = pawn.Position; } if (pawn.InBed()) { job.targetA = pawn.CurrentBed(); } job.expiryInterval = ticks; pawn.jobs.StartJob(job, JobCondition.InterruptForced, null, resumeCurJobAfterwards: true); } public static void GainComfortFromCellIfPossible(this Pawn p, int delta, bool chairsOnly = false) { if (p.Spawned && p.IsHashIntervalTick(15, delta)) { Building edifice = p.Position.GetEdifice(p.Map); if (edifice != null && (!chairsOnly || (edifice.def.category == ThingCategory.Building && edifice.def.building.isSittable))) { GainComfortFromThingIfPossible(p, edifice, delta); } } } public static void GainComfortFromThingIfPossible(Pawn p, Thing from, int delta) { if (p.IsHashIntervalTick(15, delta)) { float statValue = from.GetStatValue(StatDefOf.Comfort, applyPostProcess: true, 100); if (statValue >= 0f && p.needs != null && p.needs.comfort != null) { p.needs.comfort.ComfortUsed(statValue); } } } public static float BodyResourceGrowthSpeed(Pawn pawn) { return pawn.needs?.food?.CurCategory.HungerMultiplier() ?? 1f; } public static bool FertileMateTarget(Pawn male, Pawn female) { if (female.gender != Gender.Female || female.Sterile()) { return false; } CompEggLayer compEggLayer = female.TryGetComp(); if (compEggLayer != null) { return !compEggLayer.FullyFertilized; } return true; } public static void Mated(Pawn male, Pawn female) { if (!female.Sterile() && !male.Sterile()) { CompEggLayer compEggLayer = female.TryGetComp(); if (compEggLayer != null) { compEggLayer.Fertilize(male); } else if (Rand.Value < 0.5f) { Hediff_Pregnant hediff_Pregnant = (Hediff_Pregnant)HediffMaker.MakeHediff(HediffDefOf.Pregnant, female); hediff_Pregnant.SetParents(null, male, null); female.health.AddHediff(hediff_Pregnant); } } } public static bool PlayerForcedJobNowOrSoon(Pawn pawn) { if (pawn.jobs == null) { return false; } Job curJob = pawn.CurJob; if (curJob != null) { return curJob.playerForced; } if (pawn.jobs.jobQueue.Count > 0) { return pawn.jobs.jobQueue.Peek().job.playerForced; } return false; } public static bool TrySpawnHatchedOrBornPawn(Pawn pawn, Thing motherOrEgg, IntVec3? positionOverride = null) { if (motherOrEgg.SpawnedOrAnyParentSpawned) { return GenSpawn.Spawn(pawn, positionOverride ?? motherOrEgg.PositionHeld, motherOrEgg.MapHeld) != null; } if (motherOrEgg is Pawn pawn2) { if (pawn2.IsCaravanMember()) { pawn2.GetCaravan().AddPawn(pawn, addCarriedPawnToWorldPawnsIfAny: true); Find.WorldPawns.PassToWorld(pawn); return true; } if (pawn2.IsWorldPawn()) { Find.WorldPawns.PassToWorld(pawn); return true; } } else if (motherOrEgg.ParentHolder != null && motherOrEgg.ParentHolder is Pawn_InventoryTracker pawn_InventoryTracker) { if (pawn_InventoryTracker.pawn.IsCaravanMember()) { pawn_InventoryTracker.pawn.GetCaravan().AddPawn(pawn, addCarriedPawnToWorldPawnsIfAny: true); Find.WorldPawns.PassToWorld(pawn); return true; } if (pawn_InventoryTracker.pawn.IsWorldPawn()) { Find.WorldPawns.PassToWorld(pawn); return true; } } return false; } public static bool TryGetAvoidGrid(this Pawn p, out AvoidGrid grid, bool onlyIfLordAllows = true) { grid = null; if (!p.Spawned) { return false; } if (p.Faction == null) { return false; } if (!p.Faction.def.canUseAvoidGrid) { return false; } if (p.Faction == Faction.OfPlayer || !p.Faction.HostileTo(Faction.OfPlayer)) { return false; } if (p.kindDef.canUseAvoidGrid) { grid = p.Map.avoidGrid; return true; } if (onlyIfLordAllows) { Lord lord = p.GetLord(); LordToil lordToil = lord?.CurLordToil; if (lordToil != null && lordToil.useAvoidGrid) { grid = lord.Map.avoidGrid; return true; } return false; } grid = p.Map.avoidGrid; return true; } public static bool ShouldCollideWithPawns(Pawn p) { if (p == null || p.Downed || p.Dead) { return false; } if (p.IsShambler) { return true; } if (!p.mindState.anyCloseHostilesRecently) { return false; } if (p.IsPsychologicallyInvisible()) { return false; } if (!p.kindDef.collidesWithPawns) { return false; } return true; } public static bool AnyPawnBlockingPathAt(IntVec3 c, Pawn forPawn, bool actAsIfHadCollideWithPawnsJob = false, bool collideOnlyWithStandingPawns = false, bool forPathFinder = false, bool useId = false) { return PawnBlockingPathAt(c, forPawn, actAsIfHadCollideWithPawnsJob, collideOnlyWithStandingPawns, forPathFinder, useId) != null; } public static Pawn PawnBlockingPathAt(IntVec3 c, Pawn forPawn, bool actAsIfHadCollideWithPawnsJob = false, bool collideOnlyWithStandingPawns = false, bool forPathFinder = false, bool useId = false) { List thingList = c.GetThingList(forPawn.Map); if (thingList.Count == 0) { return null; } bool collideWithNonHostile = false; if (actAsIfHadCollideWithPawnsJob) { collideWithNonHostile = true; } else { Job curJob = forPawn.CurJob; if (curJob != null && (curJob.collideWithPawns || curJob.def.collideWithPawns || forPawn.jobs.curDriver.collideWithPawns)) { collideWithNonHostile = true; } } for (int i = 0; i < thingList.Count; i++) { Pawn pawn = thingList[i] as Pawn; if (PawnBlockedBy(forPawn, pawn, collideOnlyWithStandingPawns, collideWithNonHostile, forPathFinder, useId)) { return pawn; } } return null; } public static bool PawnBlockedBy(Pawn forPawn, Pawn other, bool collideOnlyWithStandingPawns = false, bool collideWithNonHostile = false, bool forPathFinder = false, bool useId = false) { if (!ShouldCollideWithPawns(other)) { return false; } if (useId && forPawn.thingIDNumber < other.thingIDNumber && !other.stances.FullBodyBusyRecently) { return false; } if (other == forPawn) { return false; } if (collideOnlyWithStandingPawns) { if (other.pather.MovingNow) { return false; } if (other.pather.Moving && other.pather.MovedRecently(60)) { return false; } } if (PawnsCanShareCellBecauseOfBodySize(other, forPawn)) { return false; } if (other.IsPsychologicallyInvisible()) { return false; } if (!other.kindDef.collidesWithPawns) { return false; } if (other.HostileTo(forPawn)) { return true; } if (forPawn.IsShambler && !MutantUtility.ShamblerShouldCollideWith(forPawn, other)) { return false; } if (collideWithNonHostile) { if (!forPathFinder && forPawn.Drafted && other.RaceProps.Animal) { return false; } Job curJob = other.CurJob; if (curJob != null && (curJob.collideWithPawns || curJob.def.collideWithPawns || other.jobs.curDriver.collideWithPawns)) { return true; } } return false; } private static bool PawnsCanShareCellBecauseOfBodySize(Pawn p1, Pawn p2) { float bodySize = p1.BodySize; float bodySize2 = p2.BodySize; if (bodySize >= 1.5f || bodySize2 >= 1.5f) { return false; } float num = bodySize / bodySize2; if (num < 1f) { num = 1f / num; } return num > 3.57f; } public static bool KnownDangerAt(IntVec3 c, Map map, Pawn forPawn) { return c.GetEdifice(map)?.IsDangerousFor(forPawn) ?? false; } [Obsolete("Lord and job report display validation is now checked separately.")] public static bool ShouldDisplayActionInInspectString(Pawn p) { if (p.Faction != Faction.OfPlayer && p.HostFaction != Faction.OfPlayer) { return false; } if (p.InMentalState) { return false; } if (p.IsMutant && p.mutant.Def.overrideInspectString) { return false; } return true; } public static bool ShouldDisplayLordReport(Pawn pawn) { if (ShouldShowCultistLordReport(pawn)) { return true; } return ShouldShowActionReportToPlayer(pawn); } public static bool ShouldDisplayJobReport(Pawn pawn) { if (pawn.IsMutant && pawn.mutant.Def.overrideInspectString) { return false; } if (pawn.CurJobDef != null && pawn.CurJobDef.alwaysShowReport) { return true; } if (ModsConfig.AnomalyActive && pawn.Faction == Faction.OfHoraxCult && pawn.CurJobDef == JobDefOf.HateChanting) { return true; } if (ShouldShowCultistLordReport(pawn)) { return true; } return ShouldShowActionReportToPlayer(pawn); } private static bool ShouldShowActionReportToPlayer(Pawn p) { if (p.Faction != Faction.OfPlayer && p.HostFaction != Faction.OfPlayer) { return false; } if (p.InMentalState) { return false; } if (p.IsMutant && p.mutant.Def.overrideInspectString) { return false; } return true; } public static bool ShouldDisplayFactionInInspectString(Pawn p) { if (p.IsMutant && p.mutant.Def.overrideInspectString) { return false; } return true; } private static bool ShouldShowCultistLordReport(Pawn pawn) { if (ModsConfig.AnomalyActive && pawn.Faction == Faction.OfHoraxCult && pawn.mindState.duty?.def == DutyDefOf.Invoke) { return true; } return false; } public static bool ShouldSendNotificationAbout(Pawn p) { if (Current.ProgramState != ProgramState.Playing) { return false; } if (p == null) { return false; } if (PawnGenerator.IsBeingGenerated(p)) { return false; } if (p.IsWorldPawn() && (!p.IsCaravanMember() || !p.GetCaravan().IsPlayerControlled) && !IsTravelingInTransportPodWorldObject(p) && !p.IsBorrowedByAnyFaction() && p.Corpse.DestroyedOrNull()) { return false; } if (p.IsSubhuman) { return false; } if (ModsConfig.AnomalyActive && p.Corpse is UnnaturalCorpse) { return false; } if (p.Faction != Faction.OfPlayer) { if (p.HostFaction != Faction.OfPlayer) { return false; } if (p.RaceProps.Humanlike && p.guest.Released && !p.Downed && !p.InBed()) { return false; } if (p.CurJob != null && p.CurJob.exitMapOnArrival && !PrisonBreakUtility.IsPrisonBreaking(p)) { return false; } if (IsExitingMap(p)) { return false; } } return true; } public static bool ShouldGetThoughtAbout(Pawn pawn, Pawn subject) { if (pawn.IsSubhuman || subject.IsSubhuman) { return false; } if (pawn.Faction != subject.Faction) { if (!subject.IsWorldPawn()) { return !pawn.IsWorldPawn(); } return false; } return true; } public static bool IsTeetotaler(this Pawn pawn) { if (!new HistoryEvent(HistoryEventDefOf.IngestedDrug, pawn.Named(HistoryEventArgsNames.Doer)).DoerWillingToDo()) { return true; } if (!new HistoryEvent(HistoryEventDefOf.IngestedRecreationalDrug, pawn.Named(HistoryEventArgsNames.Doer)).DoerWillingToDo()) { return true; } if (pawn.story != null) { return pawn.story.traits.DegreeOfTrait(TraitDefOf.DrugDesire) < 0; } return false; } public static bool CanTakeDrug(this Pawn pawn, ThingDef drug) { CompProperties_Drug compProperties = drug.GetCompProperties(); if (compProperties == null) { return true; } if (CanTakeDrugForDependency(pawn, drug)) { return true; } if (!compProperties.teetotalerCanConsume && pawn.IsTeetotaler()) { return false; } if (ModsConfig.IdeologyActive) { if (!IdeoUtility.DoerWillingToDo(HistoryEventDefOf.IngestedDrug, pawn)) { return false; } if (drug.IsNonMedicalDrug && !IdeoUtility.DoerWillingToDo(HistoryEventDefOf.IngestedRecreationalDrug, pawn)) { return false; } if (drug.ingestible != null && drug.ingestible.drugCategory == DrugCategory.Hard && !IdeoUtility.DoerWillingToDo(HistoryEventDefOf.IngestedHardDrug, pawn)) { return false; } } return true; } public static bool CanTakeDrugForDependency(Pawn pawn, ThingDef drug) { if (!ModsConfig.BiotechActive || pawn.genes == null) { return false; } CompProperties_Drug compProperties = drug.GetCompProperties(); if (compProperties == null) { return true; } foreach (Gene item in pawn.genes.GenesListForReading) { if (item is Gene_ChemicalDependency gene_ChemicalDependency && item.Active && gene_ChemicalDependency.def.chemical == compProperties.chemical) { return true; } } return false; } public static bool TryGetChemicalDependencyGene(Pawn pawn, out Gene_ChemicalDependency gene) { gene = null; if (!ModsConfig.BiotechActive || pawn.genes == null) { return false; } foreach (Gene item in pawn.genes.GenesListForReading) { if (item is Gene_ChemicalDependency gene_ChemicalDependency) { gene = gene_ChemicalDependency; return true; } } return false; } public static bool PawnWouldBeUnhappyTakingDrug(this Pawn pawn, ThingDef drug) { if (pawn.IsTeetotaler()) { return drug.GetCompProperties() != null; } return false; } public static bool IsProsthophobe(this Pawn pawn) { if (pawn.story != null) { return pawn.story.traits.HasTrait(TraitDefOf.BodyPurist); } return false; } public static bool IsPrisonerInPrisonCell(this Pawn pawn) { if (pawn.IsPrisoner && pawn.Spawned) { return pawn.Position.IsInPrisonCell(pawn.Map); } return false; } public static bool IsBeingArrested(Pawn pawn) { if (pawn.Map == null) { return false; } foreach (Pawn item in pawn.Map.mapPawns.AllPawnsSpawned) { if (item != pawn && item.CurJobDef == JobDefOf.Arrest && item.CurJob.AnyTargetIs(pawn)) { return true; } } return false; } public static string PawnKindsToCommaList(IEnumerable pawns, bool useAnd = false) { tmpPawns.Clear(); tmpPawns.AddRange(pawns); if (tmpPawns.Count >= 2) { tmpPawns.SortBy((Pawn x) => !x.RaceProps.Humanlike, (Pawn x) => x.GetKindLabelPlural()); } tmpAddedPawnKinds.Clear(); tmpPawnKindsStr.Clear(); for (int i = 0; i < tmpPawns.Count; i++) { if (tmpAddedPawnKinds.Contains(tmpPawns[i].kindDef)) { continue; } tmpAddedPawnKinds.Add(tmpPawns[i].kindDef); int num = 0; for (int j = 0; j < tmpPawns.Count; j++) { if (tmpPawns[j].kindDef == tmpPawns[i].kindDef) { num++; } } if (num == 1) { tmpPawnKindsStr.Add("1 " + tmpPawns[i].KindLabel); } else { tmpPawnKindsStr.Add(num + " " + tmpPawns[i].GetKindLabelPlural(num)); } } tmpPawns.Clear(); return tmpPawnKindsStr.ToCommaList(useAnd); } public static List PawnKindsToList(IEnumerable pawnKinds) { tmpPawnKinds.Clear(); tmpPawnKinds.AddRange(pawnKinds); if (tmpPawnKinds.Count >= 2) { tmpPawnKinds.SortBy((PawnKindDef x) => !x.RaceProps.Humanlike, (PawnKindDef x) => GenLabel.BestKindLabel(x, Gender.None, plural: true)); } tmpAddedPawnKinds.Clear(); tmpPawnKindsStr.Clear(); for (int i = 0; i < tmpPawnKinds.Count; i++) { if (tmpAddedPawnKinds.Contains(tmpPawnKinds[i])) { continue; } tmpAddedPawnKinds.Add(tmpPawnKinds[i]); int num = 0; for (int j = 0; j < tmpPawnKinds.Count; j++) { if (tmpPawnKinds[j] == tmpPawnKinds[i]) { num++; } } if (num == 1) { tmpPawnKindsStr.Add("1 " + GenLabel.BestKindLabel(tmpPawnKinds[i], Gender.None)); } else { tmpPawnKindsStr.Add(num + " " + GenLabel.BestKindLabel(tmpPawnKinds[i], Gender.None, plural: true, num)); } } return tmpPawnKindsStr; } public static string PawnKindsToLineList(IEnumerable pawnKinds, string prefix) { PawnKindsToList(pawnKinds); return tmpPawnKindsStr.ToLineList(prefix); } public static string PawnKindsToLineList(IEnumerable pawnKinds, string prefix, Color color) { PawnKindsToList(pawnKinds); for (int i = 0; i < tmpPawnKindsStr.Count; i++) { tmpPawnKindsStr[i] = tmpPawnKindsStr[i].Colorize(color); } return tmpPawnKindsStr.ToLineList(prefix); } public static string PawnKindsToCommaList(IEnumerable pawnKinds, bool useAnd = false) { PawnKindsToList(pawnKinds); return tmpPawnKindsStr.ToCommaList(useAnd); } public static LocomotionUrgency ResolveLocomotion(Pawn pawn, LocomotionUrgency secondPriority) { if (!pawn.Dead && pawn.mindState.duty != null && pawn.mindState.duty.locomotion != 0) { return pawn.mindState.duty.locomotion; } return secondPriority; } public static LocomotionUrgency ResolveLocomotion(Pawn pawn, LocomotionUrgency secondPriority, LocomotionUrgency thirdPriority) { LocomotionUrgency locomotionUrgency = ResolveLocomotion(pawn, secondPriority); if (locomotionUrgency != 0) { return locomotionUrgency; } return thirdPriority; } public static Danger ResolveMaxDanger(Pawn pawn, Danger secondPriority) { if (!pawn.Dead && pawn.mindState.duty != null && pawn.mindState.duty.maxDanger != 0) { return pawn.mindState.duty.maxDanger; } return secondPriority; } public static Danger ResolveMaxDanger(Pawn pawn, Danger secondPriority, Danger thirdPriority) { Danger danger = ResolveMaxDanger(pawn, secondPriority); if (danger != 0) { return danger; } return thirdPriority; } public static bool IsPermanentCombatant(this Pawn pawn) { if (pawn?.mindState == null) { return true; } if (pawn.IsAnimal && pawn.Faction != null) { return false; } if (pawn.DevelopmentalStage.Juvenile()) { return false; } return true; } public static bool IsCombatant(this Pawn pawn) { if (pawn.IsPermanentCombatant()) { return true; } return pawn.mindState.CombatantRecently; } public static bool IsFighting(this Pawn pawn) { if (pawn.CurJob != null) { if (pawn.CurJob.def != JobDefOf.AttackMelee && pawn.CurJob.def != JobDefOf.AttackStatic && pawn.CurJob.def != JobDefOf.Wait_Combat && pawn.CurJob.def != JobDefOf.PredatorHunt) { return pawn.CurJob.def == JobDefOf.ManTurret; } return true; } return false; } public static bool IsAttacking(this Pawn pawn) { if (pawn.CurJobDef == JobDefOf.AttackMelee || pawn.CurJobDef == JobDefOf.AttackStatic) { return true; } if (pawn.CurJobDef == JobDefOf.Wait_Combat && pawn.stances.curStance is Stance_Busy stance_Busy && stance_Busy.focusTarg.IsValid) { return true; } return false; } public static Hediff_Psylink GetMainPsylinkSource(this Pawn pawn) { return (Hediff_Psylink)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PsychicAmplifier); } public static int GetPsylinkLevel(this Pawn pawn) { int num = 0; foreach (Hediff hediff in pawn.health.hediffSet.hediffs) { if (hediff is Hediff_Psylink hediff_Psylink) { num += hediff_Psylink.level; } } return num; } public static int GetMaxPsylinkLevel(this Pawn pawn) { return (int)HediffDefOf.PsychicAmplifier.maxSeverity; } public static RoyalTitle GetMaxPsylinkLevelTitle(this Pawn pawn) { if (pawn.royalty == null) { return null; } int num = 0; RoyalTitle result = null; foreach (RoyalTitle item in pawn.royalty.AllTitlesInEffectForReading) { if (num < item.def.maxPsylinkLevel) { num = item.def.maxPsylinkLevel; result = item; } } return result; } public static int GetMaxPsylinkLevelByTitle(this Pawn pawn) { return pawn.GetMaxPsylinkLevelTitle()?.def.maxPsylinkLevel ?? 0; } public static void ChangePsylinkLevel(this Pawn pawn, int levelOffset, bool sendLetter = true) { Hediff_Psylink mainPsylinkSource = pawn.GetMainPsylinkSource(); if (mainPsylinkSource == null) { mainPsylinkSource = (Hediff_Psylink)HediffMaker.MakeHediff(HediffDefOf.PsychicAmplifier, pawn); try { mainPsylinkSource.suppressPostAddLetter = !sendLetter; pawn.health.AddHediff(mainPsylinkSource, pawn.health.hediffSet.GetBrain()); return; } finally { mainPsylinkSource.suppressPostAddLetter = false; } } mainPsylinkSource.ChangeLevel(levelOffset, sendLetter); } public static void GiveAllStartingPlayerPawnsThought(ThoughtDef thought) { foreach (Pawn startingAndOptionalPawn in Find.GameInitData.startingAndOptionalPawns) { if (startingAndOptionalPawn.needs.mood == null) { continue; } if (thought.IsSocial) { foreach (Pawn startingAndOptionalPawn2 in Find.GameInitData.startingAndOptionalPawns) { if (startingAndOptionalPawn2 != startingAndOptionalPawn) { startingAndOptionalPawn.needs.mood.thoughts.memories.TryGainMemory(thought, startingAndOptionalPawn2); } } } else { startingAndOptionalPawn.needs.mood.thoughts.memories.TryGainMemory(thought); } } } public static IntVec3 DutyLocation(this Pawn pawn) { Pawn_RopeTracker roping = pawn.roping; if (roping != null && roping.IsRopedToSpot) { return pawn.roping.RopedToSpot; } if (pawn.mindState.duty != null && pawn.mindState.duty.focus.IsValid) { return pawn.mindState.duty.focus.Cell; } return pawn.Position; } public static bool EverBeenColonistOrTameAnimal(Pawn pawn) { return pawn.records.GetAsInt(RecordDefOf.TimeAsColonistOrColonyAnimal) > 0; } public static bool EverBeenPrisoner(Pawn pawn) { return pawn.records.GetAsInt(RecordDefOf.TimeAsPrisoner) > 0; } public static bool EverBeenQuestLodger(Pawn pawn) { return pawn.records.GetAsInt(RecordDefOf.TimeAsQuestLodger) > 0; } public static void RecoverFromUnwalkablePositionOrKill(IntVec3 c, Map map) { if (!c.InBounds(map) || c.Walkable(map)) { return; } tmpThings.Clear(); tmpThings.AddRange(c.GetThingList(map)); for (int i = 0; i < tmpThings.Count; i++) { if (!(tmpThings[i] is Pawn pawn)) { continue; } if (CellFinder.TryFindBestPawnStandCell(pawn, out var cell)) { pawn.Position = cell; pawn.Notify_Teleported(endCurrentJob: true, resetTweenedPos: false); continue; } DamageInfo damageInfo = new DamageInfo(DamageDefOf.Crush, 99999f, 999f, -1f, null, pawn.health.hediffSet.GetBrain(), null, DamageInfo.SourceCategory.Collapse); damageInfo.SetIgnoreInstantKillProtection(ignore: true); pawn.TakeDamage(damageInfo); if (!pawn.Dead) { pawn.Kill(damageInfo); } } } public static float GetManhunterOnDamageChance(Pawn pawn, Thing instigator = null, float distance = -1f) { float num = GetManhunterOnDamageChance(pawn.def); if (pawn.health.hediffSet.HasHediff(HediffDefOf.Scaria)) { num += 0.5f; } if (instigator != null) { num *= GenMath.LerpDoubleClamped(1f, 30f, 3f, 1f, distance); num *= 1f - instigator.GetStatValue(StatDefOf.HuntingStealth); if (instigator is Pawn instigator2) { num *= GetManhunterChanceFactorForInstigator(instigator2); } } return Mathf.Clamp01(num); } public static float GetManhunterOnDamageChance(ThingDef def) { return def.race.manhunterOnDamageChance * Find.Storyteller.difficulty.manhunterChanceOnDamageFactor; } public static string GetManhunterOnDamageChanceExplanation(ThingDef def, Pawn pawn) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("HarmedRevengeChanceExplanation".Translate()); stringBuilder.AppendLine(); stringBuilder.AppendLine(def.LabelCap + ": " + def.race.manhunterOnDamageChance.ToStringPercent()); stringBuilder.AppendLine("StatsReport_DifficultyMultiplier".Translate(Find.Storyteller.difficultyDef.label) + ": " + Find.Storyteller.difficulty.manhunterChanceOnDamageFactor.ToStringPercent()); if (pawn != null && pawn.health.hediffSet.HasHediff(HediffDefOf.Scaria)) { stringBuilder.AppendLine(HediffDefOf.Scaria.LabelCap + ": " + 0.5f.ToStringPercentSigned()); } stringBuilder.AppendLine(); float f = ((pawn == null) ? GetManhunterOnDamageChance(def) : GetManhunterOnDamageChance(pawn)); stringBuilder.AppendLine("StatsReport_FinalValue".Translate() + ": " + f.ToStringPercent()); return stringBuilder.ToString(); } public static float GetManhunterChanceFactorForInstigator(Pawn instigator) { if (ModsConfig.AnomalyActive && instigator?.Faction == Faction.OfEntities) { return 0f; } float num = 1f; if (ModsConfig.IdeologyActive && instigator?.Ideo != null) { RoleEffect roleEffect = instigator.Ideo.GetRole(instigator)?.def.roleEffects?.FirstOrDefault((RoleEffect eff) => eff is RoleEffect_HuntingRevengeChanceFactor); if (roleEffect != null) { num *= ((RoleEffect_HuntingRevengeChanceFactor)roleEffect).factor; } } return num; } public static float GetManhunterOnTameFailChance(Pawn pawn) { return Mathf.Clamp01(GetManhunterOnTameFailChance(pawn.def)); } public static float GetManhunterOnTameFailChance(ThingDef def) { return def.race.manhunterOnTameFailChance; } public static string GetManhunterOnTameFailChanceExplanation(ThingDef def, Pawn pawn) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Stat_Race_Animal_TameFailedRevengeChance_Desc".Translate()); stringBuilder.AppendLine(); stringBuilder.AppendLine(def.LabelCap + ": " + def.race.manhunterOnTameFailChance.ToStringPercent()); stringBuilder.AppendLine(); float f = ((pawn == null) ? GetManhunterOnTameFailChance(def) : GetManhunterOnTameFailChance(pawn)); stringBuilder.AppendLine("StatsReport_FinalValue".Translate() + ": " + f.ToStringPercent()); return stringBuilder.ToString(); } public static MentalStateDef ManhunterStateFor(Pawn pawn) { if (pawn.health.hediffSet.HasHediff(HediffDefOf.Scaria)) { return MentalStateDefOf.ManhunterPermanent; } return MentalStateDefOf.Manhunter; } public static bool PlayerHasReproductivePair(PawnKindDef pawnKindDef) { if (!pawnKindDef.RaceProps.Animal) { return false; } List allMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction = PawnsFinder.AllMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction; bool flag = false; bool flag2 = false; for (int i = 0; i < allMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction.Count; i++) { Pawn pawn = allMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction[i]; if (pawn.kindDef == pawnKindDef && pawn.ageTracker.CurLifeStage.reproductive) { if (pawn.gender == Gender.Male) { flag = true; } else if (pawn.gender == Gender.Female) { flag2 = true; } if (flag && flag2) { return true; } } } return false; } public static float PlayerAnimalBodySizePerCapita() { float num = 0f; int num2 = 0; List allMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction = PawnsFinder.AllMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction; for (int i = 0; i < allMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction.Count; i++) { Pawn pawn = allMapsCaravansAndTravellingTransporters_Alive_OfPlayerFaction[i]; if (pawn.IsFreeColonist && !pawn.IsQuestLodger()) { num2++; } if (pawn.IsAnimal) { num += pawn.BodySize; } } if (num2 <= 0) { return 0f; } return num / (float)num2; } private static List PawnsOfFactionOnMapOrInCaravan(Pawn pawn) { if (pawn.Spawned) { return pawn.Map.mapPawns.SpawnedPawnsInFaction(pawn.Faction); } return pawn.GetCaravan()?.PawnsListForReading; } public static float PlayerVeneratedAnimalBodySizePerCapitaOnMapOrCaravan(Pawn pawn) { if (pawn.Ideo == null || pawn.Faction == null) { return 0f; } float num = 0f; int num2 = 0; List list = PawnsOfFactionOnMapOrInCaravan(pawn); for (int i = 0; i < list.Count; i++) { if (list[i].Faction == pawn.Faction && !pawn.IsQuestLodger()) { if (list[i].IsAnimal && pawn.Ideo.IsVeneratedAnimal(list[i])) { num += list[i].BodySize; } else if (list[i].RaceProps.Humanlike) { num2++; } } } return Mathf.Round(((num2 > 0) ? (num / (float)num2) : 0f) * 100f) / 100f; } public static Pawn FirstVeneratedAnimalOnMapOrCaravan(Pawn pawn) { if (pawn.Ideo == null || pawn.Faction == null) { return null; } List list = PawnsOfFactionOnMapOrInCaravan(pawn); for (int i = 0; i < list.Count; i++) { if (pawn.Faction == list[i].Faction && pawn.Ideo.IsVeneratedAnimal(list[i])) { return list[i]; } } return null; } public static bool HasClothingNotRequiredByKind(Pawn p) { if (p.apparel == null) { return false; } List wornApparel = p.apparel.WornApparel; if (wornApparel.Count > 0 && p.kindDef.apparelRequired.NullOrEmpty()) { return true; } for (int i = 0; i < wornApparel.Count; i++) { Apparel apparel = wornApparel[i]; if (apparel.def.apparel.countsAsClothingForNudity && !p.kindDef.apparelRequired.Contains(apparel.def)) { return true; } } return false; } public static IEnumerable GetCombatPawnKindsForPoints(Func selector, float points, Func selectionWeight = null) { IEnumerable allKinds = DefDatabase.AllDefsListForReading.Where(selector); if (selectionWeight == null) { selectionWeight = (PawnKindDef _) => 1f; } PawnKindDef result; while (points > 0f && allKinds.Where((PawnKindDef def) => def.combatPower > 0f && def.combatPower <= points && def.appearsRandomlyInCombatGroups).TryRandomElementByWeight(selectionWeight, out result)) { points -= result.combatPower; yield return result; } } public static int GetMaxAllowedToPickUp(Pawn pawn, ThingDef thingDef) { int maxAllowedToPickUp = GetMaxAllowedToPickUp(thingDef, pawn.Map); if (maxAllowedToPickUp <= 0) { return 0; } int num = pawn.inventory.Count((Thing t) => t.def.orderedTakeGroup == thingDef.orderedTakeGroup); return Math.Max(maxAllowedToPickUp - num, 0); } public static int GetMaxAllowedToPickUp(ThingDef thingDef, Map map = null) { if (map != null && !map.IsPlayerHome) { return int.MaxValue; } if (thingDef.orderedTakeGroup == null) { return 0; } return thingDef.orderedTakeGroup.max; } public static bool CanPickUp(Pawn pawn, ThingDef thingDef) { if (!pawn.Map.IsPlayerHome) { return true; } if (pawn.inventory != null && thingDef.orderedTakeGroup != null) { return thingDef.orderedTakeGroup.max > 0; } return false; } public static bool ShouldBeSlaughtered(this Pawn pawn) { if (!pawn.Spawned || !pawn.IsAnimal) { return false; } if (pawn.Map.designationManager.DesignationOn(pawn, DesignationDefOf.Slaughter) != null || pawn.Map.autoSlaughterManager.AnimalsToSlaughter.Contains(pawn)) { return pawn.Map.designationManager.DesignationOn(pawn, DesignationDefOf.ReleaseAnimalToWild) == null; } return false; } public static bool CanBeBuried(this Thing t) { if (t is Corpse { MapHeld: not null } corpse) { return corpse.MapHeld.designationManager.DesignationOn(corpse, DesignationDefOf.ExtractSkull) == null; } return true; } public static bool PawnHadFuneral(Pawn pawn) { Precept_Ritual precept_Ritual = (Precept_Ritual)(pawn.ideo?.Ideo?.GetPrecept(PreceptDefOf.Funeral)); if (precept_Ritual != null && !precept_Ritual.completedObligations.NullOrEmpty()) { return precept_Ritual.completedObligations.Any((RitualObligation o) => o.FirstValidTarget.Thing == pawn); } return false; } public static bool IsBiologicallyOrArtificiallyBlind(Pawn pawn) { if (!IsBiologicallyBlind(pawn)) { return IsArtificiallyBlind(pawn); } return true; } public static bool IsBiologicallyBlind(Pawn pawn) { return !pawn.health.capacities.CapableOf(PawnCapacityDefOf.Sight); } public static bool IsArtificiallyBlind(Pawn p) { if (IsBiologicallyBlind(p)) { return false; } if (p.apparel != null) { foreach (Apparel item in p.apparel.WornApparel) { if (item.def.apparel.blocksVision) { return true; } } } return false; } public static bool IsWorkTypeDisabledByAge(this Pawn pawn, WorkTypeDef workType, out int minAgeRequired) { for (int i = 0; i < pawn.RaceProps.lifeStageWorkSettings.Count; i++) { LifeStageWorkSettings lifeStageWorkSettings = pawn.RaceProps.lifeStageWorkSettings[i]; if (lifeStageWorkSettings.workType == workType && lifeStageWorkSettings.IsDisabled(pawn)) { minAgeRequired = lifeStageWorkSettings.minAge; return true; } } minAgeRequired = 0; return false; } public static bool DutyActiveWhenDown(this Pawn pawn, bool onlyInBed = false) { if (onlyInBed && !pawn.InBed()) { return false; } return (pawn.GetLord()?.LordJob?.DutyActiveWhenDown(pawn)).GetValueOrDefault(); } public static bool IsExitingMap(Pawn pawn) { Lord lord = pawn.GetLord(); if (lord == null) { return false; } if (!(lord.LordJob is LordJob_ExitMapBest) && !(lord.LordJob is LordJob_ExitMapNear) && !(lord.LordJob is LordJob_ExitOnShuttle)) { return lord.LordJob is LordJob_TravelAndExit; } return true; } public static void ForceEjectFromContainer(Pawn pawn) { Thing resultingThing; if (pawn.ParentHolder is Pawn_CarryTracker pawn_CarryTracker) { pawn_CarryTracker.TryDropCarriedThing(pawn_CarryTracker.pawn.Position, ThingPlaceMode.Near, out resultingThing); pawn_CarryTracker.pawn.jobs.EndCurrentJob(JobCondition.InterruptForced); } if (pawn.ParentHolder is Building_Enterable building_Enterable) { building_Enterable.innerContainer.TryDrop(pawn, building_Enterable.InteractionCell, building_Enterable.Map, ThingPlaceMode.Near, out resultingThing); } if (pawn.ParentHolder is Building_Casket building_Casket) { building_Casket.EjectContents(); } if (pawn.ParentHolder is Building_HoldingPlatform building_HoldingPlatform) { building_HoldingPlatform.EjectContents(); } if (pawn.ParentHolder is CompBiosculpterPod compBiosculpterPod) { compBiosculpterPod.EjectContents(interrupted: true, playSounds: true); } } } ```