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);
        }
Beispiel #3
0
        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);
                }
            }
        }
Beispiel #5
0
        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));
        }
Beispiel #6
0
 internal static void Notify_WornApparelDestroyed(this Pawn_ApparelTracker _this, Apparel apparel)
 {
     _this.Remove(apparel);
     CR_Utility.TryUpdateInventory(_this.pawn);     // Apparel was destroyed, update inventory
 }