Compare commits

..

1 commit

Author SHA1 Message Date
Akiyami Solo
4382decbc7 Merge branch 'dev' into 'dev'
Breast fullness is not displayed and the "milk yourself" action is not invoked on a pawn when the rjw-mc-biotech mod is present

See merge request lutepickle/rjw_menstruation!6
2024-02-11 23:35:27 +00:00
10 changed files with 122 additions and 57 deletions

Binary file not shown.

View file

@ -144,5 +144,11 @@
</li>
</comps>
</value>
</Operation>
</Operation>
</Patch>

View file

@ -110,6 +110,9 @@ namespace RJW_Menstruation
else return base.ShouldBeInEstrus();
}
protected override float RandomOvulationChance => 0;
protected override float RandomOvulationChance()
{
return 0;
}
}
}

View file

@ -127,6 +127,7 @@ namespace RJW_Menstruation
protected float implantationChanceCache = -1.0f;
protected int opcache = -1;
protected float antisperm = 0.0f;
protected float? originvagsize = null;
// RJW pregnancy, or Biotech pregnancy/labor/laborpushing
protected Hediff pregnancy = null;
@ -178,8 +179,7 @@ namespace RJW_Menstruation
public float HoursBetweenSimulations => (float)TickInterval / GenDate.TicksPerHour;
public Hediff Pregnancy
{
public Hediff Pregnancy {
get
{
if (pregnancy == null) return null;
@ -255,7 +255,7 @@ namespace RJW_Menstruation
public float TotalCum
{
get => cums?.Sum(cum => cum.Volume) ?? 0;
get => cums?.Sum(cum => cum.Volume) ?? 0;
}
public float TotalFertCum
{
@ -322,7 +322,6 @@ namespace RJW_Menstruation
// Implant factor will be based solely on pawn age, plus any rollover from ovulation chance
float factor = 1.0f;
StatDefOf.Fertility.GetStatPart<StatPart_FertilityByGenderAge>()?.TransformValue(StatRequest.For(Pawn), ref factor);
if (factor <= 0.0f) return 0.0f;
if (OvulationChance > 1.0f) factor *= OvulationChance;
return Props.baseImplantationChanceFactor * FertilityModifier * factor;
}
@ -357,7 +356,7 @@ namespace RJW_Menstruation
{
if (cums.NullOrEmpty()) yield return Translations.Info_noCum;
else foreach (Cum cum in cums)
yield return string.Format("{0}: {1:0.##}ml", cum.notcum ? cum.notcumLabel : cum.pawn?.Label, cum.Volume);
yield return string.Format("{0}: {1:0.##}ml", cum.notcum ? cum.notcumLabel : cum.pawn?.Label, cum.Volume);
}
}
public Color GetCumMixtureColor
@ -564,6 +563,19 @@ namespace RJW_Menstruation
}
}
public float OriginVagSize
{
get
{
if (originvagsize == null)
{
originvagsize = parent.Severity;
}
return originvagsize ?? 0.1f;
}
set => originvagsize = value;
}
public int CurStageIntervalTicks
{
get => currentIntervalTicks;
@ -596,7 +608,7 @@ namespace RJW_Menstruation
else if (Pawn.story?.bodyType == BodyTypeDefOf.Female) discoveryTime = 0.35f;
// Estimated; there's no way to get the exact value after the fact without writing it into the save
float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay));
return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity);
}
}
@ -644,6 +656,7 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true);
Scribe_Values.Look(ref eggstack, "eggstack", 0);
Scribe_Values.Look(ref estrusflag, "estrusflag", false);
Scribe_Values.Look(ref originvagsize, "originvagsize", originvagsize, true);
Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", false);
Scribe_References.Look(ref pregnancy, "pregnancy");
if (Scribe.mode == LoadSaveMode.PostLoadInit)
@ -666,10 +679,12 @@ namespace RJW_Menstruation
ovulationFactor = 1f;
noBleeding = false;
opcache = -1;
if (Pawn.genes == null || !ModsConfig.BiotechActive) return;
foreach (MenstruationModExtension extension in Pawn.genes.GenesListForReading.Select(gene => gene.def.GetModExtension<MenstruationModExtension>()).Where(ext => ext != null))
if (Pawn.genes == null || !ModsConfig.BiotechActive) return;
foreach (MenstruationModExtension extension in Pawn.genes.GenesListForReading.Select(gene => gene.def.GetModExtension<MenstruationModExtension>()))
{
if (extension == null) continue;
eggLifeSpanTicks = (int)(eggLifeSpanTicks * extension.eggLifeTimeFactor);
if (extension.alwaysEstrus) estrusLevel = EstrusLevel.Visible;
else if (extension.neverEstrus) estrusLevel = EstrusLevel.None;
@ -706,7 +721,7 @@ namespace RJW_Menstruation
{
if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed.
if (!Pawn.IsHashIntervalTick(TickInterval)) return;
if (!ShouldSimulate()) return;
// Initialize immediately if needed, but if there's an error, then don't spam it every tick
@ -715,7 +730,7 @@ namespace RJW_Menstruation
Log.Warning($"{Pawn}'s womb is ticking, but was not initialized first");
Initialize();
}
if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized");
if (Pregnancy != null && curStage != Stage.Pregnant)
@ -725,7 +740,7 @@ namespace RJW_Menstruation
}
BeforeSimulator();
if (ShouldBeInfertile()) GoNextStage(Stage.Infertile);
switch (curStage)
{
@ -802,7 +817,7 @@ namespace RJW_Menstruation
tip.Append(": ");
tip.Append(GetCurStageLabel);
string fertInfo = GetFertilizingInfo;
if (CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0)
if(CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0)
{
tip.AppendLine();
tip.Append(fertInfo);
@ -813,7 +828,7 @@ namespace RJW_Menstruation
protected virtual int TicksToNextStage()
{
return Math.Max(0, (currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration);
return Math.Max(0,(currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration);
}
public override string CompDebugString()
@ -1158,7 +1173,7 @@ namespace RJW_Menstruation
if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f);
if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent();
InitOvary();
if (currentIntervalTicks < 0)
@ -1346,7 +1361,7 @@ namespace RJW_Menstruation
//float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor);
//float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour);
float fertFailChancePerInterval = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor * HoursBetweenSimulations);
if (Rand.Chance(fertFailChancePerInterval)) return null;
Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1);
@ -1476,7 +1491,7 @@ namespace RJW_Menstruation
float interspeciesFactor = InterspeciesImplantFactor(egg.fertilizer);
float implantChance = Configurations.ImplantationChance * ImplantChance * interspeciesFactor;
Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, " +
(interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "") +
(interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "" ) +
$"father {egg.fertilizer})");
}
deadeggs.Add(egg);
@ -1562,6 +1577,9 @@ namespace RJW_Menstruation
return amount;
}
protected void EggDecay()
{
HashSet<Egg> deadeggs = new HashSet<Egg>();
@ -1638,7 +1656,7 @@ namespace RJW_Menstruation
eggnum = 1f;
}
eggnum *= ovulationFactor;
int toOvulate = Math.Max(1, (int)eggnum + eggstack);
int toOvulate = (int)eggnum + eggstack;
int ovulated = 0;
for (int i = 0; i < toOvulate; i++)
@ -1649,7 +1667,7 @@ namespace RJW_Menstruation
}
ovarypower -= ovulated;
eggstack = 0;
if (Configurations.Debug && ovulated < toOvulate)
if (Configurations.Debug && ovulated != toOvulate)
Log.Message($"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)");
GoNextStage(Stage.Luteal);
@ -1796,10 +1814,10 @@ namespace RJW_Menstruation
}
else pawnMemories.TryGainMemory(VariousDefOf.CameInsideFFetish, cummer);
}
else if (Pawn.relations.OpinionOf(cummer) <= -5)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer);
else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer);
else if (Pawn.relations.OpinionOf(cummer) <= -5)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer);
else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer);
else if (!Pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, cummer) && !Pawn.relations.DirectRelationExists(PawnRelationDefOf.Fiance, cummer))
{
if (Pawn.health.capacities.GetLevel(xxx.reproduction) < 0.50f) pawnMemories.TryGainMemory(VariousDefOf.CameInsideFLowFert, cummer);
@ -1896,13 +1914,16 @@ namespace RJW_Menstruation
else return Rand.Range(0.6f, 1.0f);
}
protected virtual float RandomOvulationChance => (float)Props.ovulationIntervalHours / GenDate.HoursPerDay;
protected virtual float RandomOvulationChance()
{
return (float)Props.ovulationIntervalHours / GenDate.HoursPerDay;
}
protected Stage RandomStage()
{
Stage stage = Rand.ElementByWeight(
Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays,
Stage.Ovulatory, RandomOvulationChance,
Stage.Ovulatory, RandomOvulationChance(),
Stage.Luteal, Props.lutealIntervalDays,
Stage.Bleeding, Props.bleedingIntervalDays);
@ -2014,6 +2035,28 @@ namespace RJW_Menstruation
public class HediffComp_Anus : HediffComp
{
public CompProperties_Anus Props => (CompProperties_Anus)props;
protected float? originanussize;
public float OriginAnusSize
{
get
{
if (originanussize == null)
{
originanussize = parent.Severity;
}
return originanussize ?? 0.1f;
}
}
public override void CompExposeData()
{
base.CompExposeData();
Scribe_Values.Look(ref originanussize, "originanussize", originanussize, true);
}
public override void CompPostTick(ref float severityAdjustment)
{
}
}
}

