diff --git a/1.4/Assemblies/RJWSexperience.dll b/1.4/Assemblies/RJWSexperience.dll index b494450..938c28f 100644 Binary files a/1.4/Assemblies/RJWSexperience.dll and b/1.4/Assemblies/RJWSexperience.dll differ 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/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/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/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 d309521..fba55b2 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(RsDefOf.Hediff.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/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..aa40cfc 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) @@ -27,71 +27,54 @@ 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; } - 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; } /// - /// If the pawn is virgin, return true. + /// Check if the pawn is virgin /// public static bool IsVirgin(this Pawn pawn) { - return pawn.records.GetValue(VariousDefOf.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/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/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..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) @@ -20,17 +20,17 @@ 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); } } } } - [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; @@ -44,9 +44,9 @@ 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); + props.pawn.TryGetComp()?.RecordOrgasm(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; @@ -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/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 6540f16..2aef4a8 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,10 +95,10 @@ - - + + - + 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/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/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 65f7928..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 @@ -90,27 +90,20 @@ 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()) + if (!partnerRecords.NullOrEmpty()) { - res = histories.Values.ToList(); + 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,51 +239,53 @@ 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); - ParentPawn.records.Increment(VariousDefOf.SexPartnerCount); + 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) @@ -442,11 +428,8 @@ namespace RJWSexperience.SexHistory defaultLabel = Keyed.RS_Sex_History, icon = HistoryUtility.HistoryIcon, defaultIconColor = HistoryUtility.HistoryColor, - hotKey = VariousDefOf.OpenSexStatistics, - action = delegate - { - UI.SexStatusWindow.ToggleWindow(ParentPawn, this); - } + hotKey = RsDefOf.KeyBinding.OpenSexStatistics, + action = () => UI.SexStatusWindow.ToggleWindow(this) }; } } diff --git a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs index cb17485..9bcc832 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 @@ -105,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; } @@ -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..e7b3538 --- /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.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.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.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.BestSexPartnerRecord, + 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(RsDefOf.Record.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(RsDefOf.Record.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(RsDefOf.Record.AmountofEatenCum); + ConsumedCum = new BarInfo( + 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(RsDefOf.Hediff.CumAddiction) + ?? Pawn.health.hediffSet.GetFirstHediffOfDef(RsDefOf.Hediff.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(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(RsDefOf.Stat.SexAbility, sexStat), + labelRight: RsDefOf.Stat.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(RsDefOf.Trait.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..f684891 --- /dev/null +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusWindow.cs @@ -0,0 +1,471 @@ +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) + { + 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); + + 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); + } + } +} 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/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)); } } } 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..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 @@ -17,7 +16,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 +35,23 @@ 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); } } + /// + /// 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(VariousDefOf.Virgin); + Trait virgin = pawn.story?.traits?.GetTrait(RsDefOf.Trait.Virgin); if (virgin == null) return null; 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