mirror of
https://gitgud.io/Tory/rimnude-unofficial.git
synced 2024-08-15 00:03:30 +00:00
Styling Station, mostly
This commit is contained in:
parent
41a120033d
commit
96bcaeb829
4 changed files with 407 additions and 7 deletions
Binary file not shown.
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue