Added a Gene for Gender Fluid Pawns

This commit is contained in:
Vegapnk 2023-07-16 08:54:01 +02:00
parent ae3c4471cf
commit 97c153f468
7 changed files with 302 additions and 5 deletions

View File

@ -1,8 +1,9 @@
# 1.2.2
# 1.3.0
**Changes:**
- New Gene for Evergrowing Cocks. Be careful.
- New Gene for Genderfluid Pawns - daily chance to change sex. Futas just change "display" and keep genitalia, other pawns switch genitalia.
**Fixes:**
@ -10,6 +11,11 @@
- Copy of Infis patch for eating cum from sexperience, #41 and #48
- Updated some Icons to have better backgrounds (thanks @WasmachenDennSachenSo #53)
*Notes*:
The pawns that are gender fluid can get pregnant.
However, with RJW 5.3.7 these pregnancies disappear.
This is a change needed upstream, but I will have a look.
# 1.2.1
**Fixes**:

View File

@ -46,4 +46,33 @@
</modExtensions>
</GeneDef>
<GeneDef>
<defName>rjw_genes_gender_fluid</defName>
<displayCategory>rjw_genes_gender</displayCategory>
<label>Gender Fluid</label>
<description>Everyday carriers of this gene might change their biological sex.</description>
<biostatCpx>0</biostatCpx>
<iconPath>Genes/Icons/Futa</iconPath>
<geneClass>RJW_Genes.Gene_GenderFluid</geneClass>
<displayOrderInCategory>2</displayOrderInCategory>
<exclusionTags>
<li>AG_Gender</li>
<li>Gender</li>
</exclusionTags>
<modExtensions>
<li MayRequire="OskarPotocki.VanillaFactionsExpanded.Core" Class="VanillaGenesExpanded.GeneExtension">
<backgroundPathEndogenes>Genes/Icons/RJW_Genes_Endogene_Background</backgroundPathEndogenes>
<backgroundPathXenogenes>Genes/Icons/RJW_Genes_Xenogene_Background</backgroundPathXenogenes>
</li>
<li Class="RJW_Genes.GenderFluidExtension">
<!-- 120k = 2 days -->
<changeInterval>1000</changeInterval>
<changeChance>1</changeChance>
</li>
</modExtensions>
</GeneDef>
</Defs>

View File

@ -32,9 +32,10 @@ namespace RJW_Genes
public static readonly GeneDef rjw_genes_extra_anus;
public static readonly GeneDef rjw_genes_no_anus;
public static readonly GeneDef rjw_genes_futa;
public static readonly GeneDef rjw_genes_featureless_chest;
// Genitalia Sizes
public static readonly GeneDef rjw_genes_big_male_genitalia;
// Genitalia Sizes
public static readonly GeneDef rjw_genes_big_male_genitalia;
public static readonly GeneDef rjw_genes_small_male_genitalia;
public static readonly GeneDef rjw_genes_loose_female_genitalia;
public static readonly GeneDef rjw_genes_tight_female_genitalia;
@ -46,6 +47,7 @@ namespace RJW_Genes
// Gender
public static readonly GeneDef rjw_genes_female_only;
public static readonly GeneDef rjw_genes_male_only;
public static readonly GeneDef rjw_genes_gender_fluid;
// Breeding
public static readonly GeneDef rjw_genes_mechbreeder;

View File

@ -0,0 +1,20 @@
using Verse;
namespace RJW_Genes
{
public class GenderFluidExtension : DefModExtension
{
/// <summary>
/// Number of ticks until the change can be triggered.
/// Just being "triggered" does not mean changing, see the changeChance below.
/// </summary>
public int changeInterval;
/// <summary>
/// How high is the chance to change gender?
/// Set to 1 for "always", set to 0 for "never".
/// Everything else is a bit statistics, but e.g. when set to .5 the chances grow per day from [50%, 75%, 82.25%, ...]
/// </summary>
public float changeChance;
}
}

View File

@ -31,7 +31,8 @@ namespace RJW_Genes
}
/// <summary>
/// Adjusts the Body Type to match the given target gender
/// Adjusts the Body Type to match the given target gender.
/// This is only for "drawing" attributes of the pawn, the genitalia are untouched at this point.
/// (for male and female only, baby,child and hulks don't change)
/// </summary>
/// <param name="pawn"></param>

View File

