@ -1,253 +1,102 @@
using AlienRace ;
using AlienRace.ExtendedGraphics ;
using HarmonyLib ;
using RimWorld ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using UnityEngine ;
using Verse ;
// The RevealingApparel mod extension allows us to add information to apparel that tells us if wearing an apparel item
// should also cover the body parts introduced by RNW. This way mod authors can make an apparel item that covers a pawn's
// torso but still draws their breasts or genitals.
namespace RevealingApparel
{
//[StaticConstructorOnStartup]
// public static class HarmonyPatching
// {
// static HarmonyPatching()
// {
// Harmony harmony = new Harmony("RevealingApparel");
// harmony.PatchAll();
// }
// }
// This is the mod extension that people will use to add revealing information to their apparel defs.
public class ApparelRevealingExtension : DefModExtension
{
// A list of RevealingExtensionEntry items, which describe what body parts are revealed when a pawn is wearing this apparel item
public List < RevealingExtensionEntry > revealingBodyPartEntries = new List < RevealingExtensionEntry > ( ) ;
public ApparelRevealingExtension ( )
{
}
}
// The entry class that describes what body parts are revealed
public class RevealingExtensionEntry
{
// The path to the body part that is revealed.
// Examples include "Breasts/FeaturelessLeft" or "Genitals/FeaturelessCrotch"
public string revealingPath ;
public String revealingPath ;
// A list of pawn body types this entry applies to.
// Examples include "Female" or "Thin" or "Hulk"
public List < BodyTypeDef > revealingBodyTypes = new List < BodyTypeDef > ( ) ;
}
public static class RevealingApparel
// We are going to postfix patch the VisibleUnderApparelOf check in HAR so we can make body parts visible if all of the apparel
// covering it is marked as revealing
[HarmonyPatch(typeof(AlienPartGenerator.BodyAddon), "VisibleUnderApparelOf")]
class HarmonyPatch_RevealingApparel_VisibleUnderApparelOf
{
public static bool CanDrawRevealing ( AlienPartGenerator . BodyAddon bodyAddon , Pawn pawn )
public static Pawn GetPawnFromWrapped ( ExtendedGraphicsPawnWrapper pawn )
{
// the below 2 conditions are added by CnArmor in attempt to fix conflict with MoHAR.
if ( pawn . apparel . WornApparel ! = null & & pawn . apparel . WornApparel . Count = = 0 ) //no need to bother if pawn is naked
return false ;
//if body addon is not mentioned in that list, don't bother
bool bodyAddonMentioned = false ;
if ( pawn . apparel . WornApparel ! = null )
{
for ( int i = 0 ; i < pawn . apparel . WornApparel . Count ; i + + )
{
Apparel apparel = pawn . apparel . WornApparel [ i ] ;
ApparelRevealingExtension ARE = apparel . def . GetModExtension < ApparelRevealingExtension > ( ) ;
if ( ARE ! = null & & ARE . revealingBodyPartEntries . Any ( ( RevealingExtensionEntry ree ) = > ( bodyAddon . path = = ree . revealingPath ) ) )
{
bodyAddonMentioned = true ;
break ;
}
}
if ( bodyAddonMentioned = = false )
return false ;
}
// the above 2 conditions are added by CnArmor in attempt to fix conflict with MoHAR. I didn't dare to touch the original code below because of complexity.
BodyTypeDef pawnBodyDef = pawn . story . bodyType ;
if ( ! ( pawn . apparel . WornApparel = = null ) & &
pawn . apparel . WornApparel . Where ( ( Apparel ap ) //First fetching everything that covers the bodypart
= > ap . def . apparel . bodyPartGroups . Any ( ( BodyPartGroupDef bpgd )
= > bodyAddon . hiddenUnderApparelFor . Contains ( bpgd ) )
| | ap . def . apparel . tags . Any ( ( string s ) = > bodyAddon . hiddenUnderApparelTag . Contains ( s ) ) )
. All ( ( Apparel ap ) //Then checking that list, if everything has the revealing flag for the current body, reveal
= > ( ap . def . GetModExtension < ApparelRevealingExtension > ( ) ? . revealingBodyPartEntries . Any ( ( RevealingExtensionEntry revealingExtensionEntry )
= > ( bodyAddon . path . Contains ( revealingExtensionEntry . revealingPath ) & & revealingExtensionEntry . revealingBodyTypes . Any ( ( BodyTypeDef revealingBodyType ) = > pawnBodyDef . defName . Contains ( revealingBodyType . defName ) ) ) )
? ? false
) ) )
{
Building_Bed building_Bed = pawn . CurrentBed ( ) ;
if ( ( building_Bed = = null | | building_Bed . def . building . bed_showSleeperBody | | bodyAddon . drawnInBed ) & & ( bodyAddon . backstoryRequirement = = null | | pawn . story . AllBackstories . Any ( ( BackstoryDef b ) = > b . identifier = = bodyAddon . backstoryRequirement . identifier ) ) )
{
if ( ! bodyAddon . drawnDesiccated )
{
Corpse corpse = pawn . Corpse ;
if ( corpse ! = null & & corpse . GetRotStage ( ) = = RotStage . Dessicated )
{
return false ;
}
}
if ( bodyAddon . bodyPart ! = null & & ! pawn . health . hediffSet . GetNotMissingParts ( BodyPartHeight . Undefined , BodyPartDepth . Undefined , null , null ) . Any ( ( BodyPartRecord bpr ) = > bpr . untranslatedCustomLabel = = bodyAddon . bodyPart . label | | bpr . def . defName = = bodyAddon . bodyPart . label ) )
{
List < AlienPartGenerator . ExtendedHediffGraphic > list = bodyAddon . hediffGraphics ;
bool flag ;
if ( list = = null )
{
flag = false ;
}
else
{
flag = list . Any ( ( AlienPartGenerator . ExtendedHediffGraphic bahg ) = > bahg . hediff = = HediffDefOf . MissingBodyPart ) ;
}
if ( ! flag )
{
return false ;
}
}
if ( ( pawn . gender = = Gender . Female ) ? bodyAddon . drawForFemale : bodyAddon . drawForMale )
{
//return bodyAddon.bodyTypeRequirement.NullOrEmpty() || pawn.story.bodyType.ToString() == bodyAddon.bodyTypeRequirement;
return bodyAddon . bodytypeGraphics . NullOrEmpty ( ) | | pawn . story . bodyType = = bodyAddon . bodyTypeRequirement ;
}
}
}
return false ;
// The pawn wrapper doesn't expose the original Pawn as a public field, so we need to use reflection
// to pull it out.
return Traverse . Create ( pawn ) . Property ( "WrappedPawn" ) . GetValue < Pawn > ( ) ;
}
}
[HarmonyPatch(typeof(AlienRace.HarmonyPatches), "DrawAddons")]
class HarmonyPatch_DrawAddons
{
//[TweakValue("AAAInfrontAAAAAAAAtittyoffset", -0.1f, 0.1f)]
private static float underClothingOffset = 0.0136f ;
//[TweakValue("AAABehindAAAAAAAAtittyoffset", -0.2f, 0.0f)]
//private static float Behind = -0.1f;
public static void Postfix ( PawnRenderFlags renderFlags , Vector3 vector , Vector3 headOffset , Pawn pawn , Quaternion quat , Rot4 rotation )
public static IEnumerable < Apparel > GetApparelCoveringPart ( Pawn pawn , AlienPartGenerator . BodyAddon bodyAddon )
{
ThingDef_AlienRace thingDef_AlienRace = pawn . def as ThingDef_AlienRace ;
if ( thingDef_AlienRace = = null | | renderFlags . FlagSet ( PawnRenderFlags . Invisible ) )
{
//Log.Message(pawn.def.defName);
if ( pawn . def . defName = = "Human" )
{
//Log.Message(pawn.def.defName);
}
return ;
}
Building_Bed building_Bed = pawn . CurrentBed ( ) ;
List < AlienPartGenerator . BodyAddon > bodyAddons = thingDef_AlienRace . alienRace . generalSettings . alienPartGenerator . bodyAddons ;
AlienPartGenerator . AlienComp comp = pawn . GetComp < AlienPartGenerator . AlienComp > ( ) ;
for ( int i = 0 ; i < bodyAddons . Count ; i + + )
{
AlienPartGenerator . BodyAddon bodyAddon = bodyAddons [ i ] ;
if ( ! bodyAddon . CanDrawAddon ( pawn ) & & RevealingApparel . CanDrawRevealing ( bodyAddon , pawn ) ) //No need to draw twice
{
pawn . apparel . WornApparel . Any ( ( Apparel ap )
= > ap . def . apparel . bodyPartGroups . Any ( ( BodyPartGroupDef bpgd )
= > bodyAddon . hiddenUnderApparelFor . Contains ( bpgd ) ) ) ;
AlienPartGenerator . RotationOffset offset = bodyAddon . defaultOffsets . GetOffset ( rotation ) ;
Vector3 a = ( offset ! = null ) ? offset . GetOffset ( renderFlags . FlagSet ( PawnRenderFlags . Portrait ) , pawn . story . bodyType , pawn . story . headType ) : Vector3 . zero ;
AlienPartGenerator . RotationOffset offset2 = bodyAddon . offsets . GetOffset ( rotation ) ;
Vector3 vector2 = a + ( ( offset2 ! = null ) ? offset2 . GetOffset ( renderFlags . FlagSet ( PawnRenderFlags . Portrait ) , pawn . story . bodyType , pawn . story . headType ) : Vector3 . zero ) ;
vector2 . y = ( bodyAddon . inFrontOfBody ? ( 0.3f + vector2 . y - underClothingOffset ) : ( - 0.3f - vector2 . y + underClothingOffset ) ) ;
// Log.Message(pawn.def.defName +" "+ bodyAddon.path + " has vector2.y " + vector2.y + "Default offset: " + bodyAddon.defaultOffsets.GetOffset(rotation).layerOffset);
if ( bodyAddon . inFrontOfBody & & vector2 . y < 0f ) //The offset of some bodyaddons is too far out of the "over body, under clothes"-range, e.g. OTY bellies.
{
vector2 . y = 0.01f ;
}
// Log.Message(pawn.def.defName + " " + bodyAddon.path + " has now vector2.y " + vector2.y + "Default offset: " + bodyAddon.defaultOffsets.GetOffset(rotation).layerOffset);
float num = bodyAddon . angle ;
if ( rotation = = Rot4 . North )
{
if ( bodyAddon . layerInvert )
{
vector2 . y = - vector2 . y ;
vector2 . y - = underClothingOffset * 2 ; //I am not sure why I am doing this, but it puts Anus under the pants layer.
}
num = 0f ;
}
if ( rotation = = Rot4 . East )
{
num = - num ;
vector2 . x = - vector2 . x ;
}
Vector3 outputVector = vector + ( bodyAddon . alignWithHead ? headOffset : Vector3 . zero ) + vector2 . RotatedBy ( Mathf . Acos ( Quaternion . Dot ( Quaternion . identity , quat ) ) * 2f * 57.29578f ) ;
Graphic graphic = comp . addonGraphics [ i ] ;
graphic . drawSize = ( ( renderFlags . FlagSet ( PawnRenderFlags . Portrait ) & & bodyAddon . drawSizePortrait ! = Vector2 . zero ) ? bodyAddon . drawSizePortrait : bodyAddon . drawSize ) * ( bodyAddon . scaleWithPawnDrawsize ? ( bodyAddon . alignWithHead ? ( renderFlags . FlagSet ( PawnRenderFlags . Portrait ) ? comp . customPortraitHeadDrawSize : comp . customHeadDrawSize ) : ( renderFlags . FlagSet ( PawnRenderFlags . Portrait ) ? comp . customPortraitDrawSize : comp . customDrawSize ) ) : Vector2 . one ) * 1.5f ;
GenDraw . DrawMeshNowOrLater (
graphic . MeshAt ( rotation ) ,
outputVector ,
Quaternion . AngleAxis ( num , Vector3 . up ) * quat ,
graphic . MatAt ( rotation , null ) ,
renderFlags . FlagSet ( PawnRenderFlags . DrawNow ) ) ;
}
}
//return true;
// Get a list of all of the apparel worn by this pawn
return pawn . apparel ? . WornApparel ? . Where (
// Where any of the body part groups this apparel covers
apparel = > apparel . def . apparel . bodyPartGroups . Any (
// Hide the passed body addon
bodyPartGroup = > bodyAddon . hiddenUnderApparelFor . Contains ( bodyPartGroup ) ) ) ;
}
public static bool IsApparelRevealingBodyPart ( Apparel apparel , AlienPartGenerator . BodyAddon bodyAddon , BodyTypeDef bodyType )
{
// Get the revealing entries for this apparel item
var revealingExtension = apparel . def . GetModExtension < ApparelRevealingExtension > ( ) ;
// And return if one of these entries matches the body part for this pawn body type
return revealingExtension ? . revealingBodyPartEntries . Any ( ( entry ) = >
{
// Does this entry reveal the body part we are considering
bool entryMatchesBodyPart = entry . revealingPath ? . Contains ( bodyAddon . GetPath ( ) ) ? ? false ;
// Does this entry apply to the pawn's body shape
bool entryMatchesPawnBody = entry . revealingBodyTypes ? . Contains ( bodyType ) ? ? false ;
// If this entry matches the part and applies to the pawn body type, then this apparel reveals this body part
return entryMatchesBodyPart & & entryMatchesPawnBody ;
} ) ? ? false ; // If there are no revealing body part entries, then this apparel covers this body part
}
public static bool Postfix ( bool __result , AlienPartGenerator . BodyAddon __instance , ExtendedGraphicsPawnWrapper pawn )
{
// If the original method returned false, we might still show it based on the revealing apparel entries
if ( __result = = false )
{
// Grab the underlying pawn from the wrapped version we were passed
var myPawn = GetPawnFromWrapped ( pawn ) ;
// Reference the pawn body type. We will need this to know if revealing body part entries apply to this pawn
var bodyType = myPawn . story . bodyType ;
// Get a list of all of the apparel worn by this pawn that covers this body part
var apparelCoveringPartList = GetApparelCoveringPart ( myPawn , __instance ) ;
// If no apparel is covering this part OR they are all revealing, then reveal this body part
if ( apparelCoveringPartList . Count ( ) = = 0 | |
apparelCoveringPartList . All ( apparel = > IsApparelRevealingBodyPart ( apparel , __instance , bodyType ) ) ) {
return true ;
}
}
// Else, return the original result
return __result ;
}
}
}