rimnude-unofficial/Source/RimNudeWorld/RimNudeWorld/patches/RevealingApparel.cs

104 lines
4.9 KiB
C#

using AlienRace;
using AlienRace.ExtendedGraphics;
using HarmonyLib;
using RimWorld;
using System.Collections.Generic;
using System.Linq;
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
{
// 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>();
}
// 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;
// A list of pawn body types this entry applies to.
// Examples include "Female" or "Thin" or "Hulk"
public List<BodyTypeDef> revealingBodyTypes = new List<BodyTypeDef>();
}
// 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 Pawn GetPawnFromWrapped(ExtendedGraphicsPawnWrapper pawn)
{
// 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>();
}
public static IEnumerable<Apparel> GetApparelCoveringPart(Pawn pawn, AlienPartGenerator.BodyAddon bodyAddon)
{
// 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;
}
}
}