@ -0,0 +1,235 @@
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
namespace RJW_Genes
{
/*
* Once per day (or slightly different per configuration) checks if the pawn changes gender.
* At the triggered tick, there is a random chance to change gender.
* This will swap genitalia, appearance and breasts.
*
* For some situations, the pawn better not change genitalia, e.g. while having vaginal sex. This would throw errors.
* For these cases a check is implemented, and if there was a block the change happens a bit later when "unblocked".
*
* TODO: Keep pregnancies.
* The pregnancies remove their things on Tick at the end, which kills it for male pawns.
* This seems to be an upstream RJW thing, but needs a bit of investigation.
*/
public class Gene_GenderFluid : RJW_Gene
{
//public const int CHANGE_INTERVAL_FALLBACK = 1000; // Test value for Quick Trials
const int CHANGE_INTERVAL_FALLBACK = 60000; // 60k == 1 day
const float SWITCH_CHANCE_FALLBACK = 0.25f;
int change_interval;
float switch_chance;
List<Hediff> storedBreasts = new List<Hediff>();
private bool sexChangeWasBlocked = false;
public Gene_GenderFluid() : base() {
GenderFluidExtension genderFluidExt = GeneDefOf.rjw_genes_gender_fluid.GetModExtension<GenderFluidExtension>();
change_interval = genderFluidExt?.changeInterval ?? CHANGE_INTERVAL_FALLBACK;
switch_chance = genderFluidExt?.changeChance ?? SWITCH_CHANCE_FALLBACK;
}
public override void Tick()
{
base.Tick();
// Case 1: We had a blocked SexChange, now Pawn is free, apply sexchange a bit delayed.
if (pawn.IsHashIntervalTick(1500) && sexChangeWasBlocked && !SexChangeBlocked(pawn)){
ChangeSex();
sexChangeWasBlocked = false;
}
// Case 2: Check every interval if the Chance triggers
else if (pawn.IsHashIntervalTick(change_interval) && (new Random()).NextDouble() < switch_chance)
{
// Case 2.A) SexChange was blocked, postpone it
if (SexChangeBlocked(pawn))
{
sexChangeWasBlocked |= true;
return;
}
// Case 2.B) Nothing blocking, change the sex.
else { ChangeSex();}
}
}
private void ChangeSex()
{
if (rjw.Genital_Helper.is_futa(pawn))
{
// Handle Futa Pawns - Keep Genitalia as is, just change RW Gender
pawn.gender = pawn.gender == Gender.Male? Gender.Female: Gender.Male;
}
// Handle Non-Futas - Change Genitalia and Store breasts.
else
{
if (pawn.gender == Gender.Female)
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"genderfluid pawn {pawn} is changing from female to male");
SwitchToMale();
}
else if (pawn.gender == Gender.Male)
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"genderfluid pawn {pawn} is changing from male to female");
SwitchToFemale();
}
}
GenderUtility.RemoveAllSexChangeThoughts(pawn);
}
private void SwitchToFemale()
{
// Change Drawing
GenderUtility.AdjustBodyToTargetGender(pawn, Verse.Gender.Female);
// Change Gender
pawn.gender = Verse.Gender.Female;
// Switch Penisses to Vaginas
var genitalsToRemove = pawn.GetGenitalsList().FindAll(g => Genital_Helper.is_penis(g) || Genital_Helper.is_vagina(g));
foreach (var genital in genitalsToRemove)
{
var genitaliaHediffDef = GenitaliaUtility.GetVaginaForGene(GenitaliaUtility.GetGenitaliaTypeGeneForPawn(pawn));
float size = genital.Severity;
pawn.health.RemoveHediff(genital);
var newVagina = HediffMaker.MakeHediff(genitaliaHediffDef, pawn, Genital_Helper.get_genitalsBPR(pawn));
pawn.health.AddHediff(newVagina);
newVagina.Severity = size;
}
SwitchBreasts();
}
private void SwitchToMale()
{
// Change Drawing
GenderUtility.AdjustBodyToTargetGender(pawn, Verse.Gender.Male);
// Change Gender
pawn.gender = Verse.Gender.Male;
// Switch Vaginas to Penisses
var genitalsToRemove = pawn.GetGenitalsList().FindAll(g => Genital_Helper.is_penis(g) || Genital_Helper.is_vagina(g));
foreach (var genital in genitalsToRemove)
{
var genitaliaHediffDef = GenitaliaUtility.GetPenisForGene(GenitaliaUtility.GetGenitaliaTypeGeneForPawn(pawn));
float size = genital.Severity;
pawn.health.RemoveHediff(genital);
var newPenis = HediffMaker.MakeHediff(genitaliaHediffDef, pawn, Genital_Helper.get_genitalsBPR(pawn));
pawn.health.AddHediff(newPenis);
newPenis.Severity = size;
}
SwitchBreasts();
}
private void SwitchBreasts()
{
List<Hediff> current_breasts = pawn.GetBreastList();
// Stored_Breasts can be empty when the pawn first ever switches gender!
if (storedBreasts.NullOrEmpty())
{
foreach (var breasts in current_breasts)
{
// Is Male, and does not have the "no breast gene"
if (pawn.gender == Gender.Male && !GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_no_breasts))
{
storedBreasts.Add(CreateNewBreasts());
}
else if (pawn.gender == Gender.Female && !GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_featureless_chest) )
{
storedBreasts.Add(CreateNewBreasts());
}
}
}
foreach (var breast in current_breasts)
{
pawn.health.RemoveHediff(breast);
}
foreach (var breast in storedBreasts)
{
pawn.health.AddHediff(breast);
}
storedBreasts.Clear();
storedBreasts.AddRange(current_breasts);
}
internal Hediff CreateNewBreasts()
{
var correctGene = GenitaliaUtility.GetGenitaliaTypeGeneForPawn(pawn);
var breastDef = GenitaliaUtility.GetBreastsForGene(correctGene);
var partBPR = Genital_Helper.get_breastsBPR(pawn);
var additional_breasts = HediffMaker.MakeHediff(breastDef, pawn,partBPR);
var CompHediff = additional_breasts.TryGetComp<rjw.CompHediffBodyPart>();
if (CompHediff != null)
{
CompHediff.initComp(pawn);
CompHediff.updatesize();
}
return additional_breasts;
}
/// <summary>
/// There are some actions that block sex change,
/// being drafted or having sex.
/// </summary>
/// <param name="pawn">The pawn that want to sexchange.</param>
/// <returns>False if the SexChange is applicable, True if there needs to be a wait timer.</returns>
internal bool SexChangeBlocked(Pawn pawn)
{
// DEVNOTE: This list might extend on new cases, thus the explicit method.
return pawn == null
|| pawn.health.Dead
|| (pawn.jobs.curDriver is JobDriver_Masturbate)
|| (pawn.jobs.curDriver is JobDriver_Sex)
|| (pawn.jobs.curDriver is JobDriver_SexBaseReciever)
|| (pawn.jobs.curDriver is JobDriver_SexBaseInitiator)
|| (pawn.jobs.curDriver is JobDriver_JoinInBed)
|| (pawn.jobs.curDriver is JobDriver_SexQuick)
|| (pawn.jobs.curDriver is JobDriver_SexBaseRecieverQuickie)
|| (pawn.jobs.curDriver is JobDriver_SexOnSpot)
|| (pawn.jobs.curDriver is JobDriver_SexOnSpotReciever)
|| (pawn.jobs.curDriver is JobDriver_Knotted)
|| (pawn.jobs.curDriver is JobDriver_Mate)
|| (pawn.jobs.curDriver is JobDriver_Mating)
|| (pawn.jobs.curDriver is JobDriver_Breeding)
|| (pawn.jobs.curDriver is JobDriver_Rape)
|| (pawn.jobs.curDriver is JobDriver_SexBaseRecieverRaped)
|| (pawn.jobs.curDriver is JobDriver_RandomRape)
|| (pawn.jobs.curDriver is JobDriver_RapeComfortPawn)
|| (pawn.jobs.curDriver is JobDriver_RapeEnemy)
|| pawn.jobs.curDriver is JobDriver_Lovin
// This is a heavy check, but this is necessary because sometimes the pawns go somewhere to have sex and then they start despite missing genitalia!
|| (pawn.jobs.curDriver is JobDriver_Goto)
|| pawn.Drafted;
}
}
}

View File

@ -44,6 +44,8 @@
<Compile Include="Genes\Breeding\Gene_MechBreeder.cs" />
<Compile Include="Genes\Breeding\PatchMechBirth.cs" />
<Compile Include="Genes\ExtraGenitalia\Gene_UdderBreasts.cs" />
<Compile Include="Genes\Gender\Defs\GenderFluidExtension.cs" />
<Compile Include="Genes\Gender\Gene_GenderFluid.cs" />
<Compile Include="Genes\GenitaliaSize\Gene_EvergrowingGenitalia.cs" />
<Compile Include="Genes\Hive\Defs\HiveOffspringChanceDef.cs" />
<Compile Include="Genes\Hive\Genes\Gene_FerventOvipositor.cs" />
@ -217,6 +219,8 @@
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="Genes\Gender\Patches\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>