diff --git a/1.3 Assembly/Assemblies/RimNudeWorld.dll b/1.3 Assembly/Assemblies/RimNudeWorld.dll
index 9f60a2b..d1544c6 100644
Binary files a/1.3 Assembly/Assemblies/RimNudeWorld.dll and b/1.3 Assembly/Assemblies/RimNudeWorld.dll differ
diff --git a/Source/RimNudeWorld/RimNudeWorld/RimNudeWorld.csproj b/Source/RimNudeWorld/RimNudeWorld/RimNudeWorld.csproj
index 59d7d3f..e7906a1 100644
--- a/Source/RimNudeWorld/RimNudeWorld/RimNudeWorld.csproj
+++ b/Source/RimNudeWorld/RimNudeWorld/RimNudeWorld.csproj
@@ -47,7 +47,7 @@
False
- ..\..\..\..\RJW-4.8.2\1.3\Assemblies\RJW.dll
+ ..\..\..\..\rjw\1.3\Assemblies\RJW.dll
False
@@ -56,20 +56,21 @@
-
- ..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll
- False
-
..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll
False
+
+ ..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.IMGUIModule.dll
+ False
+
+
diff --git a/Source/RimNudeWorld/RimNudeWorld/patches/HarmonyPatch_GetErect.cs b/Source/RimNudeWorld/RimNudeWorld/patches/HarmonyPatch_GetErect.cs
index 6c1204f..c8e839e 100644
--- a/Source/RimNudeWorld/RimNudeWorld/patches/HarmonyPatch_GetErect.cs
+++ b/Source/RimNudeWorld/RimNudeWorld/patches/HarmonyPatch_GetErect.cs
@@ -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("Genitals/Pubes/Shaved", __result.Shader, __result.drawSize, __result.color, __result.colorTwo);
return;
diff --git a/Source/RimNudeWorld/RimNudeWorld/patches/Ideology/StylingStation.cs b/Source/RimNudeWorld/RimNudeWorld/patches/Ideology/StylingStation.cs
new file mode 100644
index 0000000..cf24942
--- /dev/null
+++ b/Source/RimNudeWorld/RimNudeWorld/patches/Ideology/StylingStation.cs
@@ -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> 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>();
+
+ foreach (ThingDef_AlienRace race in DefDatabase.AllDefsListForReading)
+ {
+ if (!all_pubes.ContainsKey(race.defName))
+ {
+ all_pubes.Add(race.defName, new List());
+ }
+
+ List 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();
+ if (comp == null)
+ {
+ if (NudeSettings.debugMode)
+ Log.Message("Pawn has no AlienComp");
+ return;
+ }
+
+ // determine pawn's "equipped" pubes
+ List addonGraphics = new List(comp.addonGraphics);
+ List addonVariants = new List(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(__instance, "pawn");
+ if (p.thingIDNumber == curpawnid)
+ {
+ // get AlienComp
+ AlienPartGenerator.AlienComp comp = p.GetComp();
+ 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 ___tabs, Dialog_StylingStation.StylingTab ___curTab, float ___viewRectHeight, List ___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, "viewRectHeight");
+ //bool devEditMode = AccessTools.FieldRefAccess(dialog_StylingStation, "devEditMode");
+ Color desiredHairColor = AccessTools.FieldRefAccess(dialog_StylingStation, "desiredHairColor");
+ //MethodInfo DrawHairColors = AccessTools.Method(typeof(Dialog_StylingStation), "DrawHairColors");
+
+ Pawn pawn = AccessTools.FieldRefAccess(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();
+ if (comp == null)
+ {
+ if (NudeSettings.debugMode)
+ Log.Message("Pawn has no AlienComp");
+ return;
+ }
+
+ // determine pawn's "equipped" pubes
+ List addonGraphics = new List(comp.addonGraphics);
+ List addonVariants = new List(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.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 allHairColors;
+ private static List AllHairColors
+ {
+ get
+ {
+ if (allHairColors == null)
+ {
+ allHairColors = (from ic in DefDatabase.AllDefsListForReading
+ select ic.color).ToList();
+ allHairColors.SortByColor((Color x) => x);
+ }
+ return allHairColors;
+ }
+ }
+
+ static void AddPubicHairTab(Dialog_StylingStation stylingStation, List 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 Transpiler(IEnumerable instructions)
+ {
+ bool isHair = false;
+ MethodInfo tabAdd = AccessTools.DeclaredMethod(typeof(List), "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;
+ }
+ }
+}