View file

@ -29,14 +29,14 @@ namespace RJW_Menstruation
protected override void InitializeExtraValues()
{
base.InitializeExtraValues();
base.InitializeExtraValues();
if (averageCycleIntervalTicks < 0)
{
averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed);
if (ticksToNextCycle < -50000)
ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks);
// Make the cutoff halfway into cycle, just to be sure there isn't a double-cycle the first time
if ((curStage == Stage.Follicular || curStage == Stage.Ovulatory || curStage == Stage.Luteal || curStage == Stage.Bleeding)
if ((curStage == Stage.Follicular || curStage == Stage.Luteal || curStage == Stage.Bleeding)
&& (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed)
GoNextStage(Stage.Anestrus);
}

View file

@ -141,7 +141,7 @@ namespace RJW_Menstruation
else if (gestationProgress < 0.8f) icon = fetustex + "04";
else icon = fetustex + "05";
return TryGetTwinsIcon(icon, babycount) ?? ContentFinder<Texture2D>.Get(icon, true);
return TryGetTwinsIcon(icon, babycount) ?? ContentFinder<Texture2D>.Get((icon), true);
}
public static Texture2D TryGetTwinsIcon(string path, int babycount)
@ -160,7 +160,7 @@ namespace RJW_Menstruation
List<Hediff_InsectEgg> insectEggs = new List<Hediff_InsectEgg>();
comp.Pawn.health.hediffSet.GetHediffs(ref insectEggs);
if (insectEggs?.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon
if (!insectEggs.NullOrEmpty() && insectEggs.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon
string icon = comp.WombTex;
float cumpercent = comp.TotalCumPercent;
@ -183,7 +183,7 @@ namespace RJW_Menstruation
else if (cumpercent < 0.89f) icon += "_Cum_15";
else if (cumpercent < 0.95f) icon += "_Cum_16";
else icon += "_Cum_17";
Texture2D cumtex = ContentFinder<Texture2D>.Get(icon, true);
Texture2D cumtex = ContentFinder<Texture2D>.Get((icon), true);
return cumtex;
}
public static Texture2D GetInsectEggedIcon(this HediffComp_Menstruation comp)
@ -306,13 +306,15 @@ namespace RJW_Menstruation
}
public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp)
public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp, bool drawOrigin = false)
{
Hediff hediff = comp?.parent;
if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Vagina00", true);
//HediffComp_Menstruation comp = hediff.GetMenstruationComp();
string icon;
float severity = hediff.Severity;
float severity;
if (drawOrigin) severity = comp.OriginVagSize;
else severity = hediff.Severity;
if (comp != null) icon = comp.VagTex;
else icon = "Genitals/Vagina";
@ -329,18 +331,30 @@ namespace RJW_Menstruation
else if (severity < 1.01f) icon += "10"; //cavernous
else icon += "11"; //abyssal
return ContentFinder<Texture2D>.Get(icon, true);
return ContentFinder<Texture2D>.Get((icon), true);
}
public static Texture2D GetAnalIcon(this Pawn pawn)
public static Texture2D GetAnalIcon(this Pawn pawn, bool drawOrigin = false)
{
Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ??
pawn.health.hediffSet.hediffs.FirstOrDefault(h => h.def.defName.ToLower().Contains("anus"));
if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Anal00", true);
string icon = ((CompProperties_Anus)hediff.GetAnusComp()?.props)?.analTex ?? "Genitals/Anal";
float severity = hediff.Severity;
if (hediff == null) return ContentFinder<Texture2D>.Get(("Genitals/Anal00"), true);
string icon;
float severity;
HediffComp_Anus comp = hediff.GetAnusComp();
if (comp != null)
{
CompProperties_Anus Props = (CompProperties_Anus)comp.props;
icon = Props.analTex ?? "Genitals/Anal";
if (drawOrigin) severity = comp.OriginAnusSize;
else severity = hediff.Severity;
}
else
{
icon = "Genitals/Anal";
severity = hediff.Severity;
}
if (severity < 0.20f) icon += "00"; //micro
else if (severity < 0.40f) icon += "01"; //tight
else if (severity < 0.60f) icon += "02"; //average
@ -348,7 +362,7 @@ namespace RJW_Menstruation
else if (severity < 1.01f) icon += "04"; //cavernous
else icon += "05"; //abyssal
return ContentFinder<Texture2D>.Get(icon, true);
return ContentFinder<Texture2D>.Get((icon), true);
}
public static float GestationHours(this Hediff hediff)
@ -439,7 +453,7 @@ namespace RJW_Menstruation
if (precept != null) return true;
else return pawn.IsBreeder() ||
pawn.HasImpregnationFetish();
pawn.HasImpregnationFetish();
}
public static float DamagePants(this Pawn pawn, float fluidAmount)

