diff --git a/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs b/Source/RJWSexperience/ExtensionMethods/PawnExtensions.cs index aa40cfc..2469c21 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 == null) + if (relations.EnumerableNullOrEmpty()) return false; foreach (PawnRelationDef relation in relations) @@ -31,50 +31,67 @@ namespace RJWSexperience return 1.0f; } - public static T GetAdjacentBuilding(this Pawn pawn) where T : Building => GetAdjacentBuildings(pawn).FirstOrFallback(); - - public static IEnumerable GetAdjacentBuildings(this Pawn pawn) where T : Building + public static T GetAdjacentBuilding(this Pawn pawn) where T : Building { if (!pawn.Spawned) - yield break; + return null; EdificeGrid edifice = pawn.Map.edificeGrid; if (edifice[pawn.Position] is T building) - yield return building; + return building; + foreach (IntVec3 pos in GenAdjFast.AdjacentCells8Way(pawn.Position)) + { + if (edifice[pos] is T adjBuilding) + return adjBuilding; + } + return null; + } + public static IEnumerable GetAdjacentBuildings(this Pawn pawn) where T : Building + { + var results = new List(); + + if (!pawn.Spawned) + return results; + + EdificeGrid edifice = pawn.Map.edificeGrid; + if (edifice[pawn.Position] is T building) + results.Add(building); foreach (IntVec3 pos in GenAdjFast.AdjacentCells8Way(pawn.Position)) { if (pos.InBounds(pawn.Map) && edifice[pos] is T adjBuilding) - yield return adjBuilding; + results.Add(adjBuilding); } + return results; } /// - /// Check if the pawn is virgin + /// If the pawn is virgin, return true. /// public static bool IsVirgin(this Pawn pawn) { - 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 + return pawn.records.GetValue(RsDefOf.Record.VaginalSexCount) == 0; } /// - /// Remove virginity if pawn is virgin and announce it + /// If pawn is virgin, lose his/her virginity. /// - public static void TryRemoveVirginity(this Pawn pawn, Pawn partner, SexProps props) + public static void PoptheCherry(this Pawn pawn, Pawn partner, SexProps props) { + if (props?.sexType != xxx.rjwSextype.Vaginal) + return; + int? removedDegree = Virginity.TraitHandler.RemoveVirginTrait(pawn); - if (SexperienceMod.Settings.EnableSexHistory && pawn.IsVirgin()) + if (pawn.IsVirgin()) { - pawn.TryGetComp()?.RecordFirst(partner); + pawn.TryGetComp()?.RecordFirst(partner, props); + if (removedDegree != null) + Messages.Message(Keyed.RS_LostVirgin(pawn.LabelShort, partner.LabelShort), MessageTypeDefOf.NeutralEvent, true); } - if (removedDegree != null && removedDegree != Virginity.TraitDegree.FemaleAfterSurgery) - { - Messages.Message(Keyed.RS_LostVirgin(pawn.LabelShort, partner.LabelShort), MessageTypeDefOf.NeutralEvent, true); + if (removedDegree != null) RJWUtility.ThrowVirginHistoryEvent(pawn, partner, props, (int)removedDegree); - } } } } diff --git a/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs b/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs index 185b1b3..6749c2b 100644 --- a/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs +++ b/Source/RJWSexperience/ExtensionMethods/SexPropsExtensions.cs @@ -19,22 +19,11 @@ namespace RJWSexperience.ExtensionMethods public static bool IsBestiality(this SexProps props) { - if (props.hasPartner()) + if (props.partner != null) { return props.pawn.IsAnimal() ^ props.partner.IsAnimal(); } return false; } - - /// - /// Get a not-so-unique ID. Same interaction between the same partners will return same ID - /// - public static int GetTempId(this SexProps props) - { - return props.pawn.GetHashCode() ^ - (props.partner?.GetHashCode() ?? 0) ^ - props.dictionaryKey.GetHashCode() ^ - (props.isReceiver ? 0 : 1); - } } } diff --git a/Source/RJWSexperience/Patches/RJW_Patch.cs b/Source/RJWSexperience/Patches/RJW_Patch.cs index cbb78f6..d712827 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")] // Despite the name, called every tick + [HarmonyPatch(typeof(JobDriver_Sex), "Orgasm")] public static class RJW_Patch_Orgasm { public static void Postfix(JobDriver_Sex __instance) @@ -30,7 +30,7 @@ namespace RJWSexperience } } - [HarmonyPatch(typeof(SexUtility), nameof(SexUtility.SatisfyPersonal))] // Actual on orgasm method + [HarmonyPatch(typeof(SexUtility), nameof(SexUtility.SatisfyPersonal))] public static class RJW_Patch_SatisfyPersonal { private const float base_sat_per_fuck = 0.4f; @@ -46,7 +46,7 @@ namespace RJWSexperience CumUtility.FillCumBuckets(props); props.pawn.records?.Increment(RsDefOf.Record.OrgasmCount); if (SexperienceMod.Settings.EnableSexHistory && props.hasPartner()) - props.pawn.TryGetComp()?.RecordOrgasm(props.partner, props, satisfaction); + props.pawn.TryGetComp()?.RecordSatisfaction(props.partner, props, satisfaction); } } @@ -94,10 +94,10 @@ namespace RJWSexperience { public static void Postfix(JobDriver_SexBaseInitiator __instance) { - if (__instance.Sexprops.hasPartner() && __instance.Sexprops.sexType == xxx.rjwSextype.Vaginal) + if (__instance.Sexprops.hasPartner()) { - __instance.pawn.TryRemoveVirginity(__instance.Partner, __instance.Sexprops); - __instance.Partner.TryRemoveVirginity(__instance.pawn, __instance.Sexprops); + __instance.pawn.PoptheCherry(__instance.Partner, __instance.Sexprops); + __instance.Partner.PoptheCherry(__instance.pawn, __instance.Sexprops); } } } @@ -108,7 +108,9 @@ namespace RJWSexperience /// /// If masturbation and current map has a bucket, return location near the bucket /// - /// The place to stand near a bucket + /// + /// + /// /// Run original method public static bool Prefix(Pawn pawn, Pawn partner, ref IntVec3 __result) { diff --git a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs index a78a904..8c103a3 100644 --- a/Source/RJWSexperience/SexHistory/SexHistoryComp.cs +++ b/Source/RJWSexperience/SexHistory/SexHistoryComp.cs @@ -6,14 +6,11 @@ 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 partnerRecords = new Dictionary(); + protected Dictionary histories = new Dictionary(); protected string first = ""; protected bool dirty = true; protected xxx.rjwSextype recentSex = xxx.rjwSextype.None; @@ -46,14 +43,17 @@ namespace RJWSexperience.SexHistory protected int bestSexTickAbsCache = 0; public Gizmo Gizmo { get; private set; } + public Pawn ParentPawn => parent as Pawn; - public SexPartnerHistoryRecord FirstPartnerRecord => partnerRecords.TryGetValue(first); - public SexPartnerHistoryRecord MostPartnerRecord + + public SexPartnerHistoryRecord GetFirstPartnerHistory => histories.TryGetValue(first); + + public SexPartnerHistoryRecord GetMostPartnerHistory { get { Update(); - return partnerRecords.TryGetValue(mostPartnerCache); + return histories.TryGetValue(mostPartnerCache); } } public xxx.rjwSextype MostSextype @@ -72,13 +72,13 @@ namespace RJWSexperience.SexHistory return mostSatSextypeCache; } } - public SexPartnerHistoryRecord RecentPartnerRecord => partnerRecords.TryGetValue(recentPartner); - public SexPartnerHistoryRecord BestSexPartnerRecord + public SexPartnerHistoryRecord GetRecentPartnersHistory => histories.TryGetValue(recentPartner); + public SexPartnerHistoryRecord GetBestSexPartnerHistory { get { Update(); - return partnerRecords.TryGetValue(bestPartnerCache); + return histories.TryGetValue(bestPartnerCache); } } public float TotalSexHad @@ -96,14 +96,21 @@ namespace RJWSexperience.SexHistory { IEnumerable res = Enumerable.Empty(); Update(); - if (!partnerRecords.NullOrEmpty()) + if (!histories.NullOrEmpty()) { - res = partnerRecords.Values; + res = histories.Values; } return res; } } - public int PartnerCount => partnerRecords?.Count ?? 0; + public int PartnerCount + { + get + { + if (histories == null) histories = new Dictionary(); + return histories.Count; + } + } public int IncestuousCount => incestuous; public int RapedCount { @@ -153,6 +160,7 @@ namespace RJWSexperience.SexHistory public int FirstSexTickAbs => firstSexTickAbs; public int MostSexTickAbs => mostSexTickAbsCache; public int BestSexTickAbs => bestSexTickAbsCache; + public Pawn PreferRacePawn { get @@ -178,9 +186,9 @@ namespace RJWSexperience.SexHistory public int GetSextypeRecentTickAbs(int sextype) => sextypeRecentTickAbs[sextype]; - public float GetAVGSat(int sextype) + public float GetAVGSat(int index) { - float res = sextypeSat[sextype] / sextypeCount[sextype]; + float res = sextypeSat[index] / sextypeCount[index]; return float.IsNaN(res) ? 0f : res; } @@ -205,7 +213,7 @@ namespace RJWSexperience.SexHistory sextyperecenttickabssave = new List(); } - Scribe_Collections.Look(ref partnerRecords, "histories", LookMode.Value, LookMode.Deep); + Scribe_Collections.Look(ref histories, "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); @@ -220,8 +228,8 @@ namespace RJWSexperience.SexHistory Scribe_Collections.Look(ref sextypesatsave, "sextypesatsave", LookMode.Value); Scribe_Collections.Look(ref sextyperecenttickabssave, "sextyperecenttickabssave", LookMode.Value); - if (partnerRecords == null) - partnerRecords = new Dictionary(); + if (histories == null) + histories = new Dictionary(); if (Scribe.mode == LoadSaveMode.LoadingVars) { @@ -229,7 +237,7 @@ namespace RJWSexperience.SexHistory sextypeSat = sextypesatsave?.ToArray() ?? new float[ARRLEN]; sextypeRecentTickAbs = sextyperecenttickabssave?.ToArray() ?? new int[ARRLEN]; - foreach (KeyValuePair element in partnerRecords) + foreach (KeyValuePair element in histories) { element.Value.PartnerID = element.Key; } @@ -239,52 +247,50 @@ namespace RJWSexperience.SexHistory public void RecordSex(Pawn partner, SexProps props) { - SexPartnerHistoryRecord partnerRecord = GetPartnerRecord(partner); - partnerRecord.RecordSex(props); + RecordFirst(partner, props); + GetPartnerRecord(partner)?.RecordSex(props); recentPartner = partner.ThingID; recentSex = props.sexType; sextypeCount[(int)props.sexType]++; sextypeRecentTickAbs[(int)props.sexType] = GenTicks.TicksAbs; - if (partnerRecord.Incest) incestuous++; + if (partner.IsIncest(ParentPawn)) incestuous++; if (partner.Dead) corpsefuck++; if (props.IsBestiality()) bestiality++; else if (ParentPawn.def != partner.def) interspecies++; dirty = true; } - public void RecordOrgasm(Pawn partner, SexProps props, float satisfaction) + public void RecordSatisfaction(Pawn partner, SexProps props, float satisfaction) { - GetPartnerRecord(partner).RecordOrgasm(props.sexType, satisfaction); + RecordFirst(partner, props); + GetPartnerRecord(partner)?.RecordSatisfaction(props, satisfaction); recentSat = satisfaction; - sextypeSat[(int)props.sexType] += satisfaction; // Several orgasmsms in one sex are messing with this + sextypeSat[(int)props.sexType] += satisfaction; dirty = true; } - /// - /// 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) + public void RecordFirst(Pawn partner, SexProps props) { - first = partner.ThingID; - firstSexTickAbs = GenTicks.TicksAbs; - partner.TryGetComp()?.TakeSomeonesVirgin(ParentPawn); + if (VirginCheck() && props.sexType == xxx.rjwSextype.Vaginal) + { + first = partner.ThingID; + SexHistoryComp history = partner.TryGetComp(); + firstSexTickAbs = GenTicks.TicksAbs; + history?.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 (partnerRecords.TryGetValue(partnerId, out SexPartnerHistoryRecord record)) + if (histories.TryGetValue(partnerId, out SexPartnerHistoryRecord record)) { return record; } SexPartnerHistoryRecord newRecord = new SexPartnerHistoryRecord(partner, partner.IsIncest(ParentPawn)); - partnerRecords.Add(partnerId, newRecord); + histories.Add(partnerId, newRecord); ParentPawn.records.Increment(RsDefOf.Record.SexPartnerCount); return newRecord; } @@ -322,7 +328,7 @@ namespace RJWSexperience.SexHistory Dictionary racetotalsat = new Dictionary(); List allpartners = new List(); - foreach (KeyValuePair element in partnerRecords) + foreach (KeyValuePair element in histories) { SexPartnerHistoryRecord h = element.Value; @@ -380,9 +386,9 @@ namespace RJWSexperience.SexHistory mostPartnerCache = mostID; bestPartnerCache = bestID; - recentSexTickAbsCache = partnerRecords.TryGetValue(recentPartner)?.RecentSexTickAbs ?? 0; - mostSexTickAbsCache = partnerRecords.TryGetValue(mostPartnerCache)?.RecentSexTickAbs ?? 0; - bestSexTickAbsCache = partnerRecords.TryGetValue(bestPartnerCache)?.BestSexTickAbs ?? 0; + recentSexTickAbsCache = histories.TryGetValue(recentPartner)?.RecentSexTickAbs ?? 0; + mostSexTickAbsCache = histories.TryGetValue(mostPartnerCache)?.RecentSexTickAbs ?? 0; + bestSexTickAbsCache = histories.TryGetValue(bestPartnerCache)?.BestSexTickAbs ?? 0; racetotalsat.Clear(); allpartners.Clear(); @@ -408,6 +414,14 @@ namespace RJWSexperience.SexHistory #endregion Cache update + protected bool VirginCheck() + { + if (histories.TryGetValue(first) != null) + return false; + + return ParentPawn.IsVirgin(); + } + public override IEnumerable CompGetGizmosExtra() { if (SexperienceMod.Settings.HideGizmoWhenDrafted && ParentPawn.Drafted) diff --git a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs index 9bcc832..12c0f7d 100644 --- a/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs +++ b/Source/RJWSexperience/SexHistory/SexPartnerHistoryRecord.cs @@ -104,13 +104,13 @@ namespace RJWSexperience.SexHistory recentSexTickAbs = GenTicks.TicksAbs; } - public void RecordOrgasm(xxx.rjwSextype sextype, float satisfaction) + public void RecordSatisfaction(SexProps props, float satisfaction) { orgasms++; if (satisfaction > bestSatisfaction) { - bestSextype = sextype; + bestSextype = props.sexType; bestSatisfaction = satisfaction; bestSexTickAbs = GenTicks.TicksAbs; } diff --git a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs index e7b3538..2a80835 100644 --- a/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs +++ b/Source/RJWSexperience/SexHistory/UI/SexStatusViewModel.cs @@ -106,28 +106,28 @@ namespace RJWSexperience.SexHistory.UI InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.RecentPartnerRecord, + partnerRecord: _history.GetRecentPartnersHistory, label: Keyed.RS_Recent_Sex_Partner, tooltipLabel: Keyed.RS_Recent_Sex_Partner_ToolTip, lastSexTimeTicks: _history.RecentSexTickAbs)); InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.FirstPartnerRecord, + partnerRecord: _history.GetFirstPartnerHistory, label: Keyed.RS_First_Sex_Partner, tooltipLabel: Keyed.RS_First_Sex_Partner_ToolTip, lastSexTimeTicks: _history.FirstSexTickAbs)); InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.MostPartnerRecord, + partnerRecord: _history.GetMostPartnerHistory, label: Keyed.RS_Most_Sex_Partner, tooltipLabel: Keyed.RS_Most_Sex_Partner_ToolTip, lastSexTimeTicks: _history.MostSexTickAbs)); InfoCards.Add(new InfoCard( pawn: Pawn, - partnerRecord: _history.BestSexPartnerRecord, + partnerRecord: _history.GetBestSexPartnerHistory, label: Keyed.RS_Best_Sex_Partner, tooltipLabel: Keyed.RS_Best_Sex_Partner_ToolTip, lastSexTimeTicks: _history.BestSexTickAbs)); diff --git a/Source/RJWSexperience/Virginity/TraitHandler.cs b/Source/RJWSexperience/Virginity/TraitHandler.cs index 21cc148..31e6a5e 100644 --- a/Source/RJWSexperience/Virginity/TraitHandler.cs +++ b/Source/RJWSexperience/Virginity/TraitHandler.cs @@ -1,4 +1,5 @@ using RimWorld; +using rjw; using Verse; namespace RJWSexperience.Virginity @@ -45,10 +46,6 @@ namespace RJWSexperience.Virginity } } - /// - /// Remove virginity trait and spawn blood filth if applicable - /// - /// Degree of the removed trait public static int? RemoveVirginTrait(Pawn pawn) { Trait virgin = pawn.story?.traits?.GetTrait(RsDefOf.Trait.Virgin);