2022-10-24 01:02:02 +00:00
using RimWorld ;
using RimWorld.Planet ;
using rjw ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using UnityEngine ;
using Verse ;
namespace RJW_Menstruation
{
[Flags]
public enum SeasonalBreed
{
Always = 0 ,
Spring = 1 ,
Summer = 2 ,
Fall = 4 ,
Winter = 8 ,
FirstHalf = Spring | Summer ,
SecondHalf = Fall | Winter
}
public class CompProperties_Menstruation : HediffCompProperties
{
public float maxCumCapacity ; // ml
public float baseImplantationChanceFactor ;
public float basefertilizationChanceFactor ;
public int follicularIntervalDays = 14 ; //before ovulation including beginning of bleeding
2023-04-14 03:17:25 +00:00
public int ovulationIntervalHours = 12 ; //between the end of follicular and the egg emerging
2022-10-24 01:02:02 +00:00
public int lutealIntervalDays = 14 ; //after ovulation until bleeding
public int bleedingIntervalDays = 6 ; //must be less than folicularIntervalDays
public int recoveryIntervalDays = 10 ; //additional infertile days after gave birth
public int eggLifespanDays = 2 ; //fertiledays = ovaluationday - spermlifespan ~ ovaluationday + egglifespanday
public string wombTex = "Womb/Womb" ; //fertiledays = ovaluationday - spermlifespan ~ ovaluationday + egglifespanday
public string vagTex = "Genitals/Vagina" ; //fertiledays = ovaluationday - spermlifespan ~ ovaluationday + egglifespanday
public bool infertile = false ;
public bool concealedEstrus = false ;
public SeasonalBreed breedingSeason = SeasonalBreed . Always ;
public int estrusDaysBeforeOvulation = 3 ;
public int eggMultiplier = 1 ;
public CompProperties_Menstruation ( )
{
compClass = typeof ( HediffComp_Menstruation ) ;
}
}
public class CompProperties_Anus : HediffCompProperties
{
public string analTex = "Genitals/Anal" ;
public CompProperties_Anus ( )
{
compClass = typeof ( HediffComp_Anus ) ;
}
}
public class HediffComp_Menstruation : HediffComp
{
const float minmakefilthvalue = 1.0f ;
2023-04-14 03:17:25 +00:00
const int maxImplantDelayHours = 30 * GenDate . HoursPerDay ;
const int minImplantAgeHours = 3 * GenDate . HoursPerDay ;
2023-04-30 00:49:02 +00:00
const float fluidLeakThreshold = 3.5f ;
2022-12-25 16:51:42 +00:00
const float pulloutSuccessRate = 0.8f ;
2022-12-25 17:54:49 +00:00
const float fetishPulloutSuccessModifier = 0.25f ;
2022-10-24 01:02:02 +00:00
public CompProperties_Menstruation Props ;
public Stage curStage = Stage . Follicular ;
2023-04-14 03:17:25 +00:00
public int curStageTicks = 0 ; // Actual number of ticks equals this / cycleAcceleration
private int tickInterval = - 1 ;
2023-04-14 15:23:02 +00:00
const int recalculateTickInterval = GenTicks . TickLongInterval ;
2022-10-24 01:02:02 +00:00
public bool loaded = false ;
public bool initError = false ;
public int ovarypower = - 100000 ;
public int eggstack = 0 ;
public bool DoCleanWomb = false ;
public enum Stage
{
Follicular ,
Ovulatory ,
Luteal ,
Bleeding ,
Pregnant ,
Recover ,
None ,
2022-10-24 02:29:01 +00:00
Infertile ,
2022-10-24 01:02:02 +00:00
Anestrus
}
public enum EstrusLevel
{
None ,
Concealed ,
Visible
}
public static readonly Dictionary < Stage , Texture2D > StageTexture = new Dictionary < Stage , Texture2D > ( )
{
{ Stage . Follicular , TextureCache . FollicularTexture } ,
2023-04-30 00:49:02 +00:00
{ Stage . Ovulatory , TextureCache . OvulatoryTexture } ,
2022-10-24 01:02:02 +00:00
{ Stage . Luteal , TextureCache . LutealTexture } ,
{ Stage . Bleeding , TextureCache . BleedingTexture } ,
{ Stage . Pregnant , TextureCache . PregnantTexture } ,
{ Stage . Recover , TextureCache . RecoverTexture }
} ;
protected List < Cum > cums ;
protected List < Egg > eggs ;
protected float cycleSpeed = - 1 ;
protected float cycleVariability = - 1 ;
2023-04-14 03:17:25 +00:00
protected int currentIntervalTicks = - 1 ; // Actual number of ticks equals this / cycleAcceleration
2022-10-24 01:02:02 +00:00
protected float crampPain = - 1 ;
2023-04-15 15:30:39 +00:00
protected float fluidToLeak = 0 ;
2022-10-24 01:02:02 +00:00
protected Need sexNeed = null ;
protected string customwombtex = null ;
protected string customvagtex = null ;
protected bool estrusflag = false ;
2023-05-01 21:22:22 +00:00
protected float ovulationChanceCache = - 1.0f ; // Dirtied every simulation
protected float implantationChanceCache = - 1.0f ;
2022-10-24 01:02:02 +00:00
protected int opcache = - 1 ;
protected float antisperm = 0.0f ;
protected float? originvagsize = null ;
2022-10-27 20:57:25 +00:00
// RJW pregnancy, or Biotech pregnancy/labor/laborpushing
protected Hediff pregnancy = null ;
2022-10-24 01:02:02 +00:00
2023-04-14 03:17:25 +00:00
protected int eggLifeSpanTicks = 2 * GenDate . TicksPerDay ;
2023-01-04 06:48:06 +00:00
protected EstrusLevel estrusLevel = EstrusLevel . Visible ;
protected float ovulationFactor = 1f ;
protected bool noBleeding = false ;
2022-10-24 01:02:02 +00:00
private static readonly SimpleCurve SexFrequencyCurve = new SimpleCurve ( )
{
new CurvePoint ( 0.4f , 0.05f ) ,
new CurvePoint ( 0.6f , 0.1f ) ,
new CurvePoint ( 0.8f , 0.25f ) ,
new CurvePoint ( 1.0f , 0.5f )
} ;
private static readonly SimpleCurve SexSatisfactionCurve = new SimpleCurve ( )
{
new CurvePoint ( 0.4f , 0.5f ) ,
new CurvePoint ( 0.6f , 0.6f ) ,
new CurvePoint ( 0.8f , 0.7f ) ,
new CurvePoint ( 1.0f , 0.8f )
} ;
private static readonly SimpleCurve FertilityCurve = new SimpleCurve ( )
{
new CurvePoint ( 0.4f , 0.01f ) ,
new CurvePoint ( 0.6f , 0.1f ) ,
new CurvePoint ( 0.8f , 0.25f ) ,
new CurvePoint ( 1.0f , 0.5f )
} ;
2023-05-04 17:26:29 +00:00
protected int TickInterval
2023-04-14 03:17:25 +00:00
{
get
{
if ( tickInterval < = 0 )
{
if ( Pawn . IsAnimal ( ) ) tickInterval = Configurations . AnimalTickInterval ;
else if ( Pawn . IsColonist | | Pawn . IsPrisonerOfColony | | Pawn . IsSlaveOfColony ) tickInterval = Configurations . ColonistTickInterval ;
else tickInterval = Configurations . NonColonistTickInterval ;
2023-05-20 03:07:10 +00:00
if ( tickInterval < Configurations . TickIntervalMinimum ) tickInterval = Configurations . TickIntervalMinimum ;
else if ( tickInterval > Configurations . TickIntervalMaximum ) tickInterval = Configurations . TickIntervalMaximum ;
2023-04-14 03:17:25 +00:00
}
return tickInterval ;
}
2023-04-20 19:31:27 +00:00
set = > tickInterval = value ;
2023-04-14 03:17:25 +00:00
}
2023-06-23 04:56:26 +00:00
public float HoursBetweenSimulations = > ( float ) TickInterval / GenDate . TicksPerHour ;
2023-05-04 17:26:29 +00:00
2022-10-27 20:57:25 +00:00
public Hediff Pregnancy {
2022-10-24 01:02:02 +00:00
get
{
if ( pregnancy = = null ) return null ;
else if ( ! Pawn . health . hediffSet . hediffs . Contains ( pregnancy ) )
{
pregnancy = null ;
return null ;
}
else return pregnancy ;
}
set = > pregnancy = value ;
}
public int OvaryPowerThreshold
{
get
{
2022-11-01 03:53:57 +00:00
if ( opcache > 0 ) return opcache ;
2022-10-24 01:02:02 +00:00
float avglittersize ;
try
{
2023-04-30 03:32:46 +00:00
avglittersize = Mathf . Max ( Rand . ByCurveAverage ( Pawn . RaceProps . litterSizeCurve ) , 1.0f ) ;
2022-10-24 01:02:02 +00:00
}
catch
{
// Any exceptions in that will have been reported elsewhere in the code by now
avglittersize = 1.0f ;
} ;
2023-01-04 06:48:06 +00:00
avglittersize * = ovulationFactor ;
2022-10-24 01:02:02 +00:00
const float yearsBeforeMenopause = 6.0f ;
opcache = ( int ) ( RaceCyclesPerYear ( ) *
avglittersize *
yearsBeforeMenopause *
2023-04-30 03:32:46 +00:00
( Pawn . RaceProps . lifeExpectancy / ThingDefOf . Human . race . lifeExpectancy ) ) ;
2022-11-01 03:53:57 +00:00
if ( opcache = = 0 ) opcache = 1 ;
2022-10-24 01:02:02 +00:00
return opcache ;
}
}
// >= 1: Normal cycles
// 1 - 0: Climacteric
// <= 0: Menopause
public float EggHealth
{
get
{
2022-11-05 14:57:44 +00:00
if ( ! Configurations . EnableMenopause | | Props . infertile ) return Mathf . Max ( 1.0f , ovarypower / OvaryPowerThreshold ) ;
2023-03-06 21:51:11 +00:00
else return ( float ) ovarypower / OvaryPowerThreshold ;
2022-10-24 01:02:02 +00:00
}
}
public float SexFrequencyModifier
{
get
{
float eggHealth = EggHealth ;
if ( eggHealth > = 1 ) return 1.0f ;
else if ( eggHealth < = 0 ) return 0.01f ;
else return SexFrequencyCurve . Evaluate ( eggHealth ) ;
}
}
public float SexSatisfactionModifier
{
get
{
float eggHealth = EggHealth ;
if ( eggHealth > = 1 ) return 1.0f ;
else if ( eggHealth < = 0 ) return 0.5f ;
else return SexSatisfactionCurve . Evaluate ( eggHealth ) ;
}
}
public float FertilityModifier
{
get
{
float eggHealth = EggHealth ;
if ( eggHealth > = 1 ) return 1.0f ;
else if ( eggHealth < = 0 ) return 0.0f ;
else return FertilityCurve . Evaluate ( eggHealth ) ;
}
}
public float TotalCum
{
2023-04-10 22:32:51 +00:00
get = > cums ? . Sum ( cum = > cum . Volume ) ? ? 0 ;
2022-10-24 01:02:02 +00:00
}
public float TotalFertCum
{
2023-04-10 22:32:51 +00:00
get = > cums ? . Sum ( cum = > cum . FertVolume ) ? ? 0 ;
2022-10-24 01:02:02 +00:00
}
public float TotalCumPercent
{
2023-04-10 22:32:51 +00:00
get = > cums ? . Sum ( cum = > cum . Volume ) / Props . maxCumCapacity ? ? 0 ;
2022-10-24 01:02:02 +00:00
}
public float CumCapacity
{
get
{
float res = Props . maxCumCapacity * Pawn . BodySize ;
if ( curStage ! = Stage . Pregnant | | ( pregnancy ? . Severity ? ? 0f ) < 0.175f ) res * = 500f ;
return res ;
}
}
//make follicular interval into half and double egg lifespan
public float CycleFactor
{
get
{
2023-04-10 22:32:51 +00:00
if ( QuirkUtility . HasQuirk ( Pawn , QuirkUtility . Quirks . Breeder ) ) return 0.5f ;
2022-10-24 01:02:02 +00:00
return 1.0f ;
}
}
2023-03-07 00:53:22 +00:00
// I hate doing this, but it's the least bad option
2023-05-19 05:37:31 +00:00
private bool calculatingOvulationChance = false ;
public bool CalculatingOvulationChance { get = > calculatingOvulationChance ; }
2023-03-07 00:53:22 +00:00
2023-05-01 21:22:22 +00:00
protected float CalcuatedOvulationChance ( )
2023-03-07 00:53:22 +00:00
{
2023-05-01 21:22:22 +00:00
float ovulationChance = 1.0f ;
if ( EggHealth < = 0.0f ) return 0.0f ;
if ( EggHealth < 1.0f / 3.0f ) ovulationChance = 0.8f ;
if ( ModsConfig . BiotechActive & & xxx . is_human ( Pawn ) )
2023-03-07 00:53:22 +00:00
{
2023-05-01 21:22:22 +00:00
if ( Pawn . SterileGenes ( ) ) return 0.0f ;
// Replicate how rjw.PawnCapacityWorker_Fertility.CalculateCapacityLevel does it, but without the age factor
if ( ! Pawn . RaceHasFertility ( ) ) return 0.0f ;
if ( AndroidsCompatibility . IsAndroid ( Pawn ) & & parent . def ! = Genital_Helper . archotech_vagina ) return 0.0f ;
foreach ( var part in StatDefOf . Fertility . parts )
2023-03-07 00:53:22 +00:00
{
2023-05-01 21:22:22 +00:00
if ( part is StatPart_FertilityByGenderAge fertilityByAge )
2023-03-07 00:53:22 +00:00
{
2023-05-01 21:22:22 +00:00
float factor = 1.0f ;
fertilityByAge . TransformValue ( StatRequest . For ( Pawn ) , ref factor ) ;
if ( factor < = 0.0f ) return 0.0f ; // Too young or too old
2023-03-07 00:53:22 +00:00
}
2023-05-01 21:22:22 +00:00
else part . TransformValue ( StatRequest . For ( Pawn ) , ref ovulationChance ) ;
2023-03-07 00:53:22 +00:00
}
2023-05-01 21:22:22 +00:00
if ( Pawn . HasQuirk ( QuirkUtility . Quirks . Breeder ) ) ovulationChance * = 10.0f ;
try
{
calculatingOvulationChance = true ;
ovulationChance * = PawnCapacityUtility . CalculateCapacityLevel ( Pawn . health . hediffSet , xxx . reproduction ) ;
}
finally { calculatingOvulationChance = false ; }
}
return ovulationChance ;
}
protected float CalcuatedImplantChance ( )
{
float factor = 1.0f ;
if ( ModsConfig . BiotechActive & & xxx . is_human ( Pawn ) )
{
// Implant factor will be based solely on pawn age, plus any rollover from ovulation chance
StatPart_FertilityByGenderAge fertilityStatPart = StatDefOf . Fertility . GetStatPart < StatPart_FertilityByGenderAge > ( ) ;
fertilityStatPart ? . TransformValue ( StatRequest . For ( Pawn ) , ref factor ) ;
2023-05-02 15:35:39 +00:00
if ( OvulationChance > 1.0f ) factor * = OvulationChance ;
2023-05-01 21:22:22 +00:00
return Props . baseImplantationChanceFactor * FertilityModifier * factor ;
}
else
{
return Pawn . health . capacities . GetLevel ( xxx . reproduction ) * Props . baseImplantationChanceFactor * FertilityModifier * factor ;
2023-03-07 00:53:22 +00:00
}
}
2023-05-01 21:22:22 +00:00
public float OvulationChance
{
get
{
if ( ovulationChanceCache < 0.0f ) ovulationChanceCache = CalcuatedOvulationChance ( ) ;
return ovulationChanceCache ;
}
}
2023-03-07 00:53:22 +00:00
public float ImplantChance
2022-10-24 01:02:02 +00:00
{
get
{
2023-05-01 21:22:22 +00:00
if ( implantationChanceCache < 0.0f ) implantationChanceCache = CalcuatedImplantChance ( ) ;
return implantationChanceCache ;
2022-10-24 01:02:02 +00:00
}
}
2023-03-07 00:53:22 +00:00
2022-10-24 01:02:02 +00:00
public IEnumerable < string > GetCumsInfo
{
get
{
if ( cums . NullOrEmpty ( ) ) yield return Translations . Info_noCum ;
else foreach ( Cum cum in cums )
2023-05-20 02:45:27 +00:00
yield return string . Format ( "{0}: {1:0.##}ml" , cum . notcum ? cum . notcumLabel : cum . pawn ? . Label , cum . Volume ) ;
2022-10-24 01:02:02 +00:00
}
}
public Color GetCumMixtureColor
{
get
{
Color mixedcolor = Color . white ;
if ( cums . NullOrEmpty ( ) ) return mixedcolor ;
float mixedsofar = 0 ;
foreach ( Cum cum in cums )
{
if ( cum . Volume > 0 )
{
mixedcolor = Colors . CMYKLerp ( mixedcolor , cum . Color , cum . Volume / ( mixedsofar + cum . Volume ) ) ;
mixedsofar + = cum . Volume ;
}
}
return mixedcolor ;
}
}
public Stage CurrentVisibleStage
{
get
{
if ( curStage = = Stage . Pregnant )
{
if ( Configurations . InfoDetail = = Configurations . DetailLevel . All | | ( pregnancy ? . Visible ? ? false ) )
return Stage . Pregnant ;
else
return Stage . Luteal ;
}
return curStage ;
}
}
public string GetCurStageLabel
{
get
{
switch ( CurrentVisibleStage )
{
case Stage . Follicular :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Follicular + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Ovulatory :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Ovulatory + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Luteal :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Luteal + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Bleeding :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Bleeding + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Pregnant :
return Translations . Stage_Pregnant ;
case Stage . Recover :
return Translations . Stage_Recover ;
case Stage . None :
2022-10-24 02:29:01 +00:00
case Stage . Infertile :
2022-10-24 01:02:02 +00:00
if ( EggHealth < = 0f ) return Translations . Stage_Menopause ;
else return Translations . Stage_None ;
case Stage . Anestrus :
return Translations . Stage_Anestrus ;
default :
return "" ;
}
}
}
public virtual string GetCurStageDesc
{
get
{
switch ( CurrentVisibleStage )
{
case Stage . Follicular :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Follicular_Desc + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric_Desc : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Ovulatory :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Ovulatory_Desc + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric_Desc : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Luteal :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Luteal_Desc + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric_Desc : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Bleeding :
2023-03-06 22:40:29 +00:00
return Translations . Stage_Bleeding_Desc + ( EggHealth < 1f ? " " + Translations . Stage_Climacteric_Desc : "" ) ;
2022-10-24 01:02:02 +00:00
case Stage . Pregnant :
return Translations . Stage_Pregnant_Desc ;
case Stage . Recover :
return Translations . Stage_Recover_Desc ;
case Stage . None :
2022-10-24 02:29:01 +00:00
case Stage . Infertile :
2022-10-24 01:02:02 +00:00
if ( EggHealth < = 0f ) return Translations . Stage_Menopause_Desc ;
else return Translations . Stage_None_Desc ;
case Stage . Anestrus :
return Translations . Stage_Anestrus_Desc ;
default :
return "" ;
}
}
}
public string WombTex
{
2023-04-10 22:32:51 +00:00
get = > customwombtex ? ? Props . wombTex ;
set = > customwombtex = value ;
2022-10-24 01:02:02 +00:00
}
public string VagTex
{
2023-04-10 22:32:51 +00:00
get = > customvagtex ? ? Props . vagTex ;
set = > customvagtex = value ;
2022-10-24 01:02:02 +00:00
}
public string GetFertilizingInfo
{
get
{
if ( eggs . NullOrEmpty ( ) ) return "" ;
StringBuilder res = new StringBuilder ( ) ;
int fertilized = eggs . Count ( egg = > egg . fertilized ) ;
if ( fertilized ! = 0 ) res . AppendFormat ( "{0} {1}" , fertilized , Translations . Dialog_WombInfo05 ) ;
if ( fertilized ! = 0 & & eggs . Count - fertilized ! = 0 ) res . Append ( ", " ) ;
if ( cums . NullOrEmpty ( ) | | TotalFertCum = = 0 )
{
if ( eggs . Count - fertilized ! = 0 ) res . AppendFormat ( "{0} {1}" , eggs . Count - fertilized , Translations . Dialog_WombInfo07 ) ;
}
else
{
if ( eggs . Count - fertilized ! = 0 ) res . AppendFormat ( "{0} {1}" , eggs . Count - fertilized , Translations . Dialog_WombInfo06 ) ;
}
return res . ToString ( ) ;
}
}
public bool IsEggFertilizing
{
get
{
if ( eggs . NullOrEmpty ( ) ) return false ;
return cums ? . Any ( cum = > cum . FertVolume > 0 ) ? ? false ;
}
}
/// <summary>
/// returns fertstage. if not fertilized returns -1
/// </summary>
2023-04-14 03:17:25 +00:00
public int EggFertilizedTime
2022-10-24 01:02:02 +00:00
{
get
{
if ( eggs ? . All ( egg = > ! egg . fertilized ) ? ? true ) return - 1 ;
2023-04-14 03:17:25 +00:00
return eggs . Max ( egg = > egg . ticksSinceFertilization ) ;
2022-10-24 01:02:02 +00:00
}
}
public IEnumerable < Pawn > GetCummersAndFertilizers ( )
{
if ( ! cums . NullOrEmpty ( ) )
foreach ( Cum cum in cums )
yield return cum . pawn ;
if ( ! eggs . NullOrEmpty ( ) )
foreach ( Egg egg in eggs )
yield return egg . fertilizer ;
}
public bool IsEggExist
{
2023-04-10 22:32:51 +00:00
get = > ! eggs . NullOrEmpty ( ) ;
2022-10-24 01:02:02 +00:00
}
2023-01-04 06:48:06 +00:00
2023-04-14 03:17:25 +00:00
public int EggLifespanTicks
2023-01-04 06:48:06 +00:00
{
2023-04-14 03:17:25 +00:00
get = > eggLifeSpanTicks ;
2023-01-04 06:48:06 +00:00
}
2022-10-24 01:02:02 +00:00
public virtual bool IsDangerDay
{
get
{
2022-11-04 19:06:03 +00:00
if ( Pawn . HasIUD ( ) ) return false ;
2022-10-24 01:02:02 +00:00
switch ( curStage )
{
case Stage . Follicular :
2023-04-14 03:17:25 +00:00
return curStageTicks > 0.7f * currentIntervalTicks ;
2022-10-24 01:02:02 +00:00
case Stage . Ovulatory :
return true ;
case Stage . Luteal :
2023-04-14 03:17:25 +00:00
return curStageTicks < EggLifespanTicks ;
2022-10-24 01:02:02 +00:00
default :
return false ;
}
}
}
public int GetNumOfEggs
{
2023-04-10 22:32:51 +00:00
get = > eggs ? . Count ? ? 0 ;
2022-10-24 01:02:02 +00:00
}
public Color BloodColor
{
get
{
try
{
2023-04-30 03:32:46 +00:00
Color c = Pawn . RaceProps . BloodDef . graphicData . color ;
2022-10-24 01:02:02 +00:00
return c ;
}
catch
{
return Colors . blood ;
}
}
}
public float OriginVagSize
{
get
{
if ( originvagsize = = null )
{
originvagsize = parent . Severity ;
}
return originvagsize ? ? 0.1f ;
}
2023-04-10 22:32:51 +00:00
set = > originvagsize = value ;
2022-10-24 01:02:02 +00:00
}
2023-04-14 03:17:25 +00:00
public int CurStageIntervalTicks
2022-10-24 01:02:02 +00:00
{
2023-04-14 03:17:25 +00:00
get = > currentIntervalTicks ;
2022-10-24 01:02:02 +00:00
}
public float StageProgress
{
get
{
2023-04-14 03:17:25 +00:00
if ( pregnancy = = null ) return Mathf . Clamp01 ( ( float ) curStageTicks / currentIntervalTicks ) ;
2022-10-27 20:57:25 +00:00
bool is_discovered = false ;
switch ( pregnancy )
{
case Hediff_BasePregnancy rjw_preg :
is_discovered = rjw_preg . is_discovered ;
break ;
case Hediff_Pregnant vanilla_preg :
is_discovered = vanilla_preg . Visible ;
break ;
case Hediff_Labor _ :
case Hediff_LaborPushing _ :
2022-10-28 22:01:28 +00:00
return 1.0f ;
2022-10-27 20:57:25 +00:00
}
if ( is_discovered | | Configurations . infoDetail = = Configurations . DetailLevel . All ) return pregnancy . Severity ;
2022-10-24 01:02:02 +00:00
// Luteal will appear to progress, hitting the end of the phase when the pregnancy is discovered
float discoveryTime = 0.5f ;
if ( Pawn . story ? . bodyType = = BodyTypeDefOf . Thin ) discoveryTime = 0.25f ;
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
2023-04-14 03:17:25 +00:00
float lutealProgressWhenImplanted = Math . Min ( 0.5f , maxImplantDelayHours / ( Props . lutealIntervalDays * GenDate . HoursPerDay ) ) ;
2022-10-24 01:02:02 +00:00
return GenMath . LerpDouble ( 0 , discoveryTime , lutealProgressWhenImplanted , 1.0f , pregnancy . Severity ) ;
}
}
2023-05-01 22:11:58 +00:00
public float StageProgessNextUpdate
{
get
{
if ( pregnancy ! = null ) return StageProgress ;
return Mathf . Clamp01 ( ( float ) ( curStageTicks + TickInterval * Configurations . CycleAcceleration ) / currentIntervalTicks ) ;
}
}
2022-10-24 01:02:02 +00:00
public Texture2D GetStageTexture
{
get
{
if ( ! StageTexture . TryGetValue ( CurrentVisibleStage , out Texture2D tex ) ) tex = TextureCache . PregnantTexture ;
return tex ;
}
}
public override void CompExposeData ( )
{
base . CompExposeData ( ) ;
2023-04-14 03:17:25 +00:00
if ( Scribe . mode = = LoadSaveMode . LoadingVars )
{
int curStageHrs = - 1 ;
int currentIntervalHours = - 1 ;
Scribe_Values . Look ( ref curStageHrs , "curStageHrs" , curStageHrs , true ) ;
Scribe_Values . Look ( ref currentIntervalHours , "currentIntervalHours" , currentIntervalHours , true ) ;
if ( curStageHrs > = 0 ) curStageTicks = curStageHrs * GenDate . TicksPerHour ;
if ( currentIntervalHours > = 0 ) currentIntervalTicks = currentIntervalHours * GenDate . TicksPerHour ;
}
2022-10-24 01:02:02 +00:00
Scribe_Collections . Look ( ref cums , saveDestroyedThings : true , label : "cums" , lookMode : LookMode . Deep , ctorArgs : new object [ 0 ] ) ;
Scribe_Collections . Look ( ref eggs , saveDestroyedThings : true , label : "eggs" , lookMode : LookMode . Deep , ctorArgs : new object [ 0 ] ) ;
Scribe_Values . Look ( ref curStage , "curStage" , curStage , true ) ;
2023-04-14 03:17:25 +00:00
Scribe_Values . Look ( ref curStageTicks , "curStageTicks" , curStageTicks , true ) ;
2022-10-24 01:02:02 +00:00
Scribe_Values . Look ( ref cycleSpeed , "cycleSpeed" , cycleSpeed , true ) ;
Scribe_Values . Look ( ref cycleVariability , "cycleVariability" , cycleVariability , true ) ;
2023-04-14 03:17:25 +00:00
Scribe_Values . Look ( ref currentIntervalTicks , "currentIntervalTicks" , currentIntervalTicks , true ) ;
2022-10-24 01:02:02 +00:00
Scribe_Values . Look ( ref crampPain , "crampPain" , crampPain , true ) ;
2023-04-15 15:30:39 +00:00
Scribe_Values . Look ( ref fluidToLeak , "fluidToLeak" , 0 ) ;
2022-10-24 01:02:02 +00:00
Scribe_Values . Look ( ref ovarypower , "ovarypower" , ovarypower , true ) ;
2023-04-15 14:59:00 +00:00
Scribe_Values . Look ( ref eggstack , "eggstack" , 0 ) ;
Scribe_Values . Look ( ref estrusflag , "estrusflag" , false ) ;
2022-10-24 01:02:02 +00:00
Scribe_Values . Look ( ref originvagsize , "originvagsize" , originvagsize , true ) ;
2023-04-15 14:59:00 +00:00
Scribe_Values . Look ( ref DoCleanWomb , "DoCleanWomb" , false ) ;
2022-10-24 01:02:02 +00:00
Scribe_References . Look ( ref pregnancy , "pregnancy" ) ;
2023-07-03 16:08:38 +00:00
if ( Scribe . mode = = LoadSaveMode . PostLoadInit )
Initialize ( ) ;
2022-10-24 01:02:02 +00:00
}
public override void CompPostPostAdd ( DamageInfo ? dinfo )
{
if ( ! loaded )
{
Initialize ( ) ;
}
}
2023-01-04 06:48:06 +00:00
public void Notify_UpdatedGenes ( )
{
2023-04-14 03:17:25 +00:00
eggLifeSpanTicks = Props . eggLifespanDays * GenDate . TicksPerDay ;
2023-01-04 06:48:06 +00:00
estrusLevel = Props . concealedEstrus ? EstrusLevel . Concealed : EstrusLevel . Visible ;
ovulationFactor = 1f ;
noBleeding = false ;
if ( Pawn . genes = = null | | ! ModsConfig . BiotechActive ) return ;
2023-04-14 03:17:25 +00:00
if ( Pawn . genes . HasGene ( VariousDefOf . ShortEggLifetime ) ) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4 ;
else if ( Pawn . genes . HasGene ( VariousDefOf . DoubleEggLifetime ) ) eggLifeSpanTicks * = 2 ;
else if ( Pawn . genes . HasGene ( VariousDefOf . QuadEggLifetime ) ) eggLifeSpanTicks * = 4 ;
2023-01-04 06:48:06 +00:00
if ( Pawn . genes . HasGene ( VariousDefOf . NeverEstrus ) ) estrusLevel = EstrusLevel . None ;
else if ( Pawn . genes . HasGene ( VariousDefOf . FullEstrus ) ) estrusLevel = EstrusLevel . Visible ;
if ( Pawn . genes . HasGene ( VariousDefOf . DoubleOvulation ) ) ovulationFactor = 2f ;
else if ( Pawn . genes . HasGene ( VariousDefOf . QuadOvulation ) ) ovulationFactor = 4f ;
noBleeding = Pawn . genes . HasGene ( VariousDefOf . NoBleeding ) ;
}
2022-10-24 01:02:02 +00:00
public bool ShouldSimulate ( )
{
if ( ! Configurations . EnableAnimalCycle & & Pawn . IsAnimal ( ) ) return false ;
2023-04-18 04:52:41 +00:00
if ( Pawn . SpawnedOrAnyParentSpawned | | Pawn . IsCaravanMember ( ) | | PawnUtility . IsTravelingInTransportPodWorldObject ( Pawn ) ) return true ;
2022-10-24 01:02:02 +00:00
return false ;
}
2023-03-16 19:24:47 +00:00
public bool ShouldBeInfertile ( )
{
if ( pregnancy ! = null ) return false ;
if ( ImplantChance < = 0.0f ) return true ;
// Give the last egg ovulated a chance to implant
if ( curStage = = Stage . Luteal | | curStage = = Stage . Bleeding | | curStage = = Stage . Recover ) return false ;
if ( EggHealth < = 0.0f ) return true ;
return false ;
}
2022-10-24 01:02:02 +00:00
public override void CompPostTick ( ref float severityAdjustment )
{
base . CompPostTick ( ref severityAdjustment ) ;
// If an exception makes it out, RW will remove the hediff, so catch it here
try
{
if ( ! ShouldSimulate ( ) ) return ;
// Initialize immediately if needed, but if there's an error, then don't spam it every tick
if ( ! loaded & & ! initError )
{
Log . Warning ( $"{Pawn}'s womb is ticking, but was not initialized first" ) ;
Initialize ( ) ;
}
2023-04-20 19:31:27 +00:00
if ( Pawn . IsHashIntervalTick ( recalculateTickInterval ) ) TickInterval = - 1 ; // Every so often, force TickInterval to be recalculated in case the pawn's status changed.
2023-04-14 03:17:25 +00:00
if ( ! Pawn . IsHashIntervalTick ( TickInterval ) ) return ;
2022-10-24 01:02:02 +00:00
if ( initError ) Log . Warning ( $"Attempting to process {Pawn}'s womb uninitialized" ) ;
if ( Pregnancy ! = null & & curStage ! = Stage . Pregnant )
{
Log . Warning ( $"{Pawn}'s womb has a pregnancy, but was not in the pregnant stage" ) ;
2023-03-02 22:42:49 +00:00
GoNextStage ( Stage . Pregnant ) ;
2022-10-24 01:02:02 +00:00
}
2022-12-25 23:41:20 +00:00
BeforeSimulator ( ) ;
2022-10-24 01:02:02 +00:00
2023-03-16 19:24:47 +00:00
if ( ShouldBeInfertile ( ) ) GoNextStage ( Stage . Infertile ) ;
2022-10-24 01:02:02 +00:00
switch ( curStage )
{
case Stage . Follicular :
FollicularAction ( ) ;
break ;
case Stage . Ovulatory :
OvulatoryAction ( ) ;
break ;
case Stage . Luteal :
LutealAction ( ) ;
break ;
case Stage . Bleeding :
BleedingAction ( ) ;
break ;
case Stage . Pregnant :
PregnantAction ( ) ;
break ;
case Stage . Recover :
RecoverAction ( ) ;
break ;
case Stage . None :
break ;
2022-10-24 02:29:01 +00:00
case Stage . Infertile :
InfertileAction ( ) ;
2022-10-24 01:02:02 +00:00
break ;
case Stage . Anestrus :
AnestrusAction ( ) ;
break ;
default :
GoNextStage ( Stage . Follicular ) ;
break ;
}
AfterSimulator ( ) ;
}
catch ( Exception ex )
{
Log . Error ( $"Error processing womb of {Pawn}: {ex}" ) ;
}
}
public override void CompPostPostRemoved ( )
{
// If a hediff is removed from a pawn that does not have it, CompPostPostRemoved is still called on the pawn that does.
// If it was a legitimate removal, then it won't be in this pawn's hediff list anymore, as that removal occurs first
if ( Pawn . health . hediffSet . hediffs . Contains ( parent ) )
{
Log . Warning ( $"Attempted to remove menstruation comp from wrong pawn ({Pawn})." ) ;
return ;
}
2022-10-27 20:57:25 +00:00
switch ( pregnancy )
{
case null :
case Hediff_MechanoidPregnancy _ :
break ;
case Hediff_BasePregnancy rjw_preg :
rjw_preg . Miscarry ( ) ;
break ;
case Hediff_Pregnant vanilla_preg :
Pawn . health . RemoveHediff ( vanilla_preg ) ;
break ;
}
2022-10-24 01:02:02 +00:00
base . CompPostPostRemoved ( ) ;
}
2023-03-04 23:51:57 +00:00
public override string CompTipStringExtra
{
get
{
2023-04-23 04:57:09 +00:00
if ( Pawn . Dead ) return null ;
2023-03-04 23:51:57 +00:00
StringBuilder tip = new StringBuilder ( ) ;
tip . Append ( Translations . Dialog_WombInfo01 ) ;
tip . Append ( ": " ) ;
tip . Append ( GetCurStageLabel ) ;
string fertInfo = GetFertilizingInfo ;
if ( CurrentVisibleStage = = Stage . Luteal & & fertInfo . Length > 0 )
{
tip . AppendLine ( ) ;
tip . Append ( fertInfo ) ;
}
return tip . ToString ( ) ;
}
}
2023-04-14 03:17:25 +00:00
protected virtual int TicksToNextStage ( )
2023-03-04 23:51:57 +00:00
{
2023-04-14 03:17:25 +00:00
return Math . Max ( 0 , ( currentIntervalTicks - curStageTicks ) / Configurations . CycleAcceleration ) ;
2023-03-04 23:51:57 +00:00
}
public override string CompDebugString ( )
{
2023-04-23 04:57:09 +00:00
if ( Pawn . Dead | | curStage = = Stage . None | | curStage = = Stage . Infertile | | curStage = = Stage . Pregnant ) return null ;
2023-03-04 23:51:57 +00:00
StringBuilder debugString = new StringBuilder ( ) ;
debugString . Append ( $"Time to next state: " ) ;
2023-04-14 03:17:25 +00:00
debugString . Append ( GenDate . ToStringTicksToPeriod ( TicksToNextStage ( ) ) ) ;
2023-03-04 23:51:57 +00:00
return debugString . ToString ( ) ;
}
2022-10-24 01:02:02 +00:00
/// <summary>
/// Get fluid in womb that not a cum
/// </summary>
/// <param name="notcumlabel"></param>
/// <returns></returns>
public Cum GetNotCum ( string notcumlabel )
{
2023-04-14 03:17:25 +00:00
return cums ? . Find ( cum = > cum . notcum & & cum . notcumLabel . Equals ( notcumlabel ) ) ;
2022-10-24 01:02:02 +00:00
}
/// <summary>
/// Get pawn's cum in womb
/// </summary>
/// <param name="pawn"></param>
/// <returns></returns>
public Cum GetCum ( Pawn pawn )
{
return cums ? . Find ( cum = > ! cum . notcum & & cum . pawn = = pawn ) ;
}
/// <summary>
/// Inject pawn's cum into womb
/// </summary>
2023-04-14 03:17:25 +00:00
/// <param name="cummer"></param>
2022-10-24 01:02:02 +00:00
/// <param name="volume"></param>
/// <param name="fertility"></param>
2022-12-25 19:46:01 +00:00
/// <param name="precum"></param>
2023-04-14 03:17:25 +00:00
public void CumIn ( Pawn cummer , float volume , float fertility = 1.0f , bool precum = false )
2022-10-24 01:02:02 +00:00
{
if ( volume < = 0 ) return ;
2023-04-14 03:17:25 +00:00
if ( ! precum & & fertility > 0 & & IsDangerDay & & cummer . relations . GetPregnancyApproachForPartner ( Pawn ) = = PregnancyApproach . AvoidPregnancy )
2022-12-25 17:54:49 +00:00
{
float successChance = pulloutSuccessRate ;
2023-04-14 03:17:25 +00:00
if ( cummer . HasQuirk ( QuirkUtility . Quirks . ImpregnationFetish ) ) successChance * = fetishPulloutSuccessModifier ;
2023-03-18 03:07:00 +00:00
if ( Pawn . HasQuirk ( QuirkUtility . Quirks . ImpregnationFetish ) ) successChance * = fetishPulloutSuccessModifier ;
2022-12-25 17:54:49 +00:00
if ( Rand . Chance ( successChance ) ) return ;
}
2022-11-04 19:06:03 +00:00
if ( Pawn . HasIUD ( ) ) fertility / = 100f ;
2022-10-24 01:02:02 +00:00
float cumd = TotalCumPercent ;
float tmp = TotalCum + volume ;
if ( tmp > CumCapacity )
{
float cumoutrate = 1 - ( CumCapacity / tmp ) ;
bool merged = false ;
if ( ! cums . NullOrEmpty ( ) ) foreach ( Cum cum in cums )
{
2023-04-14 03:17:25 +00:00
if ( cum . pawn . Equals ( cummer ) )
2022-10-24 01:02:02 +00:00
{
2022-12-25 19:46:01 +00:00
cum . MergeWithCum ( volume , fertility ) ;
2022-10-24 01:02:02 +00:00
merged = true ;
}
cum . DismishForce ( cumoutrate ) ;
}
2023-04-14 03:17:25 +00:00
if ( ! merged ) cums . Add ( new Cum ( cummer , volume * ( 1 - cumoutrate ) , fertility ) ) ;
2022-10-24 01:02:02 +00:00
}
else
{
bool merged = false ;
if ( ! cums . NullOrEmpty ( ) ) foreach ( Cum cum in cums )
{
2023-04-14 03:17:25 +00:00
if ( cum . pawn . Equals ( cummer ) )
2022-10-24 01:02:02 +00:00
{
2022-12-25 19:46:01 +00:00
cum . MergeWithCum ( volume , fertility ) ;
2022-10-24 01:02:02 +00:00
merged = true ;
}
}
2023-04-14 03:17:25 +00:00
if ( ! merged ) cums . Add ( new Cum ( cummer , volume , fertility ) ) ;
2022-10-24 01:02:02 +00:00
}
cumd = TotalCumPercent - cumd ;
2022-12-25 19:46:01 +00:00
if ( ! precum )
{
Pawn . records . AddTo ( VariousDefOf . AmountofCreampied , volume ) ;
2023-04-14 03:17:25 +00:00
AfterCumIn ( cummer ) ;
2022-12-25 19:46:01 +00:00
AfterFluidIn ( cumd ) ;
}
2022-10-24 01:02:02 +00:00
}
/// <summary>
/// Inject pawn's fluid into womb
/// </summary>
2023-04-14 03:17:25 +00:00
/// <param name="cummer"></param>
2022-10-24 01:02:02 +00:00
/// <param name="volume"></param>
/// <param name="notcumlabel"></param>
/// <param name="decayresist"></param>
/// <param name="filthdef"></param>
2023-04-14 03:17:25 +00:00
public void CumIn ( Pawn cummer , float volume , string notcumlabel , float decayresist = 0 , ThingDef filthdef = null )
2022-10-24 01:02:02 +00:00
{
if ( volume < = 0 ) return ;
float tmp = TotalCum + volume ;
float cumd = TotalCumPercent ;
if ( tmp > CumCapacity )
{
float cumoutrate = 1 - ( CumCapacity / tmp ) ;
bool merged = false ;
if ( ! cums . NullOrEmpty ( ) ) foreach ( Cum cum in cums )
{
2023-04-14 03:17:25 +00:00
if ( cum . notcum & & cum . pawn . Equals ( cummer ) & & cum . notcumLabel . Equals ( notcumlabel ) )
2022-10-24 01:02:02 +00:00
{
cum . MergeWithFluid ( volume , decayresist , filthdef ) ;
merged = true ;
}
cum . DismishForce ( cumoutrate ) ;
}
2023-04-14 03:17:25 +00:00
if ( ! merged ) cums . Add ( new Cum ( cummer , volume * ( 1 - cumoutrate ) , notcumlabel , decayresist , filthdef ) ) ;
2022-10-24 01:02:02 +00:00
}
else
{
bool merged = false ;
if ( ! cums . NullOrEmpty ( ) ) foreach ( Cum cum in cums )
{
2023-04-14 03:17:25 +00:00
if ( cum . notcum & & cum . pawn . Equals ( cummer ) & & cum . notcumLabel . Equals ( notcumlabel ) )
2022-10-24 01:02:02 +00:00
{
cum . MergeWithFluid ( volume , decayresist , filthdef ) ;
merged = true ;
}
}
2023-04-14 03:17:25 +00:00
if ( ! merged ) cums . Add ( new Cum ( cummer , volume , notcumlabel , decayresist , filthdef ) ) ;
2022-10-24 01:02:02 +00:00
}
cumd = TotalCumPercent - cumd ;
AfterNotCumIn ( ) ;
AfterFluidIn ( cumd ) ;
}
protected virtual void AfterCumIn ( Pawn cummer )
{
ThoughtCumInside ( cummer ) ;
TaleCumInside ( cummer ) ;
}
protected virtual void AfterNotCumIn ( )
{
}
/// <summary>
/// Action for both Cum and NotCum
/// </summary>
/// <param name="fd">Fluid deviation</param>
protected virtual void AfterFluidIn ( float fd )
{
}
protected void BeforeCumOut ( out Absorber absorber )
{
Hediff asa = Pawn . health . hediffSet . GetFirstHediffOfDef ( VariousDefOf . Hediff_ASA ) ;
float asafactor = asa ? . Severity ? ? 0f ;
2022-11-04 19:06:03 +00:00
if ( Pawn . HasIUD ( ) ) antisperm = 0.70f + asafactor ;
2022-10-24 01:02:02 +00:00
else antisperm = 0.0f + asafactor ;
absorber = ( Absorber ) Pawn . apparel ? . WornApparel ? . Find ( x = > x is Absorber ) ;
if ( absorber ! = null )
{
2023-04-14 03:17:25 +00:00
absorber . WearEffect ( TickInterval ) ;
if ( absorber . dirty & & absorber . EffectAfterDirty ) absorber . DirtyEffect ( TickInterval ) ;
2022-10-24 01:02:02 +00:00
}
}
/// <summary>
/// For natural leaking
/// </summary>
2023-04-15 15:30:39 +00:00
protected virtual void AfterCumOut ( float amount , List < string > filthlabels )
2022-10-24 01:02:02 +00:00
{
2023-04-15 15:30:39 +00:00
fluidToLeak + = amount ;
if ( fluidToLeak > fluidLeakThreshold ) Pawn . needs ? . mood ? . thoughts ? . memories ? . TryGainMemory ( VariousDefOf . LeakingFluids ) ;
for ( ; fluidToLeak > fluidLeakThreshold ; fluidToLeak - = fluidLeakThreshold )
{
if ( cums . Count > 1 ) MakeCumFilthMixture ( fluidLeakThreshold , filthlabels ) ;
else if ( cums . Count = = 1 ) MakeCumFilth ( cums . First ( ) , fluidLeakThreshold ) ;
}
2022-10-24 01:02:02 +00:00
}
/// <summary>
/// For all type of leaking
/// </summary>
/// <param name="fd"></param>
protected virtual void AfterFluidOut ( float fd )
{
}
/// <summary>
/// Excrete cums in womb naturally
/// </summary>
public void CumOut ( )
{
float leakfactor = 1.0f ;
float totalleak = 0f ;
float cumd = TotalCumPercent ;
List < string > filthlabels = new List < string > ( ) ;
BeforeCumOut ( out Absorber absorber ) ;
if ( cums . NullOrEmpty ( ) ) return ;
if ( TotalCum > Props . maxCumCapacity * Pawn . BodySize ) leakfactor = Math . Min ( 1 + ( TotalCum - Props . maxCumCapacity * Pawn . BodySize ) / 10 , 2f ) ;
if ( absorber ! = null & & absorber . dirty & & ! absorber . LeakAfterDirty ) leakfactor = 0f ;
if ( Pawn . CurJobDef = = xxx . knotted ) leakfactor = 0f ;
foreach ( Cum cum in cums )
{
2023-06-23 04:56:26 +00:00
if ( Rand . Chance ( HoursBetweenSimulations ) ) cum . CumEffects ( Pawn ) ;
2022-10-24 01:02:02 +00:00
float vd = cum . DismishNatural ( leakfactor , this , antisperm ) ;
2023-06-23 04:56:26 +00:00
cum . MakeThinner ( ( float ) Configurations . CycleAcceleration * HoursBetweenSimulations ) ;
2022-10-24 01:02:02 +00:00
totalleak + = AbsorbCum ( vd , absorber ) ;
string tmp = "FilthLabelWithSource" . Translate ( cum . FilthDef . label , cum . pawn ? . LabelShort ? ? "Unknown" , 1. ToString ( ) ) ;
filthlabels . Add ( tmp . Replace ( " x1" , "" ) ) ;
}
2023-04-15 15:30:39 +00:00
AfterCumOut ( totalleak , filthlabels ) ;
2023-04-14 03:17:25 +00:00
cums . RemoveAll ( cum = > cum . ShouldRemove ( ) ) ;
2023-04-15 15:30:39 +00:00
if ( cums . NullOrEmpty ( ) ) fluidToLeak = 0 ;
2022-10-24 01:02:02 +00:00
cumd = TotalCumPercent - cumd ;
AfterFluidOut ( cumd ) ;
}
/// <summary>
/// Force excrete cums in womb and get excreted amount of specific cum.
/// </summary>
/// <param name="targetcum"></param>
/// <param name="portion"></param>
/// <returns>Amount of target cum</returns>
public float CumOut ( Cum targetcum , float portion = 0.1f )
{
if ( cums . NullOrEmpty ( ) ) return 0 ;
float totalleak = 0 ;
List < string > filthlabels = new List < string > ( ) ;
float outcum = 0 ;
float cumd = TotalCumPercent ;
2023-03-20 05:52:39 +00:00
HashSet < Cum > removecums = new HashSet < Cum > ( ) ;
2022-10-24 01:02:02 +00:00
foreach ( Cum cum in cums )
{
float vd = cum . DismishForce ( portion ) ;
if ( cum . Equals ( targetcum ) ) outcum = vd ;
//MakeCumFilth(cum, vd - cum.volume);
string tmp = "FilthLabelWithSource" . Translate ( cum . FilthDef . label , cum . pawn ? . LabelShort ? ? "Unknown" , 1. ToString ( ) ) ;
filthlabels . Add ( tmp . Replace ( " x1" , "" ) ) ;
totalleak + = vd ;
if ( cum . ShouldRemove ( ) ) removecums . Add ( cum ) ;
}
if ( cums . Count > 1 ) MakeCumFilthMixture ( totalleak , filthlabels ) ;
else if ( cums . Count = = 1 ) MakeCumFilth ( cums . First ( ) , totalleak ) ;
2023-03-20 05:52:39 +00:00
cums . RemoveAll ( cum = > removecums . Contains ( cum ) ) ;
2023-04-15 15:30:39 +00:00
if ( cums . NullOrEmpty ( ) ) fluidToLeak = 0 ;
2022-10-24 01:02:02 +00:00
cumd = TotalCumPercent - cumd ;
AfterFluidOut ( cumd ) ;
return outcum ;
}
/// <summary>
/// Force excrete cums in womb and get mixture of cum.
/// </summary>
/// <param name="mixtureDef"></param>
/// <param name="portion"></param>
/// <returns></returns>
public CumMixture MixtureOut ( ThingDef mixtureDef , float portion = 0.1f )
{
if ( cums . NullOrEmpty ( ) ) return null ;
Color color = GetCumMixtureColor ;
float totalleak = 0 ;
List < string > cumlabels = new List < string > ( ) ;
2023-03-20 05:52:39 +00:00
HashSet < Cum > removecums = new HashSet < Cum > ( ) ;
2022-10-24 01:02:02 +00:00
bool pure = true ;
foreach ( Cum cum in cums )
{
float vd = cum . DismishForce ( portion ) ;
string tmp = "FilthLabelWithSource" . Translate ( cum . FilthDef . label , cum . pawn ? . LabelShort ? ? "Unknown" , 1. ToString ( ) ) ;
cumlabels . Add ( tmp . Replace ( " x1" , "" ) ) ;
totalleak + = vd ;
if ( cum . ShouldRemove ( ) ) removecums . Add ( cum ) ;
if ( cum . notcum ) pure = false ;
}
2023-03-20 05:52:39 +00:00
cums . RemoveAll ( cum = > removecums . Contains ( cum ) ) ;
2023-04-15 15:30:39 +00:00
if ( cums . NullOrEmpty ( ) ) fluidToLeak = 0 ;
2022-10-24 01:02:02 +00:00
return new CumMixture ( Pawn , totalleak , cumlabels , color , mixtureDef , pure ) ;
}
/// <summary>
/// Debug: Remove all cums from a womb
/// </summary>
/// <returns></returns>
public void RemoveAllCums ( )
{
cums . Clear ( ) ;
}
2023-05-04 14:54:58 +00:00
public void RemoveAllEggs ( )
{
eggs . Clear ( ) ;
}
2022-10-24 01:02:02 +00:00
/// <summary>
/// Fertilize eggs and return the result
/// </summary>
/// <returns></returns>
protected void FertilizationCheck ( )
{
if ( eggs . NullOrEmpty ( ) ) return ;
foreach ( Egg egg in eggs )
{
if ( ! egg . fertilized ) egg . fertilizer = Fertilize ( ) ;
if ( egg . fertilizer ! = null )
{
egg . fertilized = true ;
}
}
}
public void Initialize ( )
{
initError = true ;
Props = ( CompProperties_Menstruation ) props ;
2023-04-16 19:26:11 +00:00
if ( cums = = null ) cums = new List < Cum > ( ) ;
if ( eggs = = null ) eggs = new List < Egg > ( ) ;
2023-04-17 02:41:57 +00:00
PreInitialize ( ) ;
2023-01-04 06:48:06 +00:00
2022-10-24 01:02:02 +00:00
if ( Props . infertile )
{
curStage = Stage . None ;
loaded = true ;
initError = false ;
return ;
}
if ( cycleSpeed < 0f ) cycleSpeed = Utility . RandGaussianLike ( 0.8f , 1.2f ) ;
if ( cycleVariability < 0f ) cycleVariability = MenstruationUtility . RandomVariabilityPercent ( ) ;
2023-03-07 00:53:22 +00:00
InitOvary ( ) ;
2023-04-14 03:17:25 +00:00
if ( currentIntervalTicks < 0 )
2022-10-24 01:02:02 +00:00
{
2023-03-16 19:24:47 +00:00
if ( ShouldBeInfertile ( ) ) curStage = Stage . Infertile ;
2022-10-24 01:02:02 +00:00
else if ( ! IsBreedingSeason ( ) ) curStage = Stage . Anestrus ;
else curStage = RandomStage ( ) ;
if ( curStage = = Stage . Follicular )
2023-04-14 03:17:25 +00:00
currentIntervalTicks = PeriodRandomizer ( Stage . Follicular ) - PeriodRandomizer ( Stage . Bleeding ) ;
2022-10-24 01:02:02 +00:00
else
2023-04-14 03:17:25 +00:00
currentIntervalTicks = PeriodRandomizer ( curStage ) ;
if ( currentIntervalTicks < 0 ) currentIntervalTicks = 0 ;
else if ( currentIntervalTicks < curStageTicks ) curStageTicks = currentIntervalTicks ;
2022-10-24 01:02:02 +00:00
}
if ( crampPain < 0 ) crampPain = PainRandomizer ( ) ;
2022-12-25 23:41:20 +00:00
InitializeExtraValues ( ) ;
2022-10-24 01:02:02 +00:00
TakeLoosePregnancy ( ) ;
//Log.Message(Pawn.Label + " - Initialized menstruation comp");
loaded = true ;
initError = false ;
}
2023-04-17 02:41:57 +00:00
protected virtual void PreInitialize ( )
{
Notify_UpdatedGenes ( ) ;
}
2022-12-25 23:41:20 +00:00
protected virtual void InitializeExtraValues ( )
{
}
2022-10-24 01:02:02 +00:00
protected virtual float RaceCyclesPerYear ( )
{
int breedingSeasons = 0 ;
if ( Props . breedingSeason = = SeasonalBreed . Always ) breedingSeasons = 4 ;
else
{
if ( ( Props . breedingSeason & SeasonalBreed . Spring ) ! = 0 ) breedingSeasons + + ;
if ( ( Props . breedingSeason & SeasonalBreed . Summer ) ! = 0 ) breedingSeasons + + ;
if ( ( Props . breedingSeason & SeasonalBreed . Fall ) ! = 0 ) breedingSeasons + + ;
if ( ( Props . breedingSeason & SeasonalBreed . Winter ) ! = 0 ) breedingSeasons + + ;
}
float breedingRatio = breedingSeasons / 4.0f ;
return breedingRatio * GenDate . DaysPerYear / ( ( float ) ( Props . follicularIntervalDays + Props . lutealIntervalDays ) / Configurations . CycleAccelerationDefault ) ;
}
protected virtual int PawnEggsUsed ( float pawnCyclesElapsed , float avglittersize )
{
return ( int ) ( pawnCyclesElapsed * avglittersize ) ;
}
public int GetOvaryPowerByAge ( )
{
float avglittersize ;
try
{
2023-04-30 03:32:46 +00:00
avglittersize = Mathf . Max ( Rand . ByCurveAverage ( Pawn . RaceProps . litterSizeCurve ) , 1.0f ) ;
2022-10-24 01:02:02 +00:00
}
catch ( NullReferenceException )
{
avglittersize = 1.0f ;
}
2023-01-04 06:48:06 +00:00
avglittersize * = ovulationFactor ;
2022-10-24 01:02:02 +00:00
float fertStartAge = Pawn . RaceProps . lifeStageAges ? . Find ( stage = > stage . def . reproductive ) ? . minAge ? ? 0.0f ;
float fertEndAge = Pawn . RaceProps . lifeExpectancy * ( Pawn . IsAnimal ( ) ? RJWPregnancySettings . fertility_endage_female_animal : RJWPregnancySettings . fertility_endage_female_humanlike ) ;
if ( fertEndAge < fertStartAge ) fertEndAge = fertStartAge ;
float raceCyclesPerYear = RaceCyclesPerYear ( ) ;
int lifetimeCycles = ( int ) ( raceCyclesPerYear * ( fertEndAge - fertStartAge ) ) ;
int lifetimeEggs = ( int ) ( lifetimeCycles * avglittersize * Props . eggMultiplier * Utility . RandGaussianLike ( 0.70f , 1.30f , 5 ) ) ;
float pawnCyclesPerYear = raceCyclesPerYear * cycleSpeed ;
float pawnCyclesElapsed = Mathf . Max ( ( Pawn . ageTracker . AgeBiologicalYearsFloat - fertStartAge ) * pawnCyclesPerYear , 0.0f ) ;
int pawnEggsUsed = PawnEggsUsed ( pawnCyclesElapsed , avglittersize ) ;
return Math . Max ( lifetimeEggs - pawnEggsUsed , 0 ) ;
}
protected void InitOvary ( )
{
if ( ovarypower < - 50000 )
{
ovarypower = GetOvaryPowerByAge ( ) ;
if ( Props . infertile ) curStage = Stage . None ;
else if ( ovarypower < 1 )
{
2022-10-24 02:29:01 +00:00
curStage = Stage . Infertile ;
2022-10-24 01:02:02 +00:00
}
}
}
public void RecoverOvary ( float multiply = 1.2f )
{
ovarypower = Math . Max ( 0 , ( int ) ( ovarypower * multiply ) ) ;
}
2022-12-25 23:41:20 +00:00
protected virtual void BeforeSimulator ( )
{
2023-05-01 21:22:22 +00:00
ovulationChanceCache = - 1.0f ;
implantationChanceCache = - 1.0f ;
2022-12-25 23:41:20 +00:00
CumOut ( ) ;
}
2022-10-24 01:02:02 +00:00
2022-12-25 23:41:20 +00:00
protected virtual void AfterSimulator ( )
2022-10-24 01:02:02 +00:00
{
if ( EggHealth < 1f )
{
if ( sexNeed = = null ) sexNeed = Pawn . needs . TryGetNeed ( VariousDefOf . SexNeed ) ;
2023-06-17 18:21:34 +00:00
if ( sexNeed = = null ) return ;
2023-06-23 04:56:26 +00:00
if ( sexNeed . CurLevel < 0.5f ) sexNeed . CurLevel + = 0.01f * HoursBetweenSimulations / Math . Max ( 1 , Pawn . GetMenstruationComps ( ) . Count ( ) ) ;
2022-10-24 01:02:02 +00:00
}
}
protected virtual bool ShouldBeInEstrus ( )
{
if ( ! loaded )
Initialize ( ) ;
switch ( curStage )
{
case Stage . Follicular :
2023-04-14 03:17:25 +00:00
return curStageTicks > currentIntervalTicks - Props . estrusDaysBeforeOvulation * GenDate . TicksPerDay ;
2022-10-24 01:02:02 +00:00
case Stage . Ovulatory :
return true ;
case Stage . Luteal :
2023-04-14 03:17:25 +00:00
return curStageTicks < EggLifespanTicks ;
2022-10-24 01:02:02 +00:00
default :
return false ;
}
}
public EstrusLevel GetEstrusLevel ( )
{
if ( ! ShouldBeInEstrus ( ) ) return EstrusLevel . None ;
2023-01-04 06:48:06 +00:00
else return estrusLevel ;
2022-10-24 01:02:02 +00:00
}
public void SetEstrus ( )
{
2023-01-04 06:48:06 +00:00
if ( estrusLevel = = EstrusLevel . None ) return ;
Hediff hediff = HediffMaker . MakeHediff ( estrusLevel = = EstrusLevel . Concealed ? VariousDefOf . Hediff_Estrus_Concealed : VariousDefOf . Hediff_Estrus , Pawn ) ;
2022-10-24 01:02:02 +00:00
Pawn . health . AddHediff ( hediff ) ;
}
public bool IsBreedingSeason ( )
{
if ( Props . breedingSeason = = SeasonalBreed . Always ) return true ;
int tile = Pawn . Tile ;
if ( tile < 0 ) tile = Find . AnyPlayerHomeMap ? . Tile ? ? - 1 ;
if ( tile < 0 ) return true ;
switch ( GenLocalDate . Season ( tile ) )
{
case Season . Spring :
return ( Props . breedingSeason & SeasonalBreed . Spring ) ! = 0 ;
case Season . Summer :
case Season . PermanentSummer :
return ( Props . breedingSeason & SeasonalBreed . Summer ) ! = 0 ;
case Season . Fall :
return ( Props . breedingSeason & SeasonalBreed . Fall ) ! = 0 ;
case Season . Winter :
case Season . PermanentWinter :
return ( Props . breedingSeason & SeasonalBreed . Winter ) ! = 0 ;
default :
return false ;
}
}
protected Pawn Fertilize ( )
{
if ( cums . NullOrEmpty ( ) ) return null ;
List < Cum > eligibleCum = cums . FindAll ( cum = > ! cum . notcum & & cum . FertVolume > 0 & & cum . pawn ! = null & & ( RJWPregnancySettings . bestial_pregnancy_enabled | | xxx . is_animal ( Pawn ) = = xxx . is_animal ( cum . pawn ) ) ) ;
if ( eligibleCum . Count = = 0 ) return null ;
float totalFertPower = eligibleCum . Sum ( cum = > cum . FertVolume ) ;
2023-04-14 03:17:25 +00:00
2023-04-20 19:31:27 +00:00
//float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor);
//float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour);
2023-06-23 04:56:26 +00:00
float fertFailChancePerInterval = Mathf . Pow ( 1.0f - Configurations . FertilizeChance , totalFertPower * Props . basefertilizationChanceFactor * HoursBetweenSimulations ) ;
2022-10-24 01:02:02 +00:00
2023-04-14 03:17:25 +00:00
if ( Rand . Chance ( fertFailChancePerInterval ) ) return null ;
2022-10-24 01:02:02 +00:00
Pawn . records . AddTo ( VariousDefOf . AmountofFertilizedEggs , 1 ) ;
float selection = Rand . Range ( 0.0f , totalFertPower ) ;
foreach ( Cum cum in eligibleCum )
{
selection - = cum . FertVolume ;
if ( selection < = 0 ) return cum . pawn ;
}
// We shouldn't reach here, but floating point errors exist, so just to be sure, select whomever came the most
return eligibleCum . MaxBy ( cum = > cum . FertVolume ) . pawn ;
}
protected bool Implant ( )
{
if ( eggs . NullOrEmpty ( ) ) return false ;
2023-03-20 05:48:04 +00:00
HashSet < Egg > deadeggs = new HashSet < Egg > ( ) ;
2022-10-24 01:02:02 +00:00
bool pregnant = false ;
foreach ( Egg egg in eggs )
{
if ( ! egg . fertilized | |
2023-04-14 03:17:25 +00:00
egg . ticksSinceFertilization < minImplantAgeHours * GenDate . TicksPerHour | |
egg . ageTicks < Math . Min ( Props . lutealIntervalDays * GenDate . TicksPerDay / 2 , maxImplantDelayHours * GenDate . TicksPerHour ) )
2022-10-24 01:02:02 +00:00
continue ;
else if ( egg . fertilizer = = null )
{
if ( Configurations . Debug ) Log . Message ( $"Could not implant {Pawn}'s egg due to null father" ) ;
deadeggs . Add ( egg ) ;
continue ;
}
2022-10-24 02:23:00 +00:00
else if ( Pawn . health . hediffSet . GetFirstHediff < Hediff_InsectEgg > ( ) ! = null | | pregnancy is Hediff_MechanoidPregnancy )
2022-10-24 01:02:02 +00:00
{
if ( Configurations . Debug ) Log . Message ( $"Could not implant {Pawn}'s egg due to insect or mechanoid pregnancy" ) ;
deadeggs . Add ( egg ) ;
continue ;
}
2023-03-07 00:58:23 +00:00
else if ( Rand . Chance ( Configurations . ImplantationChance * ImplantChance * InterspeciesImplantFactor ( egg . fertilizer ) ) )
2022-10-24 01:02:02 +00:00
{
if ( Configurations . Debug ) Log . Message ( $"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}" ) ;
if ( pregnancy ! = null )
{
2023-01-08 20:01:45 +00:00
if ( Configurations . PregnancySource = = Configurations . PregnancyType . Biotech & & Configurations . EnableBiotechTwins & & Configurations . EnableHeteroOvularTwins )
{
if ( Configurations . Debug ) Log . Message ( $"Adding to existing Biotech pregnancy {pregnancy}" ) ;
HediffComp_PregeneratedBabies comp = pregnancy . TryGetComp < HediffComp_PregeneratedBabies > ( ) ;
if ( comp = = null ) Log . Warning ( $"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {pregnancy}" ) ;
else
{
comp . AddNewBaby ( Pawn , egg . fertilizer ) ;
pregnant = true ;
deadeggs . Add ( egg ) ;
}
}
else if ( Configurations . PregnancySource = = Configurations . PregnancyType . MultiplePregnancy & & Configurations . EnableHeteroOvularTwins )
2022-10-24 01:02:02 +00:00
{
if ( pregnancy is Hediff_MultiplePregnancy h )
{
if ( Configurations . Debug ) Log . Message ( $"Adding to existing pregnancy {h}" ) ;
h . AddNewBaby ( Pawn , egg . fertilizer ) ;
}
pregnant = true ;
deadeggs . Add ( egg ) ;
}
else
{
pregnant = true ;
break ;
}
}
else
{
2022-10-30 17:36:38 +00:00
Configurations . PregnancyType usePregnancy = xxx . is_human ( Pawn ) ? Configurations . PregnancySource : Configurations . PregnancyType . MultiplePregnancy ;
switch ( usePregnancy )
2022-10-24 01:02:02 +00:00
{
2022-10-30 17:36:38 +00:00
case Configurations . PregnancyType . BaseRJW :
if ( Configurations . Debug ) Log . Message ( $"Creating new base RJW pregnancy" ) ;
2022-11-16 15:46:02 +00:00
PregnancyHelper . AddPregnancyHediff ( Pawn , egg . fertilizer ) ;
2022-10-30 17:36:38 +00:00
// I hate having to do this, but it gets the newest pregnancy
List < Hediff_BasePregnancy > pregnancies = new List < Hediff_BasePregnancy > ( ) ;
Pawn . health . hediffSet . GetHediffs ( ref pregnancies ) ;
pregnancy = pregnancies . MaxBy ( hediff = > hediff . loadID ) ;
pregnant = true ;
break ;
case Configurations . PregnancyType . MultiplePregnancy :
if ( Configurations . Debug ) Log . Message ( $"Creating new menstruation pregnancy" ) ;
pregnancy = Hediff_BasePregnancy . Create < Hediff_MultiplePregnancy > ( Pawn , egg . fertilizer ) ;
pregnant = true ;
deadeggs . Add ( egg ) ;
break ;
case Configurations . PregnancyType . Biotech :
if ( Configurations . Debug ) Log . Message ( $"Creating new biotech pregnancy" ) ;
pregnancy = HediffMaker . MakeHediff ( HediffDefOf . PregnantHuman , Pawn ) ;
2023-01-08 20:01:45 +00:00
if ( Configurations . EnableBiotechTwins )
pregnancy . TryGetComp < HediffComp_PregeneratedBabies > ( ) . AddNewBaby ( Pawn , egg . fertilizer ) ;
2022-10-30 17:36:38 +00:00
( ( Hediff_Pregnant ) pregnancy ) . SetParents ( Pawn , egg . fertilizer , PregnancyUtility . GetInheritedGeneSet ( egg . fertilizer , Pawn ) ) ;
Pawn . health . AddHediff ( pregnancy ) ;
2023-01-08 20:01:45 +00:00
pregnant = true ;
deadeggs . Add ( egg ) ;
2022-10-30 17:36:38 +00:00
break ;
2022-10-24 01:02:02 +00:00
}
2022-10-27 20:57:25 +00:00
if ( pregnancy is Hediff_BasePregnancy rjw_preg )
{
2022-10-30 17:36:38 +00:00
// TODO: advance biotech pregnancy
2023-04-14 03:17:25 +00:00
rjw_preg . p_start_tick - = egg . ticksSinceFertilization / Configurations . CycleAcceleration ;
rjw_preg . p_end_tick - = egg . ticksSinceFertilization / Configurations . CycleAcceleration ;
2022-10-27 20:57:25 +00:00
}
2022-10-24 01:02:02 +00:00
}
}
else
2023-04-22 19:12:53 +00:00
{
if ( Configurations . Debug )
{
float implantChance = Configurations . ImplantationChance * ImplantChance * InterspeciesImplantFactor ( egg . fertilizer ) ;
Log . Message ( $"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, father {egg.fertilizer})" ) ;
}
2022-10-24 01:02:02 +00:00
deadeggs . Add ( egg ) ;
}
}
2023-01-08 20:01:45 +00:00
if ( pregnant & &
( Configurations . PregnancySource ! = Configurations . PregnancyType . MultiplePregnancy | | ! Configurations . EnableHeteroOvularTwins ) & &
( Configurations . PregnancySource ! = Configurations . PregnancyType . Biotech | | ! Configurations . EnableBiotechTwins | | ! Configurations . EnableHeteroOvularTwins ) )
2022-10-24 01:02:02 +00:00
{
eggs . Clear ( ) ;
return true ;
}
else
2023-03-20 05:48:04 +00:00
eggs . RemoveAll ( egg = > deadeggs . Contains ( egg ) ) ;
2022-10-24 01:02:02 +00:00
return pregnant ;
}
protected void BleedOut ( )
{
2023-04-14 03:17:25 +00:00
// ~1.5 per hour times acceleration
2023-06-23 04:56:26 +00:00
float bledAmount = 0.03f * Configurations . BleedingAmount * Configurations . CycleAcceleration * Rand . Range ( 0.5f , 1.5f ) * HoursBetweenSimulations ;
2023-04-30 03:32:46 +00:00
CumIn ( Pawn , bledAmount , Translations . Menstrual_Blood , - 5.0f , Pawn . RaceProps ? . BloodDef ? ? ThingDefOf . Filth_Blood ) ;
2022-10-24 01:02:02 +00:00
Cum blood = GetNotCum ( Translations . Menstrual_Blood ) ;
if ( blood ! = null ) blood . Color = BloodColor ;
}
/// <summary>
/// Make filth ignoring absorber
/// </summary>
/// <param name="cum"></param>
/// <param name="amount"></param>
protected void MakeCumFilth ( Cum cum , float amount )
{
2023-05-09 15:46:33 +00:00
if ( Pawn . MapHeld = = null ) return ;
if ( amount > = minmakefilthvalue ) FilthMaker . TryMakeFilth ( Pawn . PositionHeld , Pawn . MapHeld , cum . FilthDef , cum . pawn ? . LabelShort ? ? "Unknown" ) ;
2022-10-24 01:02:02 +00:00
}
/// <summary>
/// Absorb cum and return leaked amount
/// </summary>
/// <param name="amount"></param>
/// <param name="absorber"></param>
///
/// <returns></returns>
protected float AbsorbCum ( float amount , Absorber absorber )
{
if ( absorber = = null )
{
//if (amount >= minmakefilthvalue) FilthMaker.TryMakeFilth(Pawn.Position, Pawn.Map, cum.FilthDef, cum.pawn.LabelShort);
return amount ;
}
float absorbable = absorber . GetStatValue ( VariousDefOf . MaxAbsorbable ) ;
absorber . SetColor ( Colors . CMYKLerp ( GetCumMixtureColor , absorber . DrawColor , 1f - amount / absorbable ) ) ;
if ( absorber . dirty )
{
//if (absorber.LeakAfterDirty) FilthMaker.TryMakeFilth(Pawn.Position, Pawn.Map, cum.FilthDef, cum.pawn.LabelShort);
return amount ;
}
absorber . absorbedfluids + = amount ;
if ( absorber . absorbedfluids > absorbable & & ! Pawn . apparel . IsLocked ( absorber ) )
{
absorber . def = absorber . DirtyDef ;
//absorber.fluidColor = GetCumMixtureColor;
absorber . dirty = true ;
}
return 0 ;
}
protected float MakeCumFilthMixture ( float amount , List < string > cumlabels )
{
2023-05-09 15:46:33 +00:00
if ( Pawn . MapHeld = = null ) return 0 ;
2022-10-24 01:02:02 +00:00
if ( amount > = minmakefilthvalue )
{
2023-05-09 15:46:33 +00:00
FilthMaker_Colored . TryMakeFilth ( Pawn . PositionHeld , Pawn . MapHeld , VariousDefOf . FilthMixture , cumlabels , GetCumMixtureColor , false ) ;
2022-10-24 01:02:02 +00:00
}
return amount ;
}
protected void EggDecay ( )
{
2023-03-20 05:48:04 +00:00
HashSet < Egg > deadeggs = new HashSet < Egg > ( ) ;
2022-10-24 01:02:02 +00:00
foreach ( Egg egg in eggs )
{
2023-04-14 03:17:25 +00:00
egg . ageTicks + = TickInterval * Configurations . CycleAcceleration ;
if ( egg . fertilized ) egg . ticksSinceFertilization + = TickInterval * Configurations . CycleAcceleration ;
2022-10-24 01:02:02 +00:00
else
{
2023-04-14 03:17:25 +00:00
egg . lifeSpanTicks - = TickInterval * Configurations . CycleAcceleration ;
if ( egg . lifeSpanTicks < 0 ) deadeggs . Add ( egg ) ;
2022-10-24 01:02:02 +00:00
}
}
2023-03-20 05:48:04 +00:00
eggs . RemoveAll ( egg = > deadeggs . Contains ( egg ) ) ;
2022-10-24 01:02:02 +00:00
}
protected void AddCrampPain ( )
{
Hediff hediff = HediffMaker . MakeHediff ( VariousDefOf . Hediff_MenstrualCramp , Pawn ) ;
hediff . Severity = crampPain * Rand . Range ( 0.9f , 1.1f ) ;
HediffCompProperties_SeverityPerDay Prop = ( HediffCompProperties_SeverityPerDay ) hediff . TryGetComp < HediffComp_SeverityPerDay > ( ) . props ;
2023-04-14 03:17:25 +00:00
Prop . severityPerDay = - hediff . Severity / ( currentIntervalTicks / GenDate . TicksPerDay ) * Configurations . CycleAcceleration ;
2023-05-15 02:25:12 +00:00
Pawn . health . AddHediff ( hediff , parent . Part ) ;
2022-10-24 01:02:02 +00:00
}
protected virtual void FollicularAction ( )
{
if ( ! IsBreedingSeason ( ) )
{
estrusflag = false ;
GoNextStage ( Stage . Anestrus ) ;
return ;
}
2023-04-14 03:17:25 +00:00
else if ( curStageTicks > = currentIntervalTicks )
2022-10-24 01:02:02 +00:00
{
2023-03-12 22:54:14 +00:00
GoOvulatoryStage ( ) ;
2022-10-24 01:02:02 +00:00
}
else
{
2023-04-14 03:17:25 +00:00
curStageTicks + = TickInterval * Configurations . CycleAcceleration ;
if ( ! estrusflag & & curStageTicks > currentIntervalTicks - Props . estrusDaysBeforeOvulation * GenDate . TicksPerDay )
2022-10-24 01:02:02 +00:00
{
estrusflag = true ;
SetEstrus ( ) ;
}
StayCurrentStage ( ) ;
}
}
protected virtual void OvulatoryAction ( )
{
2023-04-14 03:17:25 +00:00
if ( curStageTicks < currentIntervalTicks )
{
curStageTicks + = TickInterval * Configurations . CycleAcceleration ;
return ;
}
2022-10-24 01:02:02 +00:00
estrusflag = false ;
2023-01-04 06:48:06 +00:00
float eggnum ;
2022-10-24 01:02:02 +00:00
try
{
2023-04-30 03:32:46 +00:00
eggnum = Math . Max ( Rand . ByCurve ( Pawn . RaceProps . litterSizeCurve ) , 1f ) ;
2022-10-24 01:02:02 +00:00
}
catch ( NullReferenceException )
{
2023-01-04 06:48:06 +00:00
eggnum = 1f ;
2022-10-24 01:02:02 +00:00
}
catch ( ArgumentException e )
{
Log . Warning ( $"Invalid litterSizeCurve for {Pawn.def}: {e}" ) ;
2023-01-04 06:48:06 +00:00
eggnum = 1f ;
2022-10-24 01:02:02 +00:00
}
2023-01-04 06:48:06 +00:00
eggnum * = ovulationFactor ;
2023-03-11 18:21:25 +00:00
int toOvulate = ( int ) eggnum + eggstack ;
2022-10-24 01:02:02 +00:00
2023-03-11 18:21:25 +00:00
int ovulated = 0 ;
for ( int i = 0 ; i < toOvulate ; i + + )
2023-05-02 15:35:39 +00:00
if ( i < eggstack | | Rand . Chance ( OvulationChance ) ) // eggstack comes from drugs and are guaranteed ovulated
2023-03-11 18:21:25 +00:00
{
2023-04-14 03:17:25 +00:00
eggs . Add ( new Egg ( ( int ) ( EggLifespanTicks / CycleFactor ) ) ) ;
2023-03-11 18:21:25 +00:00
+ + ovulated ;
}
2023-01-04 06:48:06 +00:00
ovarypower - = ovulated ;
2023-03-16 19:24:47 +00:00
eggstack = 0 ;
2023-03-11 18:21:25 +00:00
if ( Configurations . Debug & & ovulated ! = toOvulate )
2023-05-02 15:35:39 +00:00
Log . Message ( $"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)" ) ;
2022-10-24 01:02:02 +00:00
2023-03-16 19:24:47 +00:00
GoNextStage ( Stage . Luteal ) ;
2022-10-24 01:02:02 +00:00
}
protected virtual void LutealAction ( )
{
2023-04-14 03:17:25 +00:00
if ( curStageTicks > = currentIntervalTicks )
2022-10-24 01:02:02 +00:00
{
eggs . Clear ( ) ;
2023-03-07 00:58:23 +00:00
if ( EggHealth < 1f / 4f | | ( EggHealth < 1f / 3f & & Rand . Chance ( 0.3f ) ) ) //skips bleeding
2022-10-24 01:02:02 +00:00
{
GoNextStage ( Stage . Follicular ) ;
}
else
{
GoFollicularOrBleeding ( ) ;
}
}
else if ( ! eggs . NullOrEmpty ( ) )
{
FertilizationCheck ( ) ;
EggDecay ( ) ;
if ( Implant ( ) )
{
GoNextStage ( Stage . Pregnant ) ;
}
else
{
2023-04-14 03:17:25 +00:00
curStageTicks + = TickInterval * Configurations . CycleAcceleration ;
2022-10-24 01:02:02 +00:00
StayCurrentStage ( ) ;
}
}
else
{
2023-04-14 03:17:25 +00:00
curStageTicks + = TickInterval * Configurations . CycleAcceleration ;
2022-10-24 01:02:02 +00:00
StayCurrentStage ( ) ;
}
}
protected virtual void BleedingAction ( )
{
2023-04-14 03:17:25 +00:00
if ( curStageTicks > = currentIntervalTicks )
2022-10-24 01:02:02 +00:00
{
Hediff hediff = Pawn . health . hediffSet . GetFirstHediffOfDef ( VariousDefOf . Hediff_MenstrualCramp ) ;
2022-12-27 14:52:33 +00:00
if ( hediff ! = null & & ! Pawn . GetMenstruationComps ( ) . Any ( comp = > comp ! = this & & comp . curStage = = Stage . Bleeding ) ) Pawn . health . RemoveHediff ( hediff ) ;
2023-04-14 03:17:25 +00:00
int totalFollicularTicks = PeriodRandomizer ( Stage . Follicular ) ; // The total amount of time for both bleeding and follicular
if ( totalFollicularTicks < = currentIntervalTicks ) // We've bled for so long that we completely missed the follicular phase
2022-10-24 01:02:02 +00:00
GoOvulatoryStage ( ) ;
else
{
2023-04-14 03:17:25 +00:00
currentIntervalTicks = totalFollicularTicks - currentIntervalTicks ; // I.e., the remaining follicular time equals the total minus the bleeding time elapsed
2022-10-24 01:02:02 +00:00
GoNextStage ( Stage . Follicular , false ) ;
}
}
else
{
2023-04-14 03:17:25 +00:00
if ( curStageTicks < currentIntervalTicks / 4 ) BleedOut ( ) ;
curStageTicks + = TickInterval * Configurations . CycleAcceleration ;
2022-10-24 01:02:02 +00:00
StayCurrentStage ( ) ;
}
}
protected virtual void PregnantAction ( )
{
if ( ! eggs . NullOrEmpty ( ) )
{
FertilizationCheck ( ) ;
EggDecay ( ) ;
Implant ( ) ;
}
if ( pregnancy ! = null & & Pawn . health . hediffSet . hediffs . Contains ( pregnancy ) )
{
2023-04-14 03:17:25 +00:00
curStageTicks + = TickInterval ;
2022-10-24 01:02:02 +00:00
StayCurrentStageConst ( Stage . Pregnant ) ;
}
else
{
if ( pregnancy ! = null ) pregnancy = null ;
GoNextStage ( Stage . Recover ) ;
}
}
protected virtual void RecoverAction ( )
{
2023-04-14 03:17:25 +00:00
if ( curStageTicks > = currentIntervalTicks )
2022-10-24 01:02:02 +00:00
{
2023-03-16 19:24:47 +00:00
if ( ShouldBeInfertile ( ) )
2022-10-24 01:02:02 +00:00
{
2022-10-24 02:29:01 +00:00
GoNextStage ( Stage . Infertile ) ;
2022-10-24 01:02:02 +00:00
}
else if ( ! IsBreedingSeason ( ) )
{
GoNextStage ( Stage . Anestrus ) ;
}
else
{
GoNextStage ( Stage . Follicular ) ;
}
}
else
{
2023-04-14 03:17:25 +00:00
curStageTicks + = TickInterval * Configurations . CycleAcceleration ;
2022-10-24 01:02:02 +00:00
StayCurrentStage ( ) ;
}
}
2022-10-24 02:29:01 +00:00
protected virtual void InfertileAction ( )
2022-10-24 01:02:02 +00:00
{
2023-03-16 19:24:47 +00:00
if ( ShouldBeInfertile ( ) )
2022-10-24 01:02:02 +00:00
{
2022-10-24 02:29:01 +00:00
StayCurrentStageConst ( Stage . Infertile ) ;
2022-10-24 01:02:02 +00:00
}
else
{
bool breedingSeason = IsBreedingSeason ( ) ;
GoNextStage ( breedingSeason ? Stage . Follicular : Stage . Anestrus , breedingSeason ) ;
}
}
protected virtual void AnestrusAction ( )
{
if ( IsBreedingSeason ( ) )
{
GoFollicularOrBleeding ( ) ;
}
else
{
StayCurrentStage ( ) ;
}
}
protected virtual void ThoughtCumInside ( Pawn cummer )
{
if ( ! xxx . is_human ( Pawn ) | | ! xxx . is_human ( cummer ) ) return ;
2023-05-05 00:52:42 +00:00
MemoryThoughtHandler cummerMemories = cummer . needs . mood . thoughts . memories ;
MemoryThoughtHandler pawnMemories = Pawn . needs . mood . thoughts . memories ;
if ( cummer . IsProPregnancy ( out Precept preceptm ) | | ( cummer . HasQuirk ( QuirkUtility . Quirks . Teratophile ) ! = ( Pawn . GetStatValue ( StatDefOf . PawnBeauty ) > = 0 ) ) )
2022-10-24 01:02:02 +00:00
{
2023-05-05 00:52:42 +00:00
if ( cummer . relations . OpinionOf ( Pawn ) < = - 5 )
cummerMemories . TryGainMemory ( VariousDefOf . HaterCameInsideM , Pawn ) ;
else if ( preceptm ! = null )
cummerMemories . TryGainMemory ( VariousDefOf . CameInsideMIdeo , Pawn , preceptm ) ;
2022-10-24 01:02:02 +00:00
else
2023-05-05 00:52:42 +00:00
cummerMemories . TryGainMemory ( VariousDefOf . CameInsideM , Pawn ) ;
2022-10-24 01:02:02 +00:00
}
if ( IsDangerDay )
{
2023-05-05 00:52:42 +00:00
if ( Pawn . IsProPregnancy ( out Precept preceptf ) )
2022-10-24 01:02:02 +00:00
{
2023-05-05 00:52:42 +00:00
if ( preceptf ! = null )
{
if ( Pawn . relations . OpinionOf ( cummer ) < = - 5 )
pawnMemories . TryGainMemory ( VariousDefOf . HaterCameInsideFIdeo , cummer , preceptf ) ;
else
pawnMemories . TryGainMemory ( VariousDefOf . CameInsideFIdeo , cummer , preceptf ) ;
}
else pawnMemories . TryGainMemory ( VariousDefOf . CameInsideFFetish , cummer ) ;
2022-10-24 01:02:02 +00:00
}
2023-05-05 00:52:42 +00:00
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 ) ;
2022-10-24 01:02:02 +00:00
else if ( ! Pawn . relations . DirectRelationExists ( PawnRelationDefOf . Spouse , cummer ) & & ! Pawn . relations . DirectRelationExists ( PawnRelationDefOf . Fiance , cummer ) )
{
2023-05-05 00:52:42 +00:00
if ( Pawn . health . capacities . GetLevel ( xxx . reproduction ) < 0.50f ) pawnMemories . TryGainMemory ( VariousDefOf . CameInsideFLowFert , cummer ) ;
else pawnMemories . TryGainMemory ( VariousDefOf . CameInsideF , cummer ) ;
2022-10-24 01:02:02 +00:00
}
}
else
{
2023-05-05 00:52:42 +00:00
if ( Pawn . IsProPregnancy ( out Precept _ ) )
pawnMemories . TryGainMemory ( VariousDefOf . CameInsideFFetishSafe , cummer ) ;
2022-10-24 01:02:02 +00:00
else if ( Pawn . relations . OpinionOf ( cummer ) < = - 5 )
2023-05-05 00:52:42 +00:00
pawnMemories . TryGainMemory ( VariousDefOf . HaterCameInsideFSafe , cummer ) ;
2022-10-24 01:02:02 +00:00
}
}
protected virtual void TaleCumInside ( Pawn cummer )
{
// Only make the tale for human-on-human, consentual sex. Otherwise the art just gets too hard to phrase properly
if ( ! xxx . is_human ( Pawn ) | | ! xxx . is_human ( cummer ) | | Pawn = = cummer ) return ;
if ( Pawn . CurJobDef ! = xxx . casual_sex & & Pawn . CurJobDef ! = xxx . gettin_loved ) return ;
if ( ! ( Pawn . IsColonist | | Pawn . IsPrisonerOfColony ) & & ! ( cummer . IsColonist | | cummer . IsPrisonerOfColony ) ) return ;
if ( ! IsDangerDay ) return ;
TaleRecorder . RecordTale ( VariousDefOf . TaleCameInside , new object [ ] { cummer , Pawn } ) ;
}
2023-03-07 01:05:01 +00:00
public void GoNextStage ( Stage nextstage , bool calculateHours = true )
2022-10-24 01:02:02 +00:00
{
2023-04-14 03:17:25 +00:00
curStageTicks = 0 ;
if ( calculateHours ) currentIntervalTicks = PeriodRandomizer ( nextstage ) ;
2022-10-24 01:02:02 +00:00
curStage = nextstage ;
}
protected virtual void GoOvulatoryStage ( )
{
2023-03-07 00:53:22 +00:00
GoNextStage ( Stage . Ovulatory ) ;
2022-10-24 01:02:02 +00:00
}
//stage can be interrupted in other reasons
protected void StayCurrentStage ( )
{
}
//stage never changes
protected void StayCurrentStageConst ( Stage curstage )
{
}
protected void GoFollicularOrBleeding ( )
{
2023-01-04 06:48:06 +00:00
if ( Props . bleedingIntervalDays = = 0 | | noBleeding )
2022-10-24 01:02:02 +00:00
{
GoNextStage ( Stage . Follicular ) ;
}
else
{
GoNextStage ( Stage . Bleeding ) ;
if ( crampPain > = 0.05f )
{
AddCrampPain ( ) ;
}
}
}
protected int PeriodRandomizer ( Stage stage )
{
float variabilityFactor = ( EggHealth < 1.0f ) ? 6.0f : 1.0f ;
// Most cycle lengthening or shortening occurs in the follicular phase, so weight towards that
switch ( stage )
{
case Stage . Follicular :
2023-04-14 03:17:25 +00:00
return ( int ) ( Props . follicularIntervalDays * GenDate . TicksPerDay * ( 1 + Rand . Range ( - cycleVariability , cycleVariability ) * 1.5f * variabilityFactor ) / ( 1 + ( cycleSpeed - 1 ) * 1.5f ) ) ;
case Stage . Ovulatory :
return Props . ovulationIntervalHours * GenDate . TicksPerHour ; // No variability for now
2022-10-24 01:02:02 +00:00
case Stage . Luteal :
2023-04-14 03:17:25 +00:00
return ( int ) ( Props . lutealIntervalDays * GenDate . TicksPerDay * ( 1 + Rand . Range ( - cycleVariability , cycleVariability ) * 0.5f * variabilityFactor ) / ( 1 + ( cycleSpeed - 1 ) * 0.5f ) ) ;
2022-10-24 01:02:02 +00:00
case Stage . Bleeding :
2023-04-14 03:17:25 +00:00
return ( int ) ( Props . bleedingIntervalDays * GenDate . TicksPerDay * ( 1 + Rand . Range ( - cycleVariability , cycleVariability ) * 0.5f * variabilityFactor ) / ( 1 + ( cycleSpeed - 1 ) * 0.5f ) ) ;
2022-10-24 01:02:02 +00:00
case Stage . Recover :
2023-04-14 03:17:25 +00:00
return ( int ) ( Props . recoveryIntervalDays * GenDate . TicksPerDay * Rand . Range ( 0.95f , 1.05f ) ) ;
2022-10-24 01:02:02 +00:00
case Stage . Pregnant :
2023-04-14 03:17:25 +00:00
return ( int ) ( MenstruationUtility . GestationHours ( pregnancy ) * GenDate . TicksPerHour ) ;
2023-03-02 22:42:49 +00:00
default :
2022-10-24 01:02:02 +00:00
return 1 ;
}
}
protected float InterspeciesImplantFactor ( Pawn fertilizer )
{
if ( fertilizer . def . defName = = Pawn . def . defName ) return 1.0f ;
else
{
if ( RJWPregnancySettings . complex_interspecies ) return SexUtility . BodySimilarity ( Pawn , fertilizer ) ;
else return RJWPregnancySettings . interspecies_impregnation_modifier ;
}
}
protected float PainRandomizer ( )
{
float rand = Rand . Range ( 0.0f , 1.0f ) ;
if ( rand < 0.01f ) return Rand . Range ( 0.0f , 0.2f ) ;
else if ( rand < 0.2f ) return Rand . Range ( 0.1f , 0.2f ) ;
else if ( rand < 0.8f ) return Rand . Range ( 0.2f , 0.4f ) ;
else if ( rand < 0.95f ) return Rand . Range ( 0.4f , 0.6f ) ;
else return Rand . Range ( 0.6f , 1.0f ) ;
}
protected Stage RandomStage ( )
{
Stage stage = Rand . ElementByWeight (
Stage . Follicular , Props . follicularIntervalDays - Props . bleedingIntervalDays ,
Stage . Luteal , Props . lutealIntervalDays ,
Stage . Bleeding , Props . bleedingIntervalDays ) ;
switch ( stage )
{
case Stage . Follicular :
2023-04-14 03:17:25 +00:00
curStageTicks = Rand . Range ( 0 , ( Props . follicularIntervalDays - Props . bleedingIntervalDays ) * GenDate . TicksPerDay ) ;
2022-10-24 01:02:02 +00:00
break ;
case Stage . Luteal :
2023-04-14 03:17:25 +00:00
curStageTicks = Rand . Range ( 0 , Props . lutealIntervalDays * GenDate . TicksPerDay ) ;
2022-10-24 01:02:02 +00:00
break ;
case Stage . Bleeding :
2023-04-14 03:17:25 +00:00
curStageTicks = Rand . Range ( 0 , Props . bleedingIntervalDays * GenDate . TicksPerDay ) ;
2022-10-24 01:02:02 +00:00
break ;
}
return stage ;
}
2022-10-27 20:57:25 +00:00
// Searches for a pregnancy unclaimed by any womb and put it in this one
2022-10-24 01:02:02 +00:00
public void TakeLoosePregnancy ( )
{
if ( pregnancy ! = null ) return ;
2022-10-27 20:57:25 +00:00
IEnumerable < Hediff > pregnancies = Pawn . health . hediffSet . hediffs . Where ( hediff = >
hediff is Hediff_BasePregnancy | |
hediff is Hediff_Pregnant | |
hediff is Hediff_Labor | |
hediff is Hediff_LaborPushing ) ;
2022-10-24 02:23:00 +00:00
2022-10-24 01:02:02 +00:00
pregnancy =
2022-10-24 02:23:00 +00:00
pregnancies . Except (
2022-10-24 01:02:02 +00:00
Pawn . GetMenstruationComps ( ) . Select ( comp = > comp . pregnancy ) . Where ( preg = > preg ! = null )
) . FirstOrDefault ( ) ;
if ( pregnancy ! = null )
GoNextStage ( Stage . Pregnant ) ;
}
2022-12-25 23:41:20 +00:00
public virtual void CopyCycleProperties ( HediffComp_Menstruation original )
2022-10-24 01:02:02 +00:00
{
cycleSpeed = original . cycleSpeed ;
cycleVariability = original . cycleVariability ;
ovarypower = original . ovarypower ;
crampPain = original . crampPain ;
}
2022-12-25 03:44:07 +00:00
public int EggsRestoredPerBiosculptor ( float yearsWorth )
{
return Math . Max ( 1 , ( int ) ( ( float ) RaceCyclesPerYear ( ) * yearsWorth ) ) ;
}
public void RestoreEggs ( float yearsWorth )
{
ovarypower + = EggsRestoredPerBiosculptor ( yearsWorth ) ;
}
2022-10-24 01:02:02 +00:00
public class Egg : IExposable
{
public bool fertilized ;
2023-04-30 05:28:29 +00:00
public int lifeSpanTicks ; // Actual ticks scaled by cycleAcceleration
2022-10-24 01:02:02 +00:00
public Pawn fertilizer ;
2023-04-14 03:17:25 +00:00
public int ageTicks ;
public int ticksSinceFertilization = 0 ;
2022-10-24 01:02:02 +00:00
public Egg ( )
{
fertilized = false ;
2023-04-14 03:17:25 +00:00
lifeSpanTicks = ( int ) ( 96 * GenDate . TicksPerHour * Configurations . EggLifespanMultiplier ) ;
2022-10-24 01:02:02 +00:00
fertilizer = null ;
2023-04-14 03:17:25 +00:00
ageTicks = 0 ;
2022-10-24 01:02:02 +00:00
}
2023-04-16 19:20:01 +00:00
public Egg ( int lifespanticks )
2022-10-24 01:02:02 +00:00
{
fertilized = false ;
2023-04-16 19:20:01 +00:00
lifeSpanTicks = ( int ) ( lifespanticks * Configurations . EggLifespanMultiplier ) ;
2022-10-24 01:02:02 +00:00
fertilizer = null ;
2023-04-14 03:17:25 +00:00
ageTicks = 0 ;
2022-10-24 01:02:02 +00:00
}
public void ExposeData ( )
{
2023-04-14 03:17:25 +00:00
if ( Scribe . mode = = LoadSaveMode . LoadingVars )
{
int lifespanhrs = - 1 ;
int position = - 1 ;
Scribe_Values . Look ( ref lifespanhrs , "lifespanhrs" , lifespanhrs , true ) ;
Scribe_Values . Look ( ref position , "position" , position , true ) ;
if ( lifespanhrs > = 0 ) lifeSpanTicks = lifespanhrs * GenDate . TicksPerHour ;
if ( position > = 0 ) ageTicks = position * GenDate . TicksPerHour ;
}
2022-10-24 01:02:02 +00:00
Scribe_References . Look ( ref fertilizer , "fertilizer" , true ) ;
2023-04-15 19:15:15 +00:00
Scribe_Values . Look ( ref fertilized , "fertilized" , false ) ;
2023-04-14 03:17:25 +00:00
Scribe_Values . Look ( ref lifeSpanTicks , "lifeSpanTicks" , lifeSpanTicks , true ) ;
Scribe_Values . Look ( ref ageTicks , "ageTicks" , ageTicks , true ) ;
2023-04-15 19:15:15 +00:00
Scribe_Values . Look ( ref ticksSinceFertilization , "ticksSinceFertilization" , 0 ) ;
2023-04-30 05:28:29 +00:00
if ( ticksSinceFertilization = = 0 & & Scribe . mode = = LoadSaveMode . LoadingVars )
{
// A bit awkward to do this twice, but it prevents ticksSinceFertilization from getting overwritten on a fertstage load
int fertstage = - 1 ;
Scribe_Values . Look ( ref fertstage , "fertstage" , fertstage , true ) ;
if ( fertstage > = 0 ) ticksSinceFertilization = fertstage * GenDate . TicksPerHour ;
}
2022-10-24 01:02:02 +00:00
}
}
}
public class HediffComp_Anus : HediffComp
{
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 )
{
}
}
}