View file

@ -225,7 +225,9 @@ namespace RJW_Menstruation
bool newHasStats = !DirtyDef.equippedStatOffsets.NullOrEmpty();
def = DirtyDef;
dirty = true;
Wearer.outfits?.forcedHandler?.SetForced(this, false);
OutfitForcedHandler forcedHandler = Wearer.outfits?.forcedHandler;
if (forcedHandler?.IsForced(this) ?? false)
forcedHandler.SetForced(this, false);
if (oldHasStats || newHasStats)
Wearer.health.capacities.Notify_CapacityLevelsDirty();
Wearer.apparel.Notify_ApparelChanged();
@ -272,7 +274,7 @@ namespace RJW_Menstruation
public override void DirtyEffect(int tickInterval)
{
if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !(Wearer.apparel?.IsLocked(this) ?? false))
if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !Wearer.apparel.IsLocked(this))
{
Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer));
}

View file

@ -388,9 +388,10 @@ namespace RJW_Menstruation
Rect genitalIconRect = new Rect(rect.x, rect.y + fontheight, genitalRectWidth, genitalRectHeight);
Rect genitalVaginaLabelRect = new Rect(rect.x, rect.y + 10f, genitalRectWidth, fontheight);
Rect genitalAnusLabelRect = new Rect(rect.x, rect.y + fontheight + genitalRectHeight, genitalRectWidth, fontheight);
bool showOrigin = Mouse.IsOver(genitalIconRect) && Input.GetMouseButton(0);
vagina = pawn.GetGenitalIcon(comp);
anal = pawn.GetAnalIcon();
vagina = pawn.GetGenitalIcon(comp, showOrigin);
anal = pawn.GetAnalIcon(showOrigin);
GUI.color = new Color(1.00f, 0.47f, 0.47f, 1);
GUI.Box(rect, "", boxstyle);
GUI.color = Utility.SafeSkinColor(pawn);

