2023-04-04 01:47:48 +00:00
|
|
|
|
using HarmonyLib;
|
|
|
|
|
using RimWorld;
|
|
|
|
|
using System;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
using System.Reflection;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
//using AlienRace;
|
|
|
|
|
using UnityEngine;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
using Verse;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
|
|
|
|
|
namespace SizedApparel
|
|
|
|
|
{
|
|
|
|
|
public struct Depth4Offsets
|
|
|
|
|
{
|
|
|
|
|
float South;
|
|
|
|
|
float North;
|
|
|
|
|
float East;
|
|
|
|
|
float West;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct Rot4Offsets
|
|
|
|
|
{
|
|
|
|
|
//X: right and left
|
|
|
|
|
//Y: Frong or Back
|
|
|
|
|
//Z: Up and Down
|
|
|
|
|
Vector3 South;
|
|
|
|
|
Vector3 North;
|
|
|
|
|
Vector3 East;
|
|
|
|
|
Vector3 West;
|
|
|
|
|
|
|
|
|
|
public Rot4Offsets(Vector3 vector)
|
|
|
|
|
{
|
|
|
|
|
South = vector;
|
|
|
|
|
North = vector;
|
|
|
|
|
East = vector;
|
|
|
|
|
West = vector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Rot4Offsets(Vector3 south, Vector3 north, Vector3 east, Vector3 west)
|
|
|
|
|
{
|
|
|
|
|
South = south;
|
|
|
|
|
North = north;
|
|
|
|
|
East = east;
|
|
|
|
|
West = west;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Vector3 GetOffset(Rot4 rotation)
|
|
|
|
|
{
|
|
|
|
|
if (rotation == Rot4.East)
|
|
|
|
|
return East;
|
|
|
|
|
if (rotation == Rot4.West)
|
|
|
|
|
return West;
|
|
|
|
|
if (rotation == Rot4.South)
|
|
|
|
|
return South;
|
|
|
|
|
if (rotation == Rot4.North)
|
|
|
|
|
return North;
|
|
|
|
|
else
|
|
|
|
|
return Vector3.zero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct RaceNameAndBodyType
|
|
|
|
|
{
|
|
|
|
|
public string raceName;
|
|
|
|
|
public string bodyType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class BodyPart
|
|
|
|
|
{
|
|
|
|
|
public string partName;
|
|
|
|
|
public Depth4Offsets depthOffset;
|
|
|
|
|
public List<BodyTypeAndOffset> offsets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class BodyTypeAndOffset
|
|
|
|
|
{
|
|
|
|
|
//public RaceNameAndBodyType bodyTypeData;
|
|
|
|
|
public string bodyType;
|
|
|
|
|
public Rot4Offsets offsets = new Rot4Offsets(Vector3.zero);
|
|
|
|
|
|
|
|
|
|
public BodyTypeAndOffset(bool useCenter)
|
|
|
|
|
{
|
|
|
|
|
if (useCenter)
|
|
|
|
|
{
|
|
|
|
|
offsets = new Rot4Offsets(new Vector3(0.5f, 0, 0.5f));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public BodyTypeAndOffset(Vector3 defaultOffset)
|
|
|
|
|
{
|
|
|
|
|
offsets = new Rot4Offsets(defaultOffset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum SizedApparelBodyPartColorOf
|
|
|
|
|
{
|
|
|
|
|
Skin, Hair, Custom, None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public enum SizedApparelBodyPartOf
|
|
|
|
|
{
|
|
|
|
|
All, Torso, Breasts, Crotch, Penis, Vagina, Anus, Belly, Udder, Hips, Thighs, hands, feet, None
|
|
|
|
|
}
|
|
|
|
|
public static class SizedApparelBodyPartOfExtension
|
|
|
|
|
{
|
|
|
|
|
public static bool IsPartOf(this SizedApparelBodyPartOf source, SizedApparelBodyPartOf target)
|
|
|
|
|
{
|
|
|
|
|
if (source == SizedApparelBodyPartOf.None)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
switch (target)
|
|
|
|
|
{
|
|
|
|
|
case SizedApparelBodyPartOf.All:
|
|
|
|
|
return true;
|
|
|
|
|
case SizedApparelBodyPartOf.Torso:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.hands || source == SizedApparelBodyPartOf.feet)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
case SizedApparelBodyPartOf.Breasts:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Breasts)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Crotch:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Penis || source == SizedApparelBodyPartOf.Vagina || source == SizedApparelBodyPartOf.Anus)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Penis:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Penis)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Vagina:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Vagina)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Anus:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Anus)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Belly:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Belly)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Udder:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Udder)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Hips:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Hips || source == SizedApparelBodyPartOf.Thighs || source == SizedApparelBodyPartOf.Penis || source == SizedApparelBodyPartOf.Vagina || source == SizedApparelBodyPartOf.Anus)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.Thighs:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.Thighs)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.hands:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.hands)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.feet:
|
|
|
|
|
if (source == SizedApparelBodyPartOf.feet)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
case SizedApparelBodyPartOf.None:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Log.Error("[SizedApparel] missing SizedApparelBodyPartOf!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class GraphicPointsDef : Def
|
|
|
|
|
{
|
|
|
|
|
public List<TextureWithGraphicPoints> points;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TextureWithGraphicPoints
|
|
|
|
|
{
|
|
|
|
|
public string texturePath; // texture is already classified with bodytype
|
|
|
|
|
public List<GraphicPoint> points = new List<GraphicPoint>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class GraphicPoint
|
|
|
|
|
{
|
|
|
|
|
public string pointName;
|
|
|
|
|
public Vector2 point = new Vector2(0.5f, 0.5f);
|
|
|
|
|
}
|
|
|
|
|
public class GraphicPointsWithBodyType
|
|
|
|
|
{
|
|
|
|
|
public string pointName;
|
|
|
|
|
public List<PointWithBodyType> points = new List<PointWithBodyType>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class PointWithBodyType
|
|
|
|
|
{
|
|
|
|
|
public string bodyTypeName; //null can be used too
|
|
|
|
|
public Vector2 point = new Vector2(0.5f,0.5f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct BodyPartPoint
|
|
|
|
|
{
|
|
|
|
|
string name;
|
|
|
|
|
Vector2 position;//Uv position. not pixel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Obsolete]//todo
|
|
|
|
|
public struct BodyPartSpline
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
public enum ColorMode
|
|
|
|
|
{
|
|
|
|
|
Skin, Hair
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Def for Hediff Graphic color options or else.
|
|
|
|
|
public class SizedApparelHeddifDef : Def
|
|
|
|
|
{
|
|
|
|
|
public HediffDef hediffDef;
|
|
|
|
|
//public string hediffDefName;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
//Def per graphic(texture)
|
|
|
|
|
public class SizedApparelBodyPartGraphicDef : Def
|
|
|
|
|
{
|
|
|
|
|
public string graphicPath;
|
|
|
|
|
public int severityIndex;
|
|
|
|
|
public Vector2 pivot = new Vector2(0.5f, 0.5f); // custom pivot of texture. UV. not pixel
|
|
|
|
|
//public Dictionary<string, BodyPartPoint> points = new Dictionary<string, BodyPartPoint>();
|
|
|
|
|
//public Dictionary<string, BodyPartSpline> splines = new Dictionary<string, BodyPartSpline>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Def per BodyParts
|
|
|
|
|
public class SizedApparelBodyPartDef : Def
|
|
|
|
|
{
|
|
|
|
|
SizedApparelBodyPartOf bodyPartOf = SizedApparelBodyPartOf.None;
|
|
|
|
|
public bool canPose = false;
|
|
|
|
|
public bool canAnimate = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class SizedApparelBodyPart
|
|
|
|
|
{
|
|
|
|
|
static MethodInfo overrideMatMethod = AccessTools.Method(typeof(PawnRenderer), "OverrideMaterialIfNeeded");
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
public bool AutoOffsetForFurCoveredBody = true;
|
|
|
|
|
|
|
|
|
|
public SizedApparelBodyPart(Pawn pawn, ApparelRecorderComp apparelRecorderComp, string bodyPartName, SizedApparelBodyPartOf bodyPartOf, string defaultHediffName, bool isBreast, bool isOverlay , string customPathName = null, SizedApparelBodyPartColorOf colorOf = SizedApparelBodyPartColorOf.Skin)
|
2022-08-14 20:46:19 +00:00
|
|
|
|
{
|
|
|
|
|
this.pawn = pawn; //owner
|
2023-04-04 01:47:48 +00:00
|
|
|
|
|
|
|
|
|
this.apparelRecorderCompCache = apparelRecorderComp; //for reduce GetComp Call; if it is null, it will try to get pawn's comp.
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
this.bodyPartName = bodyPartName;
|
|
|
|
|
|
|
|
|
|
this.def = DefDatabase<SizedApparelBodyPartDef>.AllDefs.FirstOrDefault(b => b.defName == bodyPartName);
|
|
|
|
|
|
|
|
|
|
this.bodyPartOf = bodyPartOf;
|
|
|
|
|
this.defaultHediffName = defaultHediffName;
|
|
|
|
|
this.isBreast = isBreast;
|
|
|
|
|
this.isOverlay = isOverlay;
|
|
|
|
|
this.customPath = customPathName;
|
|
|
|
|
this.colorType = colorOf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Vector2 OffsetFromUVOffset(Vector2 vector, Mesh mesh , bool isFliped = false)
|
|
|
|
|
{
|
|
|
|
|
//treat mesh as plane
|
|
|
|
|
//Vector3 width = mesh.vertices[2] - mesh.vertices[1];
|
|
|
|
|
//Vector3 height = mesh.vertices[1] - mesh.vertices[2];
|
|
|
|
|
Vector2 loc = new Vector2(0.5f, 0.5f) - vector;
|
|
|
|
|
if(!isFliped)
|
|
|
|
|
return new Vector2(Mathf.Lerp(mesh.vertices[0].x, mesh.vertices[2].x, loc.x), Mathf.Lerp(mesh.vertices[0].z, mesh.vertices[2].z, loc.y));
|
|
|
|
|
return new Vector2(Mathf.Lerp(mesh.vertices[3].x, mesh.vertices[1].x, loc.x), Mathf.Lerp(mesh.vertices[3].z, mesh.vertices[1].z, loc.y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//public Vector2 OffestFromUVOffset(Vector2 vector, Vector2 drawSize, bool isFliped = false)
|
|
|
|
|
|
|
|
|
|
public SizedApparelBodyPartDef def;
|
|
|
|
|
|
|
|
|
|
public Pawn pawn;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
public ApparelRecorderComp apparelRecorderCompCache; // for reduce getComp call;
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
public string bodyPartName; //breast, penis, belly, pubichair... etc. just name. not like architech something
|
|
|
|
|
public string customPath = null;
|
|
|
|
|
public SizedApparelBodyPartOf bodyPartOf = SizedApparelBodyPartOf.None;
|
|
|
|
|
public string defaultHediffName;
|
|
|
|
|
|
|
|
|
|
public bool isBreast = false;
|
|
|
|
|
public bool isOverlay = false; //write z cache?
|
|
|
|
|
|
|
|
|
|
public string currentHediffName;
|
|
|
|
|
|
|
|
|
|
public bool isVisible = true;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
public int lastPoseTick = -1;
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
public SizedApparelBodyPartColorOf colorType = SizedApparelBodyPartColorOf.Skin;
|
|
|
|
|
public Color? customColorOne;
|
|
|
|
|
public Color? customColorTwo;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//customize
|
|
|
|
|
public string customPose = null;
|
|
|
|
|
public Vector2? lookAnLocation = null;
|
|
|
|
|
public Rot4? rotOverride = null;
|
|
|
|
|
|
|
|
|
|
//variation
|
|
|
|
|
public string variation = null;
|
|
|
|
|
public Color? variationColor;
|
|
|
|
|
public colorOverrideMode variationColorMode = colorOverrideMode.Default;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO. age setting?
|
|
|
|
|
public int minDrawAge = -1;
|
|
|
|
|
public int maxDrawAge = -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void SetCustomPose(string newPose, bool autoUpdate = true, bool autoSetPawnGraphicDirty = false)
|
|
|
|
|
{
|
|
|
|
|
if (customPose == newPose)
|
|
|
|
|
return;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
if(SizedApparelSettings.Debug)
|
|
|
|
|
Log.Message("[SizedApparel] Setting Custom Pose : " + newPose);
|
2022-08-14 20:46:19 +00:00
|
|
|
|
customPose = newPose;
|
|
|
|
|
if (autoUpdate)
|
2023-04-04 01:47:48 +00:00
|
|
|
|
{
|
2022-08-14 20:46:19 +00:00
|
|
|
|
this.UpdateGraphic();
|
2023-04-04 01:47:48 +00:00
|
|
|
|
this.lastPoseTick = Find.TickManager.TicksGame;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
if(autoSetPawnGraphicDirty)
|
|
|
|
|
{
|
|
|
|
|
if (pawn == null)
|
|
|
|
|
return;
|
|
|
|
|
PortraitsCache.SetDirty(pawn);
|
|
|
|
|
GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(pawn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
public bool CheckCanPose(string targetPose, bool checkApparels, bool checkBodyParts, bool mustMatchSize)
|
2022-08-14 20:46:19 +00:00
|
|
|
|
{
|
|
|
|
|
if (checkApparels)
|
|
|
|
|
{
|
|
|
|
|
foreach(ApparelGraphicRecord agr in pawn.Drawer.renderer.graphics.apparelGraphics)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
if (!agr.sourceApparel.def.apparel.bodyPartGroups.Any(bpgd => bpgd.defName == "Torso" || bpgd.defName == "Chest"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (agr.sourceApparel.def.apparel.tags.Any(s => s.ToLower() == "SizedApparel_IgnorePose".ToLower()))
|
|
|
|
|
continue;
|
|
|
|
|
*/
|
2023-04-04 01:47:48 +00:00
|
|
|
|
//Only Check Torso Apparel Only
|
|
|
|
|
if (!agr.sourceApparel.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso))
|
|
|
|
|
continue;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
|
|
|
|
|
string originalPath = SizedApparelsDatabase.GetSupportedApparelOriginalPath(agr.graphic.path);
|
|
|
|
|
if (originalPath == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
int outInt = -1;
|
|
|
|
|
float outFloat = -1;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
SizedApparelsDatabase.SizedApparelDatabaseKey key = new SizedApparelsDatabase.SizedApparelDatabaseKey(originalPath,pawn.def.defName,pawn.story?.bodyType?.defName, pawn.gender, currentHediffName, Math.Min(currentSeverityInt, cappedSeverityInt), false, targetPose);
|
|
|
|
|
if (SizedApparelSettings.useGenderSpecificTexture)
|
|
|
|
|
key.gender = Gender.None;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
var result = SizedApparelsDatabase.GetSupportedApparelSizedPath(key, out outInt, out outFloat);
|
|
|
|
|
if (!result.isCustomPose)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-04 01:47:48 +00:00
|
|
|
|
if (checkBodyParts)
|
|
|
|
|
{
|
|
|
|
|
Graphic graphic = GetBodyPartGraphics(false, mustMatchSize, targetPose);
|
|
|
|
|
Graphic graphicH = GetBodyPartGraphics(true, mustMatchSize, targetPose);
|
|
|
|
|
if (graphic != null || graphicH != null)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO...
|
|
|
|
|
public int currentSeverityInt = -1;
|
|
|
|
|
public int cappedSeverityInt = 1000; // supported severity from worn apparel graphics
|
|
|
|
|
|
|
|
|
|
public Vector2 pivot = new Vector2(0.5f, 0.5f);
|
|
|
|
|
|
|
|
|
|
public Vector2 position = Vector2.zero;//offset from pivot //UV. not pixel
|
|
|
|
|
|
|
|
|
|
public Dictionary<string, BodyPartPoint> points;
|
|
|
|
|
public Dictionary<string, BodyPartPoint> pointsHorny;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public float rotation = 0; // +: rotate right, -: rotate left
|
|
|
|
|
public Vector2 scale = Vector2.one;
|
|
|
|
|
|
|
|
|
|
public Graphic bodyPartGraphic;
|
|
|
|
|
public Graphic bodyPartGraphicHorny;
|
|
|
|
|
|
|
|
|
|
public Vector2 positionOffset = Vector2.zero; //offset from position //UV. not pixel
|
|
|
|
|
public Vector2 positionOffsetSouth = Vector2.zero;
|
|
|
|
|
public Vector2 positionOffsetNorth = Vector2.zero;
|
|
|
|
|
public Vector2 positionOffsetEast = Vector2.zero;
|
|
|
|
|
public Vector2 positionOffsetWest = Vector2.zero;
|
|
|
|
|
|
|
|
|
|
public float depthOffset = 0f;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
//0.008f
|
2022-08-14 20:46:19 +00:00
|
|
|
|
public float depthOffsetEast = 0.008f;
|
|
|
|
|
public float depthOffsetWest = 0.008f;
|
|
|
|
|
public float depthOffsetSouth = 0.008f;
|
|
|
|
|
public float depthOffsetNorth = 0.008f;
|
|
|
|
|
|
|
|
|
|
//bigger = in front
|
|
|
|
|
public void SetDepthOffsets(float south, float north, float east, float west)
|
|
|
|
|
{
|
|
|
|
|
depthOffsetSouth = south;
|
|
|
|
|
depthOffsetNorth = north;
|
|
|
|
|
depthOffsetEast = east;
|
|
|
|
|
depthOffsetWest = west;
|
|
|
|
|
}
|
|
|
|
|
public void SetPositionOffsets(Vector2 south, Vector2 north, Vector2 east, Vector2 west)
|
|
|
|
|
{
|
|
|
|
|
positionOffsetSouth = south;
|
|
|
|
|
positionOffsetNorth = north;
|
|
|
|
|
positionOffsetEast = east;
|
|
|
|
|
positionOffsetWest = west;
|
|
|
|
|
}
|
|
|
|
|
public Graphic GetBodyPartGraphics(bool isHorny, bool mustMatchSize = false, string poseOverride = null)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<string, BodyPartPoint> var;
|
|
|
|
|
return GetBodyPartGraphics(isHorny, out var, mustMatchSize, poseOverride);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Graphic GetBodyPartGraphics(bool isHorny, out Dictionary<string, BodyPartPoint> outPoints, bool mustMatchSize = false ,string poseOverride = null, string variationOverride = null)
|
|
|
|
|
{
|
|
|
|
|
if (pawn == null)
|
|
|
|
|
{
|
|
|
|
|
outPoints = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2023-04-04 01:47:48 +00:00
|
|
|
|
var comp = apparelRecorderCompCache;
|
|
|
|
|
if (comp == null)
|
|
|
|
|
comp = pawn.GetComp<ApparelRecorderComp>();
|
2022-08-14 20:46:19 +00:00
|
|
|
|
if (comp == null)
|
|
|
|
|
{
|
|
|
|
|
outPoints = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
var key = new SizedApparelsDatabase.BodyPartDatabaseKey(pawn.def.defName, pawn.story?.bodyType?.defName, currentHediffName, customPath==null?bodyPartName: customPath, pawn.gender, Math.Min(currentSeverityInt, cappedSeverityInt), isHorny, poseOverride==null?customPose:poseOverride, variationOverride==null?variation: variationOverride);
|
2022-08-14 20:46:19 +00:00
|
|
|
|
var result = SizedApparelsDatabase.GetSupportedBodyPartPath(key, isBreast, customPath == null ? bodyPartName : customPath, defaultHediffName);
|
|
|
|
|
|
|
|
|
|
if (mustMatchSize)
|
|
|
|
|
if (Math.Min(currentSeverityInt, cappedSeverityInt) != result.size)
|
|
|
|
|
{
|
|
|
|
|
outPoints = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.pathWithSizeIndex == null)
|
|
|
|
|
{
|
|
|
|
|
outPoints = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
outPoints = result.points;
|
|
|
|
|
return GraphicDatabase.Get<Graphic_Multi>(result.pathWithSizeIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateGraphic()
|
|
|
|
|
{
|
|
|
|
|
bodyPartGraphic = GetBodyPartGraphics(false, out points, false);
|
|
|
|
|
bodyPartGraphicHorny = GetBodyPartGraphics(true, out pointsHorny, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateGraphic(int index, int indexCapped = 1000)
|
|
|
|
|
{
|
|
|
|
|
this.currentSeverityInt = index;
|
|
|
|
|
this.cappedSeverityInt = indexCapped;
|
|
|
|
|
|
|
|
|
|
UpdateGraphic();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ResetTransform()
|
|
|
|
|
{
|
|
|
|
|
this.position = Vector2.zero;
|
|
|
|
|
this.scale = Vector2.one;
|
|
|
|
|
this.rotation = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ClearGraphics()
|
|
|
|
|
{
|
|
|
|
|
this.bodyPartGraphic = null;
|
|
|
|
|
this.bodyPartGraphicHorny = null;
|
|
|
|
|
this.points = null;
|
|
|
|
|
this.pointsHorny = null;
|
|
|
|
|
}
|
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
|
|
|
|
currentHediffName = null;
|
|
|
|
|
currentSeverityInt = -1;
|
|
|
|
|
cappedSeverityInt = 1000;
|
|
|
|
|
customPose = null;
|
|
|
|
|
rotOverride = null;
|
|
|
|
|
|
|
|
|
|
ClearGraphics();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
public void SetHediffData(string name, int severityIndex , string variation = null)
|
|
|
|
|
{
|
|
|
|
|
currentHediffName = name;
|
|
|
|
|
currentSeverityInt = severityIndex;
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
public void SetHediffData(string name, int severityIndex, int cappedSeverityIndex = 1000, string variation = null)
|
|
|
|
|
{
|
|
|
|
|
currentHediffName = name;
|
|
|
|
|
currentSeverityInt = severityIndex;
|
|
|
|
|
this.cappedSeverityInt = cappedSeverityIndex;
|
|
|
|
|
this.variation = variation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DrawBodyPart (Vector3 rootLoc, float angle, Rot4 facing, RotDrawMode bodyDrawType, PawnRenderFlags flags, Mesh bodyMesh)
|
|
|
|
|
{
|
|
|
|
|
if (!isVisible)
|
|
|
|
|
return;
|
|
|
|
|
if (scale == Vector2.zero)
|
|
|
|
|
return; //Don't draw if scale is zero
|
|
|
|
|
if (pawn == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
if (bodyMesh == null)
|
|
|
|
|
{
|
|
|
|
|
if (SizedApparelSettings.Debug)
|
|
|
|
|
Log.Warning("[SizedApparel] DrawBodyParts But Null Body Mesh! : " + pawn.Name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
PawnRenderer pawnRenderer = pawn.Drawer.renderer;
|
|
|
|
|
Shader shader = ShaderDatabase.CutoutComplex;
|
|
|
|
|
Color drawColor1 = Color.white;
|
|
|
|
|
Color drawColor2 = Color.white;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
bool forceWriteZ = true;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
bool HasFurSkin = false;
|
|
|
|
|
//Furskin Check
|
|
|
|
|
if (pawn.Drawer.renderer.graphics.furCoveredGraphic != null)
|
|
|
|
|
{
|
|
|
|
|
HasFurSkin = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (colorType == SizedApparelBodyPartColorOf.Skin)
|
2022-08-14 20:46:19 +00:00
|
|
|
|
{
|
|
|
|
|
forceWriteZ = true;
|
|
|
|
|
if (bodyDrawType == RotDrawMode.Fresh)
|
|
|
|
|
{
|
2023-04-04 01:47:48 +00:00
|
|
|
|
if (HasFurSkin)
|
|
|
|
|
{
|
|
|
|
|
shader = pawn.Drawer.renderer.graphics.furCoveredGraphic.Shader;
|
|
|
|
|
if (!ShaderUtility.SupportsMaskTex(shader))
|
|
|
|
|
shader = ShaderDatabase.CutoutSkinOverlay;
|
|
|
|
|
shader = pawnRenderer.graphics.furCoveredGraphic.Shader;
|
|
|
|
|
drawColor1 = pawnRenderer.graphics.furCoveredGraphic.Color;
|
|
|
|
|
drawColor2 = pawnRenderer.graphics.furCoveredGraphic.ColorTwo;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
shader = pawn.Drawer.renderer.graphics.nakedGraphic.Shader;
|
|
|
|
|
if (!ShaderUtility.SupportsMaskTex(shader))
|
|
|
|
|
shader = ShaderDatabase.CutoutSkinOverlay;
|
|
|
|
|
drawColor1 = pawnRenderer.graphics.nakedGraphic.Color;
|
|
|
|
|
drawColor2 = pawnRenderer.graphics.nakedGraphic.ColorTwo;
|
|
|
|
|
}
|
2022-08-14 20:46:19 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (bodyDrawType == RotDrawMode.Rotting)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
shader = pawn.Drawer.renderer.graphics.rottingGraphic.Shader;
|
|
|
|
|
if (!ShaderUtility.SupportsMaskTex(shader))
|
|
|
|
|
shader = ShaderDatabase.CutoutSkinOverlay;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
drawColor1 = pawnRenderer.graphics.rottingGraphic.Color;
|
|
|
|
|
drawColor2 = pawnRenderer.graphics.nakedGraphic.ColorTwo;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
}
|
2023-04-04 01:47:48 +00:00
|
|
|
|
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
}
|
|
|
|
|
else if (colorType == SizedApparelBodyPartColorOf.Hair)
|
|
|
|
|
{
|
|
|
|
|
forceWriteZ = false;
|
|
|
|
|
shader = ShaderDatabase.Transparent;
|
|
|
|
|
if(pawn.story != null)
|
2023-04-04 01:47:48 +00:00
|
|
|
|
drawColor1 = pawn.story.HairColor;
|
2022-08-14 20:46:19 +00:00
|
|
|
|
}
|
|
|
|
|
else if (colorType == SizedApparelBodyPartColorOf.Custom)
|
|
|
|
|
{
|
|
|
|
|
forceWriteZ = true;
|
|
|
|
|
shader = ShaderDatabase.Transparent;
|
|
|
|
|
if(customColorOne != null)
|
|
|
|
|
drawColor1 = customColorOne.Value;
|
|
|
|
|
if (customColorTwo != null)
|
|
|
|
|
drawColor2 = customColorTwo.Value;
|
|
|
|
|
}
|
|
|
|
|
else if (colorType == SizedApparelBodyPartColorOf.None)
|
|
|
|
|
{
|
|
|
|
|
forceWriteZ = false;
|
|
|
|
|
shader = ShaderDatabase.Cutout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Quaternion quaternion = Quaternion.AngleAxis(angle + rotation, Vector3.up);
|
|
|
|
|
Vector3 vector = rootLoc;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
if (this.pawn.ageTracker.CurLifeStage.bodyDrawOffset != null)
|
|
|
|
|
{
|
|
|
|
|
vector += this.pawn.ageTracker.CurLifeStage.bodyDrawOffset.Value;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
Rot4 targetRot = facing;
|
|
|
|
|
if (rotOverride != null)
|
|
|
|
|
targetRot = rotOverride.Value;
|
|
|
|
|
|
|
|
|
|
if (targetRot == Rot4.South)
|
|
|
|
|
{
|
|
|
|
|
var loc = OffsetFromUVOffset(positionOffsetSouth, bodyMesh);
|
|
|
|
|
vector.x += loc.x;
|
|
|
|
|
vector.z += loc.y;
|
|
|
|
|
vector.y += depthOffsetSouth;
|
|
|
|
|
}
|
|
|
|
|
else if(targetRot == Rot4.North)
|
|
|
|
|
{
|
|
|
|
|
var loc = OffsetFromUVOffset(positionOffsetNorth, bodyMesh);
|
|
|
|
|
vector.x += loc.x;
|
|
|
|
|
vector.z += loc.y;
|
|
|
|
|
vector.y += depthOffsetNorth;
|
|
|
|
|
}
|
|
|
|
|
else if (targetRot == Rot4.East)
|
|
|
|
|
{
|
|
|
|
|
var loc = OffsetFromUVOffset(positionOffsetEast, bodyMesh);
|
|
|
|
|
vector.x += loc.x;
|
|
|
|
|
vector.z += loc.y;
|
|
|
|
|
vector.y += depthOffsetEast;
|
|
|
|
|
}
|
|
|
|
|
else if (targetRot == Rot4.West)
|
|
|
|
|
{
|
|
|
|
|
var loc = OffsetFromUVOffset(positionOffsetWest, bodyMesh);
|
|
|
|
|
vector.x += loc.x;
|
|
|
|
|
vector.z += loc.y;
|
|
|
|
|
vector.y += depthOffsetWest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
Graphic graphic = null;
|
|
|
|
|
if (SizedApparelUtility.IsHorny(pawn))
|
|
|
|
|
graphic = bodyPartGraphicHorny;
|
|
|
|
|
if (graphic == null)
|
|
|
|
|
graphic = bodyPartGraphic;
|
|
|
|
|
|
|
|
|
|
if (graphic == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-04-04 01:47:48 +00:00
|
|
|
|
//ForFurskinOffset
|
|
|
|
|
if(bodyDrawType == RotDrawMode.Fresh && HasFurSkin && AutoOffsetForFurCoveredBody)
|
|
|
|
|
{
|
|
|
|
|
//vector.y += 0.009187258f; //in PawnRenderer, it adds 0.009187258f.
|
|
|
|
|
|
|
|
|
|
//graphic.maskPath does error? need to check
|
|
|
|
|
// worn fur body and naked fur body has different offsets... wtf
|
|
|
|
|
//TODO Need to Fix
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
Material mat;
|
2023-04-04 01:47:48 +00:00
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
if (!flags.FlagSet(PawnRenderFlags.Cache)&&!isOverlay&& forceWriteZ)
|
|
|
|
|
{
|
2023-04-04 01:47:48 +00:00
|
|
|
|
graphic = graphic.GetColoredVersion(ShaderDatabase.Cutout, drawColor1, drawColor2); // ShaderDatabase.Cutout
|
2022-08-14 20:46:19 +00:00
|
|
|
|
mat = flags.FlagSet(PawnRenderFlags.Cache) ? graphic.MatAt(targetRot) : (Material)overrideMatMethod.Invoke(pawnRenderer, new object[] { graphic.MatAt(facing), pawn, flags.FlagSet(PawnRenderFlags.Portrait) });
|
|
|
|
|
GenDraw.DrawMeshNowOrLater(bodyMesh, vector, quaternion, mat, flags.FlagSet(PawnRenderFlags.DrawNow)); // draw for writeZ data to solve shadow issue
|
|
|
|
|
}
|
2023-04-04 01:47:48 +00:00
|
|
|
|
|
2022-08-14 20:46:19 +00:00
|
|
|
|
graphic = graphic.GetColoredVersion(shader, drawColor1, drawColor2);
|
|
|
|
|
vector.y += 0.00001f;
|
|
|
|
|
mat = flags.FlagSet(PawnRenderFlags.Cache) ? graphic.MatAt(targetRot) : (Material)overrideMatMethod.Invoke(pawnRenderer, new object[] { graphic.MatAt(facing), pawn, flags.FlagSet(PawnRenderFlags.Portrait) });
|
|
|
|
|
GenDraw.DrawMeshNowOrLater(bodyMesh, vector, quaternion, mat, flags.FlagSet(PawnRenderFlags.DrawNow));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO: Torso Pose?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class SizedApparelBodyDef : Def
|
|
|
|
|
{
|
|
|
|
|
//public List<SizedApparelBodyPartDef> BodyParts;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//defName = raceName ?? could it work?
|
|
|
|
|
|
|
|
|
|
public List<BodyPart> bodyParts;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
public List<BodyTypeAndOffset> penisOffset;
|
|
|
|
|
public List<BodyTypeAndOffset> vaginaOffset; //TODO
|
|
|
|
|
public List<BodyTypeAndOffset> udderOffset; //TODO
|
|
|
|
|
public List<BodyTypeAndOffset> bellyOffset; //TODO
|
|
|
|
|
public List<BodyTypeAndOffset> breastsOffset; //TODO
|
|
|
|
|
public List<BodyTypeAndOffset> anusOffset; //TODO
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class SizedApparelBody
|
|
|
|
|
{
|
|
|
|
|
public string customPoseOfBody = null;
|
|
|
|
|
|
|
|
|
|
public bool canCustomPose()
|
|
|
|
|
{
|
|
|
|
|
//check apparels
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|