2024-03-13 23:29:56 +00:00
using RimWorld ;
using rjw ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using UnityEngine ;
using Verse ;
namespace RJW_Menstruation
{
public static class MenstruationUtility
{
2024-03-17 01:36:33 +00:00
[Obsolete("This method is obsolete. Use GetMenstruationComps or a related function instead", true)]
2024-03-13 23:29:56 +00:00
public static HediffComp_Menstruation GetMenstruationComp ( this Pawn pawn )
{
return pawn . GetFirstMenstruationComp ( ) ;
}
public static IEnumerable < HediffComp_Menstruation > GetMenstruationComps ( this Pawn pawn )
{
List < Hediff > hedifflist = pawn . health . hediffSet . hediffs . FindAll ( h = > VariousDefOf . AllVaginas . Contains ( h . def ) ) ;
if ( hedifflist = = null ) yield break ;
foreach ( Hediff hediff in hedifflist )
{
HediffComp_Menstruation result = hediff . TryGetComp < HediffComp_Menstruation > ( ) ;
if ( result ! = null ) yield return result ;
}
}
public static HediffComp_Menstruation GetFirstMenstruationComp ( this Pawn pawn )
{
return pawn . GetMenstruationComps ( ) . FirstOrDefault ( ) ;
}
public static HediffComp_Menstruation GetRandomMenstruationComp ( this Pawn pawn )
{
return pawn . GetMenstruationComps ( ) . RandomElementWithFallback ( ) ;
}
public static HediffComp_Menstruation GetFertileMenstruationComp ( this Pawn pawn )
{
List < HediffComp_Menstruation > comps = pawn . GetMenstruationComps ( ) . ToList ( ) ;
return comps . Where ( c = > c . IsDangerDay ) . RandomElementWithFallback ( ) ? ? comps . RandomElementWithFallback ( ) ;
}
public static HediffComp_Menstruation GetMenstruationCompFromVagina ( this Hediff vagina )
{
if ( VariousDefOf . AllVaginas . Contains ( vagina ? . def ) )
{
return vagina . TryGetComp < HediffComp_Menstruation > ( ) ;
}
return null ;
}
public static HediffComp_Menstruation GetMenstruationCompFromPregnancy ( this Hediff pregnancy )
{
return pregnancy ? . pawn . GetMenstruationComps ( ) . FirstOrDefault ( comp = > comp . Pregnancy = = pregnancy ) ;
}
public static HediffComp_Anus GetAnusComp ( this Pawn pawn )
{
return pawn . health . hediffSet . hediffs . FirstOrDefault ( ( Hediff h ) = > VariousDefOf . AllAnuses . Contains ( h . def ) ) ? . TryGetComp < HediffComp_Anus > ( ) ;
}
[Obsolete("This method is obsolete and can cause ambiguity. Use GetMenstruationCompFromVagina or GetMenstruationCompFromPregnancy instead.", true)]
public static HediffComp_Menstruation GetMenstruationComp ( Hediff hediff )
{
switch ( hediff )
{
case Hediff_BasePregnancy rjwPreg :
return rjwPreg . GetMenstruationCompFromPregnancy ( ) ;
case Hediff_Pregnant vanillaPreg :
return vanillaPreg . GetMenstruationCompFromPregnancy ( ) ;
case Hediff_Labor vanillaLabor :
return vanillaLabor . GetMenstruationCompFromPregnancy ( ) ;
case Hediff_LaborPushing vanillaLaborPushing :
return vanillaLaborPushing . GetMenstruationCompFromPregnancy ( ) ;
case Hediff_PartBaseNatural rjwNatrual :
return rjwNatrual . GetMenstruationCompFromVagina ( ) ;
case Hediff_PartBaseArtifical rjwArtificial :
return rjwArtificial . GetMenstruationCompFromVagina ( ) ;
default :
Log . Warning ( "Obsolete GetMenstruationComp called with unknown hediff. Ensure your submods are up to date." ) ;
return null ;
}
}
public static HediffComp_Anus GetAnusComp ( this Hediff hediff )
{
if ( hediff is Hediff_PartBaseNatural | | hediff is Hediff_PartBaseArtifical )
{
return hediff . TryGetComp < HediffComp_Anus > ( ) ;
}
return null ;
}
public static float GetFertilityChance ( this HediffComp_Menstruation comp )
{
return 1.0f - Mathf . Pow ( 1.0f - Configurations . FertilizeChance , comp . TotalFertCum * comp . Props . basefertilizationChanceFactor ) ;
}
public static Texture2D GetPregnancyIcon ( this HediffComp_Menstruation comp , Hediff hediff )
{
float gestationProgress = comp . StageProgress ;
ThingDef babydef ;
int babycount ;
HediffComp_PregeneratedBabies babiescomp = hediff ? . TryGetComp < HediffComp_PregeneratedBabies > ( ) ;
if ( hediff is Hediff_MechanoidPregnancy )
{
babydef = VariousDefOf . Scyther ;
babycount = 1 ;
}
else if ( hediff is Hediff_BasePregnancy preg )
{
babydef = preg . babies ? . FirstOrDefault ( ) ? . def ? ? ThingDefOf . Human ;
babycount = preg . babies ? . Count ? ? 1 ;
}
else if ( babiescomp ? . HasBaby ? ? false )
{
babydef = babiescomp . babies . First ( ) . def ;
babycount = babiescomp . babies . Count ;
}
else
{
babydef = comp . Pawn . def ;
babycount = 1 ;
}
string fetustex = babydef . GetModExtension < PawnDNAModExtension > ( ) ? . fetusTexPath ? ? "Fetus/Fetus_Default" ;
string icon ;
if ( gestationProgress < 0.2f ) icon = comp . WombTex + "_Implanted" ;
else if ( gestationProgress < 0.4f ) icon = fetustex + "00" ;
else if ( gestationProgress < 0.5f ) icon = fetustex + "01" ;
else if ( gestationProgress < 0.6f ) icon = fetustex + "02" ;
else if ( gestationProgress < 0.7f ) icon = fetustex + "03" ;
else if ( gestationProgress < 0.8f ) icon = fetustex + "04" ;
else icon = fetustex + "05" ;
return TryGetTwinsIcon ( icon , babycount ) ? ? ContentFinder < Texture2D > . Get ( icon , true ) ;
}
public static Texture2D TryGetTwinsIcon ( string path , int babycount )
{
for ( int i = babycount ; i > 1 ; i - - )
{
Texture2D result = ContentFinder < Texture2D > . Get ( path + "_Multiplet_" + i , false ) ;
if ( result ! = null ) return result ;
}
return null ;
}
public static Texture2D GetCumIcon ( this HediffComp_Menstruation comp )
{
Pawn pawn = comp . Pawn ;
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
string icon = comp . WombTex ;
float cumpercent = comp . TotalCumPercent ;
if ( cumpercent < 0.001f ) return ContentFinder < Texture2D > . Get ( "Womb/Empty" , true ) ;
else if ( cumpercent < 0.01f ) icon + = "_Cum_00" ;
else if ( cumpercent < 0.05f ) icon + = "_Cum_01" ;
else if ( cumpercent < 0.11f ) icon + = "_Cum_02" ;
else if ( cumpercent < 0.17f ) icon + = "_Cum_03" ;
else if ( cumpercent < 0.23f ) icon + = "_Cum_04" ;
else if ( cumpercent < 0.29f ) icon + = "_Cum_05" ;
else if ( cumpercent < 0.35f ) icon + = "_Cum_06" ;
else if ( cumpercent < 0.41f ) icon + = "_Cum_07" ;
else if ( cumpercent < 0.47f ) icon + = "_Cum_08" ;
else if ( cumpercent < 0.53f ) icon + = "_Cum_09" ;
else if ( cumpercent < 0.59f ) icon + = "_Cum_10" ;
else if ( cumpercent < 0.65f ) icon + = "_Cum_11" ;
else if ( cumpercent < 0.71f ) icon + = "_Cum_12" ;
else if ( cumpercent < 0.77f ) icon + = "_Cum_13" ;
else if ( cumpercent < 0.83f ) icon + = "_Cum_14" ;
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 ) ;
return cumtex ;
}
public static Texture2D GetInsectEggedIcon ( this HediffComp_Menstruation comp )
{
List < Hediff_InsectEgg > hediffs = new List < Hediff_InsectEgg > ( ) ;
comp . Pawn . health . hediffSet . GetHediffs ( ref hediffs ) ;
if ( hediffs . NullOrEmpty ( ) ) return null ;
string path = "Womb/Insect_Egged/Womb_Egged_" ;
float sumSize = hediffs . Sum ( hediff = > hediff . eggssize ) ;
if ( sumSize > 1.0f )
{
path + = "Stuffed_" ;
if ( sumSize < 1.0f ) path + = "00" ;
else if ( sumSize < 2.0f ) path + = "01" ;
else if ( sumSize < 2.5f ) path + = "02" ;
else if ( sumSize < 3.0f ) path + = "03" ;
else path + = "04" ;
}
else
{
if ( hediffs . Max ( hediff = > hediff . eggssize ) > 0.3f ) // The threshold for "large egg" in the debug
{
path + = "L" ;
if ( hediffs . Count = = 1 ) path + = "00" ;
else if ( hediffs . Count = = 2 ) path + = "01" ;
else path + = "02" ;
}
else
{
path + = "S" ;
if ( hediffs . Count = = 1 ) path + = "00" ;
else if ( hediffs . Count = = 2 ) path + = "01" ;
else if ( hediffs . Count = = 3 ) path + = "02" ;
else if ( hediffs . Count = = 4 ) path + = "03" ;
else path + = "04" ;
}
}
return ContentFinder < Texture2D > . Get ( path ) ;
}
public static Texture2D GetWombIcon ( this HediffComp_Menstruation comp )
{
Texture2D wombtex = comp . GetInsectEggedIcon ( ) ;
if ( wombtex ! = null ) return wombtex ;
string icon = comp . WombTex ;
HediffComp_Menstruation . Stage stage = comp . curStage ;
if ( stage = = HediffComp_Menstruation . Stage . Bleeding ) icon + = "_Bleeding" ;
wombtex = ContentFinder < Texture2D > . Get ( icon , true ) ;
return wombtex ;
}
public static Texture2D GetOvaryIcon ( this HediffComp_Menstruation comp )
{
const float ovaryChanceToShow_01 = 0.2f ;
const float ovaryChanceToShow_02 = 0.8f ;
float ovulatoryProgress ;
bool isInduced = comp is HediffComp_InducedOvulator ;
if ( comp . curStage = = HediffComp_Menstruation . Stage . Follicular & &
isInduced & &
comp . Pawn . jobs . curDriver is JobDriver_Sex job & &
job . Sexprops ! = null & &
2024-03-27 18:44:49 +00:00
! UsingCondom ( comp . Pawn , job . Partner ) & &
2024-03-13 23:29:56 +00:00
( job . Sexprops . sexType = = xxx . rjwSextype . Vaginal | | job . Sexprops . sexType = = xxx . rjwSextype . DoublePenetration ) )
ovulatoryProgress = 0.0f ;
else if ( comp . curStage = = HediffComp_Menstruation . Stage . Ovulatory ) ovulatoryProgress = isInduced ? Mathf . Max ( ovaryChanceToShow_01 , comp . StageProgessNextUpdate ) : comp . StageProgessNextUpdate ;
// else if (comp.curStage == HediffComp_Menstruation.Stage.Luteal && comp.IsEggExist) return ContentFinder<Texture2D>.Get("Ovaries/Ovary_02", true);
else return ContentFinder < Texture2D > . Get ( "Womb/Empty" , true ) ;
float combinedAppearance = ovulatoryProgress * comp . OvulationChance ;
if ( combinedAppearance > = ovaryChanceToShow_02 & & comp . OvulationChance > = 1.0f ) return ContentFinder < Texture2D > . Get ( "Ovaries/Ovary_02" , true ) ;
else if ( combinedAppearance > = ovaryChanceToShow_01 ) return ContentFinder < Texture2D > . Get ( "Ovaries/Ovary_01" , true ) ;
else return ContentFinder < Texture2D > . Get ( "Ovaries/Ovary_00" , true ) ;
}
public static Texture2D GetEggIcon ( this HediffComp_Menstruation comp , bool includeOvary )
{
switch ( comp . CurrentVisibleStage )
{
case HediffComp_Menstruation . Stage . Follicular :
case HediffComp_Menstruation . Stage . Ovulatory :
if ( ! includeOvary ) break ;
else return GetOvaryIcon ( comp ) ;
case HediffComp_Menstruation . Stage . Luteal :
if ( ! comp . IsEggExist ) break ;
int fertTime = comp . EggFertilizedTime ;
if ( fertTime > = 0 )
{
if ( fertTime < = GenDate . TicksPerHour * Configurations . CycleAcceleration ) return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Fertilizing02" , true ) ;
else if ( fertTime < = 18 * GenDate . TicksPerHour ) return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Fertilized00" , true ) ;
else if ( fertTime < = 54 * GenDate . TicksPerHour ) return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Fertilized01" , true ) ;
else return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Fertilized02" , true ) ;
}
else if ( includeOvary & & comp . curStageTicks < = comp . Props . ovulationIntervalHours * 0.4f * GenDate . TicksPerHour ) // Total about as long as it spent in Ovary_01
{
return ContentFinder < Texture2D > . Get ( "Ovaries/Ovary_02" , true ) ;
}
else if ( comp . IsEggFertilizing )
{
if ( comp . GetFertilityChance ( ) < 0.5f )
return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Fertilizing00" , true ) ;
else
return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Fertilizing01" , true ) ;
}
else return ContentFinder < Texture2D > . Get ( "Eggs/Egg" , true ) ;
case HediffComp_Menstruation . Stage . Pregnant :
if ( comp . Pregnancy is Hediff_MechanoidPregnancy ) break ;
else if ( comp . GetPregnancyProgress ( ) < 0.2f ) return ContentFinder < Texture2D > . Get ( "Eggs/Egg_Implanted00" , true ) ;
else break ;
}
return ContentFinder < Texture2D > . Get ( "Womb/Empty" , true ) ;
}
public static void DrawEggOverlay ( this HediffComp_Menstruation comp , Rect wombRect , bool includeOvary )
{
Rect rect = new Rect ( wombRect . xMax - wombRect . width / 3 , wombRect . y , wombRect . width / 3 , wombRect . width / 3 ) ;
GUI . color = Color . white ;
GUI . DrawTexture ( rect , comp . GetEggIcon ( includeOvary ) , ScaleMode . ScaleToFit ) ;
}
public static Texture2D GetGenitalIcon ( this Pawn pawn , HediffComp_Menstruation comp )
{
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 ;
if ( comp ! = null ) icon = comp . VagTex ;
else icon = "Genitals/Vagina" ;
if ( severity < 0.20f ) icon + = "00" ; //micro
else if ( severity < 0.30f ) icon + = "01" ; //tight
else if ( severity < 0.40f ) icon + = "02" ; //tight
else if ( severity < 0.47f ) icon + = "03" ; //average
else if ( severity < 0.53f ) icon + = "04" ; //average
else if ( severity < 0.60f ) icon + = "05" ; //average
else if ( severity < 0.70f ) icon + = "06" ; //accomodating
else if ( severity < 0.80f ) icon + = "07" ; //accomodating
else if ( severity < 0.87f ) icon + = "08" ; //cavernous
else if ( severity < 0.94f ) icon + = "09" ; //cavernous
else if ( severity < 1.01f ) icon + = "10" ; //cavernous
else icon + = "11" ; //abyssal
return ContentFinder < Texture2D > . Get ( icon , true ) ;
}
public static Texture2D GetAnalIcon ( this Pawn pawn )
{
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 ( severity < 0.20f ) icon + = "00" ; //micro
else if ( severity < 0.40f ) icon + = "01" ; //tight
else if ( severity < 0.60f ) icon + = "02" ; //average
else if ( severity < 0.80f ) icon + = "03" ; //accomodating
else if ( severity < 1.01f ) icon + = "04" ; //cavernous
else icon + = "05" ; //abyssal
return ContentFinder < Texture2D > . Get ( icon , true ) ;
}
public static float GestationHours ( this Hediff hediff )
{
if ( hediff = = null )
{
Log . Error ( "Tried to get gestation length without a pregnancy." ) ;
return 1f ;
}
else if ( hediff is Hediff_BasePregnancy rjw_preg )
return ( rjw_preg . p_end_tick - rjw_preg . p_start_tick ) / GenDate . TicksPerHour ;
// TODO: Biotech pregnancy
else return hediff . pawn . RaceProps . gestationPeriodDays * GenDate . HoursPerDay ;
}
public static float RandomVariabilityPercent ( int recursion = 0 )
{
// Humans, in days
const float mean = 1.635f ;
const float stddev = 0.9138f ;
const float lambda = 0.234f ;
if ( recursion > = 10 ) return mean / ( 28 * 2 ) ;
float variability = Rand . Gaussian ( mean , stddev ) - Mathf . Log ( Rand . Value ) / lambda ;
variability / = 28 * 2 ; // Convert to percentage
if ( variability < 0 | | variability > 0.35f ) return RandomVariabilityPercent ( recursion + 1 ) ; // ~2% chance, about the limit on how far variability can go before things start to break
else return variability ;
}
public static bool ShouldCycle ( this Pawn pawn )
{
if ( ! Configurations . EnableAnimalCycle & & pawn . IsAnimal ( ) ) return false ;
if ( pawn . GetComp < CompEggLayer > ( ) ! = null ) return false ;
if ( pawn . RaceHasOviPregnancy ( ) ) return false ;
if ( ModsConfig . BiotechActive & & pawn . genes ! = null & &
pawn . genes . GenesListForReading . Select ( gene = > gene . def ) . Intersect ( VariousDefOf . EggLayerGenes ) . Any ( ) ) return false ;
return true ;
}
public static bool IsInEstrus ( this Pawn pawn , bool visible = true )
{
if ( pawn . Dead ) return false ;
return pawn . health ? . hediffSet ? . HasHediff ( visible ? VariousDefOf . Hediff_Estrus : VariousDefOf . Hediff_Estrus_Concealed ) ? ? false ;
}
public static HediffComp_Menstruation . EstrusLevel HighestEstrus ( this Pawn pawn )
{
HediffComp_Menstruation . EstrusLevel res = HediffComp_Menstruation . EstrusLevel . None ;
foreach ( HediffComp_Menstruation comp in pawn . GetMenstruationComps ( ) )
{
switch ( comp . GetEstrusLevel ( ) )
{
case HediffComp_Menstruation . EstrusLevel . None :
break ;
case HediffComp_Menstruation . EstrusLevel . Concealed :
res = HediffComp_Menstruation . EstrusLevel . Concealed ;
break ;
case HediffComp_Menstruation . EstrusLevel . Visible :
return HediffComp_Menstruation . EstrusLevel . Visible ;
}
}
return res ;
}
public static bool HasIUD ( this Pawn pawn )
{
if ( pawn . health . hediffSet . HasHediff ( VariousDefOf . RJW_IUD ) ) return true ;
if ( ModsConfig . BiotechActive & & pawn . health . hediffSet . HasHediff ( HediffDefOf . ImplantedIUD ) ) return true ;
return false ;
}
public static bool IsProPregnancy ( this Pawn pawn , out Precept precept )
{
precept = null ;
Ideo ideo = pawn . Ideo ;
if ( ideo ! = null )
{
precept = ideo . GetPrecept ( VariousDefOf . Pregnancy_Required ) ? ?
ideo . GetPrecept ( VariousDefOf . Pregnancy_Holy ) ? ?
ideo . GetPrecept ( VariousDefOf . Pregnancy_Elevated ) ;
}
if ( precept ! = null ) return true ;
else return pawn . IsBreeder ( ) | |
pawn . HasImpregnationFetish ( ) ;
}
public static float DamagePants ( this Pawn pawn , float fluidAmount )
{
if ( pawn . apparel = = null ) return 0 ;
Apparel pants = null ;
foreach ( Apparel apparel in pawn . apparel . WornApparel . Where ( app = > app . def . apparel . bodyPartGroups . Contains ( BodyPartGroupDefOf . Legs ) ) )
{
if ( apparel . def . apparel . LastLayer = = ApparelLayerDefOf . OnSkin )
{
pants = apparel ;
break ;
}
else if ( pants = = null | | apparel . def . apparel . LastLayer = = ApparelLayerDefOf . Middle )
// Either grab whatever's available or reassign the pants from shell to a middle
pants = apparel ;
// Pants are middle and this is a shell
else continue ;
}
if ( pants = = null ) return 0 ;
const float HPPerMl = 0.5f ;
DamageWorker . DamageResult damage = pants . TakeDamage ( new DamageInfo ( DamageDefOf . Deterioration , fluidAmount * HPPerMl , spawnFilth : false ) ) ;
if ( pants . Destroyed & & PawnUtility . ShouldSendNotificationAbout ( pawn ) & & ! pawn . Dead )
Messages . Message ( "MessageWornApparelDeterioratedAway" . Translate ( GenLabel . ThingLabel ( pants . def , pants . Stuff ) , pawn ) . CapitalizeFirst ( ) , pawn , MessageTypeDefOf . NegativeEvent ) ;
return damage . totalDamageDealt ;
}
2024-03-27 18:44:49 +00:00
public static bool UsingCondom ( Pawn pawn , Pawn partner )
{
return
( ( pawn ? . jobs ? . curDriver as JobDriver_Sex ) ? . Sexprops . usedCondom ? ? false )
| |
( ( partner ? . jobs ? . curDriver as JobDriver_Sex ) ? . Sexprops . usedCondom ? ? false ) ;
}
2024-03-13 23:29:56 +00:00
}
}