View file

@ -58,8 +58,9 @@ namespace RJW_Menstruation
get
{
if (allraces != null) return allraces;
allraces = DefDatabase<ThingDef>.AllDefsListForReading.Where(thingdef => thingdef.race?.IsFlesh ?? false).ToList();
List<ThingDef> allThings = DefDatabase<ThingDef>.AllDefsListForReading;
allraces = allThings.FindAll(x => x.race != null && x.race.IsFlesh);
return allraces;
}
}
@ -68,8 +69,9 @@ namespace RJW_Menstruation
get
{
if (allkinds != null) return allkinds;
allkinds = DefDatabase<PawnKindDef>.AllDefsListForReading.Where(pawnkinddef => pawnkinddef.race != null).ToList();
List<PawnKindDef> allKinds = DefDatabase<PawnKindDef>.AllDefsListForReading;
allkinds = allKinds.FindAll(x => x.race != null);
return allkinds;
}
}

View file

@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
<packageId>rjw.menstruation</packageId>
<name>RJW Menstruation Cycle</name>
<author>lutepickle</author>
<url>https://gitgud.io/lutepickle/rjw_menstruation/</url>
<supportedVersions>
<li>1.2</li>
<li>1.3</li>
@ -30,17 +28,13 @@
<li>Abraxas.RJW.RaceSupport</li>
<li>rjw.milk.humanoid</li>
</loadAfter>
<incompatibleWithByVersion>
<v1.4>
<li>conit.thebirdsandthebees</li> <!--Breaks fertility calculations-->
</v1.4>
</incompatibleWithByVersion>
<packageId>rjw.menstruation</packageId>
<description>Adds menstruation mechanics to vaginas:
Wombs cycle between follicular, luteal, and bleeding phases
Tracks eggs ovulated and cum in wombs to determine pregnancy
Womb icon and status window
Estrus and pheromone effects
Estrus effects
Race-specific fetus images for many vanilla animals
Pregnancies from multiple eggs and different fathers
Identical siblings