using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using RimWorld; using rjw; using Verse; namespace SizedApparel { [StaticConstructorOnStartup] public class ApparelRecorderComp : ThingComp { Pawn pawn;// Parent Cache public bool recentClothFlag = true; public bool isDrawAge = true; public bool testbool = false; public ApparelRecorderCompProperties Props => (ApparelRecorderCompProperties)this.props; public bool hasUpdateBefore = false; public bool hasUpdateBeforeSuccess = false; public bool hasGraphicUpdatedBefore = false; // not yet public bool needToCheckApparelGraphicRecords = false; public bool isDirty = true; public bool isHediffDirty = true; public bool isApparelDirty = true; public bool isSkeletonDirty = true; public bool isBodyAddonDirty = true; // reset all body addon graphics. public bool hasUnsupportedApparel = true; public bool havingSex = false;//Obsolete public bool hasUpdateForSex = false;//Obsolete string cachedBodytype; public List cachedApparelGraphicRecord = new List(); public Hediff breastHediff = null; public Hediff vaginaHediff = null; public List penisHediffs = null; // RJW can attach multiple penis public Hediff anusHediff = null; public Hediff udderHediff = null;//RJW 4.6.8: Udder is not partof chest's breast. it attached to torso. public Color? nippleColor; //for menstruation cycles Mod //TODO Optimize Update Hediff Filter private bool hasBreastsAddon = false; private bool hasVaginaAddon = false; private bool hasPenisAddon = false; private bool hasAnusAddon = false; private bool hasUdderAddon = false; private bool hasPubicHairAddon = false; public float breastSeverity = -1; public float BreastSeverityCache = 0; //public float BiggestBreastSeverityInAvailableTextures = 0; //for breasts animation or something. public bool ForceUpdateTickAnimation = false; public bool ForceBlockTickAnimation = false; // useful when you have make fixed pose public bool PrePositionCache; public Vector3? prePositionCache; public float? preAngleCache; public int? preTickCache; public Vector3 preVelocity = Vector3.zero; public float preRotation = 0; public Dictionary tickCache = new Dictionary(); public BodyDef bodyDef = null; public List bodyAddons = new List(); // BodyParts Added form Defs public SkeletonDef skeletonDef; // for rebuild Skeleton public Skeleton skeleton; public bool skipSkeleton = false; //native BodyParts public SizedApparelBodyPart bodyPartBreasts;//TODO: Make this as List public SizedApparelBodyPart bodyPartNipple;//TODO public List bodyPartPenises = new List(); public List bodyPartBalls = new List(); public SizedApparelBodyPart bodyPartVagina; public SizedApparelBodyPart bodyPartAnus; public SizedApparelBodyPart bodyPartBelly; public SizedApparelBodyPart bodyPartMuscleOverlay;//TODO public SizedApparelBodyPart bodyPartUdder; public SizedApparelBodyPart bodyPartHips; public List bodyPartThighs = new List(); public List bodyPartHands = new List(); public List bodyPartFeet = new List(); public PubicHairDef pubicHairDef = null; public PubicHairDef initialPubicHairDef = null; // For StyleStaitionWork public PubicHairDef nextPubicHairDef = null; // For StyleStaitionWork public SizedApparelBodyPart bodyPartPubicHair; public Graphic graphicSourceNaked = null; //original Graphic public Graphic graphicSourceRotten = null; //original Graphic public Graphic graphicSourceFurCovered = null; //original Graphic public Graphic graphicbaseBodyNaked = null; public SizedApparelTexturePointDef baseBodyNakedPoints; public Graphic graphicbaseBodyCorpse = null; public SizedApparelTexturePointDef baseBodyCorpsePoints; public Graphic graphicbaseBodyRotten = null; public SizedApparelTexturePointDef baseBodyRottenPoints; public Graphic graphicbaseBodyFurCovered = null; public SizedApparelTexturePointDef baseBodyFurCoveredPoints; public AlienRaceSetting raceSetting = null; public string customPose = null; public SizedApparelPose currentCustomPose = null; public bool forceHorny = false; public bool canDrawBreasts = false; public bool canDrawPenis = false; public bool canDrawVaginaAndAnus = false; public bool canDrawTorsoParts = false; //belly and udder //this may reset skeleton animation. also little have process public void InitSkeleton() { skeletonDef = DefDatabase.GetNamedSilentFail(pawn.def.defName); if (skeletonDef == null) { if (raceSetting != null && raceSetting.asHuman == true) //old: !SizedApparelSettings.UnsupportedRaceToUseHumanlike { skeletonDef = DefDatabase.GetNamedSilentFail("Human"); } } if (skeletonDef != null) { if (this.pawn.story == null || this.pawn.story.bodyType == null) { foreach (Skeleton s in skeletonDef.skeletons) { if (s.bodyType == null) { this.skeleton = new Skeleton(s); if (SizedApparelSettings.Debug) Log.Message("[SizedApparel] Apply SkeletonDef: pawn withouth bodytype " + pawn.Name); return; } } } else { //Setting Up Skeleton foreach (Skeleton s in skeletonDef.skeletons) { if (s.bodyType == this.pawn.story.bodyType.defName) { this.skeleton = new Skeleton(s); if (SizedApparelSettings.Debug) Log.Message("[SizedApparel] Apply SkeletonDef: pawn with bodytype " + pawn.Name + " with SkeletonDef : " + this.pawn.story.bodyType.defName); return; } } } this.skeleton = null; } if (SizedApparelSettings.Debug) Log.Message("[SizedApparel] Apply SkeletonDef: No Skeleton Found for " + pawn.Name); isSkeletonDirty = false; } public void InitBodyAddons() { bodyAddons.Clear(); //TODO //bodyDef = DefDatabase.GetNamed(pawn.def.defName); bodyDef = DefDatabase.GetNamedSilentFail(pawn.def.defName); //bodyDef can be null if (bodyDef == null) { if (raceSetting != null && raceSetting.asHuman) //old: !SizedApparelSettings.UnsupportedRaceToUseHumanlike { bodyDef = DefDatabase.GetNamedSilentFail("Human"); } } if (bodyDef != null && bodyDef.bodies != null) { if (pawn.story?.bodyType == null) { if(SizedApparelSettings.Debug) Log.Message("[SizedApparel] Apply BodyDef: pawn has no bodytype " + pawn.Name); BodyWithBodyType body = null; if(!bodyDef.bodies.NullOrEmpty()) body = bodyDef.bodies[0]; if (body != null && body.Addons != null) { foreach (var bodyaddon in body.Addons) { if (bodyaddon == null) continue; var a = new SizedApparelBodyPart(pawn, this, bodyaddon.partName, bodyaddon.bodyPartOf, bodyaddon.defaultHediffName, bodyaddon.isBreasts, false, bodyaddon.customPath, bodyaddon.colorType, bodyaddon.mustHaveBone); a.SetDepthOffsets(bodyaddon.depthOffset.south, bodyaddon.depthOffset.north, bodyaddon.depthOffset.east, bodyaddon.depthOffset.west); //a.SetDepthOffsets(bodyaddon.depthOffset); a.SetCenteredTexture(bodyaddon.centeredTexture); bodyAddons.Add(a); break; } } } else { foreach (BodyWithBodyType body in bodyDef.bodies) { if (body.bodyType != null && body.bodyType == this.pawn.story.bodyType.defName) { if (SizedApparelSettings.Debug) Log.Message("[SizedApparel] Apply BodyDef: matched BodyTyped Body for " + pawn.Name); if (body != null && body.Addons != null) { foreach (var bodyaddon in body.Addons) { if (bodyaddon == null) continue; var a = new SizedApparelBodyPart(pawn, this, bodyaddon.partName, bodyaddon.bodyPartOf, bodyaddon.defaultHediffName, bodyaddon.isBreasts, false, bodyaddon.customPath, bodyaddon.colorType, bodyaddon.mustHaveBone); a.SetDepthOffsets(bodyaddon.depthOffset.south, bodyaddon.depthOffset.north, bodyaddon.depthOffset.east, bodyaddon.depthOffset.west); //a.SetDepthOffsets(bodyaddon.depthOffset); a.SetCenteredTexture(bodyaddon.centeredTexture); bodyAddons.Add(a); } } break; } } } } else { if (SizedApparelSettings.Debug) Log.Message("[SizedApparel] Cannot find BodyDef for " + pawn.def.defName); } //bodyPartBreasts = new SizedApparelBodyPart(pawn, this, "Breasts", SizedApparelBodyPartOf.Breasts, "Breasts", true, false, null); //bodyPartBreasts.SetDepthOffsets(0.0113f, 0.001f, 0.0113f, 0.0113f); //bodyPartBreasts.SetPositionOffsets(Vector2.one, Vector2.one, Vector2.one, Vector2.one); //bodyPartBreasts.scale = 2f; //Nipple. should I separate from Breats? //bodyPartNipple = new SizedApparelBodyPart(pawn, this, "Nippple", SizedApparelBodyPartOf.Breasts, "Breasts", true, false); //bodyPartBreasts.SetDepthOffsets(0.0114f, 0.000f, 0.0114f, 0.0114f); //bodyPartUdder = new SizedApparelBodyPart(pawn, this, "Udder", SizedApparelBodyPartOf.Udder, "UdderBreasts", true, false); //bodyPartUdder.SetDepthOffsets(0.0112f, 0.0005f, 0.0112f, 0.0112f); //UdderNipple...? //bodyPartUdder = new SizedApparelBodyPart(p, "Udder", SizedApparelBodyPartOf.Udder, "UdderBreasts", true, false); //bodyPartUdder.SetDepthOffsets(0.0112f, 0.0005f, 0.0112f, 0.0112f);\ //bodyPartVagina = new SizedApparelBodyPart(pawn, this, "Vagina", SizedApparelBodyPartOf.Vagina, "Vagina", false, false); //bodyPartVagina.SetDepthOffsets(0.0088f, 0.0100f, 0.0088f, 0.0088f); //bodyPartAnus = new SizedApparelBodyPart(pawn, this, "Anus", SizedApparelBodyPartOf.Anus, "Anus", false, false); //bodyPartAnus.SetDepthOffsets(0.0093f, 0.0105f, 0.0093f, 0.0093f); //bodyPartBelly = new SizedApparelBodyPart(pawn, this, "Belly", SizedApparelBodyPartOf.Belly, "BellyBulge", false, false); //bodyPartBelly.SetDepthOffsets(0.0098f, 0.0002f, 0.0098f, 0.0098f); //bodyPartPubicHair = new SizedApparelBodyPart(pawn, this, "PubicHair", SizedApparelBodyPartOf.PubicHair, "Default", false, false, null, ColorType.Hair); //bodyPartPubicHair.SetDepthOffsets(0.0099f, 0.0099f, 0.0089f, 0.0089f); } public void ResetBodyAddonBoneLink() { string s; foreach(var a in bodyAddons) { s = a.bone?.name; if (s == null) continue; a.SetBone(skeleton.FindBone(s)); } } public override void Initialize(CompProperties props) { base.Initialize(props); this.pawn = this.parent as Pawn; } public override void PostDeSpawn(Map map) { base.PostDeSpawn(map); } public override void PostSpawnSetup(bool respawningAfterLoad) { base.PostSpawnSetup(respawningAfterLoad); UpdateRaceSettingData(); // include race Setting InitSkeleton(); InitBodyAddons(); } public override void PostExposeData() { base.PostExposeData(); Scribe_Values.Look(ref customPose, "customPose"); // save pawn's custom pose. Each bodyparts will not saved. Scribe_Defs.Look(ref pubicHairDef, "PubicHairDef"); if (pubicHairDef == null) { pubicHairDef = SizedApparelUtility.GetRandomPubicHair(); } //Scribe_Values.Look<>(); //TODO: save pubic hair data } /* public override void CompTick() { if (PrePositionCache) { } base.CompTick(); }*/ /* public override void CompTickRare() { base.CompTickRare(); }*/ float penisAngle = 0; public void SetPenisAngle(float angle) { penisAngle = angle; if(this.skeleton != null) { Bone penisBone = skeleton.FindBone("Penis"); if (penisBone != null) { //Log.Message("SetPenisAngle : " + angle.ToString()); penisBone.SetAngle(angle); } } bool penisDown = false; if (angle >= 120 && angle <= 250) penisDown = true; foreach (var p in bodyPartPenises) { if (penisDown) { p.SetCustomPose("PenisDown"); } else { p.SetCustomPose(null); } } } //do not call this in character creation from new game. public void SetBreastJiggle(bool jiggle, int cooltime = 5, bool checkApparelForCanPose = false) { //SetJiggle has cooltime. bool flag1 = true; //should set apparels pose? //Use First BodyAddon which is partof Breasts foreach(var a in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Breasts)) { if (flag1) { if (Find.TickManager.TicksGame < a.lastPoseTick + cooltime) return; flag1 = false; } if (jiggle) { bool flag2 = true; if (flag2 && checkApparelForCanPose) { if (!a.CheckCanPose("JiggleUp", true, false, true, true, false)) return; flag2 = false; } //may need to check apparels for aply pose //bodyPartBreasts.CheckCanPose("JiggleUp",) a.SetCustomPose("JiggleUp"); } else { //SetPoseFromPoseSet(null); a.SetCustomPose(null); } } } string[] testPose = { null, "JiggleUp", "JiggleCenter" }; string[] testPose2 = { null, "PenisDown" }; public void UpdateTickAnim(Vector3 rootLoc, float angle) // call this in DrawPawnBody <- only called when it rendered { if (ForceBlockTickAnimation)// prevent breasts jiggle from other pose return; if (skeleton == null) return; if (Find.CameraDriver == null) return; //do not calculate physics camera is far if (Find.CameraDriver.CurrentZoom >= CameraZoomRange.Furthest) return; //int IdTick = parent.thingIDNumber * 20; //hint from yayo animation mod if (!SizedApparelUtility.CanApplySizedApparel(pawn)) return; if (SizedApparelSettings.breastsPhysics || ForceUpdateTickAnimation)//SizedApparelSettings.autoJiggleBreasts { int tick; if (this.preTickCache != null) tick = Find.TickManager.TicksGame - this.preTickCache.Value; else tick = Find.TickManager.TicksGame; //if tick is not updated, don't update animation. if (tick == 0) return; Vector3 velocity; if (this.prePositionCache != null) velocity = (rootLoc - this.prePositionCache.Value);// /tick else velocity = Vector3.zero; float rotation; if (this.preAngleCache != null) rotation = angle - this.preAngleCache.Value; else rotation = 0; float rotAcc = rotation - preRotation; //Log.Message(velocity.ToString() + " , " + preVelocity.ToString()); //UnityEngine's vector.normalize is safe for zero vector //(Vector3.Dot(velocity.normalized, preVelocity.normalized)) < 0.2f float dotV = Vector3.Dot(velocity.normalized, preVelocity.normalized); float velocityOffset = (velocity - preVelocity).magnitude; //Log.Message(pawn.ToString()); //Log.Message("rotAcc : " + rotAcc.ToString()); //Log.Message("Velocity.x : " + velocity.x.ToString()); //Log.Message("Velocity.z : " + velocity.z.ToString()); //Log.Message("dotV : " + dotV.ToString()); //Log.Message("velocityOffset : " + velocityOffset.ToString()); //&& dotV > 0.4f if (((preVelocity != Vector3.zero && velocityOffset >= 0.02))|| Mathf.Abs(rotAcc) > 0.5) //(dotV == 0 ? 0:1), Mathf.Abs(dotV) // || Mathf.Abs(rotation) > 20 { //tickCache.Add("BreastsJiggleUp", Find.TickManager.TicksGame); SetBreastJiggle(true,10,true); } else { SetBreastJiggle(false,10, false); } //cache pre data this.prePositionCache = rootLoc; this.preAngleCache = angle; this.preRotation = rotation; this.preTickCache = Find.TickManager.TicksGame; this.preVelocity = velocity; } //SetPoseFromPoseSet(testPose2.RandomElement()); } public bool isApparelGraphicRecordChanged() { if (pawn == null) return false; var apparelGraphics = pawn.Drawer?.renderer?.graphics?.apparelGraphics; if (apparelGraphics == null) return false; //return false; //since 1.3 broken. force to return false; if (!apparelGraphics.SequenceEqual(cachedApparelGraphicRecord)) { if (SizedApparelSettings.Debug) Log.Message("[SizedApparel]:" + pawn + "'s apparel Record Changed! need to updating comp"); return true; } return false; } public void DrawAllBodyParts(Vector3 rootLoc, float angle, Rot4 facing, RotDrawMode bodyDrawType, PawnRenderFlags flags, Pawn ___pawn, Mesh bodyMesh) { if (this.isDrawAge && (!flags.FlagSet(PawnRenderFlags.Clothes) || !this.hasUnsupportedApparel || SizedApparelUtility.isPawnNaked(___pawn))) //TODO : Move it to CanDraw { if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawVagina && SizedApparelUtility.CanDrawVagina(___pawn, flags)) { //if (this.bodyPartVagina != null) // this.bodyPartVagina.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Vagina)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawAnus && SizedApparelUtility.CanDrawAnus(___pawn, flags)) { //if (this.bodyPartAnus != null) // this.bodyPartAnus.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Anus)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawBelly && SizedApparelUtility.CanDrawBelly(___pawn, flags)) { //if (this.bodyPartBelly != null) // this.bodyPartBelly.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Belly)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } //Draw Pubic Hair if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawPubicHair && SizedApparelUtility.CanDrawPubicHair(___pawn, flags)) { if (this.bodyPartPubicHair != null) this.bodyPartPubicHair.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.PubicHair)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawUdder && SizedApparelUtility.CanDrawUdder(___pawn, flags)) { //if (this.bodyPartUdder != null) // this.bodyPartUdder.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Udder)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawBreasts && SizedApparelUtility.CanDrawBreasts(___pawn, flags) && (SizedApparelSettings.drawSizedApparelBreastsOnlyWorn ? !SizedApparelUtility.isPawnNaked(___pawn, flags) : true)) { //if (this.bodyPartBreasts != null) // this.bodyPartBreasts.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Breasts)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } if (bodyDrawType != RotDrawMode.Dessicated && SizedApparelSettings.drawPenis && SizedApparelUtility.CanDrawPenis(___pawn, flags, true)) { if (SizedApparelSettings.hideBallOfFuta ? !Genital_Helper.is_futa(___pawn) : true) { /* foreach (SizedApparelBodyPart b in this.bodyPartBalls) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); }*/ foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Balls)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } } /* foreach (SizedApparelBodyPart p in this.bodyPartPenises) { p.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); }*/ foreach (var b in GetSizedApparelBodyParts(SizedApparelBodyPartOf.Penis)) { b.DrawBodyPart(rootLoc, angle, facing, bodyDrawType, flags, bodyMesh); } }//Draw BodyParts //Draw Modular Apparel Parts... TODO...? Is it passible? if (flags.FlagSet(PawnRenderFlags.Clothes)) { } } } //not working public override void PostPostMake() { /* Pawn pawn; try { pawn = (Pawn)parent; if (pawn != null) { if (!pawn.RaceProps.Humanlike) return; pawn.Drawer.renderer.graphics.ResolveAllGraphics(); if (SizedApparelSettings.Debug) Log.Message("[SizedApparels] Component Inint And Resolve all Graphics for "+ pawn.Name); } } catch { } */ } public void ClearHediffs() { breastHediff = null; vaginaHediff = null; if(penisHediffs != null) penisHediffs.Clear(); anusHediff = null; udderHediff = null; } public void ClearBreastCacheValue() { breastHediff = null; breastSeverity = -1; BreastSeverityCache = 0; } public void ClearPenisCacheValue() { //TODO } public void ClearCanDraw() { canDrawBreasts = false; canDrawPenis = false; canDrawTorsoParts = false; canDrawVaginaAndAnus = false; } public void ClearGraphics(bool clearBreasts = true) { //Since Injection of base body is now in Matarial. no need to keep graphicSourceNaked = null; graphicSourceFurCovered = null; graphicSourceRotten = null; hasGraphicUpdatedBefore = false; graphicbaseBodyNaked = null; graphicbaseBodyRotten = null; graphicbaseBodyFurCovered = null; return; //Clear BodyParts if (clearBreasts && bodyPartBreasts!= null) bodyPartBreasts.Clear(); if(bodyPartAnus != null) bodyPartAnus.Clear(); if (bodyPartBelly != null) bodyPartBelly.Clear(); if (bodyPartVagina != null) bodyPartVagina.Clear(); if (bodyPartPubicHair != null) bodyPartPubicHair.Clear(); foreach(var a in bodyAddons) { a.Clear(); } } public void ClearAll(bool clearGraphics = true) { ClearBreastCacheValue(); if(clearGraphics) ClearGraphics(); ClearHediffs(); ClearCanDraw(); hasUnsupportedApparel = false; hasUpdateBefore = false; hasUpdateBeforeSuccess = false; needToCheckApparelGraphicRecords = false; } public void SetDirty(bool clearPawnGraphicSet = false, bool dirtyHediff = true, bool dirtyApparel = true, bool dirtySkeleton = false, bool dirtyBodyAddons = false) { this.isDirty = true; this.isHediffDirty = dirtyHediff; this.isApparelDirty = dirtyApparel; this.isSkeletonDirty = dirtySkeleton; this.isBodyAddonDirty = dirtyBodyAddons; if (clearPawnGraphicSet) { if (pawn == null) return; if (pawn.Drawer == null) return; if (pawn.Drawer.renderer == null) return; pawn.Drawer.renderer.graphics.ClearCache(); } } public void UpdateRaceSettingData() { if (pawn == null) return; var loc_raceSetting = SizedApparelSettings.alienRaceSettings.FirstOrDefault((AlienRaceSetting s) => s.raceName == pawn.def.defName); if (loc_raceSetting == null) return; raceSetting = loc_raceSetting; } public void CheckAgeChanged() { if (pawn == null) return; if (pawn.ageTracker == null) return; //TODO. Cleanup UpdateRaceSettingData(); if (raceSetting == null) return; if (raceSetting.drawMinAge < 0 || pawn.ageTracker.AgeBiologicalYearsFloat >= raceSetting.drawMinAge) isDrawAge = true; else isDrawAge = false; } public void FindAndApplyBodyGraphic(Pawn pawn, Graphic sourceGraphic, ref Graphic targetGraphicBaseBody, ref Graphic cachedSourceGraphic, string debugName) { const string baseBodyString = "_BaseBody"; string baseBodyStringWithSex; if (SizedApparelSettings.useGenderSpecificTexture) { if (pawn.gender == Gender.Female) { baseBodyStringWithSex = baseBodyString + "F"; } else if (pawn.gender == Gender.Male) { baseBodyStringWithSex = baseBodyString + "M"; } else { baseBodyStringWithSex = baseBodyString; // + "N" //Does it need to add N? } } else baseBodyStringWithSex = baseBodyString; string targetGraphicPath = null; if (sourceGraphic != null) { //path = path.Insert(Math.Max(path.LastIndexOf('/'), 0), "/CustomPose/"+ customPose); if (cachedSourceGraphic == null) cachedSourceGraphic = sourceGraphic; targetGraphicPath = cachedSourceGraphic.path; if (customPose != null) targetGraphicPath = targetGraphicPath.Insert(Math.Max(targetGraphicPath.LastIndexOf('/'), 0), "/CustomPose/" + customPose); if (!targetGraphicPath.Contains(baseBodyString)) { if (SizedApparelSettings.useGenderSpecificTexture && pawn.gender != Gender.None) { if (targetGraphicBaseBody == null) { if (ContentFinder.Get((targetGraphicPath + baseBodyStringWithSex + "_south"), false) != null) { //cachedSourceGraphic = sourceGraphic; Shader shader = sourceGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; targetGraphicBaseBody = GraphicDatabase.Get(targetGraphicPath + baseBodyStringWithSex, shader, sourceGraphic.drawSize, sourceGraphic.color, sourceGraphic.colorTwo, sourceGraphic.data); //sourceGraphic = targetGraphicBaseBody; } else if (customPose != null) { targetGraphicPath = sourceGraphic.path; if (ContentFinder.Get((targetGraphicPath + baseBodyStringWithSex + "_south"), false) != null) { //cachedSourceGraphic = sourceGraphic; Shader shader = sourceGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; targetGraphicBaseBody = GraphicDatabase.Get(targetGraphicPath + baseBodyStringWithSex, shader, sourceGraphic.drawSize, sourceGraphic.color, sourceGraphic.colorTwo, sourceGraphic.data); //sourceGraphic = targetGraphicBaseBody; } else { if (SizedApparelSettings.Debug) Log.Warning("[SizedApparel] Missing BaseBodyTexture for "+ debugName + " Graphic: " + targetGraphicPath + baseBodyStringWithSex + "_south"); } } } } if (targetGraphicBaseBody == null) { if (ContentFinder.Get((targetGraphicPath + baseBodyString + "_south"), false) != null) { // cachedSourceGraphic = sourceGraphic; Shader shader = sourceGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; targetGraphicBaseBody = GraphicDatabase.Get(targetGraphicPath + baseBodyString, shader, sourceGraphic.drawSize, sourceGraphic.color, sourceGraphic.colorTwo, sourceGraphic.data); //sourceGraphic = targetGraphicBaseBody; } else if (customPose != null) { targetGraphicPath = sourceGraphic.path; if (ContentFinder.Get((targetGraphicPath + baseBodyString + "_south"), false) != null) { //cachedSourceGraphic = sourceGraphic; Shader shader = sourceGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; targetGraphicBaseBody = GraphicDatabase.Get(targetGraphicPath + baseBodyString, shader, sourceGraphic.drawSize, sourceGraphic.color, sourceGraphic.colorTwo, sourceGraphic.data); //sourceGraphic = targetGraphicBaseBody; } else { if (SizedApparelSettings.Debug) Log.Warning("[SizedApparel] Missing BaseBodyTexture for " + debugName + " Graphic: " + targetGraphicPath + baseBodyString + "_south"); } } } } else targetGraphicBaseBody = sourceGraphic; } } public void Update(bool cache = true, bool fromGraphicRecord = true, bool updateGraphics = true, bool CheckApparel = true) { if (cache) recentClothFlag = CheckApparel; isDirty = false; bool flag = fromGraphicRecord; needToCheckApparelGraphicRecords = false; //flag = false; //TODO:fromGraphicRecord is forced not to do for a while. it will update later UpdateRaceSettingData(); if (!hasUpdateBefore) { InitSkeleton(); InitBodyAddons(); hasUpdateBefore = true; } else { //check is bodytype changed if(cachedBodytype != pawn.story?.bodyType?.defName) { isSkeletonDirty = true; } if (isSkeletonDirty) { InitSkeleton(); if (isBodyAddonDirty) InitBodyAddons(); else ResetBodyAddonBoneLink(); } else { if (isBodyAddonDirty) InitBodyAddons(); } } if (skeleton == null) { if (Find.CurrentMap != null) return; } if (!SizedApparelUtility.CanApplySizedApparel(pawn)) return; if (pubicHairDef == null) { pubicHairDef = SizedApparelUtility.GetRandomPubicHair(); } if (SizedApparelSettings.Debug) Log.Message("[SizedApparel] Updating Component of " + pawn.Name); if (updateGraphics) { ClearGraphics(); //ClearHediffs(); } //float breastSeverityCapped = 1000; string bodyTypeDefName = null; if (pawn.story != null) bodyTypeDefName = pawn.story.bodyType?.defName; float bellySeverity = 0; if (isHediffDirty) //Update Hediff Data { ClearHediffs(); if(SizedApparelSettings.drawPenis || SizedApparelSettings.drawVagina || SizedApparelSettings.drawAnus) { BodyPartRecord genitalPart = Genital_Helper.get_genitalsBPR(pawn); if (genitalPart != null) { var genitalList = Genital_Helper.get_PartsHediffList(pawn, genitalPart); if (!genitalList.NullOrEmpty()) { if (SizedApparelSettings.Debug) { foreach (var g in genitalList) { Log.Message(" [SizedApparel] " + pawn.Name + "has hediff in genital (" + g.def.defName + ")"); } } //penisHediffs = genitalList.FindAll((Hediff h) => SizedApparelUtility.isPenis(h.def.defName)); penisHediffs = genitalList.FindAll((Hediff h) => Genital_Helper.is_penis(h)); //vaginaHediff = genitalList.FirstOrDefault((Hediff h) => SizedApparelUtility.isVagina(h.def.defName)); vaginaHediff = genitalList.FirstOrDefault((Hediff h) => Genital_Helper.is_vagina(h)); } } } if (SizedApparelSettings.drawBelly) { //EggImplement as Pregnant //need to Optimize... TODO... List pregnancies = pawn.health?.hediffSet?.hediffs?.FindAll((Hediff h) => SizedApparelUtility.isPragnencyHediff(h) || SizedApparelUtility.isRJWEggHediff(h));//pregnancy and pregnant. has some issue with "pregnancy mood" if (!pregnancies.NullOrEmpty()) { foreach (Hediff h in pregnancies) { //Set Labor Belly as Big Belly. //Code for 1.4 /* if (h.def == HediffDefOf.PregnancyLabor || h.def == HediffDefOf.PregnancyLaborPushing) bellySeverity = Math.Max(bellySeverity, 1f); else bellySeverity = Math.Max(bellySeverity, h.Severity); */ bellySeverity = Math.Max(bellySeverity, h.Severity); } } //Licentia Lab Hediff if (SizedApparelPatch.LicentiaActive) { Hediff cumflation = pawn.health?.hediffSet?.GetFirstHediffOfDef(HediffDef.Named("Cumflation")); Hediff cumstuffed = pawn.health?.hediffSet?.GetFirstHediffOfDef(HediffDef.Named("Cumstuffed")); bellySeverity += cumflation != null ? cumflation.Severity : 0; bellySeverity += cumstuffed != null ? cumstuffed.Severity : 0; } } } CheckAgeChanged(); if (SizedApparelSettings.drawBreasts) { SizedApparelUtility.GetBreastSeverity(pawn, out breastSeverity, out breastHediff); } if (breastHediff != null) { //get nipple color from Menstruation. if (SizedApparelPatch.MenstruationActive) { nippleColor = Patch_Menstruation.GetNippleColor(breastHediff); } if (pawn.gender == Gender.Male && !SizedApparelSettings.ApplyApparelPatchForMale) { CheckApparel = false; } if (CheckApparel || this.isApparelDirty) { hasUnsupportedApparel = SizedApparelUtility.hasUnSupportedApparelFromWornData(pawn, breastSeverity, breastHediff, true, flag); } else { hasUnsupportedApparel = false; } //float validBreastTextureSeverity = SizedApparelUtility.GetBreastSeverityValidTextures(pawn, breastHediff); if (SizedApparelSettings.useBreastSizeCapForApparels) // && hasUnsupportedApparel { BreastSeverityCache = Math.Min(BreastSeverityCache, breastSeverity); } else { BreastSeverityCache = 1000; } //if (SizedApparelSettings.useBreastSizeCapForApparels) //SizedApparelSettings.useBreastSizeCapForApparels //wip // breastSeverityCapped = Math.Min(BreastSeverityCache, breastSeverityCapped); //float validBreastTextureSeverity = SizedApparelUtility.GetBreastSeverityValidTextures(pawn, breastHediff); //breast cache forApparel brests Check. This ignore variation! //SizedApparelMod.CheckAndLoadAlienRaces(); var key = new SizedApparelsDatabase.BodyPartDatabaseKey(pawn.def.defName, pawn.story?.bodyType?.defName, breastHediff.def.defName, "Breasts", pawn.gender ,SizedApparelUtility.BreastSeverityInt(breastHediff.Severity)); var raceSetting = SizedApparelSettings.alienRaceSettings.FirstOrDefault((AlienRaceSetting s) => s.raceName == key.raceName); //TODO? Remove ValidBreastsTextureCheck... float validBreastTextureSeverity = SizedApparelUtility.BreastSizeIndexToSeverity(SizedApparelsDatabase.GetSupportedBodyPartPath(key,true,"Breasts","Breasts").size); //if (validBreastTextureSeverity < -5 && SizedApparelSettings.alienRaceSettings.settings[key.raceName].asHumanlike)//old: SizedApparelSettings.UnsupportedRaceToUseHumanlike // validBreastTextureSeverity = SizedApparelUtility.GetBreastSeverityValidTextures(pawn, breastHediff, "Humanlike"); BreastSeverityCache = Math.Min(BreastSeverityCache, validBreastTextureSeverity); if (SizedApparelSettings.Debug) Log.Message("[Sized Apparel] cached Severity : " + BreastSeverityCache); } //penisHediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)).FirstOrDefault((Hediff h) => h.def.defName.ToLower().Contains("penis")); //since rjw race support's part name are too variation, not handling it. var anusList = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)); if(!anusList.NullOrEmpty()) anusHediff = anusList.FirstOrDefault((Hediff h) => h.def.defName.ToLower().Contains("anus")); //udderHediff = Genital_Helper.get_PartsHediffList(pawn, pawn.RaceProps.body.AllParts.Find((BodyPartRecord bpr) => bpr.def.defName == "Torso")).FirstOrDefault((Hediff h) => h.def.defName.ToLower().Contains("anus")); //not yet supported udderHediff = pawn.health?.hediffSet?.hediffs?.FirstOrDefault((Hediff h) => h.def.defName.ToLower().Contains("udder")); var pawnRenderer = pawn.Drawer?.renderer?.graphics; if (updateGraphics) { if (pawnRenderer != null) { //pawnRenderer.ResolveAllGraphics(); //TODO Use Function To Make Clear Code /* FindAndApplyBodyGraphic(pawn, pawnRenderer.nakedGraphic, ref graphicbaseBodyNaked, ref graphicSourceNaked,"Naked"); FindAndApplyBodyGraphic(pawn, pawnRenderer.rottingGraphic, ref graphicbaseBodyRotten, ref graphicSourceRotten, "Rotting"); FindAndApplyBodyGraphic(pawn, pawnRenderer.nakedGraphic, ref graphicbaseBodyFurCovered, ref graphicSourceFurCovered, "FurCuvered"); */ const string baseBodyString = "_BaseBody"; string baseBodyStringWithSex; if (SizedApparelSettings.useGenderSpecificTexture) { if (pawn.gender == Gender.Female) { baseBodyStringWithSex = baseBodyString + "F"; } else if (pawn.gender == Gender.Male) { baseBodyStringWithSex = baseBodyString + "M"; } else { baseBodyStringWithSex = baseBodyString; // + "N" //Does it need to add N? } } else baseBodyStringWithSex = baseBodyString; string nakedGraphicPath = null; if (pawnRenderer.nakedGraphic != null) { //path = path.Insert(Math.Max(path.LastIndexOf('/'), 0), "/CustomPose/"+ customPose); if(graphicSourceNaked == null) graphicSourceNaked = pawnRenderer.nakedGraphic; nakedGraphicPath = graphicSourceNaked.path; if (customPose != null) nakedGraphicPath = nakedGraphicPath.Insert(Math.Max(nakedGraphicPath.LastIndexOf('/'), 0), "/CustomPose/" + customPose); if (!nakedGraphicPath.Contains(baseBodyString)) { if (SizedApparelSettings.useGenderSpecificTexture & pawn.gender != Gender.None) { if (graphicbaseBodyNaked == null) { if (ContentFinder.Get((nakedGraphicPath + baseBodyStringWithSex + "_south"), false) != null) { //graphicSourceNaked = pawnRenderer.nakedGraphic; Shader shader = pawnRenderer.nakedGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyNaked = GraphicDatabase.Get(nakedGraphicPath + baseBodyStringWithSex, shader, pawnRenderer.nakedGraphic.drawSize, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo, pawnRenderer.nakedGraphic.data); //pawnRenderer.nakedGraphic = graphicbaseBodyNaked; } else if (customPose != null) { nakedGraphicPath = pawnRenderer.nakedGraphic.path; if (ContentFinder.Get((nakedGraphicPath + baseBodyStringWithSex + "_south"), false) != null) { //graphicSourceNaked = pawnRenderer.nakedGraphic; Shader shader = pawnRenderer.nakedGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyNaked = GraphicDatabase.Get(nakedGraphicPath + baseBodyStringWithSex, shader, pawnRenderer.nakedGraphic.drawSize, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo, pawnRenderer.nakedGraphic.data); //pawnRenderer.nakedGraphic = graphicbaseBodyNaked; } else { if (SizedApparelSettings.Debug) Log.Warning("[SizedApparel] Missing BaseBodyTexture for naked Graphic: " + nakedGraphicPath + baseBodyStringWithSex + "_south"); } } } } if (graphicbaseBodyNaked == null) { if (ContentFinder.Get((nakedGraphicPath + baseBodyString + "_south"), false) != null) { // graphicSourceNaked = pawnRenderer.nakedGraphic; Shader shader = pawnRenderer.nakedGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyNaked = GraphicDatabase.Get(nakedGraphicPath + baseBodyString, shader, pawnRenderer.nakedGraphic.drawSize, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo, pawnRenderer.nakedGraphic.data); //pawnRenderer.nakedGraphic = graphicbaseBodyNaked; } else if (customPose != null) { nakedGraphicPath = pawnRenderer.nakedGraphic.path; if (ContentFinder.Get((nakedGraphicPath + baseBodyString + "_south"), false) != null) { //graphicSourceNaked = pawnRenderer.nakedGraphic; Shader shader = pawnRenderer.nakedGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyNaked = GraphicDatabase.Get(nakedGraphicPath + baseBodyString, shader, pawnRenderer.nakedGraphic.drawSize, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo, pawnRenderer.nakedGraphic.data); //pawnRenderer.nakedGraphic = graphicbaseBodyNaked; } else { if (SizedApparelSettings.Debug) Log.Warning("[SizedApparel] Missing BaseBodyTexture for naked Graphic: " + nakedGraphicPath + baseBodyString + "_south"); } } } } else graphicbaseBodyNaked = pawnRenderer.nakedGraphic; } //Finding Texture Points if (false && graphicbaseBodyNaked != null) { SizedApparelTexturePointDef PointsDef = DefDatabase.AllDefs.FirstOrDefault((SizedApparelTexturePointDef s) => s.Path == graphicbaseBodyNaked.path); if (SizedApparelSettings.Debug && PointsDef != null) { Log.Message("[SizedApparel] : NakedBaseBody Texture Points Def Found : " + PointsDef.defName); } baseBodyNakedPoints = PointsDef; } else baseBodyNakedPoints = null; string rottingGraphicPath = null; if (pawnRenderer.rottingGraphic != null) { if (graphicSourceRotten == null) graphicSourceRotten = pawnRenderer.rottingGraphic; rottingGraphicPath = graphicSourceRotten.path; if (customPose != null) rottingGraphicPath = rottingGraphicPath.Insert(Math.Max(rottingGraphicPath.LastIndexOf('/'), 0), "/CustomPose/" + customPose); if (!rottingGraphicPath.Contains(baseBodyString)) { if (graphicbaseBodyRotten == null) { if (ContentFinder.Get((rottingGraphicPath + baseBodyStringWithSex + "_south"), false) != null) { //graphicSourceRotten = pawnRenderer.rottingGraphic; Shader shader = pawnRenderer.rottingGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyRotten = GraphicDatabase.Get(rottingGraphicPath + baseBodyStringWithSex, shader, pawnRenderer.rottingGraphic.drawSize, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo, pawnRenderer.rottingGraphic.data); //pawnRenderer.rottingGraphic = graphicbaseBodyRotten; } else if (customPose != null) { rottingGraphicPath = pawnRenderer.rottingGraphic.path; if (ContentFinder.Get((rottingGraphicPath + baseBodyStringWithSex + "_south"), false) != null) { graphicSourceRotten = pawnRenderer.rottingGraphic; Shader shader = pawnRenderer.rottingGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyRotten = GraphicDatabase.Get(rottingGraphicPath + baseBodyStringWithSex, shader, pawnRenderer.rottingGraphic.drawSize, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo, pawnRenderer.rottingGraphic.data); //pawnRenderer.rottingGraphic = graphicbaseBodyRotten; } else { if (SizedApparelSettings.Debug) Log.Warning("[SizedApparel] Missing BaseBodyTexture for rotting Graphic: " + rottingGraphicPath + baseBodyStringWithSex + "_south"); } } if (graphicbaseBodyRotten == null) { if (ContentFinder.Get((rottingGraphicPath + baseBodyString + "_south"), false) != null) { //graphicSourceRotten = pawnRenderer.rottingGraphic; Shader shader = pawnRenderer.rottingGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyRotten = GraphicDatabase.Get(rottingGraphicPath + baseBodyString, shader, pawnRenderer.rottingGraphic.drawSize, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo, pawnRenderer.rottingGraphic.data); //pawnRenderer.rottingGraphic = graphicbaseBodyRotten; } else if (customPose != null) { rottingGraphicPath = pawnRenderer.rottingGraphic.path; if (ContentFinder.Get((rottingGraphicPath + baseBodyString + "_south"), false) != null) { graphicSourceRotten = pawnRenderer.rottingGraphic; Shader shader = pawnRenderer.rottingGraphic.Shader; //if (!ShaderUtility.SupportsMaskTex(shader)) // shader = ShaderDatabase.CutoutSkinOverlay; graphicbaseBodyRotten = GraphicDatabase.Get(rottingGraphicPath + baseBodyString, shader, pawnRenderer.rottingGraphic.drawSize, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo, pawnRenderer.rottingGraphic.data); //pawnRenderer.rottingGraphic = graphicbaseBodyRotten; } else { if (SizedApparelSettings.Debug) Log.Warning("[SizedApparel] Missing BaseBodyTexture for rotting Graphic: " + rottingGraphicPath + baseBodyString + "_south"); } } } } } else graphicbaseBodyRotten = pawnRenderer.rottingGraphic; } //Finding Texture Points if (false && graphicbaseBodyRotten != null) { SizedApparelTexturePointDef PointsDef = DefDatabase.AllDefs.FirstOrDefault((SizedApparelTexturePointDef s) => s.Path == graphicbaseBodyRotten.path); if (SizedApparelSettings.Debug && PointsDef != null) { Log.Message("[SizedApparel] : RottenBaseBody Texture Points Def Found : " + PointsDef.defName); } baseBodyRottenPoints = PointsDef; } else baseBodyRottenPoints = null; string furCoveredGraphicPath = null; //Finding Texture Points if (false && graphicbaseBodyFurCovered != null) { SizedApparelTexturePointDef PointsDef = DefDatabase.AllDefs.FirstOrDefault((SizedApparelTexturePointDef s) => s.Path == graphicbaseBodyFurCovered.path); if (SizedApparelSettings.Debug && PointsDef != null) { Log.Message("[SizedApparel] : FurCoveredBaseBody Texture Points Def Found : " + PointsDef.defName); } baseBodyFurCoveredPoints = PointsDef; } else baseBodyFurCoveredPoints = null; /* //Try to find sized Body if it's valid int offset = 9999; int currentBreastSizeIndex = 0; float currentBreastSeverity = 0; bool validNakedTexture = false; bool validRottingTexture = false; Graphic nakedGraphic; Graphic rottingGraphic; while (offset < SizedApparelUtility.size.Length) { string breastSeverityStringCache = SizedApparelUtility.BreastSeverityString(breastSeverity, offset, true, ref currentBreastSizeIndex, ref currentBreastSeverity); //search bigger //SizedApparelSettings.matchBodyTextureToMinimumApparelSize? currentBreastSizeIndex <= minSupportedBreasSizeIndex:true if (validNakedTexture == false && nakedGraphicPath !=null) { if ((ContentFinder.Get((nakedGraphicPath + breastSeverityStringCache + "_south"), false) != null)) { if (SizedApparelSettings.matchBodyTextureToMinimumApparelSize ? SizedApparelUtility.BreastSizeIndexToSeverity(currentBreastSizeIndex) <= breastSeverityCapToDraw : true) { nakedGraphic = GraphicDatabase.Get(nakedGraphicPath + breastSeverityStringCache, pawnRenderer.nakedGraphic.Shader, pawnRenderer.nakedGraphic.drawSize, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo, pawnRenderer.nakedGraphic.data); //newAgr.Add(new ApparelGraphicRecord(graphic, agr.sourceApparel)); //validTexture = true; //Log.Message(path + BreastSeverityString(BreastSeverity, offset, !findBigger) + ":Texture Found"); pawnRenderer.nakedGraphic = nakedGraphic; validNakedTexture = true; } } } if (validRottingTexture == false && rottingGraphicPath != null) { if ((ContentFinder.Get((rottingGraphicPath + breastSeverityStringCache + "_south"), false) != null)) { if (SizedApparelSettings.matchBodyTextureToMinimumApparelSize ? SizedApparelUtility.BreastSizeIndexToSeverity(currentBreastSizeIndex) <= breastSeverityCapToDraw : true) { rottingGraphic = GraphicDatabase.Get(rottingGraphicPath + breastSeverityStringCache, pawnRenderer.rottingGraphic.Shader, pawnRenderer.rottingGraphic.drawSize, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo, pawnRenderer.rottingGraphic.data); //newAgr.Add(new ApparelGraphicRecord(graphic, agr.sourceApparel)); //validTexture = true; //Log.Message(path + BreastSeverityString(BreastSeverity, offset, !findBigger) + ":Texture Found"); pawnRenderer.rottingGraphic = rottingGraphic; validRottingTexture = true; } } } //search smaller breastSeverityStringCache = SizedApparelUtility.BreastSeverityString(breastSeverity, offset, false, ref currentBreastSizeIndex, ref currentBreastSeverity); if (validNakedTexture == false) { if ((ContentFinder.Get((nakedGraphicPath + breastSeverityStringCache + "_south"), false) != null)) { if (SizedApparelSettings.matchBodyTextureToMinimumApparelSize ? SizedApparelUtility.BreastSizeIndexToSeverity(currentBreastSizeIndex) <= breastSeverityCapToDraw : true) { nakedGraphic = GraphicDatabase.Get(nakedGraphicPath + breastSeverityStringCache, pawnRenderer.nakedGraphic.Shader, pawnRenderer.nakedGraphic.drawSize, pawnRenderer.nakedGraphic.color, pawnRenderer.nakedGraphic.colorTwo, pawnRenderer.nakedGraphic.data); //newAgr.Add(new ApparelGraphicRecord(graphic, agr.sourceApparel)); //validTexture = true; //Log.Message(path + BreastSeverityString(BreastSeverity, offset, !findBigger) + ":Texture Found"); pawnRenderer.nakedGraphic = nakedGraphic; validNakedTexture = true; } } } if (validRottingTexture == false) { if ((ContentFinder.Get((rottingGraphicPath + breastSeverityStringCache + "_south"), false) != null)) { if (SizedApparelSettings.matchBodyTextureToMinimumApparelSize ? SizedApparelUtility.BreastSizeIndexToSeverity(currentBreastSizeIndex) <= breastSeverityCapToDraw : true) { rottingGraphic = GraphicDatabase.Get(rottingGraphicPath + breastSeverityStringCache, pawnRenderer.rottingGraphic.Shader, pawnRenderer.rottingGraphic.drawSize, pawnRenderer.rottingGraphic.color, pawnRenderer.rottingGraphic.colorTwo, pawnRenderer.rottingGraphic.data); //newAgr.Add(new ApparelGraphicRecord(graphic, agr.sourceApparel)); //validTexture = true; //Log.Message(path + BreastSeverityString(BreastSeverity, offset, !findBigger) + ":Texture Found"); pawnRenderer.rottingGraphic = rottingGraphic; validRottingTexture = true; } } } if (validNakedTexture == true && validRottingTexture == true) { if (SizedApparelSettings.Debug) Log.Message("[Sized Apparel] " + pawn.Name + "'s body texture has changed."); break; } offset++; } */ } //graphicBreasts = SizedApparelUtility.GetBodyPartGraphic(pawn, breastHediff, true, "Breasts", "Breasts"); //if(graphicBreasts == null && pawn.RaceProps.Humanlike && SizedApparelSettings.UnsupportedRaceToUseHumanlike) // graphicBreasts = SizedApparelUtility.GetBodyPartGraphic(pawn, breastHediff, true, "Breasts", "Breasts", false, true, "Humanlike"); if (SizedApparelSettings.drawBodyParts)//body parts update { if (SizedApparelSettings.drawBreasts) { if (breastHediff != null) { var breastvar = breastHediff.TryGetComp(); foreach(var addon in bodyAddons) { if(addon.bodyPartOf == SizedApparelBodyPartOf.Breasts) { addon.SetHediffData(breastHediff.def.defName, SizedApparelUtility.BreastSeverityInt(breastHediff.Severity), SizedApparelUtility.BreastSeverityInt(BreastSeverityCache), breastvar?.variation); addon.SetBone(skeleton?.FindBone("Breasts")); addon.UpdateGraphic(); } } //bodyPartBreasts.SetHediffData(breastHediff.def.defName, SizedApparelUtility.BreastSeverityInt(breastHediff.Severity), SizedApparelUtility.BreastSeverityInt(breastSeverityCapped), breastvar?.variation); //bodyPartBreasts.UpdateGraphic(); } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Breasts) { addon.SetHediffData(null, -1); } } } } if (SizedApparelSettings.drawUdder) { if (udderHediff != null) { var udderVar = udderHediff.TryGetComp(); foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Udder) { addon.SetHediffData(udderHediff.def.defName, SizedApparelUtility.BreastSeverityInt(udderHediff.Severity), 1000, udderVar?.variation); addon.SetBone(skeleton?.FindBone("Udder")); addon.UpdateGraphic(); } } //bodyPartUdder.SetHediffData(udderHediff.def.defName, SizedApparelUtility.BreastSeverityInt(udderHediff.Severity), 1000, udderVar?.variation); //bodyPartUdder.UpdateGraphic(); } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Udder) { addon.SetHediffData(null, -1); } } } } if (SizedApparelSettings.drawBelly) { if (bellySeverity >= 0) { //var bellyVar = breastHediff.GetComp(); string BellyVar = null; foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Belly) { addon.SetHediffData("BellyBulge", SizedApparelUtility.PrivatePartSeverityInt(bellySeverity), 1000, BellyVar); addon.SetBone(skeleton?.FindBone("Belly")); addon.UpdateGraphic(); } } //bodyPartBelly.SetHediffData("BellyBulge", SizedApparelUtility.PrivatePartSeverityInt(bellySeverity), 1000, BellyVar); //bodyPartBelly.UpdateGraphic(); } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Belly) { addon.SetHediffData(null, -1); } } } } if (SizedApparelSettings.drawVagina) { if (vaginaHediff != null) { var vaginaVar = vaginaHediff.TryGetComp(); foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Vagina) { addon.SetHediffData(vaginaHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(vaginaHediff.Severity), 1000, vaginaVar?.variation); addon.SetBone(skeleton?.FindBone("Vagina")); addon.UpdateGraphic(); } } //bodyPartVagina.SetHediffData(vaginaHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(vaginaHediff.Severity), 1000, vaginaVar?.variation); //bodyPartVagina.UpdateGraphic(); } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Vagina) { addon.SetHediffData(null, -1); } } } } if (SizedApparelSettings.drawPubicHair) { if (pubicHairDef != null && pubicHairDef.defName != "None") // pubicHairDef != null // for testing { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.PubicHair) { addon.SetHediffData(pubicHairDef.defName, 0, 1000, null); addon.SetBone(skeleton?.FindBone("PubicHair")); addon.UpdateGraphic(); } } //bodyPartPubicHair.SetHediffData(pubicHairDef.defName, 0, 1000, null); //bodyPartPubicHair.UpdateGraphic(); } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.PubicHair) { addon.SetHediffData(null, -1); } } } } if (SizedApparelSettings.drawPenis) { //TODO: Optimize.... memory leak issue? //bodyPartPenises.Clear(); //bodyPartBalls.Clear(); if (!penisHediffs.NullOrEmpty()) { var penisHediff = penisHediffs[0]; if (penisHediff != null) { var penisVar = penisHediff.TryGetComp(); foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Penis) { addon.SetHediffData(penisHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(penisHediff.Severity), 1000, penisVar?.variation); addon.SetBone(skeleton?.FindBone("Penis")); addon.UpdateGraphic(); } if (addon.bodyPartOf == SizedApparelBodyPartOf.Balls) { addon.SetHediffData(penisHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(penisHediff.Severity), 1000, penisVar?.variation); addon.SetBone(skeleton?.FindBone("Balls")); addon.UpdateGraphic(); } } } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Penis) { addon.SetHediffData(null, -1); } if (addon.bodyPartOf == SizedApparelBodyPartOf.Balls) { addon.SetHediffData(null, -1); } } } // Multiple Penis Drawing. obsolte /* for (int i = 0; i < penisHediffs.Count; i++) { penisHediff = penisHediffs[i]; float offsetX = 0; // right and left float offsetZ = 0; // up and down float t = (i + 1f) / (penisHediffs.Count + 1); offsetX = Mathf.Lerp(-0.05f, 0.05f, t); offsetZ = Mathf.Lerp(-0.02f, 0.02f, t); if (penisHediff == null) continue; Bone penisBone = null; Bone ballsBone = null; if(skeleton != null) { penisBone = skeleton.FindBone("Penis"); ballsBone = skeleton.FindBone("Balls"); if (ballsBone == null) ballsBone = penisBone; } var penisVar = penisHediff.TryGetComp(); SizedApparelBodyPart penis = new SizedApparelBodyPart(pawn, this, "Penis", SizedApparelBodyPartOf.Penis, "Penis", false, false, null,ColorType.Skin, penisBone); penis.SetDepthOffsets(0.0108f, 0.0025f, 0.0108f, 0.0108f); penis.SetPositionOffsets(new Vector2(offsetX, 0), new Vector2(-offsetX, 0), new Vector2(offsetX, offsetZ), new Vector2(offsetX, -offsetZ)); penis.SetHediffData(penisHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(penisHediff.Severity), 1000, penisVar?.variation); penis.centeredTexture = true; // Test Bone penis.UpdateGraphic(); bodyPartPenises.Add(penis); SizedApparelBodyPart balls = new SizedApparelBodyPart(pawn, this, "Balls", SizedApparelBodyPartOf.Balls, "Penis", false, false, "Penis/Balls", ColorType.Skin, ballsBone); balls.SetDepthOffsets(0.0096f, 0.0085f, 0.0096f, 0.0096f); balls.SetPositionOffsets(new Vector2(offsetX, 0), new Vector2(-offsetX, 0), new Vector2(offsetX, offsetZ), new Vector2(offsetX, -offsetZ)); balls.SetHediffData(penisHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(penisHediff.Severity), 1000, penisVar?.variation); balls.centeredTexture = true; // Test Bone balls.UpdateGraphic(); bodyPartBalls.Add(balls); */ } } else { bodyPartPenises.Clear(); bodyPartBalls.Clear(); } if (SizedApparelSettings.drawAnus) { /* graphicAnus = SizedApparelUtility.GetBodyPartGraphic(pawn, anusHediff, false, "Anus", "Anus"); if (graphicAnus == null && pawn.RaceProps.Humanlike && SizedApparelSettings.UnsupportedRaceToUseHumanlike) graphicAnus = SizedApparelUtility.GetBodyPartGraphic(pawn, anusHediff, false, "Anus", "Anus", false, true, "Humanlike"); graphicAnus_horny = SizedApparelUtility.GetBodyPartGraphic(pawn, anusHediff, false, "Anus", "Anus", true); if (graphicAnus_horny == null && pawn.RaceProps.Humanlike && SizedApparelSettings.UnsupportedRaceToUseHumanlike) graphicAnus_horny = SizedApparelUtility.GetBodyPartGraphic(pawn, anusHediff, false, "Anus", "Anus", true, true, "Humanlike"); */ if (anusHediff != null) { var anusVar = anusHediff.TryGetComp(); foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Anus) { addon.SetHediffData(anusHediff.def.defName, SizedApparelUtility.PrivatePartSeverityInt(anusHediff.Severity), 1000, anusVar?.variation); addon.SetBone(skeleton?.FindBone("Anus")); addon.UpdateGraphic(); } } } else { foreach (var addon in bodyAddons) { if (addon.bodyPartOf == SizedApparelBodyPartOf.Anus) { addon.SetHediffData(null, -1); } } } } } hasGraphicUpdatedBefore = true; } if(CheckApparel) cachedApparelGraphicRecord = pawnRenderer.apparelGraphics.ToList(); else cachedApparelGraphicRecord = new List(); hasUpdateBeforeSuccess = true; this.isHediffDirty = false; this.isApparelDirty = false; this.isSkeletonDirty = false; } //public bool hasUnsupportedApparel => Props.hasUnsupportedApparel; //public bool hasUpdateBefore => Props.hasUpdateBefore; /* public void SetHasUnsupportedApparel(bool _hasUnsupportedApparel) { if(Props !=null) Props.hasUnsupportedApparel = _hasUnsupportedApparel; this.hasUnsupportedApparel = _hasUnsupportedApparel; } public void SetHasUpdateBefore(bool _hasUpdateBefore) { if (Props != null) Props.hasUpdateBefore = _hasUpdateBefore; this.hasUpdateBefore = _hasUpdateBefore; }*/ public IEnumerable GetAllSizedApparelBodyPart() // can return null bodyparts { yield return bodyPartBreasts; yield return bodyPartNipple; /* foreach (SizedApparelBodyPart bp in bodyPartBreasts) { yield return bp; } foreach (SizedApparelBodyPart bp in bodyPartNipple) { yield return bp; }*/ foreach (SizedApparelBodyPart bp in bodyPartPenises) { yield return bp; } foreach (SizedApparelBodyPart bp in bodyPartBalls) { yield return bp; } yield return bodyPartVagina; yield return bodyPartAnus; yield return bodyPartBelly; yield return bodyPartMuscleOverlay;//TODO yield return bodyPartUdder; yield return bodyPartPubicHair; //TODO yield return bodyPartHips; foreach (SizedApparelBodyPart bp in bodyPartThighs) { yield return bp; } foreach (SizedApparelBodyPart bp in bodyPartHands) { yield return bp; } foreach (SizedApparelBodyPart bp in bodyPartFeet) { yield return bp; } foreach (SizedApparelBodyPart bp in bodyAddons) { yield return bp; } } public IEnumerable GetSizedApparelBodyParts(SizedApparelBodyPartOf targetPartOf, bool returnNullPart = false) { foreach(SizedApparelBodyPart bp in GetAllSizedApparelBodyPart()) { if (bp == null) { if (returnNullPart) yield return bp; else continue; } if (bp.bodyPartOf.IsPartOf(targetPartOf)) yield return bp; } } public void SetPoseFromPoseSet(string poseSetName, bool autoUpdate = true, bool autoSetPawnGraphicDirty = false) { if (poseSetName.NullOrEmpty()) { ClearAllPose(autoUpdate, autoSetPawnGraphicDirty); return; } var poseSetDef = DefDatabase.GetNamed(poseSetName,false); if (poseSetDef == null) return; if (poseSetDef.poses.NullOrEmpty()) return; foreach (SizedApparelPose pose in poseSetDef.poses) { var bodyParts = GetSizedApparelBodyParts(pose.targetBodyPart); //if (bodyParts == null) // continue; if (bodyParts.EnumerableNullOrEmpty()) continue; foreach (SizedApparelBodyPart bp in bodyParts.ToList()) { if(bp != null) bp.SetCustomPose(poseSetName, autoUpdate, autoSetPawnGraphicDirty); } } } public void ClearAllPose(bool autoUpdate = true, bool autoSetPawnGraphicDirty = false) { foreach (SizedApparelBodyPart bp in GetAllSizedApparelBodyPart()) { if(bp != null) bp.SetCustomPose(null, autoUpdate, autoSetPawnGraphicDirty); } } public void ClearPose(SizedApparelBodyPartOf targetPartOf , bool autoUpdate = true, bool autoSetPawnGraphicDirty = false) { foreach (SizedApparelBodyPart bp in GetSizedApparelBodyParts(targetPartOf)) { if(bp != null) bp.SetCustomPose(null, autoUpdate, autoSetPawnGraphicDirty); } } } [StaticConstructorOnStartup] public class ApparelRecorderCompProperties : CompProperties { public bool hasUnsupportedApparel = false; public bool hasUpdateBefore = false; public ApparelRecorderCompProperties() { this.compClass = typeof(ApparelRecorderComp); } public ApparelRecorderCompProperties(Type compClass) : base(compClass) { this.compClass = compClass; } } }