private static void ValidateApparelForChangedPawn([NotNull] Pawn pawn, [NotNull] ThingDef oldRace) { Pawn_ApparelTracker apparel = pawn.apparel; if (apparel == null) { return; } _apparelCache.Clear(); _apparelCache.AddRange(apparel.WornApparel.MakeSafe()); foreach (Apparel ap in _apparelCache) //use a copy so we can remove them safely while iterating { if (!ApparelUtility.HasPartsToWear(pawn, ap.def)) { if (DebugLogUtils.ShouldLog(LogLevel.Messages)) { Log.Message($"removing {ap.Label}"); } if (apparel.TryDrop(ap)) { apparel.Remove(ap); } } } }
internal static bool TryDrop(this Pawn_ApparelTracker _this, Apparel ap, out Apparel resultingAp, IntVec3 pos, bool forbid = true) { // drop all toolbelt & backpack stuff so that it won't disappear Apparel_Backpack backpack = ap as Apparel_Backpack; Apparel_Toolbelt toolbelt = ap as Apparel_Toolbelt; Thing dropThing = null; if (backpack?.SlotsComp?.slots?.Count >= 1) { foreach (Thing slot in backpack.SlotsComp.slots) { GenThing.TryDropAndSetForbidden(slot, pos, ap.Map, ThingPlaceMode.Near, out dropThing, forbid); } } if (toolbelt?.slotsComp?.slots?.Count >= 1) { foreach (Thing slot in toolbelt.slotsComp.slots) { GenThing.TryDropAndSetForbidden(slot, pos, ap.Map, ThingPlaceMode.Near, out dropThing, forbid); } for (int i = MapComponent_ToolsForHaul.CachedToolEntries.Count - 1; i >= 0; i--) { var entry = MapComponent_ToolsForHaul.CachedToolEntries[i]; if (entry.pawn == _this.pawn) { MapComponent_ToolsForHaul.CachedToolEntries.RemoveAt(i); } } } if (!_this.WornApparel.Contains(ap)) { Log.Warning(_this.pawn.LabelCap + " tried to drop apparel he didn't have: " + ap.LabelCap); resultingAp = null; return(false); } if (_this.pawn.MapHeld == null) { Log.Warning(_this.pawn.LabelCap + " tried to drop apparel but his MapHeld is null."); resultingAp = null; return(false); } ap.Notify_Stripped(_this.pawn); _this.Remove(ap); Thing thing = null; bool result = GenThing.TryDropAndSetForbidden(ap, pos, _this.pawn.MapHeld, ThingPlaceMode.Near, out thing, forbid); resultingAp = (thing as Apparel); #if CR Combat_Realism.CR_Utility.TryUpdateInventory(_this.pawn); // Apparel was dropped, update inventory #endif return(result); }
internal static bool TryDrop(this Pawn_ApparelTracker _this, Apparel ap, out Apparel resultingAp, IntVec3 pos, bool forbid = true) { if (!_this.WornApparel.Contains(ap)) { Log.Warning(_this.pawn.LabelCap + " tried to drop apparel he didn't have: " + ap.LabelCap); resultingAp = null; return(false); } if (_this.pawn.MapHeld == null) { Log.Warning(_this.pawn.LabelCap + " tried to drop apparel but his MapHeld is null."); resultingAp = null; return(false); } ap.Notify_Stripped(_this.pawn); _this.Remove(ap); Thing thing = null; bool result = GenThing.TryDropAndSetForbidden(ap, pos, _this.pawn.MapHeld, ThingPlaceMode.Near, out thing, forbid); resultingAp = (thing as Apparel); CR_Utility.TryUpdateInventory(_this.pawn); // Apparel was dropped, update inventory return(result); }
/// <summary> /// applies damage to all apparel the pawn is wearing based on /// </summary> /// <param name="pawn">The pawn.</param> /// <param name="newRace">The new race.</param> /// <param name="mutagen">the mutagen that caused the transformation, if null uses default values for <see cref="MutagenDamageProperties"/></param> /// <exception cref="System.ArgumentNullException"> /// pawn /// or /// newRace /// </exception> public static void ApplyTfDamageToApparel([NotNull] Pawn pawn, [NotNull] ThingDef newRace, [CanBeNull] MutagenDef mutagen) { if (pawn == null) { throw new ArgumentNullException(nameof(pawn)); } if (newRace == null) { throw new ArgumentNullException(nameof(newRace)); } Pawn_ApparelTracker apparelTracker = pawn.apparel; List <Apparel> cachedApparel = apparelTracker?.WornApparel?.ToList(); //make a copy of all worn apparel if (cachedApparel == null || cachedApparel.Count == 0) { return; } MutagenDamageProperties damageProps = mutagen?.damageProperties ?? DefaultDamageValues; float oldSize = pawn.RaceProps.baseBodySize; float newSize = newRace.race.baseBodySize; float percentDiff; if (oldSize < EPSILON && newSize < EPSILON) //prevent division by zero { percentDiff = 0; //if they're both really small say no change } else if (oldSize < EPSILON) { percentDiff = MAX_APPAREL_PDIFF; //if just old size is small then completely destroy the apparel } else { percentDiff = (newSize - oldSize) / oldSize; //signed percent difference between percentDiff += APPAREL_PDIFF_OFFSET; //add a little offset so if the body size is the same or slightly smaller we still apply some damage //trying to account for differences in 'body shape' } float percentDamage = Mathf.Clamp(percentDiff, 0, MAX_APPAREL_PDIFF); //clamp pDiff between [0, Max], if they shrink don't damage apparel percentDamage /= MAX_APPAREL_PDIFF; //normalize the percentDifference to get a percentage to damage apparel by int totalStuffProduced = 0; foreach (Apparel apparel in cachedApparel) { int damage = Mathf.CeilToInt(apparel.MaxHitPoints * percentDamage * damageProps.apparelDamageMultiplier) + damageProps.apparelDamageOffset; int newHitPoints = Mathf.Max(apparel.HitPoints - damage, 0); var damageDone = apparel.HitPoints - newHitPoints; //save the actual damage done apparel.HitPoints = newHitPoints; if (apparel.HitPoints == 0) { apparelTracker.Remove(apparel); apparelTracker.Notify_ApparelRemoved(apparel); apparel.Destroy(); } var stuffProduced = Mathf.FloorToInt(damageDone * damageProps.spawnedBiproductMult); totalStuffProduced += Mathf.Min(stuffProduced, MAX_APPAREL_DAMAGE_PRODUCT_PER_APPAREL); } if (damageProps.biproduct != null && damageProps.spawnedBiproductMult > EPSILON) { Thing thing = ThingMaker.MakeThing(damageProps.biproduct); thing.stackCount = totalStuffProduced; if (pawn.Spawned) { GenPlace.TryPlaceThing(thing, pawn.PositionHeld, pawn.MapHeld, ThingPlaceMode.Near); } else { Caravan caravan = pawn.GetCaravan(); caravan?.AddPawnOrItem(thing, false); } } }
internal static void Wear(this Pawn_ApparelTracker _this, Apparel newApparel, bool dropReplacedApparel = true) { SlotGroupUtility.Notify_TakingThing(newApparel); if (newApparel.Spawned) { newApparel.DeSpawn(); } if (!ApparelUtility.HasPartsToWear(_this.pawn, newApparel.def)) { Log.Warning(string.Concat(new object[] { _this.pawn, " tried to wear ", newApparel, " but he has no body parts required to wear it." })); return; } for (int i = _this.WornApparel.Count - 1; i >= 0; i--) { Apparel apparel = _this.WornApparel[i]; if (!ApparelUtility.CanWearTogether(newApparel.def, apparel.def)) { bool forbid = _this.pawn.Faction.HostileTo(Faction.OfPlayer); if (dropReplacedApparel) { Apparel apparel2; if (!_this.TryDrop(apparel, out apparel2, _this.pawn.Position, forbid)) { Log.Error(_this.pawn + " could not drop " + apparel); return; } } else { _this.Remove(apparel); } } } if (newApparel.wearer != null) { Log.Warning(string.Concat(new object[] { _this.pawn, " is trying to wear ", newApparel, " but this apparel already has a wearer (", newApparel.wearer, "). This may or may not cause bugs." })); } _this.WornApparel.Add(newApparel); newApparel.wearer = _this.pawn; _this.SortWornApparelIntoDrawOrder(); _this.ApparelChanged(); //CR PART CR_Utility.TryUpdateInventory(_this.pawn); // Apparel was added, update inventory MethodInfo methodInfo = typeof(Pawn_ApparelTracker).GetMethod("SortWornApparelIntoDrawOrder", BindingFlags.Instance | BindingFlags.NonPublic); methodInfo.Invoke(_this, new object[] { }); LongEventHandler.ExecuteWhenFinished(new Action(_this.ApparelChanged)); }
internal static void Notify_WornApparelDestroyed(this Pawn_ApparelTracker _this, Apparel apparel) { _this.Remove(apparel); CR_Utility.TryUpdateInventory(_this.pawn); // Apparel was destroyed, update inventory }