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; + } + } +}