From cdc79acfa3080b3b06f2c80aa35c44e5cf7d5869 Mon Sep 17 00:00:00 2001 From: amevarashi Date: Sun, 16 Apr 2023 12:09:50 +0500 Subject: [PATCH 1/6] Refactored/optimized SexStatusWindow --- Languages/English/Keyed/RJW_Sexperience.xml | 1 + Source/RJWSexperience/Keyed.cs | 9 +- Source/RJWSexperience/RJWSexperience.csproj | 13 +- .../SexHistory/HistoryUtility.cs | 1 - .../SexHistory/SexHistoryComp.cs | 11 +- .../SexHistory/SexPartnerHistoryRecord.cs | 29 - .../RJWSexperience/SexHistory/UI/BarInfo.cs | 35 + .../RJWSexperience/SexHistory/UI/InfoCard.cs | 86 +++ .../SexHistory/UI/PartnerOrderMode.cs | 18 + .../SexHistory/UI/PartnerPortraitInfo.cs | 34 + .../SexHistory/UI/PreferedRaceCard.cs | 56 ++ .../SexHistory/UI/RJWUIUtility.cs | 101 --- .../RJWSexperience/SexHistory/UI/SexStatus.cs | 654 ------------------ .../SexHistory/UI/SexStatusViewModel.cs | 374 ++++++++++ .../SexHistory/UI/SexStatusWindow.cs | 473 +++++++++++++ .../RJWSexperience/SexHistory/UI/UIUtility.cs | 80 +++ 16 files changed, 1178 insertions(+), 797 deletions(-) create mode 100644 Source/RJWSexperience/SexHistory/UI/BarInfo.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/InfoCard.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/PartnerOrderMode.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/PartnerPortraitInfo.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/PreferedRaceCard.cs delete mode 100644 Source/RJWSexperience/SexHistory/UI/RJWUIUtility.cs delete mode 100644 Source/RJWSexperience/SexHistory/UI/SexStatus.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs create mode 100644 Source/RJWSexperience/SexHistory/UI/UIUtility.cs diff --git a/Languages/English/Keyed/RJW_Sexperience.xml b/Languages/English/Keyed/RJW_Sexperience.xml index daa7bc3..b093beb 100644 --- a/Languages/English/Keyed/RJW_Sexperience.xml +++ b/Languages/English/Keyed/RJW_Sexperience.xml @@ -43,6 +43,7 @@ ago last sex Had best sex {0}. + Lock displayed pawn.\n\nWhen unlocked, window will automatically switch to a currently selected pawn. Main diff --git a/Source/RJWSexperience/Keyed.cs b/Source/RJWSexperience/Keyed.cs index 598ae22..ee3eb09 100644 --- a/Source/RJWSexperience/Keyed.cs +++ b/Source/RJWSexperience/Keyed.cs @@ -9,12 +9,14 @@ namespace RJWSexperience public static class Keyed { public static string RS_LostVirgin(string pawn, string partner) => "RS_LostVirgin".Translate(pawn.Colorize(Color.yellow), partner.Colorize(Color.yellow)); - public static string RS_Sex_Info(string sextype, string sexcount) => "RS_Sex_Info".Translate(sextype, sexcount); - public static string RS_SAT_AVG(string avgsat) => "RS_SAT_AVG".Translate(avgsat); + public static string RS_SexInfo(string sextype, int sexcount) => string.Format(RS_Sex_Info, sextype, sexcount); + public static string RS_SatAVG(float avgsat) => string.Format(RS_SAT_AVG, avgsat.ToStringPercent()); public static string RS_HadBestSexDaysAgo(string days) => "RS_HadBestSexDaysAgo".Translate(days); public static readonly string Mod_Title = "RS_Mod_Title".Translate(); public static readonly string RSTotalGatheredCum = "RSTotalGatheredCum".Translate(); + public static readonly string RS_Sex_Info = "RS_Sex_Info".Translate(); + public static readonly string RS_SAT_AVG = "RS_SAT_AVG".Translate(); public static readonly string RS_Best_Sextype = "RS_Best_Sextype".Translate(); public static readonly string RS_Recent_Sextype = "RS_Recent_Sextype".Translate(); public static readonly string RS_Sex_Partners = "RS_Sex_Partners".Translate(); @@ -51,7 +53,8 @@ namespace RJWSexperience public static readonly string RS_SexSkill = "RS_SexSkill".Translate(); public static readonly string RS_NumofTimes = "RS_NumofTimes".Translate(); public static readonly string RS_Ago = "RS_Ago".Translate(); - public static readonly string RS_LastSex = "RS_LastSex".Translate(); + public static readonly string RS_LastSex = "RS_LastSex".Translate().CapitalizeFirst(); + public static readonly string RS_PawnLockDesc = "RS_PawnLockDesc".Translate(); [MayRequireRoyalty] public static readonly string Slave = "Slave".Translate(); public static readonly string TabLabelMain = "RSTabLabelMain".Translate(); diff --git a/Source/RJWSexperience/RJWSexperience.csproj b/Source/RJWSexperience/RJWSexperience.csproj index 6540f16..ac5c39f 100644 --- a/Source/RJWSexperience/RJWSexperience.csproj +++ b/Source/RJWSexperience/RJWSexperience.csproj @@ -30,6 +30,9 @@ prompt 4 + + true + ..\..\..\rjw\1.4\Assemblies\RJW.dll @@ -64,6 +67,12 @@ + + + + + + @@ -86,8 +95,8 @@ - - + + diff --git a/Source/RJWSexperience/SexHistory/HistoryUtility.cs b/Source/RJWSexperience/SexHistory/HistoryUtility.cs index 9e06c28..fede9bc 100644 --- a/Source/RJWSexperience/SexHistory/HistoryUtility.cs +++ b/Source/RJWSexperience/SexHistory/HistoryUtility.cs @@ -1,5 +1,4 @@ using RimWorld; -using System; using UnityEngine; using Verse; diff --git a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs index 65f7928..dcd25e1 100644 --- a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs +++ b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs @@ -90,15 +90,15 @@ namespace RJWSexperience.SexHistory } } public int VirginsTaken => virginsTaken; - public List PartnerList + public IEnumerable PartnerList { get { - List res = null; + IEnumerable res = Enumerable.Empty(); Update(); if (!histories.NullOrEmpty()) { - res = histories.Values.ToList(); + res = histories.Values; } return res; } @@ -443,10 +443,7 @@ namespace RJWSexperience.SexHistory icon = HistoryUtility.HistoryIcon, defaultIconColor = HistoryUtility.HistoryColor, hotKey = VariousDefOf.OpenSexStatistics, - action = delegate - { - UI.SexStatusWindow.ToggleWindow(ParentPawn, this); - } + action = () => UI.SexStatusWindow.ToggleWindow(this) }; } } diff --git a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs index cb17485..12c0f7d 100644 --- a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs +++ b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs @@ -1,6 +1,5 @@ using rjw; using RJWSexperience.ExtensionMethods; -using System.Collections.Generic; using Verse; namespace RJWSexperience.SexHistory @@ -131,33 +130,5 @@ namespace RJWSexperience.SexHistory } partner = Find.WorldPawns.AllPawnsAliveOrDead.Find(x => x.ThingID.Equals(partnerID)); } - - #region OrderComparers - - public class RecentOrderComparer : IComparer - { - public int Compare(SexPartnerHistoryRecord x, SexPartnerHistoryRecord y) - { - return y.RecentSexTickAbs.CompareTo(x.RecentSexTickAbs); - } - } - - public class MostOrderComparer : IComparer - { - public int Compare(SexPartnerHistoryRecord x, SexPartnerHistoryRecord y) - { - return y.TotalSexCount.CompareTo(x.TotalSexCount); - } - } - - public class NameOrderComparer : IComparer - { - public int Compare(SexPartnerHistoryRecord x, SexPartnerHistoryRecord y) - { - return x.Label.CompareTo(y.Label); - } - } - - #endregion OrderComparers } } diff --git a/Source/RJWSexperience/SexHistory/UI/BarInfo.cs b/Source/RJWSexperience/SexHistory/UI/BarInfo.cs new file mode 100644 index 0000000..b03341b --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/BarInfo.cs @@ -0,0 +1,35 @@ +using UnityEngine; +using Verse; + +namespace RJWSexperience.SexHistory.UI +{ + public readonly struct BarInfo + { + public readonly string label; + public readonly float fillPercent; + public readonly Texture2D fillTexture; + public readonly TipSignal tooltip; + public readonly string labelRight; + public readonly Texture2D border; + + public BarInfo(string label, float fillPercent, Texture2D fillTexture, TipSignal tooltip, string labelRight = "", Texture2D border = null) + { + this.label = label.CapitalizeFirst(); + this.fillPercent = Mathf.Clamp01(fillPercent); + this.fillTexture = fillTexture; + this.tooltip = tooltip; + this.labelRight = labelRight.CapitalizeFirst(); + this.border = border; + } + + public BarInfo(string label, float fillPercent, Texture2D fillTexture, string labelRight = "") + { + this.label = label.CapitalizeFirst(); + this.fillPercent = Mathf.Clamp01(fillPercent); + this.fillTexture = fillTexture; + this.tooltip = default; + this.labelRight = labelRight.CapitalizeFirst(); + this.border = null; + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/InfoCard.cs b/Source/RJWSexperience/SexHistory/UI/InfoCard.cs new file mode 100644 index 0000000..108305e --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/InfoCard.cs @@ -0,0 +1,86 @@ +using RimWorld; +using rjw; +using System; +using UnityEngine; +using Verse; + +namespace RJWSexperience.SexHistory.UI +{ + public readonly struct InfoCard + { + public readonly SexPartnerHistoryRecord partnerRecord; + public readonly string label; + public readonly string lastSexTime; + public readonly string name; + public readonly string sexCount; + public readonly string orgasms; + public readonly string relations; + public readonly BarInfo bestSextype; + public readonly PartnerPortraitInfo portraitInfo; + public readonly TipSignal tooltip; + + public InfoCard(Pawn pawn, SexPartnerHistoryRecord partnerRecord, string label, string tooltipLabel, int lastSexTimeTicks) + { + this.partnerRecord = partnerRecord; + this.label = label; + + lastSexTime = UIUtility.GetSexDays(lastSexTimeTicks); + portraitInfo = new PartnerPortraitInfo(pawn, partnerRecord); + + if (partnerRecord != null) + { + name = partnerRecord.Partner?.Name?.ToStringFull ?? partnerRecord.Label.CapitalizeFirst(); + sexCount = Keyed.RS_Sex_Count + partnerRecord.TotalSexCount; + + if (partnerRecord.Raped > 0) + { + sexCount += " " + Keyed.RS_Raped + partnerRecord.Raped; + } + if (partnerRecord.RapedMe > 0) + { + sexCount += " " + Keyed.RS_RapedMe + partnerRecord.RapedMe; + } + + orgasms = Keyed.RS_Orgasms + partnerRecord.OrgasmCount; + relations = pawn.GetRelationsString(partnerRecord.Partner); + tooltip = new TipSignal(() => + { + string completeTip = tooltipLabel; + + if (partnerRecord.Incest) + { + completeTip += " - " + Keyed.Incest; + } + if (partnerRecord.IamFirst) + { + completeTip += "\n" + Keyed.RS_LostVirgin(partnerRecord.Label, pawn.LabelShort); + } + if (partnerRecord.BestSexTickAbs != 0) + { + completeTip += "\n" + Keyed.RS_HadBestSexDaysAgo(partnerRecord.BestSexElapsedTicks.ToStringTicksToDays() + " " + Keyed.RS_Ago); + } + return completeTip; + }, tooltipLabel.GetHashCode()); + + float relativeBestSatisfaction = partnerRecord.BestSatisfaction / UIUtility.BASESAT; + bestSextype = new BarInfo( + label: Keyed.RS_Best_Sextype + ": " + Keyed.Sextype[(int)partnerRecord.BestSextype], + fillPercent: relativeBestSatisfaction / 2, + fillTexture: HistoryUtility.SextypeColor[(int)partnerRecord.BestSextype], + labelRight: relativeBestSatisfaction.ToStringPercent()); + } + else + { + name = Keyed.Unknown; + sexCount = Keyed.RS_Sex_Count + "?"; + orgasms = Keyed.RS_Orgasms + "?"; + relations = string.Empty; + tooltip = default; + bestSextype = new BarInfo( + label: String.Format(Keyed.RS_Best_Sextype + ": {0}", Keyed.Sextype[(int)xxx.rjwSextype.None]), + fillPercent: 0f, + fillTexture: Texture2D.linearGrayTexture); + } + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/PartnerOrderMode.cs b/Source/RJWSexperience/SexHistory/UI/PartnerOrderMode.cs new file mode 100644 index 0000000..2fc7401 --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/PartnerOrderMode.cs @@ -0,0 +1,18 @@ +namespace RJWSexperience.SexHistory.UI +{ + public enum PartnerOrderMode + { + Normal = 0, + Recent = 1, + Most = 2, + Name = 3 + }; + + public static class PartnerOrderModeExtension + { + public static PartnerOrderMode Next(this PartnerOrderMode mode) + { + return (PartnerOrderMode)(((int)mode + 1) % 4); + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/PartnerPortraitInfo.cs b/Source/RJWSexperience/SexHistory/UI/PartnerPortraitInfo.cs new file mode 100644 index 0000000..2de32de --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/PartnerPortraitInfo.cs @@ -0,0 +1,34 @@ +using RimWorld; +using System; +using UnityEngine; +using Verse; + +namespace RJWSexperience.SexHistory.UI +{ + public readonly struct PartnerPortraitInfo + { + public readonly SexPartnerHistoryRecord partnerRecord; + public readonly bool lover; + public readonly Func portraitGetter; + + public PartnerPortraitInfo(Pawn pawn, SexPartnerHistoryRecord partnerRecord) + { + this.partnerRecord = partnerRecord; + lover = false; + + if (partnerRecord?.Partner != null) + { + portraitGetter = (size) => PortraitsCache.Get(partnerRecord.Partner, size, Rot4.South, default, 1, true, true, false, false); + lover = LovePartnerRelationUtility.LovePartnerRelationExists(pawn, partnerRecord.Partner); + } + else if (partnerRecord?.Race?.uiIcon != null) + { + portraitGetter = (_) => partnerRecord.Race.uiIcon; + } + else + { + portraitGetter = (_) => HistoryUtility.UnknownPawn; + } + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/PreferedRaceCard.cs b/Source/RJWSexperience/SexHistory/UI/PreferedRaceCard.cs new file mode 100644 index 0000000..65d9e74 --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/PreferedRaceCard.cs @@ -0,0 +1,56 @@ +using System; +using UnityEngine; + +namespace RJWSexperience.SexHistory.UI +{ + public readonly struct PreferedRaceCard + { + public readonly string preferRaceLabel; + public readonly string preferRaceTypeLabel; + public readonly string sexCount; + public readonly BarInfo? barInfo; + public readonly Func portraitGetter; + + public PreferedRaceCard(SexHistoryComp sexHistory) + { + if (sexHistory.PreferRace == null) + { + preferRaceLabel = Keyed.None; + preferRaceTypeLabel = null; + sexCount = null; + barInfo = null; + portraitGetter = (_) => HistoryUtility.UnknownPawn; + return; + } + + preferRaceLabel = sexHistory.PreferRace.LabelCap; + sexCount = Keyed.RS_Sex_Count + sexHistory.PreferRaceSexCount; + portraitGetter = (size) => UIUtility.GetRaceIcon(sexHistory.PreferRacePawn, size); + + if (sexHistory.PreferRace != sexHistory.ParentPawn.def) + { + if (sexHistory.PreferRace.race.Animal != sexHistory.ParentPawn.def.race.Animal) + { + preferRaceTypeLabel = Keyed.RS_Bestiality; + barInfo = new BarInfo( + label: Keyed.RS_SexInfo(Keyed.RS_Bestiality, sexHistory.BestialityCount), + fillPercent: sexHistory.BestialityCount / 100f, + fillTexture: Texture2D.linearGrayTexture); + } + else + { + preferRaceTypeLabel = Keyed.RS_Interspecies; + barInfo = new BarInfo( + label: Keyed.RS_SexInfo(Keyed.RS_Interspecies, sexHistory.InterspeciesCount), + fillPercent: sexHistory.InterspeciesCount / 100f, + fillTexture: Texture2D.linearGrayTexture); + } + } + else + { + preferRaceTypeLabel = null; + barInfo = null; + } + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/RJWUIUtility.cs b/Source/RJWSexperience/SexHistory/UI/RJWUIUtility.cs deleted file mode 100644 index f1e70de..0000000 --- a/Source/RJWSexperience/SexHistory/UI/RJWUIUtility.cs +++ /dev/null @@ -1,101 +0,0 @@ -using RimWorld; -using rjw; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; -using Verse; - -namespace RJWSexperience.SexHistory.UI -{ - public static class RJWUIUtility - { - public const float FONTHEIGHT = 22f; - public const float CARDHEIGHT = 110f; - public const float LISTPAWNSIZE = 100f; - public const float BASESAT = 0.40f; - public const float ICONSIZE = 30f; - - public static void DrawQuirk(this Rect rect, Pawn pawn) - { - List quirks = Quirk.All.FindAll(x => pawn.Has(x)); - string quirkstr = quirks.Select(x => x.Key).ToCommaList(); - string tooltip = ""; - - Widgets.Label(rect, "Quirks".Translate() + quirkstr); - - if (Mouse.IsOver(rect)) - { - if (quirks.NullOrEmpty()) - { - tooltip = "NoQuirks".Translate(); - } - else - { - StringBuilder stringBuilder = new StringBuilder(); - foreach (var q in quirks) - { - stringBuilder.AppendLine(q.Key.Colorize(Color.yellow)); - stringBuilder.AppendLine(q.LocaliztionKey.Translate(pawn.Named("pawn")).AdjustedFor(pawn).Resolve()); - stringBuilder.AppendLine(""); - } - tooltip = stringBuilder.ToString().TrimEndNewlines(); - } - Widgets.DrawHighlight(rect); - } - - TooltipHandler.TipRegion(rect, tooltip); - } - - public static void DrawSexuality(this Rect rect, CompRJW comp) - { - if (comp != null) - { - string sexuality = Keyed.Sexuality[(int)comp.orientation]; - Widgets.Label(rect, Keyed.RS_Sexuality + ": " + sexuality); - Widgets.DrawHighlightIfMouseover(rect); - } - } - - public static string GetRelationsString(this Pawn pawn, Pawn otherpawn) - { - if (otherpawn != null) - { - IEnumerable relations = pawn.GetRelations(otherpawn); - if (!relations.EnumerableNullOrEmpty()) return relations.Select(x => x.GetGenderSpecificLabel(otherpawn)).ToCommaList().CapitalizeFirst(); - } - return ""; - } - - public static void DrawBorder(this Rect rect, Texture border, float thickness = 1f) - { - GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, thickness), border); - GUI.DrawTexture(new Rect(rect.x + rect.width - thickness, rect.y, thickness, rect.height), border); - GUI.DrawTexture(new Rect(rect.x, rect.y + rect.height - thickness, rect.width, thickness), border); - GUI.DrawTexture(new Rect(rect.x, rect.y, thickness, rect.height), border); - } - - public static string GetStatExplanation(Pawn pawn, StatDef stat, float val) - { - if (!pawn.Dead) - return stat.description + "\n" + stat.Worker.GetExplanationFull(StatRequest.For(pawn), ToStringNumberSense.Undefined, val); - return "Dead".Translate(); - } - - public static string GetSexDays(int absticks, bool printUnknown = false) - { - if (absticks != 0) - return GenDate.ToStringTicksToDays(GenTicks.TicksAbs - absticks) + " " + Keyed.RS_Ago; - else if (printUnknown) - return Keyed.Unknown; - return ""; - } - - public static Texture GetRaceIcon(Pawn pawn, Vector2 size) - { - if (pawn != null) - return PortraitsCache.Get(pawn, size, Rot4.South, default, 1, true, true, false, false); - return HistoryUtility.UnknownPawn; - } - } -} diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatus.cs b/Source/RJWSexperience/SexHistory/UI/SexStatus.cs deleted file mode 100644 index 880933b..0000000 --- a/Source/RJWSexperience/SexHistory/UI/SexStatus.cs +++ /dev/null @@ -1,654 +0,0 @@ -using RimWorld; -using rjw; -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using Verse; -using Verse.Sound; - -namespace RJWSexperience.SexHistory.UI -{ - public enum PartnerOrderMode - { - Normal = 0, - Recent = 1, - Most = 2, - Name, MaxValue = 3 - }; - - public static class PartnerOrderModeExtension - { - public static PartnerOrderMode Next(this PartnerOrderMode mode) - { - return (PartnerOrderMode)(((int)mode + 1) % ((int)PartnerOrderMode.MaxValue + 1)); - } - } - - public class SexStatusWindow : Window - { - public const float WINDOW_WIDTH = 900f; - public const float WINDOW_HEIGHT = 600f; - public const float FONTHEIGHT = RJWUIUtility.FONTHEIGHT; - public const float CARDHEIGHT = RJWUIUtility.CARDHEIGHT; - public const float LISTPAWNSIZE = RJWUIUtility.LISTPAWNSIZE; - public const float BASESAT = RJWUIUtility.BASESAT; - public const float ICONSIZE = RJWUIUtility.ICONSIZE; - - public static readonly int[] Sextype = - { - (int)xxx.rjwSextype.Vaginal, - (int)xxx.rjwSextype.Anal, - (int)xxx.rjwSextype.Oral, - (int)xxx.rjwSextype.Fellatio, - (int)xxx.rjwSextype.Cunnilingus, - (int)xxx.rjwSextype.DoublePenetration, - (int)xxx.rjwSextype.Boobjob, - (int)xxx.rjwSextype.Handjob, - (int)xxx.rjwSextype.Footjob, - (int)xxx.rjwSextype.Fingering, - (int)xxx.rjwSextype.Scissoring, - (int)xxx.rjwSextype.MutualMasturbation, - (int)xxx.rjwSextype.Fisting, - (int)xxx.rjwSextype.Rimming, - (int)xxx.rjwSextype.Sixtynine - }; - - protected Pawn pawn; - protected SexPartnerHistoryRecord selectedPawn; - protected SexHistoryComp history; - protected CompRJW rjwcomp; - protected List partnerList; - protected PartnerOrderMode orderMode; - - private static GUIStyle fontStyleCenter; - private static GUIStyle fontStyleRight; - private static GUIStyle fontStyleLeft; - private static GUIStyle boxStyle; - private static GUIStyle buttonStyle; - - private static Vector2 LastWindowPosition { get; set; } - private Vector2 scroll; - - private static void InitStyles() - { - if (fontStyleCenter != null) - { - return; - } - - GUIStyleState fontStyleState = new GUIStyleState() { textColor = Color.white }; - GUIStyleState boxStyleState = GUI.skin.textArea.normal; - GUIStyleState buttonStyleState = GUI.skin.button.normal; - fontStyleCenter = new GUIStyle() { alignment = TextAnchor.MiddleCenter, normal = fontStyleState }; - fontStyleRight = new GUIStyle() { alignment = TextAnchor.MiddleRight, normal = fontStyleState }; - fontStyleLeft = new GUIStyle() { alignment = TextAnchor.MiddleLeft, normal = fontStyleState }; - boxStyle = new GUIStyle(GUI.skin.textArea) { hover = boxStyleState, onHover = boxStyleState, onNormal = boxStyleState }; - buttonStyle = new GUIStyle(GUI.skin.button) { hover = buttonStyleState, onHover = buttonStyleState, onNormal = buttonStyleState }; - } - - public SexStatusWindow(Pawn pawn, SexHistoryComp history) - { - this.pawn = pawn; - this.history = history; - this.selectedPawn = null; - this.rjwcomp = pawn.TryGetComp(); - this.partnerList = history?.PartnerList; - orderMode = PartnerOrderMode.Recent; - SortPartnerList(orderMode); - - soundClose = SoundDefOf.CommsWindow_Close; - absorbInputAroundWindow = false; - forcePause = false; - preventCameraMotion = false; - draggable = true; - doCloseX = true; - } - - protected override void SetInitialSizeAndPosition() - { - base.SetInitialSizeAndPosition(); - - if (LastWindowPosition == Vector2.zero) - return; - - windowRect.x = LastWindowPosition.x; - windowRect.y = LastWindowPosition.y; - } - - public override Vector2 InitialSize => new Vector2(WINDOW_WIDTH, WINDOW_HEIGHT); - - public override void PreOpen() - { - base.PreOpen(); - InitStyles(); - } - - public override void PreClose() - { - base.PreClose(); - LastWindowPosition = windowRect.position; - } - - public override void DoWindowContents(Rect inRect) - { - if (!SexperienceMod.Settings.SelectionLocked) - { - List selected = Find.Selector.SelectedPawns; - if (selected.Count == 1) - { - Pawn p = selected.First(); - if (p != pawn) - { - SexHistoryComp h = p.TryGetComp(); - if (h != null) ChangePawn(p, h); - } - } - } - - DrawSexStatus(inRect, history); - } - - public static void ToggleWindow(Pawn pawn, SexHistoryComp history) - { - SexStatusWindow window = (SexStatusWindow)Find.WindowStack.Windows.FirstOrDefault(x => x.GetType() == typeof(SexStatusWindow)); - if (window != null) - { - if (window.pawn != pawn) - { - SoundDefOf.TabOpen.PlayOneShotOnCamera(); - window.ChangePawn(pawn, history); - } - } - else - { - Find.WindowStack.Add(new SexStatusWindow(pawn, history)); - } - } - - public void ChangePawn(Pawn pawn, SexHistoryComp history) - { - List selected = Find.Selector.SelectedPawns; - if (!selected.NullOrEmpty()) - { - foreach (Pawn p in selected) - { - Find.Selector.Deselect(p); - } - } - - this.pawn = pawn; - this.history = history; - this.selectedPawn = null; - this.rjwcomp = pawn.TryGetComp(); - this.partnerList = history?.PartnerList; - if (!pawn.DestroyedOrNull() && Find.CurrentMap == pawn.Map) Find.Selector.Select(pawn); - SortPartnerList(orderMode); - } - - public void SortPartnerList(PartnerOrderMode mode) - { - if (partnerList.NullOrEmpty()) return; - switch (mode) - { - default: - partnerList = history?.PartnerList; - break; - case PartnerOrderMode.Recent: - partnerList.Sort(new SexPartnerHistoryRecord.RecentOrderComparer()); - break; - case PartnerOrderMode.Most: - partnerList.Sort(new SexPartnerHistoryRecord.MostOrderComparer()); - break; - case PartnerOrderMode.Name: - partnerList.Sort(new SexPartnerHistoryRecord.NameOrderComparer()); - break; - } - } - - /// - /// Main contents - /// - protected void DrawSexStatus(Rect mainrect, SexHistoryComp history) - { - float sectionwidth = mainrect.width / 3; - - Rect leftRect = new Rect(mainrect.x, mainrect.y, sectionwidth, mainrect.height); - Rect centerRect = new Rect(mainrect.x + sectionwidth, mainrect.y, sectionwidth, mainrect.height); - Rect rightRect = new Rect(mainrect.x + (sectionwidth * 2), mainrect.y, sectionwidth, mainrect.height); - - if (history != null) - { - //Left section - DrawBaseSexInfoLeft(leftRect.ContractedBy(4f)); - - //Center section - DrawBaseSexInfoCenter(centerRect.ContractedBy(4f), history.ParentPawn); - - //Right section - DrawBaseSexInfoRight(rightRect.ContractedBy(4f)); - } - } - - protected void DrawInfoWithPortrait(Rect rect, SexPartnerHistoryRecord history, string tooltip = "") - { - Widgets.DrawMenuSection(rect); - string str = tooltip; - Rect portraitRect = new Rect(rect.x, rect.y, rect.height - FONTHEIGHT, rect.height - FONTHEIGHT); - Rect nameRect = new Rect(rect.x + portraitRect.width, rect.y, rect.width - portraitRect.width, FONTHEIGHT); - Rect sexinfoRect = new Rect(rect.x + portraitRect.width, rect.y + FONTHEIGHT, rect.width - portraitRect.width, FONTHEIGHT); - Rect sexinfoRect2 = new Rect(rect.x + portraitRect.width, rect.y + (FONTHEIGHT * 2), rect.width - portraitRect.width, FONTHEIGHT); - Rect bestsexRect = new Rect(rect.x + 2f, rect.y + (FONTHEIGHT * 3), rect.width - 4f, FONTHEIGHT - 2f); - - if (history != null) - { - if (history.Incest) str += " - " + Keyed.Incest; - Pawn partner = history.Partner; - DrawPawn(portraitRect, history); - Widgets.DrawHighlightIfMouseover(portraitRect); - if (Widgets.ButtonInvisible(portraitRect)) - { - SexHistoryComp pawnhistory = partner?.TryGetComp(); - if (pawnhistory != null) - { - ChangePawn(partner, pawnhistory); - SoundDefOf.Click.PlayOneShotOnCamera(); - } - else - { - SoundDefOf.ClickReject.PlayOneShotOnCamera(); - } - } - - string rapeInfo = ""; - if (history.Raped > 0) rapeInfo += Keyed.RS_Raped + history.Raped + " "; - if (history.RapedMe > 0) rapeInfo += Keyed.RS_RapedMe + history.RapedMe; - - GUI.Label(nameRect, partner?.Name?.ToStringFull ?? history.Label.CapitalizeFirst(), fontStyleLeft); - GUI.Label(sexinfoRect, Keyed.RS_Sex_Count + history.TotalSexCount + " " + rapeInfo, fontStyleLeft); - GUI.Label(sexinfoRect2, Keyed.RS_Orgasms + history.OrgasmCount, fontStyleLeft); - GUI.Label(sexinfoRect2, pawn.GetRelationsString(partner) + " ", fontStyleRight); - float p = history.BestSatisfaction / BASESAT; - FillableBarLabeled(bestsexRect, String.Format(Keyed.RS_Best_Sextype + ": {0}", Keyed.Sextype[(int)history.BestSextype]), p / 2, HistoryUtility.SextypeColor[(int)history.BestSextype], Texture2D.blackTexture, null, String.Format("{0:P2}", p)); - - if (history.IamFirst) - str += "\n" + Keyed.RS_LostVirgin(history.Label, pawn.LabelShort); - if (history.BestSexTickAbs != 0) - str += "\n" + Keyed.RS_HadBestSexDaysAgo(history.BestSexElapsedTicks.ToStringTicksToDays() + " " + Keyed.RS_Ago); - - TooltipHandler.TipRegion(rect, str); - } - else - { - Widgets.DrawTextureFitted(portraitRect, HistoryUtility.UnknownPawn, 1.0f); - Widgets.Label(nameRect, Keyed.Unknown); - Widgets.Label(sexinfoRect, Keyed.RS_Sex_Count + "?"); - Widgets.Label(sexinfoRect2, Keyed.RS_Orgasms + "?"); - FillableBarLabeled(bestsexRect, String.Format(Keyed.RS_Best_Sextype + ": {0}", Keyed.Sextype[(int)xxx.rjwSextype.None]), 0, Texture2D.linearGrayTexture, Texture2D.blackTexture); - } - } - - protected void DrawSexInfoCard(Rect rect, SexPartnerHistoryRecord history, string label, string tooltip, string rightlabel = "") - { - Rect labelRect = new Rect(rect.x, rect.y, rect.width, FONTHEIGHT); - Rect infoRect = new Rect(rect.x, rect.y + FONTHEIGHT, rect.width, rect.height - FONTHEIGHT); - GUI.Label(labelRect, label, fontStyleLeft); - GUI.Label(labelRect, rightlabel, fontStyleRight); - DrawInfoWithPortrait(infoRect, history, tooltip); - } - - /// - /// Right section - /// - protected void DrawBaseSexInfoRight(Rect rect) - { - Listing_Standard listmain = new Listing_Standard(); - listmain.Begin(rect.ContractedBy(4f)); - DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), history.GetRecentPartnersHistory, Keyed.RS_Recent_Sex_Partner, Keyed.RS_Recent_Sex_Partner_ToolTip, RJWUIUtility.GetSexDays(history.RecentSexTickAbs)); - DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), history.GetFirstPartnerHistory, Keyed.RS_First_Sex_Partner, Keyed.RS_First_Sex_Partner_ToolTip, RJWUIUtility.GetSexDays(history.FirstSexTickAbs)); - DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), history.GetMostPartnerHistory, Keyed.RS_Most_Sex_Partner, Keyed.RS_Most_Sex_Partner_ToolTip, RJWUIUtility.GetSexDays(history.MostSexTickAbs)); - DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), history.GetBestSexPartnerHistory, Keyed.RS_Best_Sex_Partner, Keyed.RS_Best_Sex_Partner_ToolTip, RJWUIUtility.GetSexDays(history.BestSexTickAbs)); - GUI.Label(listmain.GetRect(FONTHEIGHT), Keyed.RS_PreferRace, fontStyleLeft); - DrawPreferRace(listmain.GetRect(66f + 15f)); - listmain.GetRect(15f); - listmain.End(); - } - - protected void DrawPreferRace(Rect rect) - { - Widgets.DrawMenuSection(rect); - Rect portraitRect = new Rect(rect.x, rect.y, rect.height - 15f, rect.height - 15f); - Rect infoRect1 = new Rect(rect.x + portraitRect.width, rect.y, rect.width - portraitRect.width, FONTHEIGHT); - Rect infoRect2 = new Rect(rect.x + portraitRect.width, rect.y + FONTHEIGHT, rect.width - portraitRect.width, FONTHEIGHT); - Rect infoRect3 = new Rect(rect.x + portraitRect.width, rect.y + (FONTHEIGHT * 2), rect.width - portraitRect.width - 2f, FONTHEIGHT); - - if (history.PreferRace != null) - { - Widgets.DrawTextureFitted(portraitRect, RJWUIUtility.GetRaceIcon(history.PreferRacePawn, portraitRect.size), 1.0f); - GUI.Label(infoRect1, history.PreferRace?.label.CapitalizeFirst() ?? Keyed.None, fontStyleLeft); - GUI.Label(infoRect2, Keyed.RS_Sex_Count + history.PreferRaceSexCount, fontStyleLeft); - if (history.PreferRace != pawn.def) - { - if (history.PreferRace.race.Animal ^ pawn.def.race.Animal) - { - GUI.Label(infoRect1, Keyed.RS_Bestiality + " ", fontStyleRight); - FillableBarLabeled(infoRect3, Keyed.RS_Sex_Info(Keyed.RS_Bestiality, history.BestialityCount.ToString()), history.BestialityCount / 100f, Texture2D.linearGrayTexture, Texture2D.blackTexture); - } - else - { - GUI.Label(infoRect1, Keyed.RS_Interspecies + " ", fontStyleRight); - FillableBarLabeled(infoRect3, Keyed.RS_Sex_Info(Keyed.RS_Interspecies, history.InterspeciesCount.ToString()), history.InterspeciesCount / 100f, Texture2D.linearGrayTexture, Texture2D.blackTexture); - } - } - } - else - { - Widgets.DrawTextureFitted(portraitRect, HistoryUtility.UnknownPawn, 1.0f); - GUI.Label(infoRect1, Keyed.None, fontStyleLeft); - } - } - - /// - /// Center section - /// - protected void DrawBaseSexInfoCenter(Rect rect, Pawn pawn) - { - Rect portraitRect = new Rect(rect.x + (rect.width / 4), rect.y, rect.width / 2, rect.width / 1.5f); - Rect nameRect = new Rect(portraitRect.x, portraitRect.yMax - (FONTHEIGHT * 2), portraitRect.width, FONTHEIGHT * 2); - Rect infoRect = new Rect(rect.x, rect.y + portraitRect.height, rect.width, rect.height - portraitRect.height); - Rect lockRect = new Rect(portraitRect.xMax - ICONSIZE, portraitRect.y, ICONSIZE, ICONSIZE); - Rect tmp; - - if (Mouse.IsOver(portraitRect)) - { - Configurations settings = SexperienceMod.Settings; - Texture lockicon = settings.SelectionLocked ? HistoryUtility.Locked : HistoryUtility.Unlocked; - Widgets.DrawTextureFitted(lockRect, lockicon, 1.0f); - if (Widgets.ButtonInvisible(lockRect)) - { - SoundDefOf.Click.PlayOneShotOnCamera(); - settings.SelectionLocked.Value = !settings.SelectionLocked.Value; - } - } - - GUI.Box(portraitRect, "", boxStyle); - Widgets.DrawTextureFitted(portraitRect, PortraitsCache.Get(pawn, portraitRect.size, Rot4.South, default, 1, true, true, false, false), 1.0f); - Widgets.DrawHighlightIfMouseover(portraitRect); - if (Widgets.ButtonInvisible(portraitRect)) - { - SoundDefOf.Click.PlayOneShotOnCamera(); - selectedPawn = null; - } - - GUI.Box(nameRect, "", boxStyle); - GUI.Label(nameRect.TopHalf(), pawn.Name?.ToStringFull ?? pawn.Label, fontStyleCenter); - if (pawn.story != null) GUI.Label(nameRect.BottomHalf(), pawn.ageTracker.AgeBiologicalYears + ", " + pawn.story.Title, fontStyleCenter); - else GUI.Label(nameRect.BottomHalf(), pawn.ageTracker.AgeBiologicalYears + ", " + pawn.def.label, fontStyleCenter); - - Listing_Standard listmain = new Listing_Standard(); - listmain.Begin(infoRect); - listmain.Gap(20f); - float p; - - Trait virginity = pawn.story?.traits?.GetTrait(VariousDefOf.Virgin); - if (virginity != null && virginity.Degree != Virginity.TraitDegree.FemaleAfterSurgery) - { - tmp = listmain.GetRect(FONTHEIGHT); - GUI.color = Color.red; - GUI.Box(tmp, "", boxStyle); - GUI.color = Color.white; - GUI.Label(tmp, virginity.Label, fontStyleCenter); - } - else - { - p = history.TotalSexHad; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_TotalSexHad + ": {0:0} ({1:0})", p, pawn.records.GetValue(xxx.CountOfSex)), p / 100, HistoryUtility.TotalSex, Texture2D.blackTexture, null, Keyed.RS_SAT_AVG(String.Format("{0:P2}", history.AVGSat))); - } - listmain.Gap(1f); - - tmp = listmain.GetRect(FONTHEIGHT); - p = pawn.records.GetValue(VariousDefOf.Lust); - FillableBarLabeled(tmp, String.Format(Keyed.Lust + ": {0:0.00}", p), Mathf.Clamp01(p.Normalization(-SexperienceMod.Settings.LustLimit * 3, SexperienceMod.Settings.LustLimit * 3)), HistoryUtility.Slaanesh, Texture2D.blackTexture, null, String.Format(xxx.sex_drive_stat.LabelCap + ": {0:P2}", pawn.Dead ? 0 : pawn.GetStatValue(xxx.sex_drive_stat))); - listmain.Gap(1f); - if (Mouse.IsOver(tmp)) - { - TooltipHandler.TipRegion(tmp, RJWUIUtility.GetStatExplanation(pawn, xxx.sex_drive_stat, pawn.Dead ? 0 : pawn.GetStatValue(xxx.sex_drive_stat))); - } - - p = history.GetBestSextype(out xxx.rjwSextype sextype) / BASESAT; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_Best_Sextype + ": {0}", Keyed.Sextype[(int)sextype]), p / 2, HistoryUtility.SextypeColor[(int)sextype], Texture2D.blackTexture, null, Keyed.RS_SAT_AVG(String.Format("{0:P2}", p))); - listmain.Gap(1f); - - p = history.GetRecentSextype(out sextype) / BASESAT; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_Recent_Sextype + ": {0}", Keyed.Sextype[(int)sextype]), p / 2, HistoryUtility.SextypeColor[(int)sextype], Texture2D.blackTexture, null, String.Format("{0:P2}", p)); - listmain.Gap(1f); - - if (history.IncestuousCount < history.CorpseFuckCount) - { - p = history.CorpseFuckCount; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_Necrophile + ": {0}", p), p / 50, HistoryUtility.Nurgle, Texture2D.blackTexture); - listmain.Gap(1f); - } - else - { - p = history.IncestuousCount; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.Incest + ": {0}", p), p / 50, HistoryUtility.Nurgle, Texture2D.blackTexture); - listmain.Gap(1f); - } - - p = pawn.records.GetValue(VariousDefOf.AmountofEatenCum); - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_Cum_Swallowed + ": {0} mL, {1} " + Keyed.RS_NumofTimes, p, pawn.records.GetValue(VariousDefOf.NumofEatenCum)), p / 1000, Texture2D.linearGrayTexture, Texture2D.blackTexture); - listmain.Gap(1f); - - Hediff addiction = pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.CumAddiction) - ?? pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.CumTolerance); - if (addiction != null) - { - p = addiction.Severity; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), $"{addiction.Label}: {p.ToStringPercent()}", p, Texture2D.linearGrayTexture, Texture2D.blackTexture, addiction.GetTooltip(pawn, false)); - } - else - { - listmain.GetRect(FONTHEIGHT); - } - listmain.Gap(1f); - - p = history.RapedCount; - tmp = listmain.GetRect(FONTHEIGHT); - if (p < history.BeenRapedCount) - { - p = history.BeenRapedCount; - FillableBarLabeled(tmp, String.Format(Keyed.RS_BeenRaped + ": {0}", p), p / 50, Texture2D.grayTexture, Texture2D.blackTexture, null, String.Format(xxx.vulnerability_stat.LabelCap + ": {0:P2}", pawn.Dead ? 0 : pawn.GetStatValue(xxx.vulnerability_stat))); - listmain.Gap(1f); - } - else - { - FillableBarLabeled(tmp, String.Format(Keyed.RS_RapedSomeone + ": {0}", p), p / 50, HistoryUtility.Khorne, Texture2D.blackTexture, null, String.Format(xxx.vulnerability_stat.LabelCap + ": {0:P2}", pawn.Dead ? 0 : pawn.GetStatValue(xxx.vulnerability_stat))); - listmain.Gap(1f); - } - if (Mouse.IsOver(tmp)) - { - TooltipHandler.TipRegion(tmp, RJWUIUtility.GetStatExplanation(pawn, xxx.vulnerability_stat, pawn.Dead ? 0 : pawn.GetStatValue(xxx.vulnerability_stat))); - } - - p = pawn.Dead ? 0 : pawn.GetStatValue(xxx.sex_satisfaction); - tmp = listmain.GetRect(FONTHEIGHT); - FillableBarLabeled(tmp, String.Format(xxx.sex_satisfaction.LabelCap + ": {0:P2}", p), p / 2, HistoryUtility.Satisfaction, Texture2D.blackTexture); - listmain.Gap(1f); - if (Mouse.IsOver(tmp)) - { - TooltipHandler.TipRegion(tmp, RJWUIUtility.GetStatExplanation(pawn, xxx.sex_satisfaction, pawn.Dead ? 0 : pawn.GetStatValue(xxx.sex_satisfaction))); - } - - SkillRecord skill = pawn.skills?.GetSkill(VariousDefOf.Sex); - p = skill?.Level ?? 0; - tmp = listmain.GetRect(FONTHEIGHT); - FillableBarLabeled(tmp, $"{Keyed.RS_SexSkill}: {p}, {skill?.xpSinceLastLevel / skill?.XpRequiredForLevelUp:P2}", p / 20, HistoryUtility.Tzeentch, Texture2D.blackTexture, null, $"{VariousDefOf.SexAbility.LabelCap}: {pawn.GetSexStat():P2}", HistoryUtility.GetPassionBG(skill?.passion)); - if (Mouse.IsOver(tmp)) - { - TooltipHandler.TipRegion(tmp, RJWUIUtility.GetStatExplanation(pawn, VariousDefOf.SexAbility, pawn.GetSexStat())); - } - - listmain.Gap(1f); - - if (selectedPawn != null) DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), selectedPawn, Keyed.RS_Selected_Partner, Keyed.RS_Selected_Partner, RJWUIUtility.GetSexDays(selectedPawn.RecentSexTickAbs)); - else DrawExtraInfo(listmain.GetRect(CARDHEIGHT)); - - listmain.End(); - } - - protected void DrawExtraInfo(Rect rect) - { - Widgets.DrawMenuSection(rect); - Rect inRect = rect.ContractedBy(4f); - Listing_Standard listmain = new Listing_Standard(); - listmain.Begin(inRect); - listmain.Gap(4f); - listmain.GetRect(FONTHEIGHT).DrawSexuality(rjwcomp); - listmain.Gap(1f); - listmain.GetRect(FONTHEIGHT * 3f).DrawQuirk(pawn); - listmain.End(); - } - - /// - /// Left section - /// - protected void DrawBaseSexInfoLeft(Rect rect) - { - Listing_Standard listmain = new Listing_Standard(); - listmain.Begin(rect); - float p; - - //Sex statistics - GUI.Label(listmain.GetRect(FONTHEIGHT), " " + Keyed.RS_Statistics, fontStyleLeft); - listmain.Gap(1f); - float maxSatisfaction = history.GetBestSextype(out _); - if (maxSatisfaction == 0f) maxSatisfaction = BASESAT; - for (int i = 0; i < Sextype.Length; i++) - { - int sexindex = Sextype[i]; - float relativeSat = history.GetAVGSat(sexindex) / maxSatisfaction; - p = history.GetAVGSat(sexindex) / BASESAT; - string label = Keyed.RS_Sex_Info(Keyed.Sextype[sexindex], history.GetSexCount(sexindex).ToString()); - Rect tmpRect = listmain.GetRect(FONTHEIGHT); - FillableBarLabeled(tmpRect, label, relativeSat, HistoryUtility.SextypeColor[sexindex], Texture2D.blackTexture, null, Keyed.RS_SAT_AVG(String.Format("{0:P2}", p))); - if (Mouse.IsOver(tmpRect)) - { - TooltipHandler.TipRegion(tmpRect, Keyed.RS_LastSex.CapitalizeFirst() + ": " + RJWUIUtility.GetSexDays(history.GetSextypeRecentTickAbs(Sextype[i]), true)); - } - - listmain.Gap(1f); - } - - p = history.PartnerCount; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_Sex_Partners + ": {0} ({1})", p, pawn.records.GetValue(VariousDefOf.SexPartnerCount)), p / 50, HistoryUtility.Partners, Texture2D.blackTexture); - listmain.Gap(1f); - - p = history.VirginsTaken; - FillableBarLabeled(listmain.GetRect(FONTHEIGHT), String.Format(Keyed.RS_VirginsTaken + ": {0:0}", p), p / 100, HistoryUtility.Partners, Texture2D.blackTexture); - listmain.Gap(1f); - - //Partner list - Rect listLabelRect = listmain.GetRect(FONTHEIGHT); - Rect sortbtnRect = new Rect(listLabelRect.xMax - 80f, listLabelRect.y, 80f, listLabelRect.height); - GUI.Label(listLabelRect, " " + Keyed.RS_PartnerList, fontStyleLeft); - if (Widgets.ButtonText(sortbtnRect, orderMode.Translate())) - { - SoundDefOf.Click.PlayOneShotOnCamera(); - orderMode = orderMode.Next(); - SortPartnerList(orderMode); - } - - listmain.Gap(1f); - - Rect scrollRect = listmain.GetRect(CARDHEIGHT + 1f); - GUI.Box(scrollRect, "", buttonStyle); - if (!partnerList.NullOrEmpty()) - { - Rect listRect = new Rect(scrollRect.x, scrollRect.y, LISTPAWNSIZE * partnerList.Count, scrollRect.height - 30f); - Widgets.ScrollHorizontal(scrollRect, ref scroll, listRect); - Widgets.BeginScrollView(scrollRect, ref scroll, listRect); - DrawPartnerList(listRect, partnerList); - Widgets.EndScrollView(); - } - - listmain.End(); - } - - protected void DrawPartnerList(Rect rect, List partnerList) - { - Rect pawnRect = new Rect(rect.x, rect.y, LISTPAWNSIZE, LISTPAWNSIZE); - for (int i = 0; i < partnerList.Count; i++) - { - Rect labelRect = new Rect(pawnRect.x, pawnRect.yMax - FONTHEIGHT, pawnRect.width, FONTHEIGHT); - - DrawPawn(pawnRect, partnerList[i]); - Widgets.DrawHighlightIfMouseover(pawnRect); - GUI.Label(labelRect, partnerList[i].Label, fontStyleCenter); - if (Widgets.ButtonInvisible(pawnRect)) - { - selectedPawn = partnerList[i]; - SoundDefOf.Click.PlayOneShotOnCamera(); - } - if (partnerList[i] == selectedPawn) - { - Widgets.DrawHighlightSelected(pawnRect); - } - - pawnRect.x += LISTPAWNSIZE; - } - } - - protected void DrawPawn(Rect rect, SexPartnerHistoryRecord history) - { - if (history != null) - { - bool drawheart = false; - Rect iconRect = new Rect(rect.x + (rect.width * 3 / 4), rect.y, rect.width / 4, rect.height / 4); - Texture img = HistoryUtility.UnknownPawn; - - if (history.IamFirst) - { - GUI.color = HistoryUtility.HistoryColor; - Widgets.DrawTextureFitted(rect, HistoryUtility.FirstOverlay, 1.0f); - GUI.color = Color.white; - } - - if (history.Partner != null) - { - img = PortraitsCache.Get(history.Partner, rect.size, Rot4.South, default, 1, true, true, false, false); - drawheart = LovePartnerRelationUtility.LovePartnerRelationExists(pawn, history.Partner); - } - else if (history.Race?.uiIcon != null) - { - img = history.Race.uiIcon; - } - - if (history.Incest) - { - Widgets.DrawTextureFitted(iconRect, HistoryUtility.Incest, 1.0f); - iconRect.x -= iconRect.width; - } - Widgets.DrawTextureFitted(rect, img, 1.0f); - if (drawheart) - { - Widgets.DrawTextureFitted(iconRect, HistoryUtility.Heart, 1.0f); - iconRect.x -= iconRect.width; - } - } - } - - public static void FillableBarLabeled(Rect rect, string label, float fillPercent, Texture2D filltexture, Texture2D bgtexture, string tooltip = null, string rightlabel = "", Texture2D border = null) - { - Widgets.FillableBar(rect, Math.Min(fillPercent, 1.0f), filltexture, bgtexture, true); - GUI.Label(rect, " " + label.CapitalizeFirst(), fontStyleLeft); - GUI.Label(rect, rightlabel.CapitalizeFirst() + " ", fontStyleRight); - Widgets.DrawHighlightIfMouseover(rect); - if (tooltip != null) TooltipHandler.TipRegion(rect, tooltip); - if (border != null) - { - rect.DrawBorder(border, 2f); - } - } - } -} diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs new file mode 100644 index 0000000..556e0ba --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs @@ -0,0 +1,374 @@ +using RimWorld; +using rjw; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using Verse; + +namespace RJWSexperience.SexHistory.UI +{ + public sealed class SexStatusViewModel + { + private static readonly int[] Sextype = + { + (int)xxx.rjwSextype.Vaginal, + (int)xxx.rjwSextype.Anal, + (int)xxx.rjwSextype.Oral, + (int)xxx.rjwSextype.Fellatio, + (int)xxx.rjwSextype.Cunnilingus, + (int)xxx.rjwSextype.DoublePenetration, + (int)xxx.rjwSextype.Boobjob, + (int)xxx.rjwSextype.Handjob, + (int)xxx.rjwSextype.Footjob, + (int)xxx.rjwSextype.Fingering, + (int)xxx.rjwSextype.Scissoring, + (int)xxx.rjwSextype.MutualMasturbation, + (int)xxx.rjwSextype.Fisting, + (int)xxx.rjwSextype.Rimming, + (int)xxx.rjwSextype.Sixtynine + }; + + private readonly SexHistoryComp _history; + private readonly CompRJW _rjwComp; + private int _validUntilTick; + + public SexStatusViewModel(SexHistoryComp history, PartnerOrderMode mode) + { + _history = history; + _rjwComp = history.ParentPawn.TryGetComp(); + SetPartnerOrder(mode); + + Name = Pawn.Name?.ToStringFull ?? Pawn.Label; + if (Pawn.story != null) + { + AgeAndTitle = Pawn.ageTracker.AgeBiologicalYears + ", " + Pawn.story.Title; + } + else + { + AgeAndTitle = Pawn.ageTracker.AgeBiologicalYears + ", " + Pawn.def.label; + } + } + + public Pawn Pawn => _history.ParentPawn; + public string Name { get; } + public string AgeAndTitle { get; } + public List InfoCards { get; } = new List(); + public InfoCard SelectedPartnerCard { get; private set; } + public PreferedRaceCard PreferedRaceCard { get; private set; } + public List SexTypes { get; } = new List(); + public BarInfo TotalSex { get; private set; } + public BarInfo Lust { get; private set; } + public BarInfo BestSextype { get; private set; } + public BarInfo RecentSextype { get; private set; } + public BarInfo Necro { get; private set; } + public BarInfo Incest { get; private set; } + public BarInfo ConsumedCum { get; private set; } + public BarInfo? CumHediff { get; private set; } + public BarInfo BeenRaped { get; private set; } + public BarInfo Raped { get; private set; } + public BarInfo SexSatisfaction { get; private set; } + public BarInfo SexSkill { get; private set; } + public string VirginLabel { get; private set; } + public string SexualityLabel { get; private set; } + public string QuirksLabel { get; private set; } + public TipSignal QuirksTooltip { get; private set; } + public SexPartnerHistoryRecord SelectedPartner { get; private set; } + public IEnumerable Partners { get; private set; } + + public void Update() + { + if (Find.TickManager.TicksGame <= _validUntilTick) + { + return; + } + + UpdateInfoCards(); + UpdateBars(); + UpdateQuirks(); + UpdateVirginAndSexuality(); + PreferedRaceCard = new PreferedRaceCard(_history); + + int tickRateMultiplier = (int)Find.TickManager.TickRateMultiplier; + if (tickRateMultiplier == 0) // Paused + { + _validUntilTick = Find.TickManager.TicksGame; + return; + } + + _validUntilTick = Find.TickManager.TicksGame + (60 * tickRateMultiplier); + } + + private void UpdateInfoCards() + { + InfoCards.Clear(); + + InfoCards.Add(new InfoCard( + pawn: Pawn, + partnerRecord: _history.GetRecentPartnersHistory, + label: Keyed.RS_Recent_Sex_Partner, + tooltipLabel: Keyed.RS_Recent_Sex_Partner_ToolTip, + lastSexTimeTicks: _history.RecentSexTickAbs)); + + InfoCards.Add(new InfoCard( + pawn: Pawn, + partnerRecord: _history.GetFirstPartnerHistory, + label: Keyed.RS_First_Sex_Partner, + tooltipLabel: Keyed.RS_First_Sex_Partner_ToolTip, + lastSexTimeTicks: _history.FirstSexTickAbs)); + + InfoCards.Add(new InfoCard( + pawn: Pawn, + partnerRecord: _history.GetMostPartnerHistory, + label: Keyed.RS_Most_Sex_Partner, + tooltipLabel: Keyed.RS_Most_Sex_Partner_ToolTip, + lastSexTimeTicks: _history.MostSexTickAbs)); + + InfoCards.Add(new InfoCard( + pawn: Pawn, + partnerRecord: _history.GetBestSexPartnerHistory, + label: Keyed.RS_Best_Sex_Partner, + tooltipLabel: Keyed.RS_Best_Sex_Partner_ToolTip, + lastSexTimeTicks: _history.BestSexTickAbs)); + + if (SelectedPartner != null) + { + UpdateSelectedPartnerCard(); + } + } + + private void UpdateBars() + { + float maxSatisfaction = _history.GetBestSextype(out _); + if (maxSatisfaction == 0f) + { + maxSatisfaction = UIUtility.BASESAT; + } + + SexTypes.Clear(); + + for (int i = 0; i < Sextype.Length; i++) + { + int sexIndex = Sextype[i]; + float AverageSatisfaction = _history.GetAVGSat(sexIndex); + float relativeSat = AverageSatisfaction / maxSatisfaction; + float satisfactionRelativeToBase = AverageSatisfaction / UIUtility.BASESAT; + SexTypes.Add(new BarInfo( + label: Keyed.RS_SexInfo(Keyed.Sextype[sexIndex], _history.GetSexCount(sexIndex)), + fillPercent: relativeSat, + fillTexture: HistoryUtility.SextypeColor[sexIndex], + tooltip: Keyed.RS_LastSex + ": " + UIUtility.GetSexDays(_history.GetSextypeRecentTickAbs(sexIndex), true), + labelRight: Keyed.RS_SatAVG(satisfactionRelativeToBase))); + } + + SexTypes.Add(new BarInfo( + label: String.Format(Keyed.RS_Sex_Partners + ": {0} ({1})", _history.PartnerCount, Pawn.records.GetValue(VariousDefOf.SexPartnerCount)), + fillPercent: _history.PartnerCount / 50, + fillTexture: HistoryUtility.Partners)); + + SexTypes.Add(new BarInfo( + label: String.Format(Keyed.RS_VirginsTaken + ": {0:0}", _history.VirginsTaken), + fillPercent: _history.VirginsTaken / 100, + fillTexture: HistoryUtility.Partners)); + + TotalSex = new BarInfo( + label: String.Format(Keyed.RS_TotalSexHad + ": {0:0} ({1:0})", _history.TotalSexHad, Pawn.records.GetValue(xxx.CountOfSex)), + fillPercent: _history.TotalSexHad / 100, + fillTexture: HistoryUtility.TotalSex, + labelRight: Keyed.RS_SatAVG(_history.AVGSat)); + + float lust = Pawn.records.GetValue(VariousDefOf.Lust); + float sexDrive = GetStatValue(xxx.sex_drive_stat); + float lustLimit = SexperienceMod.Settings.LustLimit * 3f; + Lust = new BarInfo( + label: String.Format(Keyed.Lust + ": {0:0.00}", lust), + fillPercent: Mathf.Clamp01(lust.Normalization(-lustLimit, lustLimit)), + fillTexture: HistoryUtility.Slaanesh, + tooltip: GetStatTooltip(xxx.sex_drive_stat, sexDrive), + labelRight: xxx.sex_drive_stat.LabelCap + ": " + sexDrive.ToStringPercent()); + + float bestSextypeRelativeSatisfaction = _history.GetBestSextype(out xxx.rjwSextype bestSextype) / UIUtility.BASESAT; + BestSextype = new BarInfo( + label: String.Format(Keyed.RS_Best_Sextype + ": {0}", Keyed.Sextype[(int)bestSextype]), + fillPercent: bestSextypeRelativeSatisfaction / 2, + fillTexture: HistoryUtility.SextypeColor[(int)bestSextype], + labelRight: Keyed.RS_SatAVG(bestSextypeRelativeSatisfaction)); + + float recentSextypeRelativeSatisfaction = _history.GetRecentSextype(out xxx.rjwSextype recentSextype) / UIUtility.BASESAT; + RecentSextype = new BarInfo( + label: String.Format(Keyed.RS_Recent_Sextype + ": {0}", Keyed.Sextype[(int)recentSextype]), + fillPercent: recentSextypeRelativeSatisfaction / 2, + fillTexture: HistoryUtility.SextypeColor[(int)recentSextype], + labelRight: recentSextypeRelativeSatisfaction.ToStringPercent()); + + Necro = new BarInfo( + label: String.Format(Keyed.RS_Necrophile + ": {0}", _history.CorpseFuckCount), + fillPercent: _history.CorpseFuckCount / 50, + fillTexture: HistoryUtility.Nurgle); + + Incest = new BarInfo( + label: String.Format(Keyed.Incest + ": {0}", _history.IncestuousCount), + fillPercent: _history.IncestuousCount / 50, + fillTexture: HistoryUtility.Nurgle); + + float amountofEatenCum = Pawn.records.GetValue(VariousDefOf.AmountofEatenCum); + ConsumedCum = new BarInfo( + label: String.Format(Keyed.RS_Cum_Swallowed + ": {0} mL, {1} " + Keyed.RS_NumofTimes, amountofEatenCum, Pawn.records.GetValue(VariousDefOf.NumofEatenCum)), + fillPercent: amountofEatenCum / 1000, + fillTexture: Texture2D.linearGrayTexture); + + Hediff cumHediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.CumAddiction) + ?? Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.CumTolerance); + if (cumHediff != null) + { + CumHediff = new BarInfo( + label: $"{cumHediff.Label}: {cumHediff.Severity.ToStringPercent()}", + fillPercent: cumHediff.Severity, + fillTexture: Texture2D.linearGrayTexture, + tooltip: new TipSignal(() => cumHediff.GetTooltip(Pawn, false), cumHediff.Label.GetHashCode())); + } + + float vulnerability = GetStatValue(xxx.vulnerability_stat); + string vulnerabilityLabel = xxx.vulnerability_stat.LabelCap + ": " + vulnerability.ToStringPercent(); + TipSignal vulnerabilityTip = GetStatTooltip(xxx.vulnerability_stat, vulnerability); + + Raped = new BarInfo( + label: String.Format(Keyed.RS_RapedSomeone + ": {0}", _history.RapedCount), + fillPercent: _history.RapedCount / 50, + fillTexture: HistoryUtility.Khorne, + tooltip: vulnerabilityTip, + labelRight: vulnerabilityLabel); + + BeenRaped = new BarInfo( + label: String.Format(Keyed.RS_BeenRaped + ": {0}", _history.BeenRapedCount), + fillPercent: _history.BeenRapedCount / 50, + fillTexture: Texture2D.grayTexture, + tooltip: vulnerabilityTip, + labelRight: vulnerabilityLabel); + + float sexSatisfaction = GetStatValue(xxx.sex_satisfaction); + SexSatisfaction = new BarInfo( + label: xxx.sex_satisfaction.LabelCap + ": " + sexSatisfaction.ToStringPercent(), + fillPercent: sexSatisfaction / 2, + fillTexture: HistoryUtility.Satisfaction, + tooltip: GetStatTooltip(xxx.sex_satisfaction, sexSatisfaction)); + + SkillRecord skill = Pawn.skills?.GetSkill(VariousDefOf.Sex); + float sexSkillLevel = skill?.Level ?? 0f; + float sexStat = Pawn.GetSexStat(); + SexSkill = new BarInfo( + label: $"{Keyed.RS_SexSkill}: {sexSkillLevel}, {skill?.xpSinceLastLevel / skill?.XpRequiredForLevelUp:P2}", + fillPercent: sexSkillLevel / 20, + fillTexture: HistoryUtility.Tzeentch, + tooltip: GetStatTooltip(VariousDefOf.SexAbility, sexStat), + labelRight: VariousDefOf.SexAbility.LabelCap + ": " + sexStat.ToStringPercent(), + border: HistoryUtility.GetPassionBG(skill?.passion)); + } + + private void UpdateQuirks() + { + List quirks = Quirk.All.FindAll(x => Pawn.Has(x)); + string quirkstr = quirks.Select(x => x.Key).ToCommaList(); + QuirksLabel = "Quirks".Translate() + quirkstr; + + if (quirks.NullOrEmpty()) + { + QuirksTooltip = "NoQuirks".Translate(); + } + else + { + QuirksTooltip = new TipSignal(() => + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (Quirk q in quirks) + { + stringBuilder.AppendLine(q.Key.Colorize(Color.yellow)); + stringBuilder.AppendLine(q.LocaliztionKey.Translate(Pawn.Named("pawn")).AdjustedFor(Pawn).Resolve()); + stringBuilder.AppendLine(""); + } + return stringBuilder.ToString().TrimEndNewlines(); + }, "Quirks".GetHashCode()); + } + } + + private void UpdateVirginAndSexuality() + { + Trait virginity = Pawn.story?.traits?.GetTrait(VariousDefOf.Virgin); + if (virginity != null && virginity.Degree != Virginity.TraitDegree.FemaleAfterSurgery) + { + VirginLabel = virginity.Label; + } + else + { + VirginLabel = null; + } + + if (_rjwComp != null) + { + SexualityLabel = Keyed.RS_Sexuality + ": " + Keyed.Sexuality[(int)_rjwComp.orientation]; + } + } + + public void SetPartnerOrder(PartnerOrderMode mode) + { + if (_history == null) + { + return; + } + + var partners = _history.PartnerList.Select(x => new PartnerPortraitInfo(Pawn, x)); + + switch (mode) + { + default: + Partners = partners; + break; + case PartnerOrderMode.Recent: + Partners = partners.OrderBy(x => x.partnerRecord.RecentSexTickAbs); + break; + case PartnerOrderMode.Most: + Partners = partners.OrderBy(x => x.partnerRecord.TotalSexCount); + break; + case PartnerOrderMode.Name: + Partners = partners.OrderBy(x => x.partnerRecord.Label); + break; + } + } + + public void SetSelectedPartner(SexPartnerHistoryRecord sexPartner) + { + SelectedPartner = sexPartner; + UpdateSelectedPartnerCard(); + } + + private void UpdateSelectedPartnerCard() + { + if (SelectedPartner == null) + { + SelectedPartnerCard = default; + return; + } + + SelectedPartnerCard = new InfoCard( + pawn: Pawn, + partnerRecord: SelectedPartner, + label: Keyed.RS_Selected_Partner, + tooltipLabel: Keyed.RS_Selected_Partner, + lastSexTimeTicks: SelectedPartner.RecentSexTickAbs); + } + + private float GetStatValue(StatDef statDef) => Pawn.Dead ? 0f : Pawn.GetStatValue(statDef); + + private TipSignal GetStatTooltip(StatDef stat, float val) + { + if (!Pawn.Dead) + { + return new TipSignal( + textGetter: () => stat.description + "\n\n" + stat.Worker.GetExplanationFull(StatRequest.For(Pawn), ToStringNumberSense.Undefined, val), + uniqueId: stat.GetHashCode()); + } + return "Dead".Translate(); + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs b/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs new file mode 100644 index 0000000..fedb84b --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs @@ -0,0 +1,473 @@ +using RimWorld; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; +using Verse.Sound; + +namespace RJWSexperience.SexHistory.UI +{ + public class SexStatusWindow : Window + { + public const float WINDOW_WIDTH = 900f; + public const float WINDOW_HEIGHT = 600f; + public const float FONTHEIGHT = UIUtility.FONTHEIGHT; + public const float CARDHEIGHT = UIUtility.CARDHEIGHT; + public const float LISTPAWNSIZE = UIUtility.LISTPAWNSIZE; + public const float BASESAT = UIUtility.BASESAT; + public const float ICONSIZE = UIUtility.ICONSIZE; + + private static GUIStyle fontStyleCenter; + private static GUIStyle fontStyleRight; + private static GUIStyle fontStyleLeft; + private static GUIStyle boxStyle; + private static GUIStyle buttonStyle; + + private SexStatusViewModel _context; + + protected PartnerOrderMode orderMode; + + private static Vector2 LastWindowPosition { get; set; } + private Vector2 scroll; + + private static void InitStyles() + { + if (fontStyleCenter != null) + { + return; + } + + GUIStyleState fontStyleState = new GUIStyleState() { textColor = Color.white }; + GUIStyleState boxStyleState = GUI.skin.textArea.normal; + GUIStyleState buttonStyleState = GUI.skin.button.normal; + fontStyleCenter = new GUIStyle() { alignment = TextAnchor.MiddleCenter, normal = fontStyleState }; + fontStyleRight = new GUIStyle() { alignment = TextAnchor.MiddleRight, normal = fontStyleState }; + fontStyleLeft = new GUIStyle() { alignment = TextAnchor.MiddleLeft, normal = fontStyleState }; + boxStyle = new GUIStyle(GUI.skin.textArea) { hover = boxStyleState, onHover = boxStyleState, onNormal = boxStyleState }; + buttonStyle = new GUIStyle(GUI.skin.button) { hover = buttonStyleState, onHover = buttonStyleState, onNormal = buttonStyleState }; + } + + public SexStatusWindow(SexHistoryComp history) + { + orderMode = PartnerOrderMode.Recent; + _context = new SexStatusViewModel(history, orderMode); + + soundClose = SoundDefOf.CommsWindow_Close; + absorbInputAroundWindow = false; + forcePause = false; + preventCameraMotion = false; + draggable = true; + doCloseX = true; + } + + protected override void SetInitialSizeAndPosition() + { + base.SetInitialSizeAndPosition(); + + if (LastWindowPosition == Vector2.zero) + return; + + windowRect.x = LastWindowPosition.x; + windowRect.y = LastWindowPosition.y; + } + + public override Vector2 InitialSize => new Vector2(WINDOW_WIDTH, WINDOW_HEIGHT); + + public override void PreOpen() + { + base.PreOpen(); + InitStyles(); + } + + public override void PreClose() + { + base.PreClose(); + LastWindowPosition = windowRect.position; + } + + public override void DoWindowContents(Rect inRect) + { + if (!SexperienceMod.Settings.SelectionLocked) + { + List selectedPawns = Find.Selector.SelectedPawns; + if (selectedPawns.Count == 1) + { + Pawn selectedPawn = selectedPawns[0]; + if (selectedPawn != _context.Pawn) + { + SexHistoryComp h = selectedPawn.TryGetComp(); + if (h != null) ChangePawn(h); + } + } + } + + _context.Update(); + DrawSexStatus(inRect); + } + + public static void ToggleWindow(SexHistoryComp history) + { + SexStatusWindow window = Find.WindowStack.WindowOfType(); + if (window != null) + { + if (window._context.Pawn != history.ParentPawn) + { + SoundDefOf.TabOpen.PlayOneShotOnCamera(); + window.ChangePawn(history); + } + } + else + { + Find.WindowStack.Add(new SexStatusWindow(history)); + } + } + + public void ChangePawn(SexHistoryComp history) + { + Find.Selector.ClearSelection(); + _context = new SexStatusViewModel(history, orderMode); + if (!_context.Pawn.DestroyedOrNull() && Find.CurrentMap == _context.Pawn.Map) + { + Find.Selector.Select(_context.Pawn); + } + } + + /// + /// Main contents + /// + protected void DrawSexStatus(Rect mainrect) + { + float sectionwidth = mainrect.width / 3; + + Rect leftRect = new Rect(mainrect.x, mainrect.y, sectionwidth, mainrect.height); + Rect centerRect = new Rect(mainrect.x + sectionwidth, mainrect.y, sectionwidth, mainrect.height); + Rect rightRect = new Rect(mainrect.x + (sectionwidth * 2), mainrect.y, sectionwidth, mainrect.height); + + //Left section + DrawBaseSexInfoLeft(leftRect.ContractedBy(4f)); + + //Center section + DrawBaseSexInfoCenter(centerRect.ContractedBy(4f), _context.Pawn); + + //Right section + DrawBaseSexInfoRight(rightRect.ContractedBy(4f)); + } + + protected void DrawInfoWithPortrait(Rect rect, InfoCard context) + { + Widgets.DrawMenuSection(rect); + Rect portraitRect = new Rect(rect.x, rect.y, rect.height - FONTHEIGHT, rect.height - FONTHEIGHT); + Rect nameRect = new Rect(rect.x + portraitRect.width, rect.y, rect.width - portraitRect.width, FONTHEIGHT); + Rect sexinfoRect = new Rect(rect.x + portraitRect.width, rect.y + FONTHEIGHT, rect.width - portraitRect.width, FONTHEIGHT); + Rect sexinfoRect2 = new Rect(rect.x + portraitRect.width, rect.y + (FONTHEIGHT * 2), rect.width - portraitRect.width, FONTHEIGHT); + Rect bestsexRect = new Rect(rect.x + 2f, rect.y + (FONTHEIGHT * 3), rect.width - 4f, FONTHEIGHT - 2f); + + if (context.partnerRecord != null) + { + DrawPartnerPortrait(portraitRect, context.portraitInfo); + Widgets.DrawHighlightIfMouseover(portraitRect); + if (Widgets.ButtonInvisible(portraitRect)) + { + SexHistoryComp partnerHistory = context.partnerRecord.Partner?.TryGetComp(); + if (partnerHistory != null) + { + ChangePawn(partnerHistory); + SoundDefOf.Click.PlayOneShotOnCamera(); + } + else + { + SoundDefOf.ClickReject.PlayOneShotOnCamera(); + } + } + + GUI.Label(sexinfoRect2, context.relations + " ", fontStyleRight); + TooltipHandler.TipRegion(rect, context.tooltip); + } + else + { + Widgets.DrawTextureFitted(portraitRect, HistoryUtility.UnknownPawn, 1.0f); + } + Widgets.Label(nameRect, context.name); + Widgets.Label(sexinfoRect, context.sexCount); + Widgets.Label(sexinfoRect2, context.orgasms); + UIUtility.FillableBarLabeled(bestsexRect, context.bestSextype); + } + + protected void DrawSexInfoCard(Rect rect, InfoCard context) + { + rect.SplitHorizontally(FONTHEIGHT, out Rect labelRect, out Rect infoRect); + GUI.Label(labelRect, context.label, fontStyleLeft); + GUI.Label(labelRect, context.lastSexTime, fontStyleRight); + DrawInfoWithPortrait(infoRect, context); + } + + /// + /// Right section + /// + protected void DrawBaseSexInfoRight(Rect rect) + { + Listing_Standard listmain = new Listing_Standard(); + listmain.Begin(rect.ContractedBy(4f)); + foreach(InfoCard infoCard in _context.InfoCards) + { + DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), infoCard); + } + GUI.Label(listmain.GetRect(FONTHEIGHT), Keyed.RS_PreferRace, fontStyleLeft); + DrawPreferRace(listmain.GetRect(66f + 15f), _context.PreferedRaceCard); + listmain.End(); + } + + protected void DrawPreferRace(Rect rect, PreferedRaceCard preferedRaceCard) + { + Widgets.DrawMenuSection(rect); + rect.SplitVertically(rect.height - 15f, out Rect portraitRect, out Rect infoRect); + portraitRect.height = portraitRect.width; + Rect infoRect1 = new Rect(infoRect.x, infoRect.y, infoRect.width, FONTHEIGHT); + Rect infoRect2 = new Rect(infoRect.x, infoRect.y + FONTHEIGHT, infoRect.width, FONTHEIGHT); + Rect infoRect3 = new Rect(infoRect.x, infoRect.y + (FONTHEIGHT * 2), infoRect.width - 2f, FONTHEIGHT); + + Widgets.DrawTextureFitted(portraitRect, preferedRaceCard.portraitGetter(portraitRect.size), 1.0f); + GUI.Label(infoRect1, preferedRaceCard.preferRaceLabel, fontStyleLeft); + + if (preferedRaceCard.preferRaceTypeLabel != null) + { + GUI.Label(infoRect1, preferedRaceCard.preferRaceTypeLabel + " ", fontStyleRight); + } + + if (preferedRaceCard.sexCount != null) + { + GUI.Label(infoRect2, preferedRaceCard.sexCount, fontStyleLeft); + } + + if (preferedRaceCard.barInfo != null) + { + UIUtility.FillableBarLabeled(infoRect3, (BarInfo)preferedRaceCard.barInfo); + } + } + + /// + /// Center section + /// + protected void DrawBaseSexInfoCenter(Rect rect, Pawn pawn) + { + Rect portraitRect = new Rect(rect.x + (rect.width / 4), rect.y, rect.width / 2, rect.width / 1.5f); + Rect nameRect = new Rect(portraitRect.x, portraitRect.yMax - (FONTHEIGHT * 2), portraitRect.width, FONTHEIGHT * 2); + Rect infoRect = rect.BottomPartPixels(rect.height - portraitRect.height - 20f); + + if (Mouse.IsOver(portraitRect)) + { + Rect lockRect = new Rect(portraitRect.xMax - ICONSIZE, portraitRect.y, ICONSIZE, ICONSIZE); + Configurations settings = SexperienceMod.Settings; + Texture lockIcon = settings.SelectionLocked ? HistoryUtility.Locked : HistoryUtility.Unlocked; + Widgets.DrawTextureFitted(lockRect, lockIcon, 1.0f); + TooltipHandler.TipRegion(lockRect, Keyed.RS_PawnLockDesc); + if (Widgets.ButtonInvisible(lockRect)) + { + SoundDefOf.Click.PlayOneShotOnCamera(); + settings.SelectionLocked.Value = !settings.SelectionLocked.Value; + } + } + + GUI.Box(portraitRect, "", boxStyle); + Widgets.DrawTextureFitted(portraitRect, PortraitsCache.Get(pawn, portraitRect.size, Rot4.South, default, 1, true, true, false, false), 1.0f); + if (_context.SelectedPartner != null) + { + Widgets.DrawHighlightIfMouseover(portraitRect); + if (Widgets.ButtonInvisible(portraitRect)) + { + SoundDefOf.Click.PlayOneShotOnCamera(); + _context.SetSelectedPartner(null); + } + } + + GUI.Box(nameRect, "", boxStyle); + GUI.Label(nameRect.TopHalf(), _context.Name, fontStyleCenter); + GUI.Label(nameRect.BottomHalf(), _context.AgeAndTitle, fontStyleCenter); + + Listing_Standard listmain = new Listing_Standard(); + listmain.Begin(infoRect); + //listmain.Gap(20f); + + if (_context.VirginLabel != null) + { + Rect tmp = listmain.GetRect(FONTHEIGHT); + GUI.color = Color.red; + GUI.Box(tmp, "", boxStyle); + GUI.color = Color.white; + GUI.Label(tmp, _context.VirginLabel, fontStyleCenter); + listmain.Gap(1f); + } + else + { + listmain.FillableBarLabeled(_context.TotalSex); + } + + listmain.FillableBarLabeled(_context.Lust); + listmain.FillableBarLabeled(_context.BestSextype); + listmain.FillableBarLabeled(_context.RecentSextype); + + if (_context.Incest.fillPercent < _context.Necro.fillPercent) + { + listmain.FillableBarLabeled(_context.Necro); + } + else + { + listmain.FillableBarLabeled(_context.Incest); + } + + listmain.FillableBarLabeled(_context.ConsumedCum); + + if (_context.CumHediff != null) + { + listmain.FillableBarLabeled((BarInfo)_context.CumHediff); + } + else + { + listmain.Gap(FONTHEIGHT + 1f); + } + + if (_context.Raped.fillPercent < _context.BeenRaped.fillPercent) + { + listmain.FillableBarLabeled(_context.BeenRaped); + } + else + { + listmain.FillableBarLabeled(_context.Raped); + } + + listmain.FillableBarLabeled(_context.SexSatisfaction); + listmain.FillableBarLabeled(_context.SexSkill); + + if (_context.SelectedPartner != null) + { + DrawSexInfoCard(listmain.GetRect(CARDHEIGHT), _context.SelectedPartnerCard); + } + else + { + DrawExtraInfo(listmain.GetRect(CARDHEIGHT)); + } + listmain.End(); + } + + /// + /// Sexuality and quirks + /// + protected void DrawExtraInfo(Rect rect) + { + Widgets.DrawMenuSection(rect); + Rect inRect = rect.ContractedBy(4f); + Listing_Standard listmain = new Listing_Standard(); + listmain.Begin(inRect); + listmain.Gap(4f); + + Rect sexualityRect = listmain.GetRect(FONTHEIGHT); + if (_context.SexualityLabel != null) + { + Widgets.Label(sexualityRect, _context.SexualityLabel); + Widgets.DrawHighlightIfMouseover(sexualityRect); + } + listmain.Gap(1f); + + Rect quirkRect = listmain.GetRect(FONTHEIGHT * 3f); + Widgets.Label(quirkRect, _context.QuirksLabel); + Widgets.DrawHighlightIfMouseover(quirkRect); + TooltipHandler.TipRegion(quirkRect, _context.QuirksTooltip); + listmain.End(); + } + + /// + /// Left section + /// + protected void DrawBaseSexInfoLeft(Rect rect) + { + Listing_Standard listmain = new Listing_Standard(); + listmain.Begin(rect); + + //Sex statistics + GUI.Label(listmain.GetRect(FONTHEIGHT), " " + Keyed.RS_Statistics, fontStyleLeft); + listmain.Gap(1f); + + for (int i = 0; i < _context.SexTypes.Count; i++) + { + listmain.FillableBarLabeled(_context.SexTypes[i]); + } + + //Partner list + Rect listLabelRect = listmain.GetRect(FONTHEIGHT); + Rect sortbtnRect = new Rect(listLabelRect.xMax - 80f, listLabelRect.y, 80f, listLabelRect.height); + GUI.Label(listLabelRect, " " + Keyed.RS_PartnerList, fontStyleLeft); + if (Widgets.ButtonText(sortbtnRect, orderMode.Translate())) + { + SoundDefOf.Click.PlayOneShotOnCamera(); + orderMode = orderMode.Next(); + _context.SetPartnerOrder(orderMode); + } + + listmain.Gap(1f); + + Rect scrollRect = listmain.GetRect(CARDHEIGHT + 1f); + GUI.Box(scrollRect, "", buttonStyle); + if (!_context.Partners.EnumerableNullOrEmpty()) + { + Rect listRect = new Rect(scrollRect.x, scrollRect.y, LISTPAWNSIZE * _context.Partners.Count(), scrollRect.height - 30f); + Widgets.ScrollHorizontal(scrollRect, ref scroll, listRect); + Widgets.BeginScrollView(scrollRect, ref scroll, listRect); + DrawPartnerList(listRect, _context.Partners); + Widgets.EndScrollView(); + } + + listmain.End(); + } + + /// + /// Partners at the bottom of the left section + /// + protected void DrawPartnerList(Rect rect, IEnumerable partners) + { + Rect pawnRect = new Rect(rect.x, rect.y, LISTPAWNSIZE, LISTPAWNSIZE); + foreach (PartnerPortraitInfo partner in partners) + { + Rect labelRect = new Rect(pawnRect.x, pawnRect.yMax - FONTHEIGHT, pawnRect.width, FONTHEIGHT); + + DrawPartnerPortrait(pawnRect, partner); + Widgets.DrawHighlightIfMouseover(pawnRect); + GUI.Label(labelRect, partner.partnerRecord.Label, fontStyleCenter); + if (Widgets.ButtonInvisible(pawnRect)) + { + _context.SetSelectedPartner(partner.partnerRecord); + SoundDefOf.Click.PlayOneShotOnCamera(); + } + if (partner.partnerRecord == _context.SelectedPartner) + { + Widgets.DrawHighlightSelected(pawnRect); + } + + pawnRect.x += LISTPAWNSIZE; + } + } + + protected void DrawPartnerPortrait(Rect rect, PartnerPortraitInfo context) + { + Rect iconRect = new Rect(rect.x + (rect.width * 3 / 4), rect.y, rect.width / 4, rect.height / 4); + Texture img = context.portraitGetter(rect.size); + + if (context.partnerRecord.IamFirst) + { + GUI.color = HistoryUtility.HistoryColor; + Widgets.DrawTextureFitted(rect, HistoryUtility.FirstOverlay, 1.0f); + GUI.color = Color.white; + } + + if (context.partnerRecord.Incest) + { + Widgets.DrawTextureFitted(iconRect, HistoryUtility.Incest, 1.0f); + iconRect.x -= iconRect.width; + } + Widgets.DrawTextureFitted(rect, img, 1.0f); + if (context.lover) + { + Widgets.DrawTextureFitted(iconRect, HistoryUtility.Heart, 1.0f); + } + } + } +} diff --git a/Source/RJWSexperience/SexHistory/UI/UIUtility.cs b/Source/RJWSexperience/SexHistory/UI/UIUtility.cs new file mode 100644 index 0000000..29721d4 --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/UIUtility.cs @@ -0,0 +1,80 @@ +using RimWorld; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Remoting.Contexts; +using UnityEngine; +using Verse; + +namespace RJWSexperience.SexHistory.UI +{ + public static class UIUtility + { + public const float FONTHEIGHT = 22f; + public const float CARDHEIGHT = 110f; + public const float LISTPAWNSIZE = 100f; + public const float BASESAT = 0.40f; + public const float ICONSIZE = 30f; + + public static string GetRelationsString(this Pawn pawn, Pawn otherpawn) + { + if (otherpawn != null) + { + IEnumerable relations = pawn.GetRelations(otherpawn); + if (!relations.EnumerableNullOrEmpty()) + return relations.Select(x => x.GetGenderSpecificLabel(otherpawn)).ToCommaList().CapitalizeFirst(); + } + return ""; + } + + public static void DrawBorder(this Rect rect, Texture border, float thickness = 1f) + { + GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, thickness), border); + GUI.DrawTexture(new Rect(rect.x + rect.width - thickness, rect.y, thickness, rect.height), border); + GUI.DrawTexture(new Rect(rect.x, rect.y + rect.height - thickness, rect.width, thickness), border); + GUI.DrawTexture(new Rect(rect.x, rect.y, thickness, rect.height), border); + } + + public static string GetSexDays(int absticks, bool printUnknown = false) + { + if (absticks != 0) + return GenDate.ToStringTicksToDays(GenTicks.TicksAbs - absticks) + " " + Keyed.RS_Ago; + else if (printUnknown) + return Keyed.Unknown; + return ""; + } + + public static Texture GetRaceIcon(Pawn pawn, Vector2 size) + { + if (pawn != null) + return PortraitsCache.Get(pawn, size, Rot4.South, default, 1, true, true, false, false); + return HistoryUtility.UnknownPawn; + } + + public static void FillableBarLabeled(Rect rect, BarInfo context) + { + Widgets.FillableBar(rect, context.fillPercent, context.fillTexture, null, true); + Rect labelRect = rect.ContractedBy(4f, 0f); + Text.Anchor = TextAnchor.MiddleLeft; + Widgets.Label(labelRect, context.label); + if (context.labelRight != "") + { + Text.Anchor = TextAnchor.MiddleRight; + Widgets.Label(labelRect, context.labelRight); + } + GenUI.ResetLabelAlign(); + Widgets.DrawHighlightIfMouseover(rect); + TooltipHandler.TipRegion(rect, context.tooltip); + + if (context.border != null) + { + rect.DrawBorder(context.border, 2f); + } + } + + public static void FillableBarLabeled(this Listing_Standard list, BarInfo context) + { + FillableBarLabeled(list.GetRect(FONTHEIGHT), context); + list.Gap(1f); + } + } +} From 8a48d2e463fe360412cdb3b11238166a9f0d4ee8 Mon Sep 17 00:00:00 2001 From: amevarashi Date: Mon, 17 Apr 2023 14:17:07 +0500 Subject: [PATCH 2/6] Optimized Ate Cum thought --- 1.4/Defs/ThoughtDefs/Thoughts_ate.xml | 4 +- Source/RJWSexperience/Cum/Thought_AteCum.cs | 34 +++----------- .../SexHistory/UI/SexStatusWindow.cs | 2 - .../ThoughtDefExtension_StageFromRecord.cs | 44 ++++++++++++++++++- .../Thoughts/Thought_Recordbased.cs | 33 +++++--------- 5 files changed, 61 insertions(+), 56 deletions(-) diff --git a/1.4/Defs/ThoughtDefs/Thoughts_ate.xml b/1.4/Defs/ThoughtDefs/Thoughts_ate.xml index 0bbeb15..b8e9d60 100644 --- a/1.4/Defs/ThoughtDefs/Thoughts_ate.xml +++ b/1.4/Defs/ThoughtDefs/Thoughts_ate.xml @@ -15,12 +15,12 @@
  • - Tastes bad and stinky. + It's stinky and tastes bad. -2
  • - Tastes bad. But i liked it. + Tastes bad. But I liked it. 1
  • diff --git a/Source/RJWSexperience/Cum/Thought_AteCum.cs b/Source/RJWSexperience/Cum/Thought_AteCum.cs index d309521..79fc834 100644 --- a/Source/RJWSexperience/Cum/Thought_AteCum.cs +++ b/Source/RJWSexperience/Cum/Thought_AteCum.cs @@ -1,39 +1,17 @@ -using RimWorld; - -namespace RJWSexperience // Change in namespace will lead to save incompatibility +namespace RJWSexperience // Change in namespace will lead to save incompatibility { public class Thought_AteCum : Thought_Recordbased { - public override int CurStageIndex + protected override void UpdateCurStage() { - get + if (pawn?.health?.hediffSet?.HasHediff(VariousDefOf.CumAddiction) ?? false) { - if (pawn?.health?.hediffSet?.HasHediff(VariousDefOf.CumAddiction) ?? false) - return def.stages.Count - 1; - return base.CurStageIndex; + SetForcedStage(def.stages.Count - 1); } - } - - public override bool TryMergeWithExistingMemory(out bool showBubble) - { - ThoughtHandler thoughts = pawn.needs.mood.thoughts; - if (thoughts.memories.NumMemoriesInGroup(this) >= def.stackLimit) + else { - Thought_AteCum thought_Memory = (Thought_AteCum)thoughts.memories.OldestMemoryInGroup(this); - if (thought_Memory != null) - { - showBubble = thought_Memory.age > thought_Memory.def.DurationTicks / 2; - thought_Memory.Merged(); - return true; - } + base.UpdateCurStage(); } - showBubble = true; - return false; - } - - protected virtual void Merged() - { - age = 0; } } } diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs b/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs index fedb84b..f684891 100644 --- a/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs @@ -112,7 +112,6 @@ namespace RJWSexperience.SexHistory.UI { if (window._context.Pawn != history.ParentPawn) { - SoundDefOf.TabOpen.PlayOneShotOnCamera(); window.ChangePawn(history); } } @@ -286,7 +285,6 @@ namespace RJWSexperience.SexHistory.UI Listing_Standard listmain = new Listing_Standard(); listmain.Begin(infoRect); - //listmain.Gap(20f); if (_context.VirginLabel != null) { diff --git a/Source/RJWSexperience/Thoughts/ThoughtDefExtension_StageFromRecord.cs b/Source/RJWSexperience/Thoughts/ThoughtDefExtension_StageFromRecord.cs index 6ff0001..cfa836e 100644 --- a/Source/RJWSexperience/Thoughts/ThoughtDefExtension_StageFromRecord.cs +++ b/Source/RJWSexperience/Thoughts/ThoughtDefExtension_StageFromRecord.cs @@ -5,11 +5,51 @@ using Verse; namespace RJWSexperience { + [SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "Field value loaded from XML")] public class ThoughtDefExtension_StageFromRecord : DefModExtension { - [SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "Field value loaded from XML")] public RecordDef recordDef; - [SuppressMessage("Minor Code Smell", "S1104:Fields should not have public accessibility", Justification = "Field value loaded from XML")] public List minimumValueforStage = new List(); + + public int GetStageIndex(Pawn pawn) + { + float value = pawn?.records?.GetValue(recordDef) ?? 0f; + + for (int i = minimumValueforStage.Count - 1; i > 0; i--) + { + if (minimumValueforStage[i] < value) + { + return i; + } + } + + return 0; + } + + public override IEnumerable ConfigErrors() + { + foreach (string error in base.ConfigErrors()) + { + yield return error; + } + + if (recordDef == null) + { + yield return " is null"; + } + + if (minimumValueforStage.NullOrEmpty()) + { + yield return " should have an entry for every stage"; + } + + for (int i = 0; i < minimumValueforStage.Count - 1; i++) + { + if (minimumValueforStage[i] > minimumValueforStage[i + 1]) + { + yield return "Values in should be ordered from the lowest to the highest"; + } + } + } } } diff --git a/Source/RJWSexperience/Thoughts/Thought_Recordbased.cs b/Source/RJWSexperience/Thoughts/Thought_Recordbased.cs index 48dc19a..dd79ab4 100644 --- a/Source/RJWSexperience/Thoughts/Thought_Recordbased.cs +++ b/Source/RJWSexperience/Thoughts/Thought_Recordbased.cs @@ -1,39 +1,28 @@ using RimWorld; -using System.Collections.Generic; +using Verse; namespace RJWSexperience { /// - /// Thought class using record. + /// Thought class that uses record to select active stage /// public class Thought_Recordbased : Thought_Memory { private ThoughtDefExtension_StageFromRecord extension; + protected ThoughtDefExtension_StageFromRecord Extension => extension ?? (extension = def.GetModExtension()); - protected ThoughtDefExtension_StageFromRecord Extension + /// + /// This method is called for every thought right after the pawn is assigned + /// + public override bool TryMergeWithExistingMemory(out bool showBubble) { - get - { - if (extension == null) - extension = def.GetModExtension(); - return extension; - } + UpdateCurStage(); + return base.TryMergeWithExistingMemory(out showBubble); } - protected RecordDef RecordDef => Extension.recordDef; - protected List MinimumValueforStage => Extension.minimumValueforStage; - - public override int CurStageIndex + protected virtual void UpdateCurStage() { - get - { - float value = pawn?.records?.GetValue(RecordDef) ?? 0f; - for (int i = MinimumValueforStage.Count - 1; i > 0; i--) - { - if (MinimumValueforStage[i] < value) return i; - } - return 0; - } + SetForcedStage(Extension.GetStageIndex(pawn)); } } } From 67c2328ad6c41ad5b5cdd0535ab71f8a1175a514 Mon Sep 17 00:00:00 2001 From: amevarashi Date: Mon, 17 Apr 2023 16:34:35 +0500 Subject: [PATCH 3/6] Change VariousDefOf to RsDefOf Fix some IDE messages --- .../RJWSexperience/Cum/Building_Cumbucket.cs | 8 +- Source/RJWSexperience/Cum/CumUtility.cs | 2 +- .../SpecialThingFilterWorker_Cum.cs | 2 +- .../SpecialThingFilterWorker_CumBase.cs | 10 +-- .../SpecialThingFilterWorker_NoCum.cs | 2 +- .../IngestionOutcomeDoer_RecordEatenCum.cs | 4 +- .../CumAddictPartKindUsageRule.cs | 6 +- Source/RJWSexperience/Cum/Thought_AteCum.cs | 2 +- Source/RJWSexperience/DebugAction.cs | 55 +++++++------ .../ExtensionMethods/PawnExtensions.cs | 4 +- Source/RJWSexperience/LustUtility.cs | 4 +- Source/RJWSexperience/Patches/RJW_Patch.cs | 8 +- .../RJWSexperience/Patches/Rimworld_Patch.cs | 4 +- Source/RJWSexperience/RJWSexperience.csproj | 2 +- Source/RJWSexperience/RJWUtility.cs | 36 ++++----- Source/RJWSexperience/RsDefOf.cs | 78 +++++++++++++++++++ .../SexHistory/RecordRandomizer.cs | 52 ++++++------- .../SexHistory/SexHistoryComp.cs | 4 +- .../SexHistory/UI/SexStatusViewModel.cs | 20 ++--- Source/RJWSexperience/StatParts.cs | 2 +- Source/RJWSexperience/VariousDefOf.cs | 37 --------- .../Virginity/Recipe_HymenSurgery.cs | 8 +- .../RJWSexperience/Virginity/TraitHandler.cs | 8 +- 23 files changed, 196 insertions(+), 162 deletions(-) create mode 100644 Source/RJWSexperience/RsDefOf.cs delete mode 100644 Source/RJWSexperience/VariousDefOf.cs diff --git a/Source/RJWSexperience/Cum/Building_Cumbucket.cs b/Source/RJWSexperience/Cum/Building_Cumbucket.cs index 4598f5f..2cccea7 100644 --- a/Source/RJWSexperience/Cum/Building_Cumbucket.cs +++ b/Source/RJWSexperience/Cum/Building_Cumbucket.cs @@ -42,8 +42,8 @@ namespace RJWSexperience // Used in Menstruation with this namespace if (SexperienceMod.Settings.DevMode) { stringBuilder.AppendLine(); - stringBuilder.AppendLine($"[Debug] stored: {StoredStackCount}"); - stringBuilder.Append($"[Debug] storedDecimalRemainder: {storedDecimalRemainder}"); + stringBuilder.Append("[Debug] stored: ").Append(StoredStackCount).AppendLine(); + stringBuilder.Append("[Debug] storedDecimalRemainder: ").Append(storedDecimalRemainder); } return stringBuilder.ToString(); @@ -51,7 +51,7 @@ namespace RJWSexperience // Used in Menstruation with this namespace public void AddCum(float amount) { - AddCum(amount, VariousDefOf.GatheredCum); + AddCum(amount, RsDefOf.Thing.GatheredCum); } public void AddCum(float amount, ThingDef cumDef) @@ -69,7 +69,7 @@ namespace RJWSexperience // Used in Menstruation with this namespace cum.stackCount = num; if (cum.stackCount > 0 && !GenPlace.TryPlaceThing(cum, PositionHeld, Map, ThingPlaceMode.Direct, out Thing res)) { - FilthMaker.TryMakeFilth(PositionHeld, Map, VariousDefOf.FilthCum, num); + FilthMaker.TryMakeFilth(PositionHeld, Map, RsDefOf.Thing.FilthCum, num); } storedDecimalRemainder -= num; } diff --git a/Source/RJWSexperience/Cum/CumUtility.cs b/Source/RJWSexperience/Cum/CumUtility.cs index 988349d..e3be0e9 100644 --- a/Source/RJWSexperience/Cum/CumUtility.cs +++ b/Source/RJWSexperience/Cum/CumUtility.cs @@ -125,7 +125,7 @@ namespace RJWSexperience.Cum const float allOf = 1000f; log.Message($"FeedCum({pawn.NameShortColored}, {amount})"); - Thing cum = ThingMaker.MakeThing(VariousDefOf.GatheredCum); + Thing cum = ThingMaker.MakeThing(RsDefOf.Thing.GatheredCum); cum.stackCount = (int)Math.Ceiling(amount); log.Message($"Created a stack of {cum.stackCount} cum"); cum.Ingested(pawn, allOf); diff --git a/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_Cum.cs b/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_Cum.cs index 81ea225..2f89284 100644 --- a/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_Cum.cs +++ b/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_Cum.cs @@ -6,7 +6,7 @@ namespace RJWSexperience.Cum.FilterWorkers { public override bool Matches(Thing t) { - return IsCum(t) || IsFoodWithCum(t); + return IsCum(t.def) || IsFoodWithCum(t); } } } diff --git a/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_CumBase.cs b/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_CumBase.cs index d622aa0..ce9fb03 100644 --- a/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_CumBase.cs +++ b/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_CumBase.cs @@ -10,20 +10,18 @@ namespace RJWSexperience.Cum.FilterWorkers return def.IsIngestible && def.IsProcessedFood; } - protected bool IsCum(Thing t) => IsCum(t.def); - - protected bool IsCum(ThingDef t) => t == VariousDefOf.GatheredCum; + protected bool IsCum(ThingDef t) => t == RsDefOf.Thing.GatheredCum; protected bool IsFoodWithCum(Thing food) { CompIngredients compIngredients = food.TryGetComp(); - if (compIngredients == null) + if (compIngredients?.ingredients == null) return false; - foreach (ThingDef ingredient in compIngredients.ingredients) + for (int i = 0; i < compIngredients.ingredients.Count; i++) { - if (IsCum(ingredient)) + if (IsCum(compIngredients.ingredients[i])) return true; } diff --git a/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_NoCum.cs b/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_NoCum.cs index d288316..b9b32e3 100644 --- a/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_NoCum.cs +++ b/Source/RJWSexperience/Cum/FilterWorkers/SpecialThingFilterWorker_NoCum.cs @@ -6,7 +6,7 @@ namespace RJWSexperience.Cum.FilterWorkers { public override bool Matches(Thing t) { - return !IsCum(t) && !IsFoodWithCum(t); + return !IsCum(t.def) && !IsFoodWithCum(t); } } } diff --git a/Source/RJWSexperience/Cum/IngestionOutcomeDoer_RecordEatenCum.cs b/Source/RJWSexperience/Cum/IngestionOutcomeDoer_RecordEatenCum.cs index 29c9798..812abc5 100644 --- a/Source/RJWSexperience/Cum/IngestionOutcomeDoer_RecordEatenCum.cs +++ b/Source/RJWSexperience/Cum/IngestionOutcomeDoer_RecordEatenCum.cs @@ -12,8 +12,8 @@ namespace RJWSexperience.Cum { int amount = ingested.stackCount * (int)unitAmount; Logs.LogManager.GetLogger().Message($"Record {pawn.NameShortColored} eating {amount} ml of cum"); - pawn.records.Increment(VariousDefOf.NumofEatenCum); - pawn.records.AddTo(VariousDefOf.AmountofEatenCum, amount); + pawn.records.Increment(RsDefOf.Record.NumofEatenCum); + pawn.records.AddTo(RsDefOf.Record.AmountofEatenCum, amount); } } } diff --git a/Source/RJWSexperience/Cum/Interactions/CumAddictPartKindUsageRule.cs b/Source/RJWSexperience/Cum/Interactions/CumAddictPartKindUsageRule.cs index bdd4762..946dad7 100644 --- a/Source/RJWSexperience/Cum/Interactions/CumAddictPartKindUsageRule.cs +++ b/Source/RJWSexperience/Cum/Interactions/CumAddictPartKindUsageRule.cs @@ -17,7 +17,7 @@ namespace RJWSexperience.Cum.Interactions if (context.Internals.Submissive.Parts.Penises.Any()) return GetForCumAddict(context.Internals.Dominant.Pawn); - if (AddictionUtility.IsAddicted(context.Internals.Submissive.Pawn, VariousDefOf.Cum)) + if (AddictionUtility.IsAddicted(context.Internals.Submissive.Pawn, RsDefOf.Chemical.Cum)) return GetForPartner(); return Enumerable.Empty>(); @@ -28,7 +28,7 @@ namespace RJWSexperience.Cum.Interactions if (context.Internals.Dominant.Parts.Penises.Any()) return GetForCumAddict(context.Internals.Submissive.Pawn); - if (AddictionUtility.IsAddicted(context.Internals.Dominant.Pawn, VariousDefOf.Cum)) + if (AddictionUtility.IsAddicted(context.Internals.Dominant.Pawn, RsDefOf.Chemical.Cum)) return GetForPartner(); return Enumerable.Empty>(); @@ -42,7 +42,7 @@ namespace RJWSexperience.Cum.Interactions var log = LogManager.GetLogger(); log.Message($"Called for {pawn.NameShortColored}"); - if (!(pawn.needs?.TryGetNeed(VariousDefOf.Chemical_Cum) is Need_Chemical cumNeed)) + if (!(pawn.needs?.TryGetNeed(RsDefOf.Need.Chemical_Cum) is Need_Chemical cumNeed)) yield break; log.Message($"{pawn.NameShortColored} is cum addict, current desire level: {cumNeed.CurCategory}"); diff --git a/Source/RJWSexperience/Cum/Thought_AteCum.cs b/Source/RJWSexperience/Cum/Thought_AteCum.cs index 79fc834..fba55b2 100644 --- a/Source/RJWSexperience/Cum/Thought_AteCum.cs +++ b/Source/RJWSexperience/Cum/Thought_AteCum.cs @@ -4,7 +4,7 @@ { protected override void UpdateCurStage() { - if (pawn?.health?.hediffSet?.HasHediff(VariousDefOf.CumAddiction) ?? false) + if (pawn?.health?.hediffSet?.HasHediff(RsDefOf.Hediff.CumAddiction) ?? false) { SetForcedStage(def.stages.Count - 1); } diff --git a/Source/RJWSexperience/DebugAction.cs b/Source/RJWSexperience/DebugAction.cs index ca1b971..71a7a85 100644 --- a/Source/RJWSexperience/DebugAction.cs +++ b/Source/RJWSexperience/DebugAction.cs @@ -8,9 +8,9 @@ namespace RJWSexperience public static class DebugToolsSexperience { [DebugAction("RJW Sexperience", "Reset pawn's record", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.PlayingOnMap)] - private static void ResetRecords(Pawn p) + public static void ResetRecords(Pawn p) { - Trait virgin = p.story?.traits?.GetTrait(VariousDefOf.Virgin); + Trait virgin = p.story?.traits?.GetTrait(RsDefOf.Trait.Virgin); if (virgin != null) p.story.traits.RemoveTrait(virgin); ResetRecord(p, true); if (ResetRecord(p, false)) @@ -19,7 +19,7 @@ namespace RJWSexperience } [DebugAction("RJW Sexperience", "Reset pawn's record(virgin)", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.PlayingOnMap)] - private static void ResetRecordsZero(Pawn p) + public static void ResetRecordsZero(Pawn p) { ResetRecord(p, true); Virginity.TraitHandler.AddVirginTrait(p); @@ -27,32 +27,31 @@ namespace RJWSexperience } [DebugAction("RJW Sexperience", "Reset lust", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.PlayingOnMap)] - private static void ResetLust(Pawn p) + public static void ResetLust(Pawn p) { float lust = RecordRandomizer.RandomizeLust(p); MoteMaker.ThrowText(p.TrueCenter(), p.Map, "Lust: " + lust); } [DebugAction("RJW Sexperience", "Set lust to 0", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.PlayingOnMap)] - private static void SetLust(Pawn p) + public static void SetLust(Pawn p) { - p.records.SetTo(VariousDefOf.Lust, 0); + p.records.SetTo(RsDefOf.Record.Lust, 0); MoteMaker.ThrowText(p.TrueCenter(), p.Map, "Lust: 0"); } - [DebugAction("RJW Sexperience", "Add 10 to lust", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.PlayingOnMap)] - private static void AddLust(Pawn p) + public static void AddLust(Pawn p) { - p.records.AddTo(VariousDefOf.Lust, 10); - MoteMaker.ThrowText(p.TrueCenter(), p.Map, "Lust: " + p.records.GetValue(VariousDefOf.Lust)); + p.records.AddTo(RsDefOf.Record.Lust, 10); + MoteMaker.ThrowText(p.TrueCenter(), p.Map, "Lust: " + p.records.GetValue(RsDefOf.Record.Lust)); } [DebugAction("RJW Sexperience", "Subtract 10 to lust", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.PlayingOnMap)] - private static void SubtractLust(Pawn p) + public static void SubtractLust(Pawn p) { - p.records.AddTo(VariousDefOf.Lust, -10); - MoteMaker.ThrowText(p.TrueCenter(), p.Map, "Lust: " + p.records.GetValue(VariousDefOf.Lust)); + p.records.AddTo(RsDefOf.Record.Lust, -10); + MoteMaker.ThrowText(p.TrueCenter(), p.Map, "Lust: " + p.records.GetValue(RsDefOf.Record.Lust)); } private static bool ResetRecord(Pawn pawn, bool allzero) @@ -66,21 +65,21 @@ namespace RJWSexperience } else { - pawn.records.SetTo(VariousDefOf.Lust, 0); - pawn.records.SetTo(VariousDefOf.NumofEatenCum, 0); - pawn.records.SetTo(VariousDefOf.AmountofEatenCum, 0); - pawn.records.SetTo(VariousDefOf.VaginalSexCount, 0); - pawn.records.SetTo(VariousDefOf.AnalSexCount, 0); - pawn.records.SetTo(VariousDefOf.OralSexCount, 0); - pawn.records.SetTo(VariousDefOf.BlowjobCount, 0); - pawn.records.SetTo(VariousDefOf.CunnilingusCount, 0); - pawn.records.SetTo(VariousDefOf.GenitalCaressCount, 0); - pawn.records.SetTo(VariousDefOf.HandjobCount, 0); - pawn.records.SetTo(VariousDefOf.FingeringCount, 0); - pawn.records.SetTo(VariousDefOf.FootjobCount, 0); - pawn.records.SetTo(VariousDefOf.MiscSexualBehaviorCount, 0); - pawn.records.SetTo(VariousDefOf.SexPartnerCount, 0); - pawn.records.SetTo(VariousDefOf.OrgasmCount, 0); + pawn.records.SetTo(RsDefOf.Record.Lust, 0); + pawn.records.SetTo(RsDefOf.Record.NumofEatenCum, 0); + pawn.records.SetTo(RsDefOf.Record.AmountofEatenCum, 0); + pawn.records.SetTo(RsDefOf.Record.VaginalSexCount, 0); + pawn.records.SetTo(RsDefOf.Record.AnalSexCount, 0); + pawn.records.SetTo(RsDefOf.Record.OralSexCount, 0); + pawn.records.SetTo(RsDefOf.Record.BlowjobCount, 0); + pawn.records.SetTo(RsDefOf.Record.CunnilingusCount, 0); + pawn.records.SetTo(RsDefOf.Record.GenitalCaressCount, 0); + pawn.records.SetTo(RsDefOf.Record.HandjobCount, 0); + pawn.records.SetTo(RsDefOf.Record.FingeringCount, 0); + pawn.records.SetTo(RsDefOf.Record.FootjobCount, 0); + pawn.records.SetTo(RsDefOf.Record.MiscSexualBehaviorCount, 0); + pawn.records.SetTo(RsDefOf.Record.SexPartnerCount, 0); + pawn.records.SetTo(RsDefOf.Record.OrgasmCount, 0); pawn.records.SetTo(xxx.CountOfBeenRapedByAnimals, 0); pawn.records.SetTo(xxx.CountOfBeenRapedByHumanlikes, 0); pawn.records.SetTo(xxx.CountOfBeenRapedByInsects, 0); diff --git a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs index 9e3a7f9..2469c21 100644 --- a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs +++ b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs @@ -27,7 +27,7 @@ namespace RJWSexperience public static float GetSexStat(this Pawn pawn) { if (xxx.is_human(pawn) && !pawn.Dead) - return pawn.GetStatValue(VariousDefOf.SexAbility); + return pawn.GetStatValue(RsDefOf.Stat.SexAbility); return 1.0f; } @@ -70,7 +70,7 @@ namespace RJWSexperience /// public static bool IsVirgin(this Pawn pawn) { - return pawn.records.GetValue(VariousDefOf.VaginalSexCount) == 0; + return pawn.records.GetValue(RsDefOf.Record.VaginalSexCount) == 0; } /// diff --git a/Source/RJWSexperience/LustUtility.cs b/Source/RJWSexperience/LustUtility.cs index ba7aa8f..a89dd54 100644 --- a/Source/RJWSexperience/LustUtility.cs +++ b/Source/RJWSexperience/LustUtility.cs @@ -74,7 +74,7 @@ namespace RJWSexperience public static void UpdateLust(SexProps props, float satisfaction, float baseSatisfaction) { - float? lust = props.pawn.records?.GetValue(VariousDefOf.Lust); + float? lust = props.pawn.records?.GetValue(RsDefOf.Record.Lust); if (lust == null) return; @@ -97,7 +97,7 @@ namespace RJWSexperience return; LogManager.GetLogger("LustUtility").Message($"{props.pawn.NameShortColored}'s lust changed by {lustDelta} (from {lust})"); - props.pawn.records.AddTo(VariousDefOf.Lust, lustDelta); + props.pawn.records.AddTo(RsDefOf.Record.Lust, lustDelta); } private static float LustIncrementFactor(float lust) diff --git a/Source/RJWSexperience/Patches/RJW_Patch.cs b/Source/RJWSexperience/Patches/RJW_Patch.cs index b3ccd7c..d712827 100644 --- a/Source/RJWSexperience/Patches/RJW_Patch.cs +++ b/Source/RJWSexperience/Patches/RJW_Patch.cs @@ -20,11 +20,11 @@ namespace RJWSexperience { if (__instance.Sexprops.isRape && __instance.Sexprops.isReceiver) { - __instance.pawn?.skills?.Learn(VariousDefOf.Sex, 0.05f, true); + __instance.pawn?.skills?.Learn(RsDefOf.Skill.Sex, 0.05f, true); } else { - __instance.pawn?.skills?.Learn(VariousDefOf.Sex, 0.35f, true); + __instance.pawn?.skills?.Learn(RsDefOf.Skill.Sex, 0.35f, true); } } } @@ -44,7 +44,7 @@ namespace RJWSexperience { LustUtility.UpdateLust(props, satisfaction, base_sat_per_fuck); CumUtility.FillCumBuckets(props); - props.pawn.records?.Increment(VariousDefOf.OrgasmCount); + props.pawn.records?.Increment(RsDefOf.Record.OrgasmCount); if (SexperienceMod.Settings.EnableSexHistory && props.hasPartner()) props.pawn.TryGetComp()?.RecordSatisfaction(props.partner, props, satisfaction); } @@ -64,7 +64,7 @@ namespace RJWSexperience { public static void Postfix(Pawn pawn) { - SkillRecord sexskill = pawn.skills.GetSkill(VariousDefOf.Sex); + SkillRecord sexskill = pawn.skills.GetSkill(RsDefOf.Skill.Sex); if (sexskill != null) { sexskill.passion = Passion.Major; diff --git a/Source/RJWSexperience/Patches/Rimworld_Patch.cs b/Source/RJWSexperience/Patches/Rimworld_Patch.cs index 0db6038..d00a943 100644 --- a/Source/RJWSexperience/Patches/Rimworld_Patch.cs +++ b/Source/RJWSexperience/Patches/Rimworld_Patch.cs @@ -32,11 +32,11 @@ namespace RJWSexperience if (!pawn.relations.DirectRelationExists(PawnRelationDefOf.Parent, newMother)) return; - Trait virgin = newMother.story?.traits?.GetTrait(VariousDefOf.Virgin, Virginity.TraitDegree.FemaleVirgin); + Trait virgin = newMother.story?.traits?.GetTrait(RsDefOf.Trait.Virgin, Virginity.TraitDegree.FemaleVirgin); if (virgin != null) { newMother.story.traits.RemoveTrait(virgin); - newMother.story.traits.GainTrait(new Trait(VariousDefOf.Virgin, Virginity.TraitDegree.FemaleAfterSurgery)); + newMother.story.traits.GainTrait(new Trait(RsDefOf.Trait.Virgin, Virginity.TraitDegree.FemaleAfterSurgery)); } } } diff --git a/Source/RJWSexperience/RJWSexperience.csproj b/Source/RJWSexperience/RJWSexperience.csproj index ac5c39f..2aef4a8 100644 --- a/Source/RJWSexperience/RJWSexperience.csproj +++ b/Source/RJWSexperience/RJWSexperience.csproj @@ -98,7 +98,7 @@ - + diff --git a/Source/RJWSexperience/RJWUtility.cs b/Source/RJWSexperience/RJWUtility.cs index 19d4160..d980f0c 100644 --- a/Source/RJWSexperience/RJWUtility.cs +++ b/Source/RJWSexperience/RJWUtility.cs @@ -58,52 +58,52 @@ namespace RJWSexperience { case xxx.rjwSextype.Vaginal: case xxx.rjwSextype.Scissoring: - IncreaseSameRecords(pawn, partner, VariousDefOf.VaginalSexCount); + IncreaseSameRecords(pawn, partner, RsDefOf.Record.VaginalSexCount); break; case xxx.rjwSextype.Anal: - IncreaseSameRecords(pawn, partner, VariousDefOf.AnalSexCount); + IncreaseSameRecords(pawn, partner, RsDefOf.Record.AnalSexCount); break; case xxx.rjwSextype.Oral: case xxx.rjwSextype.Fellatio: if (Genital_Helper.has_penis_fertile(giver) || Genital_Helper.has_penis_infertile(giver)) { - IncreaseRecords(giver, receiver, VariousDefOf.OralSexCount, VariousDefOf.BlowjobCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.OralSexCount, RsDefOf.Record.BlowjobCount); } else if (Genital_Helper.has_penis_fertile(receiver) || Genital_Helper.has_penis_infertile(receiver)) { - IncreaseRecords(giver, receiver, VariousDefOf.BlowjobCount, VariousDefOf.OralSexCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.BlowjobCount, RsDefOf.Record.OralSexCount); } break; case xxx.rjwSextype.Sixtynine: - IncreaseSameRecords(pawn, partner, VariousDefOf.OralSexCount); + IncreaseSameRecords(pawn, partner, RsDefOf.Record.OralSexCount); RecordDef recordpawn, recordpartner; if (Genital_Helper.has_penis_fertile(pawn) || Genital_Helper.has_penis_infertile(pawn)) { - recordpartner = VariousDefOf.BlowjobCount; + recordpartner = RsDefOf.Record.BlowjobCount; } else { - recordpartner = VariousDefOf.CunnilingusCount; + recordpartner = RsDefOf.Record.CunnilingusCount; } if (Genital_Helper.has_penis_fertile(partner) || Genital_Helper.has_penis_infertile(partner)) { - recordpawn = VariousDefOf.BlowjobCount; + recordpawn = RsDefOf.Record.BlowjobCount; } else { - recordpawn = VariousDefOf.CunnilingusCount; + recordpawn = RsDefOf.Record.CunnilingusCount; } IncreaseRecords(pawn, partner, recordpawn, recordpartner); break; case xxx.rjwSextype.Cunnilingus: if (Genital_Helper.has_vagina(giver)) { - IncreaseRecords(giver, receiver, VariousDefOf.OralSexCount, VariousDefOf.CunnilingusCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.OralSexCount, RsDefOf.Record.CunnilingusCount); } else if (Genital_Helper.has_vagina(receiver)) { - IncreaseRecords(giver, receiver, VariousDefOf.CunnilingusCount, VariousDefOf.OralSexCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.CunnilingusCount, RsDefOf.Record.OralSexCount); } break; case xxx.rjwSextype.Masturbation: @@ -111,29 +111,29 @@ namespace RJWSexperience case xxx.rjwSextype.Handjob: if (Genital_Helper.has_penis_fertile(giver) || Genital_Helper.has_penis_infertile(giver)) { - IncreaseRecords(giver, receiver, VariousDefOf.GenitalCaressCount, VariousDefOf.HandjobCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.GenitalCaressCount, RsDefOf.Record.HandjobCount); } else { - IncreaseRecords(giver, receiver, VariousDefOf.HandjobCount, VariousDefOf.GenitalCaressCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.HandjobCount, RsDefOf.Record.GenitalCaressCount); } break; case xxx.rjwSextype.Fingering: case xxx.rjwSextype.Fisting: if (Genital_Helper.has_vagina(giver)) { - IncreaseRecords(giver, receiver, VariousDefOf.GenitalCaressCount, VariousDefOf.FingeringCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.GenitalCaressCount, RsDefOf.Record.FingeringCount); } else { - IncreaseRecords(giver, receiver, VariousDefOf.FingeringCount, VariousDefOf.GenitalCaressCount); + IncreaseRecords(giver, receiver, RsDefOf.Record.FingeringCount, RsDefOf.Record.GenitalCaressCount); } break; case xxx.rjwSextype.Footjob: - IncreaseSameRecords(pawn, partner, VariousDefOf.FootjobCount); + IncreaseSameRecords(pawn, partner, RsDefOf.Record.FootjobCount); break; default: - IncreaseSameRecords(pawn, partner, VariousDefOf.MiscSexualBehaviorCount); + IncreaseSameRecords(pawn, partner, RsDefOf.Record.MiscSexualBehaviorCount); break; } } @@ -154,7 +154,7 @@ namespace RJWSexperience // Moved this method back because of Menstruation public static Building_CumBucket FindClosestBucket(this Pawn pawn) { - List buckets = pawn.Map.listerBuildings.allBuildingsColonist.FindAll(x => x is Building_CumBucket bucket && bucket.StoredStackCount < VariousDefOf.GatheredCum.stackLimit); + List buckets = pawn.Map.listerBuildings.allBuildingsColonist.FindAll(x => x is Building_CumBucket bucket && bucket.StoredStackCount < RsDefOf.Thing.GatheredCum.stackLimit); if (buckets.NullOrEmpty()) return null; diff --git a/Source/RJWSexperience/RsDefOf.cs b/Source/RJWSexperience/RsDefOf.cs new file mode 100644 index 0000000..d617947 --- /dev/null +++ b/Source/RJWSexperience/RsDefOf.cs @@ -0,0 +1,78 @@ +using RimWorld; +using Verse; + +namespace RJWSexperience +{ + public static class RsDefOf + { + [DefOf] + public static class Record + { + public static readonly RecordDef NumofEatenCum; + public static readonly RecordDef AmountofEatenCum; + public static readonly RecordDef Lust; + public static readonly RecordDef VaginalSexCount; + public static readonly RecordDef AnalSexCount; + public static readonly RecordDef OralSexCount; + public static readonly RecordDef BlowjobCount; + public static readonly RecordDef CunnilingusCount; + public static readonly RecordDef GenitalCaressCount; + public static readonly RecordDef HandjobCount; + public static readonly RecordDef FingeringCount; + public static readonly RecordDef FootjobCount; + public static readonly RecordDef MiscSexualBehaviorCount; + public static readonly RecordDef SexPartnerCount; + public static readonly RecordDef OrgasmCount; + } + + [DefOf] + public static class Skill + { + public static readonly SkillDef Sex; + } + + [DefOf] + public static class Thing + { + public static readonly ThingDef GatheredCum; + public static readonly ThingDef FilthCum; + } + + [DefOf] + public static class Chemical + { + public static readonly ChemicalDef Cum; + } + + [DefOf] + public static class Need + { + public static readonly NeedDef Chemical_Cum; + } + + [DefOf] + public static class Trait + { + public static readonly TraitDef Virgin; + } + + [DefOf] + public static class KeyBinding + { + public static readonly KeyBindingDef OpenSexStatistics; + } + + [DefOf] + public static class Stat + { + public static readonly StatDef SexAbility; + } + + [DefOf] + public static class Hediff + { + public static readonly HediffDef CumAddiction; + public static readonly HediffDef CumTolerance; + } + } +} diff --git a/Source/RJWSexperience/SexHistory/RecordRandomizer.cs b/Source/RJWSexperience/SexHistory/RecordRandomizer.cs index 98d807b..b1c61f9 100644 --- a/Source/RJWSexperience/SexHistory/RecordRandomizer.cs +++ b/Source/RJWSexperience/SexHistory/RecordRandomizer.cs @@ -95,8 +95,8 @@ namespace RJWSexperience.SexHistory minValue = float.MinValue; value = Mathf.Clamp(value, minValue, float.MaxValue); - float recordvalue = pawn.records.GetValue(VariousDefOf.Lust); - pawn.records.AddTo(VariousDefOf.Lust, value - recordvalue); + float recordvalue = pawn.records.GetValue(RsDefOf.Record.Lust); + pawn.records.AddTo(RsDefOf.Record.Lust, value - recordvalue); return value; } @@ -145,7 +145,7 @@ namespace RJWSexperience.SexHistory totalSexCount += RandomizeRecord(pawn, xxx.CountOfSexWithHumanlikes, avgsex, deviation); if (totalSexCount > 0) - pawn.records.AddTo(VariousDefOf.SexPartnerCount, Math.Max(1, Rand.Range(0, totalSexCount / 7))); + pawn.records.AddTo(RsDefOf.Record.SexPartnerCount, Math.Max(1, Rand.Range(0, totalSexCount / 7))); return totalSexCount; } @@ -170,67 +170,67 @@ namespace RJWSexperience.SexHistory Gender prefer = PreferredGender(pawn); int sex = (int)(totalsex * RJWPreferenceSettings.vaginal / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.VaginalSexCount, sex); + pawn.records.AddTo(RsDefOf.Record.VaginalSexCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.anal / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.AnalSexCount, sex); + pawn.records.AddTo(RsDefOf.Record.AnalSexCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.fellatio / totalweight); totalsex -= sex; - if (prefer == Gender.Male) pawn.records.AddTo(VariousDefOf.BlowjobCount, sex); - else pawn.records.AddTo(VariousDefOf.OralSexCount, sex); + if (prefer == Gender.Male) pawn.records.AddTo(RsDefOf.Record.BlowjobCount, sex); + else pawn.records.AddTo(RsDefOf.Record.OralSexCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.cunnilingus / totalweight); totalsex -= sex; - if (prefer == Gender.Male) pawn.records.AddTo(VariousDefOf.OralSexCount, sex); - else pawn.records.AddTo(VariousDefOf.CunnilingusCount, sex); + if (prefer == Gender.Male) pawn.records.AddTo(RsDefOf.Record.OralSexCount, sex); + else pawn.records.AddTo(RsDefOf.Record.CunnilingusCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.rimming / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.MiscSexualBehaviorCount, sex); + pawn.records.AddTo(RsDefOf.Record.MiscSexualBehaviorCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.double_penetration / totalweight) / 2; totalsex -= sex; totalsex -= sex; - pawn.records.AddTo(VariousDefOf.VaginalSexCount, sex); - pawn.records.AddTo(VariousDefOf.AnalSexCount, sex); + pawn.records.AddTo(RsDefOf.Record.VaginalSexCount, sex); + pawn.records.AddTo(RsDefOf.Record.AnalSexCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.breastjob / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.MiscSexualBehaviorCount, sex); + pawn.records.AddTo(RsDefOf.Record.MiscSexualBehaviorCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.handjob / totalweight); totalsex -= sex; - if (prefer == Gender.Male) pawn.records.AddTo(VariousDefOf.HandjobCount, sex); - else pawn.records.AddTo(VariousDefOf.GenitalCaressCount, sex); + if (prefer == Gender.Male) pawn.records.AddTo(RsDefOf.Record.HandjobCount, sex); + else pawn.records.AddTo(RsDefOf.Record.GenitalCaressCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.fingering / totalweight); totalsex -= sex; - if (prefer == Gender.Female) pawn.records.AddTo(VariousDefOf.FingeringCount, sex); - else pawn.records.AddTo(VariousDefOf.GenitalCaressCount, sex); + if (prefer == Gender.Female) pawn.records.AddTo(RsDefOf.Record.FingeringCount, sex); + else pawn.records.AddTo(RsDefOf.Record.GenitalCaressCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.mutual_masturbation / totalweight); totalsex -= sex; - if (prefer == Gender.Male) pawn.records.AddTo(VariousDefOf.HandjobCount, sex); - else pawn.records.AddTo(VariousDefOf.FingeringCount, sex); - pawn.records.AddTo(VariousDefOf.GenitalCaressCount, sex); + if (prefer == Gender.Male) pawn.records.AddTo(RsDefOf.Record.HandjobCount, sex); + else pawn.records.AddTo(RsDefOf.Record.FingeringCount, sex); + pawn.records.AddTo(RsDefOf.Record.GenitalCaressCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.footjob / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.FootjobCount, sex); + pawn.records.AddTo(RsDefOf.Record.FootjobCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.scissoring / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.MiscSexualBehaviorCount, sex); + pawn.records.AddTo(RsDefOf.Record.MiscSexualBehaviorCount, sex); sex = (int)(totalsex * RJWPreferenceSettings.fisting / totalweight); totalsex -= sex; - pawn.records.AddTo(VariousDefOf.MiscSexualBehaviorCount, sex); + pawn.records.AddTo(RsDefOf.Record.MiscSexualBehaviorCount, sex); - pawn.records.AddTo(VariousDefOf.OralSexCount, totalsex); - if (prefer == Gender.Male) pawn.records.AddTo(VariousDefOf.BlowjobCount, totalsex); - else pawn.records.AddTo(VariousDefOf.CunnilingusCount, totalsex); + pawn.records.AddTo(RsDefOf.Record.OralSexCount, totalsex); + if (prefer == Gender.Male) pawn.records.AddTo(RsDefOf.Record.BlowjobCount, totalsex); + else pawn.records.AddTo(RsDefOf.Record.CunnilingusCount, totalsex); } private static Gender PreferredGender(Pawn pawn) diff --git a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs index dcd25e1..8c103a3 100644 --- a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs +++ b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs @@ -291,7 +291,7 @@ namespace RJWSexperience.SexHistory SexPartnerHistoryRecord newRecord = new SexPartnerHistoryRecord(partner, partner.IsIncest(ParentPawn)); histories.Add(partnerId, newRecord); - ParentPawn.records.Increment(VariousDefOf.SexPartnerCount); + ParentPawn.records.Increment(RsDefOf.Record.SexPartnerCount); return newRecord; } @@ -442,7 +442,7 @@ namespace RJWSexperience.SexHistory defaultLabel = Keyed.RS_Sex_History, icon = HistoryUtility.HistoryIcon, defaultIconColor = HistoryUtility.HistoryColor, - hotKey = VariousDefOf.OpenSexStatistics, + hotKey = RsDefOf.KeyBinding.OpenSexStatistics, action = () => UI.SexStatusWindow.ToggleWindow(this) }; } diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs index 556e0ba..2a80835 100644 --- a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs @@ -163,7 +163,7 @@ namespace RJWSexperience.SexHistory.UI } SexTypes.Add(new BarInfo( - label: String.Format(Keyed.RS_Sex_Partners + ": {0} ({1})", _history.PartnerCount, Pawn.records.GetValue(VariousDefOf.SexPartnerCount)), + label: String.Format(Keyed.RS_Sex_Partners + ": {0} ({1})", _history.PartnerCount, Pawn.records.GetValue(RsDefOf.Record.SexPartnerCount)), fillPercent: _history.PartnerCount / 50, fillTexture: HistoryUtility.Partners)); @@ -178,7 +178,7 @@ namespace RJWSexperience.SexHistory.UI fillTexture: HistoryUtility.TotalSex, labelRight: Keyed.RS_SatAVG(_history.AVGSat)); - float lust = Pawn.records.GetValue(VariousDefOf.Lust); + float lust = Pawn.records.GetValue(RsDefOf.Record.Lust); float sexDrive = GetStatValue(xxx.sex_drive_stat); float lustLimit = SexperienceMod.Settings.LustLimit * 3f; Lust = new BarInfo( @@ -212,14 +212,14 @@ namespace RJWSexperience.SexHistory.UI fillPercent: _history.IncestuousCount / 50, fillTexture: HistoryUtility.Nurgle); - float amountofEatenCum = Pawn.records.GetValue(VariousDefOf.AmountofEatenCum); + float amountofEatenCum = Pawn.records.GetValue(RsDefOf.Record.AmountofEatenCum); ConsumedCum = new BarInfo( - label: String.Format(Keyed.RS_Cum_Swallowed + ": {0} mL, {1} " + Keyed.RS_NumofTimes, amountofEatenCum, Pawn.records.GetValue(VariousDefOf.NumofEatenCum)), + label: String.Format(Keyed.RS_Cum_Swallowed + ": {0} mL, {1} " + Keyed.RS_NumofTimes, amountofEatenCum, Pawn.records.GetValue(RsDefOf.Record.NumofEatenCum)), fillPercent: amountofEatenCum / 1000, fillTexture: Texture2D.linearGrayTexture); - Hediff cumHediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.CumAddiction) - ?? Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.CumTolerance); + Hediff cumHediff = Pawn.health.hediffSet.GetFirstHediffOfDef(RsDefOf.Hediff.CumAddiction) + ?? Pawn.health.hediffSet.GetFirstHediffOfDef(RsDefOf.Hediff.CumTolerance); if (cumHediff != null) { CumHediff = new BarInfo( @@ -254,15 +254,15 @@ namespace RJWSexperience.SexHistory.UI fillTexture: HistoryUtility.Satisfaction, tooltip: GetStatTooltip(xxx.sex_satisfaction, sexSatisfaction)); - SkillRecord skill = Pawn.skills?.GetSkill(VariousDefOf.Sex); + SkillRecord skill = Pawn.skills?.GetSkill(RsDefOf.Skill.Sex); float sexSkillLevel = skill?.Level ?? 0f; float sexStat = Pawn.GetSexStat(); SexSkill = new BarInfo( label: $"{Keyed.RS_SexSkill}: {sexSkillLevel}, {skill?.xpSinceLastLevel / skill?.XpRequiredForLevelUp:P2}", fillPercent: sexSkillLevel / 20, fillTexture: HistoryUtility.Tzeentch, - tooltip: GetStatTooltip(VariousDefOf.SexAbility, sexStat), - labelRight: VariousDefOf.SexAbility.LabelCap + ": " + sexStat.ToStringPercent(), + tooltip: GetStatTooltip(RsDefOf.Stat.SexAbility, sexStat), + labelRight: RsDefOf.Stat.SexAbility.LabelCap + ": " + sexStat.ToStringPercent(), border: HistoryUtility.GetPassionBG(skill?.passion)); } @@ -294,7 +294,7 @@ namespace RJWSexperience.SexHistory.UI private void UpdateVirginAndSexuality() { - Trait virginity = Pawn.story?.traits?.GetTrait(VariousDefOf.Virgin); + Trait virginity = Pawn.story?.traits?.GetTrait(RsDefOf.Trait.Virgin); if (virginity != null && virginity.Degree != Virginity.TraitDegree.FemaleAfterSurgery) { VirginLabel = virginity.Label; diff --git a/Source/RJWSexperience/StatParts.cs b/Source/RJWSexperience/StatParts.cs index eafdc43..206a296 100644 --- a/Source/RJWSexperience/StatParts.cs +++ b/Source/RJWSexperience/StatParts.cs @@ -24,7 +24,7 @@ namespace RJWSexperience val *= GetLustFactor(pawn); } - protected float GetLustFactor(Pawn pawn) => LustUtility.GetLustFactor(pawn.records.GetValue(VariousDefOf.Lust)); + protected float GetLustFactor(Pawn pawn) => LustUtility.GetLustFactor(pawn.records.GetValue(RsDefOf.Record.Lust)); } /// diff --git a/Source/RJWSexperience/VariousDefOf.cs b/Source/RJWSexperience/VariousDefOf.cs deleted file mode 100644 index 3f03d67..0000000 --- a/Source/RJWSexperience/VariousDefOf.cs +++ /dev/null @@ -1,37 +0,0 @@ -using RimWorld; -using Verse; - -namespace RJWSexperience -{ - [DefOf] - public static class VariousDefOf - { - public static readonly RecordDef NumofEatenCum; - public static readonly RecordDef AmountofEatenCum; - public static readonly RecordDef Lust; - public static readonly RecordDef VaginalSexCount; - public static readonly RecordDef AnalSexCount; - public static readonly RecordDef OralSexCount; - public static readonly RecordDef BlowjobCount; - public static readonly RecordDef CunnilingusCount; - public static readonly RecordDef GenitalCaressCount; - public static readonly RecordDef HandjobCount; - public static readonly RecordDef FingeringCount; - public static readonly RecordDef FootjobCount; - public static readonly RecordDef MiscSexualBehaviorCount; - public static readonly RecordDef SexPartnerCount; - public static readonly RecordDef OrgasmCount; - public static readonly SkillDef Sex; - public static readonly ThingDef CumBucket; - public static readonly ThingDef GatheredCum; - public static readonly ThingDef FilthCum; - public static readonly ChemicalDef Cum; - public static readonly NeedDef Chemical_Cum; - public static readonly TraitDef Virgin; - public static readonly KeyBindingDef OpenSexStatistics; - public static readonly StatDef SexAbility; - - public static readonly HediffDef CumAddiction; - public static readonly HediffDef CumTolerance; - } -} diff --git a/Source/RJWSexperience/Virginity/Recipe_HymenSurgery.cs b/Source/RJWSexperience/Virginity/Recipe_HymenSurgery.cs index f859e46..c215696 100644 --- a/Source/RJWSexperience/Virginity/Recipe_HymenSurgery.cs +++ b/Source/RJWSexperience/Virginity/Recipe_HymenSurgery.cs @@ -26,14 +26,10 @@ namespace RJWSexperience.Virginity if (billDoer == null) return; - TaleRecorder.RecordTale(TaleDefOf.DidSurgery, new object[] - { - billDoer, - pawn - }); + TaleRecorder.RecordTale(TaleDefOf.DidSurgery, billDoer, pawn); TraitHandler.AddVirginTrait(pawn); } - private static bool HasHymen(Pawn pawn) => pawn.story?.traits?.GetTrait(VariousDefOf.Virgin)?.Degree > 0; + private static bool HasHymen(Pawn pawn) => pawn.story?.traits?.GetTrait(RsDefOf.Trait.Virgin)?.Degree > 0; } } diff --git a/Source/RJWSexperience/Virginity/TraitHandler.cs b/Source/RJWSexperience/Virginity/TraitHandler.cs index 40261d6..31e6a5e 100644 --- a/Source/RJWSexperience/Virginity/TraitHandler.cs +++ b/Source/RJWSexperience/Virginity/TraitHandler.cs @@ -17,7 +17,7 @@ namespace RJWSexperience.Virginity { if (Rand.Chance(hymenSurgeryChance)) { - Trait virgin = new Trait(VariousDefOf.Virgin, TraitDegree.FemaleAfterSurgery, true); + Trait virgin = new Trait(RsDefOf.Trait.Virgin, TraitDegree.FemaleAfterSurgery, true); pawn.story.traits.GainTrait(virgin); } return; @@ -36,19 +36,19 @@ namespace RJWSexperience.Virginity int degree = TraitDegree.MaleVirgin; if (pawn.gender == Gender.Female) degree = TraitDegree.FemaleVirgin; - Trait virgin = new Trait(VariousDefOf.Virgin, degree, true); + Trait virgin = new Trait(RsDefOf.Trait.Virgin, degree, true); pawn.story.traits.GainTrait(virgin); } else if (pawn.gender == Gender.Female) { - Trait virgin = new Trait(VariousDefOf.Virgin, TraitDegree.FemaleAfterSurgery, true); + Trait virgin = new Trait(RsDefOf.Trait.Virgin, TraitDegree.FemaleAfterSurgery, true); pawn.story.traits.GainTrait(virgin); } } public static int? RemoveVirginTrait(Pawn pawn) { - Trait virgin = pawn.story?.traits?.GetTrait(VariousDefOf.Virgin); + Trait virgin = pawn.story?.traits?.GetTrait(RsDefOf.Trait.Virgin); if (virgin == null) return null; From ab485c677fda37b2fcad1de8eebeeb5636ef6cd3 Mon Sep 17 00:00:00 2001 From: amevarashi Date: Fri, 21 Apr 2023 20:19:37 +0500 Subject: [PATCH 4/6] Simplified virginity checks Updated names in the SexHistoryComp --- .../ExtensionMethods/PawnExtensions.cs | 25 +++-- .../ExtensionMethods/SexPropsExtensions.cs | 13 ++- Source/RJWSexperience/Patches/RJW_Patch.cs | 16 ++- .../SexHistory/SexHistoryComp.cs | 98 ++++++++----------- .../SexHistory/SexPartnerHistoryRecord.cs | 4 +- .../SexHistory/UI/SexStatusViewModel.cs | 8 +- .../RJWSexperience/Virginity/TraitHandler.cs | 5 +- 7 files changed, 83 insertions(+), 86 deletions(-) diff --git a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs index 2469c21..15f8175 100644 --- a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs +++ b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs @@ -13,7 +13,7 @@ namespace RJWSexperience return false; IEnumerable relations = pawn.GetRelations(otherpawn); - if (relations.EnumerableNullOrEmpty()) + if (relations == null) return false; foreach (PawnRelationDef relation in relations) @@ -66,32 +66,31 @@ namespace RJWSexperience } /// - /// If the pawn is virgin, return true. + /// Check if the pawn is virgin /// public static bool IsVirgin(this Pawn pawn) { - return pawn.records.GetValue(RsDefOf.Record.VaginalSexCount) == 0; + return pawn.records.GetValue(RsDefOf.Record.VaginalSexCount) == 0 || + pawn.relations?.ChildrenCount > 0; // Male is a virgins unless he stick into vagina? Not sure it should work this way } /// - /// If pawn is virgin, lose his/her virginity. + /// Remove virginity if pawn is virgin and announce it /// - public static void PoptheCherry(this Pawn pawn, Pawn partner, SexProps props) + public static void TryRemoveVirginity(this Pawn pawn, Pawn partner, SexProps props) { - if (props?.sexType != xxx.rjwSextype.Vaginal) - return; - int? removedDegree = Virginity.TraitHandler.RemoveVirginTrait(pawn); - if (pawn.IsVirgin()) + if (SexperienceMod.Settings.EnableSexHistory && pawn.IsVirgin()) { - pawn.TryGetComp()?.RecordFirst(partner, props); - if (removedDegree != null) - Messages.Message(Keyed.RS_LostVirgin(pawn.LabelShort, partner.LabelShort), MessageTypeDefOf.NeutralEvent, true); + pawn.TryGetComp()?.RecordFirst(partner); } - if (removedDegree != null) + if (removedDegree != null && removedDegree != Virginity.TraitDegree.FemaleAfterSurgery) + { + Messages.Message(Keyed.RS_LostVirgin(pawn.LabelShort, partner.LabelShort), MessageTypeDefOf.NeutralEvent, true); RJWUtility.ThrowVirginHistoryEvent(pawn, partner, props, (int)removedDegree); + } } } } diff --git a/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs b/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs index 6749c2b..185b1b3 100644 --- a/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs +++ b/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs @@ -19,11 +19,22 @@ namespace RJWSexperience.ExtensionMethods public static bool IsBestiality(this SexProps props) { - if (props.partner != null) + if (props.hasPartner()) { return props.pawn.IsAnimal() ^ props.partner.IsAnimal(); } return false; } + + /// + /// Get a not-so-unique ID. Same interaction between the same partners will return same ID + /// + public static int GetTempId(this SexProps props) + { + return props.pawn.GetHashCode() ^ + (props.partner?.GetHashCode() ?? 0) ^ + props.dictionaryKey.GetHashCode() ^ + (props.isReceiver ? 0 : 1); + } } } diff --git a/Source/RJWSexperience/Patches/RJW_Patch.cs b/Source/RJWSexperience/Patches/RJW_Patch.cs index d712827..cbb78f6 100644 --- a/Source/RJWSexperience/Patches/RJW_Patch.cs +++ b/Source/RJWSexperience/Patches/RJW_Patch.cs @@ -11,7 +11,7 @@ using Verse; namespace RJWSexperience { - [HarmonyPatch(typeof(JobDriver_Sex), "Orgasm")] + [HarmonyPatch(typeof(JobDriver_Sex), "Orgasm")] // Despite the name, called every tick public static class RJW_Patch_Orgasm { public static void Postfix(JobDriver_Sex __instance) @@ -30,7 +30,7 @@ namespace RJWSexperience } } - [HarmonyPatch(typeof(SexUtility), nameof(SexUtility.SatisfyPersonal))] + [HarmonyPatch(typeof(SexUtility), nameof(SexUtility.SatisfyPersonal))] // Actual on orgasm method public static class RJW_Patch_SatisfyPersonal { private const float base_sat_per_fuck = 0.4f; @@ -46,7 +46,7 @@ namespace RJWSexperience CumUtility.FillCumBuckets(props); props.pawn.records?.Increment(RsDefOf.Record.OrgasmCount); if (SexperienceMod.Settings.EnableSexHistory && props.hasPartner()) - props.pawn.TryGetComp()?.RecordSatisfaction(props.partner, props, satisfaction); + props.pawn.TryGetComp()?.RecordOrgasm(props.partner, props, satisfaction); } } @@ -94,10 +94,10 @@ namespace RJWSexperience { public static void Postfix(JobDriver_SexBaseInitiator __instance) { - if (__instance.Sexprops.hasPartner()) + if (__instance.Sexprops.hasPartner() && __instance.Sexprops.sexType == xxx.rjwSextype.Vaginal) { - __instance.pawn.PoptheCherry(__instance.Partner, __instance.Sexprops); - __instance.Partner.PoptheCherry(__instance.pawn, __instance.Sexprops); + __instance.pawn.TryRemoveVirginity(__instance.Partner, __instance.Sexprops); + __instance.Partner.TryRemoveVirginity(__instance.pawn, __instance.Sexprops); } } } @@ -108,9 +108,7 @@ namespace RJWSexperience /// /// If masturbation and current map has a bucket, return location near the bucket /// - /// - /// - /// + /// The place to stand near a bucket /// Run original method public static bool Prefix(Pawn pawn, Pawn partner, ref IntVec3 __result) { diff --git a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs index 8c103a3..a78a904 100644 --- a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs +++ b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs @@ -6,11 +6,14 @@ using Verse; namespace RJWSexperience.SexHistory { + /// + /// Stores pawn's sex history and handles its updates + /// public class SexHistoryComp : ThingComp { public const int ARRLEN = 20; - protected Dictionary histories = new Dictionary(); + protected Dictionary partnerRecords = new Dictionary(); protected string first = ""; protected bool dirty = true; protected xxx.rjwSextype recentSex = xxx.rjwSextype.None; @@ -43,17 +46,14 @@ namespace RJWSexperience.SexHistory protected int bestSexTickAbsCache = 0; public Gizmo Gizmo { get; private set; } - public Pawn ParentPawn => parent as Pawn; - - public SexPartnerHistoryRecord GetFirstPartnerHistory => histories.TryGetValue(first); - - public SexPartnerHistoryRecord GetMostPartnerHistory + public SexPartnerHistoryRecord FirstPartnerRecord => partnerRecords.TryGetValue(first); + public SexPartnerHistoryRecord MostPartnerRecord { get { Update(); - return histories.TryGetValue(mostPartnerCache); + return partnerRecords.TryGetValue(mostPartnerCache); } } public xxx.rjwSextype MostSextype @@ -72,13 +72,13 @@ namespace RJWSexperience.SexHistory return mostSatSextypeCache; } } - public SexPartnerHistoryRecord GetRecentPartnersHistory => histories.TryGetValue(recentPartner); - public SexPartnerHistoryRecord GetBestSexPartnerHistory + public SexPartnerHistoryRecord RecentPartnerRecord => partnerRecords.TryGetValue(recentPartner); + public SexPartnerHistoryRecord BestSexPartnerRecord { get { Update(); - return histories.TryGetValue(bestPartnerCache); + return partnerRecords.TryGetValue(bestPartnerCache); } } public float TotalSexHad @@ -96,21 +96,14 @@ namespace RJWSexperience.SexHistory { IEnumerable res = Enumerable.Empty(); Update(); - if (!histories.NullOrEmpty()) + if (!partnerRecords.NullOrEmpty()) { - res = histories.Values; + res = partnerRecords.Values; } return res; } } - public int PartnerCount - { - get - { - if (histories == null) histories = new Dictionary(); - return histories.Count; - } - } + public int PartnerCount => partnerRecords?.Count ?? 0; public int IncestuousCount => incestuous; public int RapedCount { @@ -160,7 +153,6 @@ namespace RJWSexperience.SexHistory public int FirstSexTickAbs => firstSexTickAbs; public int MostSexTickAbs => mostSexTickAbsCache; public int BestSexTickAbs => bestSexTickAbsCache; - public Pawn PreferRacePawn { get @@ -186,9 +178,9 @@ namespace RJWSexperience.SexHistory public int GetSextypeRecentTickAbs(int sextype) => sextypeRecentTickAbs[sextype]; - public float GetAVGSat(int index) + public float GetAVGSat(int sextype) { - float res = sextypeSat[index] / sextypeCount[index]; + float res = sextypeSat[sextype] / sextypeCount[sextype]; return float.IsNaN(res) ? 0f : res; } @@ -213,7 +205,7 @@ namespace RJWSexperience.SexHistory sextyperecenttickabssave = new List(); } - Scribe_Collections.Look(ref histories, "histories", LookMode.Value, LookMode.Deep); + Scribe_Collections.Look(ref partnerRecords, "histories", LookMode.Value, LookMode.Deep); Scribe_Values.Look(ref first, "first", string.Empty); Scribe_Values.Look(ref recentSex, "recentsex", xxx.rjwSextype.None); Scribe_Values.Look(ref recentSat, "recentsat", 0); @@ -228,8 +220,8 @@ namespace RJWSexperience.SexHistory Scribe_Collections.Look(ref sextypesatsave, "sextypesatsave", LookMode.Value); Scribe_Collections.Look(ref sextyperecenttickabssave, "sextyperecenttickabssave", LookMode.Value); - if (histories == null) - histories = new Dictionary(); + if (partnerRecords == null) + partnerRecords = new Dictionary(); if (Scribe.mode == LoadSaveMode.LoadingVars) { @@ -237,7 +229,7 @@ namespace RJWSexperience.SexHistory sextypeSat = sextypesatsave?.ToArray() ?? new float[ARRLEN]; sextypeRecentTickAbs = sextyperecenttickabssave?.ToArray() ?? new int[ARRLEN]; - foreach (KeyValuePair element in histories) + foreach (KeyValuePair element in partnerRecords) { element.Value.PartnerID = element.Key; } @@ -247,50 +239,52 @@ namespace RJWSexperience.SexHistory public void RecordSex(Pawn partner, SexProps props) { - RecordFirst(partner, props); - GetPartnerRecord(partner)?.RecordSex(props); + SexPartnerHistoryRecord partnerRecord = GetPartnerRecord(partner); + partnerRecord.RecordSex(props); recentPartner = partner.ThingID; recentSex = props.sexType; sextypeCount[(int)props.sexType]++; sextypeRecentTickAbs[(int)props.sexType] = GenTicks.TicksAbs; - if (partner.IsIncest(ParentPawn)) incestuous++; + if (partnerRecord.Incest) incestuous++; if (partner.Dead) corpsefuck++; if (props.IsBestiality()) bestiality++; else if (ParentPawn.def != partner.def) interspecies++; dirty = true; } - public void RecordSatisfaction(Pawn partner, SexProps props, float satisfaction) + public void RecordOrgasm(Pawn partner, SexProps props, float satisfaction) { - RecordFirst(partner, props); - GetPartnerRecord(partner)?.RecordSatisfaction(props, satisfaction); + GetPartnerRecord(partner).RecordOrgasm(props.sexType, satisfaction); recentSat = satisfaction; - sextypeSat[(int)props.sexType] += satisfaction; + sextypeSat[(int)props.sexType] += satisfaction; // Several orgasmsms in one sex are messing with this dirty = true; } - public void RecordFirst(Pawn partner, SexProps props) + /// + /// Record first partner and time of sex. No checks are performed, the caller should make sure it is the first time. + /// + /// + public void RecordFirst(Pawn partner) { - if (VirginCheck() && props.sexType == xxx.rjwSextype.Vaginal) - { - first = partner.ThingID; - SexHistoryComp history = partner.TryGetComp(); - firstSexTickAbs = GenTicks.TicksAbs; - history?.TakeSomeonesVirgin(ParentPawn); - } + first = partner.ThingID; + firstSexTickAbs = GenTicks.TicksAbs; + partner.TryGetComp()?.TakeSomeonesVirgin(ParentPawn); } + /// + /// Retrive an existing partner record or add a new one. Increments SexPartnerCount when new record is added + /// protected SexPartnerHistoryRecord GetPartnerRecord(Pawn partner) { string partnerId = partner.ThingID; - if (histories.TryGetValue(partnerId, out SexPartnerHistoryRecord record)) + if (partnerRecords.TryGetValue(partnerId, out SexPartnerHistoryRecord record)) { return record; } SexPartnerHistoryRecord newRecord = new SexPartnerHistoryRecord(partner, partner.IsIncest(ParentPawn)); - histories.Add(partnerId, newRecord); + partnerRecords.Add(partnerId, newRecord); ParentPawn.records.Increment(RsDefOf.Record.SexPartnerCount); return newRecord; } @@ -328,7 +322,7 @@ namespace RJWSexperience.SexHistory Dictionary racetotalsat = new Dictionary(); List allpartners = new List(); - foreach (KeyValuePair element in histories) + foreach (KeyValuePair element in partnerRecords) { SexPartnerHistoryRecord h = element.Value; @@ -386,9 +380,9 @@ namespace RJWSexperience.SexHistory mostPartnerCache = mostID; bestPartnerCache = bestID; - recentSexTickAbsCache = histories.TryGetValue(recentPartner)?.RecentSexTickAbs ?? 0; - mostSexTickAbsCache = histories.TryGetValue(mostPartnerCache)?.RecentSexTickAbs ?? 0; - bestSexTickAbsCache = histories.TryGetValue(bestPartnerCache)?.BestSexTickAbs ?? 0; + recentSexTickAbsCache = partnerRecords.TryGetValue(recentPartner)?.RecentSexTickAbs ?? 0; + mostSexTickAbsCache = partnerRecords.TryGetValue(mostPartnerCache)?.RecentSexTickAbs ?? 0; + bestSexTickAbsCache = partnerRecords.TryGetValue(bestPartnerCache)?.BestSexTickAbs ?? 0; racetotalsat.Clear(); allpartners.Clear(); @@ -414,14 +408,6 @@ namespace RJWSexperience.SexHistory #endregion Cache update - protected bool VirginCheck() - { - if (histories.TryGetValue(first) != null) - return false; - - return ParentPawn.IsVirgin(); - } - public override IEnumerable CompGetGizmosExtra() { if (SexperienceMod.Settings.HideGizmoWhenDrafted && ParentPawn.Drafted) diff --git a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs index 12c0f7d..9bcc832 100644 --- a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs +++ b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs @@ -104,13 +104,13 @@ namespace RJWSexperience.SexHistory recentSexTickAbs = GenTicks.TicksAbs; } - public void RecordSatisfaction(SexProps props, float satisfaction) + public void RecordOrgasm(xxx.rjwSextype sextype, float satisfaction) { orgasms++; if (satisfaction > bestSatisfaction) { - bestSextype = props.sexType; + bestSextype = sextype; bestSatisfaction = satisfaction; bestSexTickAbs = GenTicks.TicksAbs; } diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs index 2a80835..e7b3538 100644 --- a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs @@ -106,28 +106,28 @@ namespace RJWSexperience.SexHistory.UI InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.GetRecentPartnersHistory, + partnerRecord: _history.RecentPartnerRecord, label: Keyed.RS_Recent_Sex_Partner, tooltipLabel: Keyed.RS_Recent_Sex_Partner_ToolTip, lastSexTimeTicks: _history.RecentSexTickAbs)); InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.GetFirstPartnerHistory, + partnerRecord: _history.FirstPartnerRecord, label: Keyed.RS_First_Sex_Partner, tooltipLabel: Keyed.RS_First_Sex_Partner_ToolTip, lastSexTimeTicks: _history.FirstSexTickAbs)); InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.GetMostPartnerHistory, + partnerRecord: _history.MostPartnerRecord, label: Keyed.RS_Most_Sex_Partner, tooltipLabel: Keyed.RS_Most_Sex_Partner_ToolTip, lastSexTimeTicks: _history.MostSexTickAbs)); InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.GetBestSexPartnerHistory, + partnerRecord: _history.BestSexPartnerRecord, label: Keyed.RS_Best_Sex_Partner, tooltipLabel: Keyed.RS_Best_Sex_Partner_ToolTip, lastSexTimeTicks: _history.BestSexTickAbs)); diff --git a/Source/RJWSexperience/Virginity/TraitHandler.cs b/Source/RJWSexperience/Virginity/TraitHandler.cs index 31e6a5e..21cc148 100644 --- a/Source/RJWSexperience/Virginity/TraitHandler.cs +++ b/Source/RJWSexperience/Virginity/TraitHandler.cs @@ -1,5 +1,4 @@ using RimWorld; -using rjw; using Verse; namespace RJWSexperience.Virginity @@ -46,6 +45,10 @@ namespace RJWSexperience.Virginity } } + /// + /// Remove virginity trait and spawn blood filth if applicable + /// + /// Degree of the removed trait public static int? RemoveVirginTrait(Pawn pawn) { Trait virgin = pawn.story?.traits?.GetTrait(RsDefOf.Trait.Virgin); From cd9647f999587dd8feef7b5fbf155ae8a754f751 Mon Sep 17 00:00:00 2001 From: amevarashi Date: Fri, 21 Apr 2023 20:52:32 +0500 Subject: [PATCH 5/6] Simplified GetAdjacentBuilding methods --- .../ExtensionMethods/PawnExtensions.cs | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs index 15f8175..aa40cfc 100644 --- a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs +++ b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs @@ -31,38 +31,22 @@ namespace RJWSexperience return 1.0f; } - public static T GetAdjacentBuilding(this Pawn pawn) where T : Building - { - if (!pawn.Spawned) - return null; - - EdificeGrid edifice = pawn.Map.edificeGrid; - if (edifice[pawn.Position] is T building) - return building; - foreach (IntVec3 pos in GenAdjFast.AdjacentCells8Way(pawn.Position)) - { - if (edifice[pos] is T adjBuilding) - return adjBuilding; - } - return null; - } + public static T GetAdjacentBuilding(this Pawn pawn) where T : Building => GetAdjacentBuildings(pawn).FirstOrFallback(); public static IEnumerable GetAdjacentBuildings(this Pawn pawn) where T : Building { - var results = new List(); - if (!pawn.Spawned) - return results; + yield break; EdificeGrid edifice = pawn.Map.edificeGrid; if (edifice[pawn.Position] is T building) - results.Add(building); + yield return building; + foreach (IntVec3 pos in GenAdjFast.AdjacentCells8Way(pawn.Position)) { if (pos.InBounds(pawn.Map) && edifice[pos] is T adjBuilding) - results.Add(adjBuilding); + yield return adjBuilding; } - return results; } /// From 3ef5faf86b24f4f5788b6c2f46987f57697221b8 Mon Sep 17 00:00:00 2001 From: amevarashi Date: Sat, 22 Apr 2023 12:35:52 +0500 Subject: [PATCH 6/6] 1.1.4.0 --- 1.4/Assemblies/RJWSexperience.dll | Bin 92672 -> 102400 bytes About/Manifest.xml | 2 +- changelogs.txt | 7 +++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/1.4/Assemblies/RJWSexperience.dll b/1.4/Assemblies/RJWSexperience.dll index b494450e18f143872f90c999d6f9a004aa196478..938c28fd79648427f71750316b68f2196cbcdcf0 100644 GIT binary patch literal 102400 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 z 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 O_P|Pb1o;0L1OEoAhCy8b literal 92672 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 K{QGZ*f&UNt)(_$U diff --git a/About/Manifest.xml b/About/Manifest.xml index b412805..409b150 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@ RJWSexperience - 1.1.3.0 + 1.1.4.0
  • RimJobWorld >= 5.3.0
  • diff --git a/changelogs.txt b/changelogs.txt index ff36265..eb12aa7 100644 --- a/changelogs.txt +++ b/changelogs.txt @@ -1,3 +1,10 @@ +Version 1.1.4.0 + - Optimized SexStatusWindow + - Optimized Ate Cum thought + - Optimized? cum food filter + - Simplified virginity checks code + - Simplified bucket selection code + Version 1.1.3.0 - Removed "Hide Sex History button with RJW designators" setting - Fixed error with pawn masturbated on the map border