Styling Station, mostly

This commit is contained in:
nugerumon 2022-01-15 23:09:36 +00:00 committed by Tory
parent 41a120033d
commit 96bcaeb829
4 changed files with 407 additions and 7 deletions

View file

@ -47,7 +47,7 @@
<Private>False</Private>
</Reference>
<Reference Include="RJW">
<HintPath>..\..\..\..\RJW-4.8.2\1.3\Assemblies\RJW.dll</HintPath>
<HintPath>..\..\..\..\rjw\1.3\Assemblies\RJW.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
@ -56,20 +56,21 @@
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="UnityEngine">
<HintPath>..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedGraphics.cs" />
<Compile Include="patches\BodyTypeAddon.cs" />
<Compile Include="patches\HarmonyPatch_GetErect.cs" />
<Compile Include="patches\HAR\HarmonyPatch_GenitalRotation.cs" />
<Compile Include="patches\Ideology\StylingStation.cs" />
<Compile Include="patches\RevealingApparel.cs" />
<Compile Include="patches\rjw\HarmonyPatch_AddBodyGraphicForRJW.cs" />
<Compile Include="patches\rjw\HarmonyPatch_ResolveGraphicsOnStart.cs" />

View file

@ -21,6 +21,7 @@ namespace RimNudeWorld
Harmony har = new Harmony("RimNudeWorld");
har.PatchAll(Assembly.GetExecutingAssembly());
PubesManager.PatchStylingStation(har);
}
}
@ -71,9 +72,10 @@ namespace RimNudeWorld
return;
}
else if (NudeSettings.pubicHair && originalPath.Length >= 5 && originalPath.Contains("Pubes"))
// if pawn can not do lovin, or pubicHair is supposed to be hidden, use shaved texture
else if ((NudeSettings.pubicHair || (rjw.xxx.is_human(pawn) && !rjw.xxx.can_do_loving(pawn))) && originalPath.Length >= 5 && originalPath.Contains("Pubes"))
{
__result = GraphicDatabase.Get<Graphic_Multi>("Genitals/Pubes/Shaved", __result.Shader, __result.drawSize, __result.color, __result.colorTwo);
return;

View file

@ -0,0 +1,397 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using RimWorld;
using Verse;
using System.Reflection;
using System.Reflection.Emit;
using UnityEngine;
using AlienRace;
using System.Linq;
using Verse.Sound;
namespace RimNudeWorld
{
[StaticConstructorOnStartup]
static class PubesManager
{
public static Dictionary<string, List<AlienPartGenerator.BodyAddon>> all_pubes;
public static int curpawnid;
public static string curgraphicspath;
public static int curvariant;
public static int curcompindex;
static PubesManager()
{
// find all pubes that exist, sorted by race name
all_pubes = new Dictionary<string, List<AlienPartGenerator.BodyAddon>>();
foreach (ThingDef_AlienRace race in DefDatabase<ThingDef_AlienRace>.AllDefsListForReading)
{
if (!all_pubes.ContainsKey(race.defName))
{
all_pubes.Add(race.defName, new List<AlienPartGenerator.BodyAddon>());
}
List<AlienPartGenerator.BodyAddon> ba_pubes = race?.alienRace?.generalSettings?.alienPartGenerator?.bodyAddons.Where(x => x.bodyPart == "Genitals" && x.path.ToLower().Contains("pubes")).ToList();
if (ba_pubes == null || ba_pubes.Count() <= 0)
continue;
foreach (AlienPartGenerator.BodyAddon ba in ba_pubes)
{
all_pubes[race.defName].Add(ba);
}
}
if (NudeSettings.debugMode)
{
foreach (string k in all_pubes.Keys)
{
foreach (AlienPartGenerator.BodyAddon b in all_pubes[k])
{
Log.Message("> " + k + " : " + b.path + " (" + b.variantCount.ToString() + " Variants)");
}
}
}
}
public static void PatchStylingStation(Harmony h)
{
// when opening styling station window (i.e. on constructor), save current pubes of pawn
// figuring out how to postfix constructor was a pita
MethodBase StylingStationConstructor = (MethodBase)(typeof(Dialog_StylingStation).GetMember(".ctor", AccessTools.all)[0]);
MethodInfo savePubes = typeof(PubesManager).GetMethod("saveCurPubes");
h.Patch(StylingStationConstructor, null, new HarmonyMethod(savePubes));
// prefix to restoring pubes, as end of reset() function has some mark-as-dirty stuff
MethodInfo StylingStationReset = typeof(Dialog_StylingStation).GetMethod("Reset", AccessTools.all);
MethodInfo ResetPubes = typeof(PubesManager).GetMethod("restorePubes");
h.Patch(StylingStationReset, new HarmonyMethod(ResetPubes));
}
public static void saveCurPubes(Pawn pawn, Thing stylingStation)
{
// invalidate potentially saved variables in case of error, so resetting doesn't try to set pubes from previous styling station user
curpawnid = -1;
// get AlienComp
AlienPartGenerator.AlienComp comp = pawn.GetComp<AlienPartGenerator.AlienComp>();
if (comp == null)
{
if (NudeSettings.debugMode)
Log.Message("Pawn has no AlienComp");
return;
}
// determine pawn's "equipped" pubes
List<Graphic> addonGraphics = new List<Graphic>(comp.addonGraphics);
List<int> addonVariants = new List<int>(comp.addonVariants);
if (addonGraphics.Count() != addonVariants.Count())
{
if (NudeSettings.debugMode)
Log.Message("addonGraphics and addonVariants not same length");
return;
}
string mygraphicspath = "";
int myvariant = -1;
int compindex = -1;
for (int i = 0; i < addonGraphics.Count(); i++)
{
if (addonGraphics[i].path.ToLower().Contains("pubes"))
{
mygraphicspath = addonGraphics[i].path;
myvariant = addonVariants[i];
compindex = i;
break;
}
}
// save
curpawnid = pawn.thingIDNumber;
curgraphicspath = mygraphicspath;
curvariant = myvariant;
curcompindex = compindex;
}
public static bool restorePubes(bool resetColors, Dialog_StylingStation __instance)
{
// Reset() is called with parameter resetColors==true when either "Cancel" or "Reset" buttons are clicked
// Reset() might be called with parameter resetColors==false when "Accept" is clicked
// -> if resetColors==true, reset pubes to original style
if (NudeSettings.debugMode)
Log.Message("Restoring Pubes: " + resetColors.ToString());
if (resetColors)
{
Pawn p = AccessTools.FieldRefAccess<Dialog_StylingStation, Pawn>(__instance, "pawn");
if (p.thingIDNumber == curpawnid)
{
// get AlienComp
AlienPartGenerator.AlienComp comp = p.GetComp<AlienPartGenerator.AlienComp>();
if (comp == null)
{
if (NudeSettings.debugMode)
Log.Message("Pawn has no AlienComp");
return true;
}
comp.addonVariants[curcompindex] = curvariant;
}
}
return true;
}
}
[HarmonyPatch(typeof(Dialog_StylingStation), "DrawTabs")]
class StylingStation
{
public static void Postfix(Rect rect, ref Vector2 ___hairScrollPosition, Dialog_StylingStation __instance, List<TabRecord> ___tabs, Dialog_StylingStation.StylingTab ___curTab, float ___viewRectHeight, List<StyleItemDef> ___tmpStyleItems, bool ___devEditMode, Pawn ___pawn)
{
switch (___curTab)
{
case (Dialog_StylingStation.StylingTab)48: // was 24
//Draw PubicHair Tab Code here!
DrawStylingTypePubicHair(__instance, rect, ref ___hairScrollPosition);
return;
default:
return;
}
}
public static float[] zoomOffsets()
{
// have these offsets be returned by a public function so mods adding larger pubes (or pubes that are offset) can change them easily
return new float[4]{.5f, .17f, 2f, 2f };
}
static void DrawStylingTypePubicHair(Dialog_StylingStation dialog_StylingStation, Rect rect, ref Vector2 scrollPosition)
{
// access all the variables needed...
//float viewRectHeight = AccessTools.FieldRefAccess<Dialog_StylingStation, float>(dialog_StylingStation, "viewRectHeight");
//bool devEditMode = AccessTools.FieldRefAccess<Dialog_StylingStation, bool>(dialog_StylingStation, "devEditMode");
Color desiredHairColor = AccessTools.FieldRefAccess<Dialog_StylingStation, Color>(dialog_StylingStation, "desiredHairColor");
//MethodInfo DrawHairColors = AccessTools.Method(typeof(Dialog_StylingStation), "DrawHairColors");
Pawn pawn = AccessTools.FieldRefAccess<Dialog_StylingStation, Pawn>(dialog_StylingStation, "pawn");
// get available pubes
ThingDef_AlienRace race = pawn.def as ThingDef_AlienRace;
// determine number of available pubes
int total_pubes_count = 0;
foreach (AlienPartGenerator.BodyAddon pube in PubesManager.all_pubes[race.defName])
{
total_pubes_count += pube.variantCount;
}
if (total_pubes_count <= 0)
{
Widgets.NoneLabelCenteredVertically(rect, "(" + "NoneUsableForPawn".Translate(pawn.Named("PAWN")) + ")");
return;
}
// get AlienComp
AlienPartGenerator.AlienComp comp = pawn.GetComp<AlienPartGenerator.AlienComp>();
if (comp == null)
{
if (NudeSettings.debugMode)
Log.Message("Pawn has no AlienComp");
return;
}
// determine pawn's "equipped" pubes
List<Graphic> addonGraphics = new List<Graphic>(comp.addonGraphics);
List<int> addonVariants = new List<int>(comp.addonVariants);
if (addonGraphics.Count() != addonVariants.Count())
{
if(NudeSettings.debugMode)
Log.Message("addonGraphics and addonVariants not same length");
return;
}
string mygraphicspath = "";
int myvariant = -1;
int compindex = -1;
for (int i = 0; i < addonGraphics.Count(); i++)
{
if (addonGraphics[i].path.ToLower().Contains("pubes"))
{
mygraphicspath = addonGraphics[i].path;
myvariant = addonVariants[i];
compindex = i;
break;
}
}
//Log.Message("current pubes: " + mygraphicspath + " / " + myvariant.ToString());
const float btn_size = 70f; // how much space is reserverd for on icon, including border / distance to next // to mimic vanilla, use 70
int items_per_row = Mathf.FloorToInt((rect.width - 16) / btn_size);
int total_rows = Mathf.CeilToInt(total_pubes_count / items_per_row);
Rect viewRect = new Rect(rect.x, rect.y, rect.width - 16f, total_rows * btn_size);
float border_space = ((rect.width - 16f) - (items_per_row * btn_size)) / 2;
Vector2 currentDrawPos = new Vector2(rect.x + border_space, rect.y);
Widgets.BeginScrollView(rect, ref scrollPosition, viewRect, true);
foreach (AlienPartGenerator.BodyAddon pube in PubesManager.all_pubes[race.defName])
{
for (int i = 0; i < pube.variantCount; i++)
{
// if larger than initial position and next would go too far right, go to new line
if (currentDrawPos.x > rect.x+border_space && currentDrawPos.x+btn_size > rect.x + rect.width - 16)
{
currentDrawPos.x = rect.x + border_space;
currentDrawPos.y += btn_size;
}
Rect rect_outer = new Rect(currentDrawPos.x, currentDrawPos.y, btn_size-10, btn_size-10);
string _path = pube.path + ((i == 0) ? "" : i.ToString());
Texture2D pubes_texture = ContentFinder<Texture2D>.Get(_path+"_south");
Widgets.DrawHighlight(rect_outer);
if (mygraphicspath == _path || mygraphicspath==(_path+"_Female"))
{
// draw box around currently "equipped" pubes
Widgets.DrawBox(rect_outer, 2);
}
if (Mouse.IsOver(rect_outer))
{
Widgets.DrawHighlight(rect_outer);
//TooltipHandler.TipRegion(rect2, styleItemDef.LabelCap);
}
if (Widgets.ButtonInvisible(rect_outer, true))
{
// this button applies the pubes
comp.addonVariants[compindex] = i;
SoundDefOf.Tick_High.PlayOneShotOnCamera();
pawn.Drawer.renderer.graphics.SetAllGraphicsDirty();
PortraitsCache.SetDirty(pawn);
}
// actually paint pubes
GUI.color = desiredHairColor;
Rect zoomRect = new Rect(rect_outer.x - zoomOffsets()[0] * rect_outer.width,
rect_outer.y - zoomOffsets()[1] * rect_outer.height,
zoomOffsets()[2] * rect_outer.width,
zoomOffsets()[3] * rect_outer.height);
GUI.DrawTexture(zoomRect, pubes_texture);
GUI.color = Color.white;
currentDrawPos.x += btn_size;
}
}
Widgets.EndScrollView();
// draw hair colors with inlined dye requirements
// just copypasted from vanilla
Rect newrect = new Rect(rect.x, rect.yMax - 100f, rect.width, 110f);
Color _desiredHairColor = desiredHairColor;
float num = newrect.y;
Widgets.ColorSelector(new Rect(newrect.x, num, newrect.width, 92f), ref _desiredHairColor, AllHairColors, null, 22, 2);
if (_desiredHairColor != desiredHairColor)
{
var desiredHairColor_ = dialog_StylingStation.GetType().GetField("desiredHairColor", System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance);
desiredHairColor_.SetValue(dialog_StylingStation, _desiredHairColor);
}
num += 60f;
if (desiredHairColor != pawn.story.hairColor && desiredHairColor != pawn.style.nextHairColor)
{
Widgets.ThingIcon(new Rect(newrect.x, num, Text.LineHeight, Text.LineHeight), ThingDefOf.Dye, null, null, 1.1f, null);
string text = "Required".Translate() + ": 1 " + ThingDefOf.Dye.label;
float x = Text.CalcSize(text).x;
Widgets.Label(new Rect(newrect.x + Text.LineHeight + 4f, num, x, Text.LineHeight), text);
Rect rect2 = new Rect(newrect.x, num, x + Text.LineHeight + 8f, Text.LineHeight);
if (Mouse.IsOver(rect2))
{
Widgets.DrawHighlight(rect2);
TooltipHandler.TipRegionByKey(rect2, "TooltipDyeExplanation");
}
num += Text.LineHeight;
if (pawn.Map.resourceCounter.GetCount(ThingDefOf.Dye) < 1)
{
rect2 = new Rect(newrect.x, num, newrect.width, Text.LineHeight);
Color color = GUI.color;
GUI.color = ColorLibrary.RedReadable;
Widgets.Label(rect2, "NotEnoughDye".Translate() + " " + "NotEnoughDyeWillRecolorHair".Translate());
GUI.color = color;
num += rect2.height;
}
}
}
// copypasted from vanilla
private static List<Color> allHairColors;
private static List<Color> AllHairColors
{
get
{
if (allHairColors == null)
{
allHairColors = (from ic in DefDatabase<ColorDef>.AllDefsListForReading
select ic.color).ToList<Color>();
allHairColors.SortByColor((Color x) => x);
}
return allHairColors;
}
}
static void AddPubicHairTab(Dialog_StylingStation stylingStation, List<TabRecord> tabs)
{
var curTabField = AccessTools.Field(typeof(Dialog_StylingStation), "curTab");
//tabs.Add(new TabRecord("PubicHair".Translate().CapitalizeFirst(), delegate ()
tabs.Add(new TabRecord("Pubic Hair", delegate ()
{
curTabField.SetValue(stylingStation, (Dialog_StylingStation.StylingTab)48);// was 24
}, (Dialog_StylingStation.StylingTab)curTabField.GetValue(stylingStation) == (Dialog_StylingStation.StylingTab)48)); // was 24
}
// code copied from OTYOTY's Sized Apparel mod
static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
bool isHair = false;
MethodInfo tabAdd = AccessTools.DeclaredMethod(typeof(List<TabRecord>), "Add");
foreach (var instruction in instructions)
{
if (instruction.opcode == OpCodes.Ldstr)
{
if (instruction.OperandIs("Hair"))
isHair = true;
else
isHair = false;
}
if (isHair && instruction.opcode == OpCodes.Callvirt && instruction.OperandIs(tabAdd))
{
yield return instruction;//finish add hairTab
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(Dialog_StylingStation), "tabs"));
yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(StylingStation), "AddPubicHairTab"));
isHair = false;
}
else
yield return instruction;
}
yield break;
}
}
}