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
{
2023-04-04 01:47:49 +00:00
public class Depth4Offsets
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:47:49 +00:00
public float south = 0 ;
public float north = 0 ;
public float east = 0 ;
public float west = 0 ;
public Depth4Offsets ( ) { }
public Depth4Offsets ( Vector4 arg )
{
south = arg . x ;
north = arg . y ;
east = arg . z ;
west = arg . w ;
}
public Depth4Offsets ( float s , float n , float e , float w )
{
south = s ;
north = n ;
east = e ;
west = w ;
}
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
public class Rot4Offsets
2022-08-14 20:46:19 +00:00
{
//X: right and left
//Y: Frong or Back
//Z: Up and Down
Vector3 South ;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
Vector3 North ;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
Vector3 East ;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
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 ;
}
2023-04-04 01:47:49 +00:00
public class BodyWithBodyType
{
public string bodyType ;
public List < BodyPart > Addons = new List < BodyPart > ( ) ;
}
2022-08-14 20:46:19 +00:00
public class BodyPart
{
2023-04-04 01:47:49 +00:00
public string partName = null ;
public string customPath = null ;
public string defaultHediffName = null ; // for missing Hediff
public bool isBreasts = false ;
2023-04-04 01:47:50 +00:00
public bool centeredTexture = true ;
public bool mustMatchBodyType = false ; // TODO
2023-04-04 01:47:49 +00:00
public string boneName = null ;
public Bone bone = null ; // For Graphic Positioning System
2023-04-04 01:47:50 +00:00
public bool mustHaveBone = true ; // when bone is missing, don't draw
2023-04-04 01:47:49 +00:00
public SizedApparelBodyPartOf bodyPartOf = SizedApparelBodyPartOf . None ;
public ColorType colorType = ColorType . Skin ;
public Depth4Offsets depthOffset = new Depth4Offsets ( ) ;
public BodyTypeAndOffset offsets = new BodyTypeAndOffset ( ) ;
2022-08-14 20:46:19 +00:00
}
public class BodyTypeAndOffset
{
//public RaceNameAndBodyType bodyTypeData;
public string bodyType ;
public Rot4Offsets offsets = new Rot4Offsets ( Vector3 . zero ) ;
2023-04-04 01:47:49 +00:00
public BodyTypeAndOffset ( )
{
}
2022-08-14 20:46:19 +00:00
public BodyTypeAndOffset ( bool useCenter )
{
if ( useCenter )
{
2023-04-04 01:47:49 +00:00
offsets = new Rot4Offsets ( Vector3 . zero ) ;
2022-08-14 20:46:19 +00:00
}
}
public BodyTypeAndOffset ( Vector3 defaultOffset )
{
offsets = new Rot4Offsets ( defaultOffset ) ;
}
}
2023-04-04 01:47:49 +00:00
public enum ColorType
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:48:55 +00:00
Skin , Hair , Nipple , Custom , None
2022-08-14 20:46:19 +00:00
}
public enum SizedApparelBodyPartOf
{
2023-04-04 01:48:56 +00:00
All , Torso , Breasts , Nipples , Crotch , Penis , Balls , Vagina , Anus , Belly , PubicHair , Udder , Hips , Thighs , hands , feet , None
2022-08-14 20:46:19 +00:00
}
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 :
2023-04-04 01:48:56 +00:00
if ( source = = SizedApparelBodyPartOf . Breasts | | source = = SizedApparelBodyPartOf . Nipples )
return true ;
return false ;
case SizedApparelBodyPartOf . Nipples :
if ( source = = SizedApparelBodyPartOf . Nipples )
2022-08-14 20:46:19 +00:00
return true ;
return false ;
case SizedApparelBodyPartOf . Crotch :
2023-04-04 01:47:49 +00:00
if ( source = = SizedApparelBodyPartOf . Crotch | | source = = SizedApparelBodyPartOf . Penis | | source = = SizedApparelBodyPartOf . Vagina | | source = = SizedApparelBodyPartOf . Anus | | source = = SizedApparelBodyPartOf . PubicHair | | source = = SizedApparelBodyPartOf . Balls )
2022-08-14 20:46:19 +00:00
return true ;
return false ;
case SizedApparelBodyPartOf . Penis :
if ( source = = SizedApparelBodyPartOf . Penis )
return true ;
return false ;
2023-04-04 01:47:49 +00:00
case SizedApparelBodyPartOf . Balls :
if ( source = = SizedApparelBodyPartOf . Balls )
return true ;
return false ;
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:49 +00:00
case SizedApparelBodyPartOf . PubicHair :
if ( source = = SizedApparelBodyPartOf . PubicHair )
return true ;
return false ;
2022-08-14 20:46:19 +00:00
case SizedApparelBodyPartOf . None :
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 ;
2023-04-04 01:47:49 +00:00
public Vector2 point = Vector2 . zero ;
2022-08-14 20:46:19 +00:00
}
public class GraphicPointsWithBodyType
{
public string pointName ;
public List < PointWithBodyType > points = new List < PointWithBodyType > ( ) ;
}
public class PointWithBodyType
{
public string bodyTypeName ; //null can be used too
2023-04-04 01:47:49 +00:00
public Vector2 point = Vector2 . zero ;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
public class BodyPartPoint
2022-08-14 20:46:19 +00:00
{
string name ;
2023-04-04 01:47:49 +00:00
Vector2 position = Vector2 . zero ; //Uv position. not pixel
2022-08-14 20:46:19 +00:00
}
[Obsolete] //todo
public struct BodyPartSpline
{
}
2023-04-04 01:47:49 +00:00
2023-04-04 01:47:48 +00:00
//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)
2023-04-04 01:47:49 +00:00
[Obsolete]
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:49 +00:00
public bool canPose = true ;
public List < string > TexturePaths ;
2022-08-14 20:46:19 +00:00
}
public class SizedApparelBodyPart
{
static MethodInfo overrideMatMethod = AccessTools . Method ( typeof ( PawnRenderer ) , "OverrideMaterialIfNeeded" ) ;
2023-04-04 01:48:55 +00:00
static Color defaultNippleColor = Color . white ; //nipple texture is already colored with pink. so make it white as default to avoid double coloring pink //Strong Pink Color = new ColorInt(255, 121, 121).ToColor
//this is for RGB Channel Edit
static string texturePath_White = "SizedApparel/Masks/White" ;
static string texturePath_Black = "SizedApparel/Masks/Black" ;
static string texturePath_Red = "SizedApparel/Masks/Red" ;
static string texturePath_Green = "SizedApparel/Masks/Green" ;
static string texturePath_Blue = "SizedApparel/Masks/Blue" ;
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:48 +00:00
public bool AutoOffsetForFurCoveredBody = true ;
2023-04-04 01:47:50 +00:00
public SizedApparelBodyPart ( Pawn pawn , ApparelRecorderComp apparelRecorderComp , string bodyPartName , SizedApparelBodyPartOf bodyPartOf , string defaultHediffName , bool isBreast , bool isOverlay , string customPathName = null , ColorType colorOf = ColorType . Skin , bool needBoneToRender = true , Bone parentBone = null , bool isCenteredTexture = false )
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 ;
2023-04-04 01:47:49 +00:00
this . bone = parentBone ;
2023-04-04 01:47:50 +00:00
this . mustHaveBone = needBoneToRender ;
2023-04-04 01:47:49 +00:00
this . centeredTexture = isCenteredTexture ;
}
public void SetCenteredTexture ( bool isCentered )
{
this . centeredTexture = isCentered ;
2022-08-14 20:46:19 +00:00
}
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];
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
if ( ! isFliped )
2023-04-04 01:47:49 +00:00
return new Vector2 ( ( mesh . vertices [ 2 ] . x - mesh . vertices [ 0 ] . x ) * vector . x , ( mesh . vertices [ 0 ] . z - mesh . vertices [ 2 ] . z ) * vector . y ) ;
return new Vector2 ( ( mesh . vertices [ 2 ] . x - mesh . vertices [ 0 ] . x ) * vector . x , ( mesh . vertices [ 2 ] . z - mesh . vertices [ 0 ] . z ) * vector . y ) ;
/ *
* Vector2 loc = new Vector2 ( 0.5f , 0.5f ) - vector ;
if ( ! isFliped )
2022-08-14 20:46:19 +00:00
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 ) ) ;
2023-04-04 01:47:49 +00:00
* /
2022-08-14 20:46:19 +00:00
}
//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;
2023-04-04 01:47:49 +00:00
public Bone bone ;
2023-04-04 01:47:50 +00:00
private bool mustHaveBone ;
2023-04-04 01:47:49 +00:00
public bool centeredTexture = false ; // false to keep original position from mesh. and consider this graphics pivot as bone position
2023-04-04 01:47:48 +00:00
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 ;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:49 +00:00
public ColorType colorType = ColorType . Skin ;
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:49 +00:00
public void SetBone ( Bone bone )
{
this . bone = bone ;
}
2022-08-14 20:46:19 +00:00
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:48:55 +00:00
public bool CheckCanPose ( string targetPose , bool checkApparels , bool checkBodyParts , bool mustMatchSize , bool mustMatchBodytype , bool mustMatchRace )
2022-08-14 20:46:19 +00:00
{
if ( checkApparels )
{
2023-04-04 01:47:49 +00:00
if ( ! SizedApparelUtility . CanPoseApparels ( pawn , targetPose , currentHediffName , currentSeverityInt , cappedSeverityInt ) )
return false ;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:48 +00:00
if ( checkBodyParts )
{
2023-04-04 01:48:55 +00:00
Graphic graphic = GetBodyPartGraphics ( false , mustMatchSize , mustMatchBodytype , mustMatchRace ) ;
Graphic graphicH = GetBodyPartGraphics ( true , mustMatchSize , mustMatchBodytype , mustMatchRace ) ;
2023-04-04 01:47:48 +00:00
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
2023-04-04 01:47:49 +00:00
public Vector2 pivot = Vector2 . zero ;
2022-08-14 20:46:19 +00:00
public Vector2 position = Vector2 . zero ; //offset from pivot //UV. not pixel
2023-04-04 01:47:49 +00:00
public SizedApparelTexturePointDef points ;
public SizedApparelTexturePointDef pointsHorny ;
2022-08-14 20:46:19 +00:00
public float rotation = 0 ; // +: rotate right, -: rotate left
2023-04-04 01:47:49 +00:00
public float scale = 1f ;
2022-08-14 20:46:19 +00:00
public Graphic bodyPartGraphic ;
public Graphic bodyPartGraphicHorny ;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:50 +00:00
2022-08-14 20:46:19 +00:00
//bigger = in front
public void SetDepthOffsets ( float south , float north , float east , float west )
{
depthOffsetSouth = south ;
depthOffsetNorth = north ;
depthOffsetEast = east ;
depthOffsetWest = west ;
}
2023-04-04 01:47:49 +00:00
public void SetDepthOffsets ( Depth4Offsets oppsets )
{
depthOffsetSouth = oppsets . south ;
depthOffsetNorth = oppsets . north ;
depthOffsetEast = oppsets . east ;
depthOffsetWest = oppsets . west ;
}
2022-08-14 20:46:19 +00:00
public void SetPositionOffsets ( Vector2 south , Vector2 north , Vector2 east , Vector2 west )
{
positionOffsetSouth = south ;
positionOffsetNorth = north ;
positionOffsetEast = east ;
positionOffsetWest = west ;
}
2023-04-04 01:48:56 +00:00
public Graphic GetBodyPartGraphics ( bool isHorny , bool mustMatchSize = false , bool mustMatchBodytype = false , bool mustMatchRace = false , string poseOverride = null , string variationOverride = null )
2022-08-14 20:46:19 +00:00
{
2023-04-04 01:47:49 +00:00
SizedApparelTexturePointDef var ;
2023-04-04 01:48:56 +00:00
return GetBodyPartGraphics ( isHorny , out var , mustMatchBodytype , mustMatchSize , mustMatchRace , poseOverride , variationOverride ) ;
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:48:55 +00:00
public Graphic GetBodyPartGraphics ( bool isHorny , out SizedApparelTexturePointDef outPoints , bool mustMatchSize = false , bool mustMatchBodyType = false , bool mustMatchRace = false , string poseOverride = null , string variationOverride = null )
2022-08-14 20:46:19 +00:00
{
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:49 +00:00
string bodyTypeString = pawn . story ? . bodyType ? . defName ;
var key = new SizedApparelsDatabase . BodyPartDatabaseKey ( pawn . def . defName , bodyTypeString , 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 ) ;
2023-04-04 01:47:49 +00:00
2022-08-14 20:46:19 +00:00
if ( mustMatchSize )
if ( Math . Min ( currentSeverityInt , cappedSeverityInt ) ! = result . size )
{
outPoints = null ;
return null ;
}
2023-04-04 01:47:50 +00:00
if ( mustMatchBodyType )
{
if ( result . bodyType ! = pawn . story ? . bodyType ? . defName )
{
outPoints = null ;
return null ;
}
}
2023-04-04 01:48:55 +00:00
if ( mustMatchRace )
{
if ( result . raceName ! = pawn . def . defName )
{
outPoints = null ;
return null ;
}
}
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:49 +00:00
this . scale = 1f ;
2022-08-14 20:46:19 +00:00
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 ;
2023-04-04 01:47:49 +00:00
if ( scale = = 0f )
2022-08-14 20:46:19 +00:00
return ; //Don't draw if scale is zero
if ( pawn = = null )
return ;
2023-04-04 01:47:50 +00:00
if ( mustHaveBone & & bone = = 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 ;
2023-04-04 01:48:55 +00:00
Shader shader = shader = ShaderDatabase . CutoutSkinOverlay ;
2022-08-14 20:46:19 +00:00
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 ;
}
2023-04-04 01:47:49 +00:00
if ( colorType = = ColorType . 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 ) )
2023-04-04 01:48:55 +00:00
shader = shader = ShaderDatabase . CutoutSkinOverlay ;
2023-04-04 01:47:48 +00:00
shader = pawnRenderer . graphics . furCoveredGraphic . Shader ;
drawColor1 = pawnRenderer . graphics . furCoveredGraphic . Color ;
drawColor2 = pawnRenderer . graphics . furCoveredGraphic . ColorTwo ;
}
else
{
shader = pawn . Drawer . renderer . graphics . nakedGraphic . Shader ;
2023-04-04 01:48:55 +00:00
if ( ! ShaderUtility . SupportsMaskTex ( shader ) )
shader = shader = ShaderDatabase . CutoutSkinOverlay ;
drawColor1 = pawnRenderer . graphics . nakedGraphic . Color ;
drawColor2 = pawnRenderer . graphics . nakedGraphic . ColorTwo ;
}
}
else if ( bodyDrawType = = RotDrawMode . Rotting )
{
shader = pawn . Drawer . renderer . graphics . rottingGraphic . Shader ;
if ( ! ShaderUtility . SupportsMaskTex ( shader ) )
shader = shader = ShaderDatabase . CutoutSkinOverlay ;
drawColor1 = pawnRenderer . graphics . rottingGraphic . Color ;
drawColor2 = pawnRenderer . graphics . nakedGraphic . ColorTwo ;
}
}
else if ( colorType = = ColorType . Nipple )
{
forceWriteZ = true ;
//Get SkinColor first.
if ( bodyDrawType = = RotDrawMode . Fresh )
{
if ( HasFurSkin )
{
shader = pawn . Drawer . renderer . graphics . furCoveredGraphic . Shader ;
2023-04-04 01:47:48 +00:00
if ( ! ShaderUtility . SupportsMaskTex ( shader ) )
shader = ShaderDatabase . CutoutSkinOverlay ;
2023-04-04 01:48:55 +00:00
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 = shader = ShaderDatabase . CutoutSkinOverlay ;
2023-04-04 01:47:48 +00:00
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 ) )
2023-04-04 01:48:55 +00:00
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
2023-04-04 01:48:55 +00:00
if ( apparelRecorderCompCache ! = null )
{
if ( apparelRecorderCompCache . nippleColor ! = null )
{
drawColor1 = apparelRecorderCompCache . nippleColor . Value ; //* drawColor1;
drawColor2 = apparelRecorderCompCache . nippleColor . Value ; //* drawColor2; //maybe can be issue
}
else
{
//nipple Color is null
//Ust Default Color for Nipple with SkinColor
drawColor1 = defaultNippleColor * drawColor1 ;
drawColor2 = defaultNippleColor * drawColor2 ;
}
}
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:47:49 +00:00
else if ( colorType = = ColorType . Hair )
2022-08-14 20:46:19 +00:00
{
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
}
2023-04-04 01:47:49 +00:00
else if ( colorType = = ColorType . Custom )
2022-08-14 20:46:19 +00:00
{
forceWriteZ = true ;
shader = ShaderDatabase . Transparent ;
if ( customColorOne ! = null )
drawColor1 = customColorOne . Value ;
if ( customColorTwo ! = null )
drawColor2 = customColorTwo . Value ;
}
2023-04-04 01:47:49 +00:00
else if ( colorType = = ColorType . None )
2022-08-14 20:46:19 +00:00
{
forceWriteZ = false ;
shader = ShaderDatabase . Cutout ;
}
2023-04-04 01:48:55 +00:00
if ( isOverlay )
{
if ( shader = = ShaderDatabase . Cutout )
shader = ShaderDatabase . Transparent ;
else if ( shader = = ShaderDatabase . CutoutSkin | | shader = = ShaderDatabase . CutoutSkinColorOverride )
shader = ShaderDatabase . CutoutSkinOverlay ;
else
shader = ShaderDatabase . Transparent ;
}
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:49 +00:00
Mesh scaledBodyMesh ;
BoneTransform boneTransform = null ;
if ( bone ! = null )
{
if ( facing = = Rot4 . South )
{
boneTransform = bone . south ;
}
else if ( facing = = Rot4 . North )
{
boneTransform = bone . north ;
}
else if ( facing = = Rot4 . East )
{
boneTransform = bone . east ;
}
else if ( facing = = Rot4 . West )
{
boneTransform = bone . west ;
if ( boneTransform = = null )
boneTransform = bone . east ;
}
}
float drawScale = scale ;
float drawRotation = angle ;
Vector3 drawPosition = rootLoc ;
2023-04-04 01:47:50 +00:00
if ( this . pawn . ageTracker . CurLifeStage . bodyDrawOffset ! = null )
{
drawPosition + = this . pawn . ageTracker . CurLifeStage . bodyDrawOffset . Value ;
}
2023-04-04 01:47:49 +00:00
if ( boneTransform ! = null )
{
//TODO fixed angle for IK?
2023-04-04 01:48:55 +00:00
float width = ( bodyMesh . vertices [ 2 ] . x - bodyMesh . vertices [ 0 ] . x ) ;
2023-04-04 01:47:49 +00:00
if ( centeredTexture )
{
bool westUsingEast = false ;
if ( facing = = Rot4 . West & & bone . west = = null )
westUsingEast = true ;
2023-04-04 01:48:55 +00:00
2023-04-04 01:47:49 +00:00
drawRotation = boneTransform . InitialAngle + boneTransform . angleOffset ;
//not sure it work correct
Vector3 v = ( boneTransform . InitialPosition + boneTransform . positionOffset ) * width ;
var q = Quaternion . AngleAxis ( westUsingEast ? - angle : angle , Vector3 . up ) ;
v = q * v ;
drawPosition = ( v ) ; // calculate rotated point
//Log.Message(boneTransform.angleOffset.ToString());
if ( westUsingEast )
{
//already scaled with "width"?
//its using east as west. so flip position
drawPosition . Scale ( new Vector3 ( - 1f , 0f , 1f ) ) ;
//drawRotation = boneTransform.InitialAngle - boneTransform.angleOffset;
}
drawPosition + = rootLoc ;
drawRotation + = angle ;
}
else
2023-04-04 01:48:55 +00:00
{
//NotCentered Texture not yet support Rotation System.
2023-04-04 01:47:49 +00:00
//wip
2023-04-04 01:48:55 +00:00
Vector3 v = Vector3 . zero ;
2023-04-04 01:47:49 +00:00
bool westUsingEast = false ;
if ( facing = = Rot4 . West & & bone . west = = null )
westUsingEast = true ;
2023-04-04 01:48:55 +00:00
drawRotation = boneTransform . InitialAngle ; // + boneTransform.angleOffset
2023-04-04 01:47:49 +00:00
//not sure it work correct
2023-04-04 01:48:55 +00:00
//Vector3 v = (boneTransform.InitialPosition); //initialpos as custom pivot
//var q = Quaternion.AngleAxis(westUsingEast ? -drawRotation : drawRotation, Vector3.up);
//var q2 = Quaternion.AngleAxis(westUsingEast ? -angle : angle, Vector3.up);
//v = q * v; // calculate final draw position with torso angle
//v = v + boneTransform.InitialPosition + boneTransform.positionOffset;
//v = q2 * v; //NotCentered Texture not yet support Rotation System.
2023-04-04 01:47:49 +00:00
drawPosition = ( v ) * width ;
if ( westUsingEast )
{
//already scaled with "width"?
//its using east as west. so flip position
drawPosition . Scale ( new Vector3 ( - 1f , 0f , 1f ) ) ;
//drawRotation = boneTransform.InitialAngle - boneTransform.angleOffset;
}
drawPosition + = rootLoc ; // adjust with result
//Log.Message(boneTransform.angleOffset.ToString());
drawRotation + = angle ;
}
}
if ( drawScale ! = 1f )
{
// scale Only Rimworld Plane Mesh
if ( bodyMesh . vertexCount = = 4 )
{
float width = ( bodyMesh . vertices [ 2 ] . x - bodyMesh . vertices [ 0 ] . x ) ;
var meshSet = MeshPool . GetMeshSetForWidth ( scale * width ) ;
scaledBodyMesh = meshSet . MeshAt ( facing ) ;
}
else
scaledBodyMesh = bodyMesh ;
}
else
{
scaledBodyMesh = bodyMesh ;
}
Quaternion quaternion = Quaternion . AngleAxis ( drawRotation , Vector3 . up ) ;
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:50 +00:00
2023-04-04 01:47:48 +00:00
2022-08-14 20:46:19 +00:00
Rot4 targetRot = facing ;
if ( rotOverride ! = null )
targetRot = rotOverride . Value ;
if ( targetRot = = Rot4 . South )
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset ( positionOffsetSouth , scaledBodyMesh ) ;
drawPosition . x + = loc . x ;
drawPosition . z + = loc . y ;
drawPosition . y + = depthOffsetSouth ;
2022-08-14 20:46:19 +00:00
}
else if ( targetRot = = Rot4 . North )
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset ( positionOffsetNorth , scaledBodyMesh ) ;
drawPosition . x + = loc . x ;
drawPosition . z + = loc . y ;
drawPosition . y + = depthOffsetNorth ;
2022-08-14 20:46:19 +00:00
}
else if ( targetRot = = Rot4 . East )
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset ( positionOffsetEast , scaledBodyMesh ) ;
drawPosition . x + = loc . x ;
drawPosition . z + = loc . y ;
drawPosition . y + = depthOffsetEast ;
2022-08-14 20:46:19 +00:00
}
else if ( targetRot = = Rot4 . West )
{
2023-04-04 01:47:49 +00:00
var loc = OffsetFromUVOffset ( positionOffsetWest , scaledBodyMesh ) ;
drawPosition . x + = loc . x ;
drawPosition . z + = loc . y ;
drawPosition . y + = depthOffsetWest ;
2022-08-14 20:46:19 +00:00
}
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
2023-04-04 01:48:55 +00:00
//If ForceWriteDepth, draw Cutout mesh for write depth. this is for avoid problem of body addons drawing under other meshes. such as breasts front of body but behind tail.
2023-04-27 20:02:48 +00:00
if ( forceWriteZ ) //|| (!flags.FlagSet(PawnRenderFlags.Cache) && !isOverlay) //(!flags.FlagSet(PawnRenderFlags.Cache) && !isOverlay)
2022-08-14 20:46:19 +00:00
{
2023-04-27 20:02:48 +00:00
Color c1 = drawColor1 ;
c1 . a = 0 ;
Color c2 = drawColor2 ;
c2 . a = 0 ;
Graphic Zgraphic = graphic . GetColoredVersion ( ShaderDatabase . CutoutSkin , c1 , c2 ) ; // ShaderDatabase.Cutout //ShaderDatabase.CutoutComplex
2023-04-04 01:48:55 +00:00
Vector3 drawPositionZ = drawPosition ;
2023-04-27 20:02:48 +00:00
//drawPositionZ.y = Mathf.Min(rootLoc.y + 0.008687258f + 0.0014478763f, drawPosition.y) - 0.001f; //Mathf.Min( rootLoc.y , drawPosition.y) - 0.0001f; //Send Back of body, but it can still write depth.
//Rimworld's Pawn Renderer Add 0.008687258f and 0.0014478763f to render naked body
drawPositionZ . y = drawPositionZ . y - 0.0001f ; ; //Send Back of body, but it can still write depth.
2023-04-04 01:48:55 +00:00
mat = flags . FlagSet ( PawnRenderFlags . Cache ) ? Zgraphic . MatAt ( targetRot ) : ( Material ) overrideMatMethod . Invoke ( pawnRenderer , new object [ ] { Zgraphic . MatAt ( facing ) , pawn , flags . FlagSet ( PawnRenderFlags . Portrait ) } ) ;
GenDraw . DrawMeshNowOrLater ( scaledBodyMesh , drawPositionZ , quaternion , mat , flags . FlagSet ( PawnRenderFlags . DrawNow ) ) ; // draw for writeZ data to solve shadow issue
2023-04-27 20:02:48 +00:00
//Draw Zero Opacity Mesh for write depth only.
//This is Nessesary for avoid cliping graphics such as BroadshieldPack.
2022-08-14 20:46:19 +00:00
}
2023-04-04 01:48:55 +00:00
//shader must be mask
2022-08-14 20:46:19 +00:00
graphic = graphic . GetColoredVersion ( shader , drawColor1 , drawColor2 ) ;
2023-04-04 01:48:55 +00:00
if ( graphic . maskPath = = null )
{
//Test
//graphic.maskPath = texturePath_Red;
}
//drawPosition.y += 0.00001f;
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 ) } ) ;
2023-04-04 01:47:49 +00:00
GenDraw . DrawMeshNowOrLater ( scaledBodyMesh , drawPosition , quaternion , mat , flags . FlagSet ( PawnRenderFlags . DrawNow ) ) ;
2022-08-14 20:46:19 +00:00
}
}
//TODO: Torso Pose?
2023-04-04 01:47:49 +00:00
public class BodyDef : Def
2022-08-14 20:46:19 +00:00
{
//public List<SizedApparelBodyPartDef> BodyParts;
//defName = raceName ?? could it work?
2023-04-04 01:47:49 +00:00
public List < BodyWithBodyType > bodies = new List < BodyWithBodyType > ( ) ;
2022-08-14 20:46:19 +00:00
2023-04-04 01:47:49 +00:00
//public List<BodyTypeAndOffset> penisOffset;
//public List<BodyTypeAndOffset> vaginaOffset;
//public List<BodyTypeAndOffset> pubicHairOffset;
//public List<BodyTypeAndOffset> udderOffset;
//public List<BodyTypeAndOffset> bellyOffset;
//public List<BodyTypeAndOffset> breastsOffset;
//public List<BodyTypeAndOffset> anusOffset;
2022-08-14 20:46:19 +00:00
}
public class SizedApparelBody
{
public string customPoseOfBody = null ;
public bool canCustomPose ( )
{
//check apparels
return false ;
}
}
2023-04-04 01:47:49 +00:00
public class SizedApparelBodyPartOfssetDef : Def
{
//defName IsRaceName
}
2022-08-14 20:46:19 +00:00
}