diff --git a/CHANGELOG.md b/CHANGELOG.md index c177c58..5b63394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # 2.2.0 (dd-mm-2024) -**Genetic Diseases** +## Explanations + +**Genetic Diseases**: This update introduces genetic diseases that are shared on sex. Infection is handled when sex finishes, so a coitus-interruptus will not result in infections. @@ -20,14 +22,65 @@ Most of the genes so far were positive or neutral, so I got some fair requests to introduce negative genes to keep xenotypes balanced. I know that this is some overlap with the STD mod, but well ... you are free to turn things off? +**Genetic Infectors**: + +These Genes can apply a genetic disease, but are not genetic diseases themselves. +A single infector gene can have multiple resulting diseases, see this extension example: + +```xml +
  • + 0.05 + +
  • rjw_genes_size_blinded
  • +
  • rjw_genes_infectious_bisexuality
  • + + +``` + +The infection-chance is applied per gene - for the example above there would be a 5% chance to apply `size_blinded` and 5% chance to apply `infectious_bisexuality`. +Multiple infections can happen on a single sex. +The `infectionGenes` can be any gene, this is not limited to genetic diseases (e.g. ugly or something). + +*Infectors* are always applied even if the genetic disease spread is turned off. +The created genetic diseases will follow the logic outlined above. + +**Disease Immunity**: + +Pawns can be immune to genetic diseases. + +This is either done with a specialised gene (`rjw_genes_genetic_disease_immunity`) +or by genes giving specific immunities. + +Any gene can give immunity against any genetic disease using an extension: + +```xml +
  • + +
  • rjw_genes_size_blinded
  • + + +``` + +These extensions can be slapped on any gene, +but they are meant mostly to have infectors immune against their own diseases. + +## Changelog + **Additions:** -- Passive Gene: *Genetic Disease Immunity* - cannot get infected by any genetic diseases, and won't be affected by some other genes (see relevant genes) +- Passive Gene: Genetic Disease Immunity. cannot get infected by any genetic diseases, and won't be affected by some other genes (see relevant genes) - Disease Gene: Vulnerability. Pawn is likelier to be raped - Disease Gene: Infectious Hypersexuality - Disease Gene: Infectious Homosexuality & Bisexuality +- Disease Gene: Infectious lower fertility +- Disease Gene: Infectious higher sex need - Disease Gene: Fluctual Sexual Need. (Configurable) Chance to reset sex-need to near-zero and gain a bit of rest-need. - Disease Gene: Size Blinded. Pawns have a higher chance for hooking up with pawns with a big cock, lower chance for small cocks. +- Infector Gene: Genetic Stretcher. Pawns can infect other pawns with *Size Blinded* +- Gene: Hardwired Progenity. Pawns with this get a malus on having no-children, and bonus on having a lot. +- Gene: Sexual Genetic Swap. Pawns have a chance to switch a random gene with their sexpartner. +- (Archite) Gene: Sexual Genetic Thief. Pawns have a chance to steal a gene from their sexpartner. Genetic Disease Immunity shields against this. +- Pawns will have negative thoughts about pawns with more genetic diseases than themselves. **Fixes:** diff --git a/Common/Defs/GeneDefs/GeneDefs_Breeding.xml b/Common/Defs/GeneDefs/GeneDefs_Breeding.xml index bb6fbe4..b3a9844 100644 --- a/Common/Defs/GeneDefs/GeneDefs_Breeding.xml +++ b/Common/Defs/GeneDefs/GeneDefs_Breeding.xml @@ -115,4 +115,41 @@ -1 + + rjw_genes_hardwired_progenity + + Carriers of this gene need to procreate. They suffer negative effects if they are childless, and have increased capabilities if they reach a high amount of offsprings. + Genes/Icons/RJW_Genes_PheromoneSpit + 70 + + +
  • + + -0.1 + 1.2 + 1.2 + -0.15 + -0.1 + +
  • +
  • + + 0.05 + 0.1 + 0.1 + 0.4 + +
  • +
  • + + 0.15 + 0.25 + 0.25 + +
  • +
    + + 1 + 0 +
    \ No newline at end of file diff --git a/Common/Defs/GeneDefs/GeneDefs_Diseases.xml b/Common/Defs/GeneDefs/GeneDefs_Diseases.xml index ecc3ee8..b9c1a45 100644 --- a/Common/Defs/GeneDefs/GeneDefs_Diseases.xml +++ b/Common/Defs/GeneDefs/GeneDefs_Diseases.xml @@ -85,6 +85,69 @@ + + rjw_genes_infectious_low_fertility + + Carriers of this genetic disease have lower fertility. + 1 + 1 + 0.9 + UI/Icons/ColonistBar/Idle + 5 + + + -0.15 + + + +
  • + 0.05 +
  • +
    +
    + + + rjw_genes_infectious_increased_sex_need + + Carriers of this genetic disease need more sex. + 0 + 1 + 0.9 + UI/Icons/ColonistBar/Idle + 5 + + + 0.15 + + + +
  • + 0.08 +
  • +
    +
    + + + rjw_genes_infectious_major_increased_sex_need + + Carriers of this genetic disease need a lot more sex. + 1 + 2 + 0.85 + UI/Icons/ColonistBar/Idle + 5 + + + 1.0 + + + +
  • + 0.03 +
  • +
    +
    + rjw_genes_infectious_hypersexuality @@ -202,4 +265,27 @@ + + rjw_genes_stretcher + + Pawns with this gene have a chance to alter the genes of their sexual partners to prefer large cocks. + UI/Icons/ColonistBar/Idle + 1 + 0 + 12 + +
  • + +
  • rjw_genes_size_blinded
  • + + +
  • + 0.05 + +
  • rjw_genes_size_blinded
  • + + +
    +
    + \ No newline at end of file diff --git a/Common/Defs/GeneDefs/GeneDefs_SexSpecial.xml b/Common/Defs/GeneDefs/GeneDefs_SexSpecial.xml index 189a6c8..0fe012a 100644 --- a/Common/Defs/GeneDefs/GeneDefs_SexSpecial.xml +++ b/Common/Defs/GeneDefs/GeneDefs_SexSpecial.xml @@ -140,7 +140,7 @@ rjw_genes_sex_tamer - + sextamer Bestiality has a chance to tame animals or advance their training. Genes/Icons/RJW_Genes_SexualTamer @@ -149,4 +149,35 @@ -1 + + rjw_genes_sexual_genetic_swap + + Carriers with this gene may switch a gene with their sex-partner. Switched Genes are always endogenes. + UI/Icons/Genes/Gene_PsychicBonding + 20 + 3 + 0 + +
  • + 0.1 +
  • +
    +
    + + + rjw_genes_sexual_genetic_thief + + Carriers with this gene may steal a gene from their sex-partner. Stolen genes are always xenogenes. + UI/Icons/Genes/Gene_PsychicBonding + 21 + 5 + -2 + 1 + +
  • + 0.2 +
  • +
    +
    + \ No newline at end of file diff --git a/Common/Defs/ThoughtDefs/Thoughts_Disease.xml b/Common/Defs/ThoughtDefs/Thoughts_Disease.xml index d0721ee..8997a52 100644 --- a/Common/Defs/ThoughtDefs/Thoughts_Disease.xml +++ b/Common/Defs/ThoughtDefs/Thoughts_Disease.xml @@ -24,4 +24,26 @@ + + rjw_genes_has_more_diseases + Thought_SituationalSocial + RJW_Genes.ThoughtWorker_HasMoreDiseasesThanMe_Social + true + + +
  • + + -3 +
  • +
  • + + -8 +
  • +
  • + + -18 +
  • +
    +
    + diff --git a/Common/Languages/English/Keyed/StatsReports.xml b/Common/Languages/English/Keyed/StatsReports.xml new file mode 100644 index 0000000..715bccb --- /dev/null +++ b/Common/Languages/English/Keyed/StatsReports.xml @@ -0,0 +1,8 @@ + + + + Pawn doesn't have any children. + Pawn has a decent amount of children. + Pawn has a lot of children. + + diff --git a/Source/Common/Defs/ChanceExtension.cs b/Source/Common/Defs/ChanceExtension.cs new file mode 100644 index 0000000..ff8b260 --- /dev/null +++ b/Source/Common/Defs/ChanceExtension.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public class ChanceExtension : DefModExtension + { + public float chance; + } + +} diff --git a/Source/GeneDefOf.cs b/Source/GeneDefOf.cs index 2e59f20..873df6f 100644 --- a/Source/GeneDefOf.cs +++ b/Source/GeneDefOf.cs @@ -73,6 +73,7 @@ namespace RJW_Genes public static readonly GeneDef rjw_genes_fervent_ovipositor; public static readonly GeneDef rjw_genes_insectbreeder; public static readonly GeneDef rjw_genes_insectincubator; + public static readonly GeneDef rjw_genes_hardwired_progenity; // Cum public static readonly GeneDef rjw_genes_no_cum; @@ -104,6 +105,8 @@ namespace RJW_Genes public static readonly GeneDef rjw_genes_hormonal_saliva; public static readonly GeneDef rjw_genes_cocoonweaver; public static readonly GeneDef rjw_genes_sex_tamer; + public static readonly GeneDef rjw_genes_sexual_genetic_swap; + public static readonly GeneDef rjw_genes_sexual_genetic_thief; // Cosmetic public static readonly GeneDef rjw_genes_succubus_tail; diff --git a/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_ManyChildren.cs b/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_ManyChildren.cs new file mode 100644 index 0000000..c483eb3 --- /dev/null +++ b/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_ManyChildren.cs @@ -0,0 +1,38 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public class ConditionalStatAffecter_ManyChildren : ConditionalStatAffecter + { + public override string Label => (string)"StatsReport_ManyChildren".Translate(); + + public const int THRESHOLD_FOR_CHILDREN = 3; + + public override bool Applies(StatRequest req) + { + if (req == null || req.Thing == null || !req.Thing.Spawned) return false; + + if (req.Thing is Pawn pawn) + { + // Do nothing if Pawn is Baby or Child (#25) + if (!pawn.ageTracker.Adult) + return false; + + if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_hardwired_progenity)) + { + // This "middle" Conditional Stat Affecter only fires if the other one does not apply + return pawn.relations.ChildrenCount >= THRESHOLD_FOR_CHILDREN + && pawn.relations.ChildrenCount < ConditionalStatAffecter_VeryManyChildren.THRESHOLD_FOR_CHILDREN; + } + } + + return false; + } + } +} diff --git a/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_NoChildren.cs b/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_NoChildren.cs new file mode 100644 index 0000000..4f50994 --- /dev/null +++ b/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_NoChildren.cs @@ -0,0 +1,43 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + /// + /// This conditional stat affecter "fires" if the pawn has no children. + /// + /// DevNote: I salvaged this from 1.3.3 Halamyr Conditional Stat Affecters. + /// It seems that with RW 1.5 there was a change how these work, as the req.Pawn seems to be null. + /// Now, the pawn is in req.Thing. + /// + public class ConditionalStatAffecter_NoChildren : ConditionalStatAffecter + { + public override string Label => (string)"StatsReport_NoChildren".Translate(); + + public override bool Applies(StatRequest req) + { + if (req == null || req.Thing == null || !req.Thing.Spawned) return false; + + if (req.Thing is Pawn pawn) + { + // Do nothing if Pawn is Baby or Child (#25) + if (!pawn.ageTracker.Adult) + return false; + + if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_hardwired_progenity)) + { + return pawn.relations.ChildrenCount == 0; + } + } + + return false; + } + + } + +} diff --git a/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_VeryManyChildren.cs b/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_VeryManyChildren.cs new file mode 100644 index 0000000..e14d4f1 --- /dev/null +++ b/Source/Genes/Breeding/ConditionalStatAffecters/ConditionalStatAffecter_VeryManyChildren.cs @@ -0,0 +1,36 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public class ConditionalStatAffecter_VeryManyChildren : ConditionalStatAffecter + { + public override string Label => (string)"StatsReport_VeryManyChildren".Translate(); + + public const int THRESHOLD_FOR_CHILDREN = 8; + + public override bool Applies(StatRequest req) + { + if (req == null || req.Thing == null || !req.Thing.Spawned) return false; + + if (req.Thing is Pawn pawn) + { + // Do nothing if Pawn is Baby or Child (#25) + if (!pawn.ageTracker.Adult) + return false; + + if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_hardwired_progenity)) + { + return pawn.relations.ChildrenCount >= THRESHOLD_FOR_CHILDREN; + } + } + + return false; + } + } +} diff --git a/Source/Genes/Diseases/Defs/GeneticInfectorExtension.cs b/Source/Genes/Diseases/Defs/GeneticInfectorExtension.cs new file mode 100644 index 0000000..2f00ce5 --- /dev/null +++ b/Source/Genes/Diseases/Defs/GeneticInfectorExtension.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public class GeneticInfectorExtension : DefModExtension + { + public float infectionChance; + + public List infectionGenes; + } + +} diff --git a/Source/Genes/Diseases/Defs/ImmunityAgainstGenesExtension.cs b/Source/Genes/Diseases/Defs/ImmunityAgainstGenesExtension.cs new file mode 100644 index 0000000..18fe8e0 --- /dev/null +++ b/Source/Genes/Diseases/Defs/ImmunityAgainstGenesExtension.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public class ImmunityAgainstGenesExtension : DefModExtension + { + /// + /// A list of the exact defnames of disease-genes that this extension will make immune against. + /// Must perfectly match! + /// + public List givesImmunityAgainst; + } + +} diff --git a/Source/Genes/Diseases/DiseaseHelper.cs b/Source/Genes/Diseases/DiseaseHelper.cs new file mode 100644 index 0000000..873a0e1 --- /dev/null +++ b/Source/Genes/Diseases/DiseaseHelper.cs @@ -0,0 +1,145 @@ +using rjw; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public static class DiseaseHelper + { + + /// + /// Checks for a pawn if it is immune against a disease. + /// + /// The pawn for which immunity is checked + /// The genetic disease that is checked against + /// True if the pawn is immune, false if the pawn can be infected by it. + public static bool IsImmuneAgainstGeneticDisease(Pawn pawn, GeneDef disease) + { + // Case 1: Something is null / not working, return Immune (to have less follow up effects) + if (pawn == null || pawn.genes == null) return true; + if (disease == null) return true; + // Case 1.B: Dead people can spread, but not receive, diseases. + if (pawn.Dead) return true; + + // Case 2: The pawn has general genetic immunity to diseases + if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_genetic_disease_immunity)) + return true; + + // Case 3: The pawn already has the genetic disease + if (GeneUtility.HasGeneNullCheck(pawn, disease)) + return true; + + // Case 4: Check all genes if one of them has the Immunity Extension that covers the GeneDef + List genes = pawn.genes.GenesListForReading; + genes = genes.Where(x => pawn.genes.HasActiveGene(x.def)).ToList(); + + foreach (Gene gene in genes) + { + ImmunityAgainstGenesExtension ext = gene.def.GetModExtension(); + if (ext != null) { + foreach (string defname in ext.givesImmunityAgainst) + if (disease.defName == defname) + return true; + } + } + + // Case 5: Nothing special happens, so return false (not immune) + return false; + } + + /// + /// Returns all active Genes with the `GeneticDiseaseExtension`. + /// + /// + /// List of all active Genes with the `GeneticDiseaseExtension` in pawn + public static List GetGeneticDiseaseGenes(Pawn pawn) + { + if (pawn != null && pawn.genes != null) + { + return pawn.genes + .GenesListForReading + .ConvertAll(gene => gene.def) + .Where(genedef => pawn.genes.HasActiveGene(genedef)) + .Where(IsGeneticDiseaseGene) + .ToList(); + } + + return new List() { }; + } + + public static List GetGeneticInfectorGenes(Pawn pawn) + { + if (pawn != null && pawn.genes != null) + { + return pawn.genes + .GenesListForReading + .ConvertAll(gene => gene.def) + .Where(genedef => pawn.genes.HasActiveGene(genedef)) + .Where(IsGeneticInfectorGene) + .ToList(); + } + + return new List() { }; + } + + public static List LookupInfectionGeneDefs(GeneticInfectorExtension infectorExt) + { + if (infectorExt == null) new List(); + + return RimWorld.GeneUtility + .GenesInOrder + .Where(genedef => infectorExt.infectionGenes.Contains(genedef.defName)) + .ToList(); + } + + /// + /// Checks if the performed sex was penetrative. + /// Condom check is not done here! + /// + /// The sexprops + /// + public static bool IsPenetrativeSex(SexProps props) + { + if (props == null) return false; + + return props.sexType == + xxx.rjwSextype.Vaginal + || props.sexType == xxx.rjwSextype.Anal + || props.sexType == xxx.rjwSextype.Oral + || props.sexType == xxx.rjwSextype.DoublePenetration + || props.sexType == xxx.rjwSextype.Fellatio + || props.sexType == xxx.rjwSextype.Sixtynine; + } + + public static bool IsGeneticDiseaseGene(GeneDef geneDef) + { + if (geneDef == null) return false; + GeneticDiseaseExtension diseaseExt = geneDef.GetModExtension(); + return diseaseExt != null; + } + + public static bool IsGeneticInfectorGene(GeneDef geneDef) + { + if (geneDef == null) return false; + GeneticInfectorExtension infectorExt = geneDef.GetModExtension(); + return infectorExt != null; + } + + public static float LookupDiseaseInfectionChance(GeneDef geneDef) + { + if (IsGeneticDiseaseGene(geneDef)) + { + GeneticDiseaseExtension diseaseExt = geneDef.GetModExtension(); + return diseaseExt != null ? diseaseExt.infectionChance : 0.0f; + } + else + return 0.0f; + } + + + } +} diff --git a/Source/Genes/Diseases/Patches/Patch_AfterSexUtility_ApplyGeneticInfectors.cs b/Source/Genes/Diseases/Patches/Patch_AfterSexUtility_ApplyGeneticInfectors.cs new file mode 100644 index 0000000..1ee1e9f --- /dev/null +++ b/Source/Genes/Diseases/Patches/Patch_AfterSexUtility_ApplyGeneticInfectors.cs @@ -0,0 +1,57 @@ +using HarmonyLib; +using rjw; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Remoting.Lifetime; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + + [HarmonyPatch(typeof(SexUtility), "Aftersex")] + public class Patch_AfterSexUtility_ApplyGeneticInfectors + { + public static void Postfix(SexProps props) + { + if (props == null || props.pawn == null || props.partner == null) return; + + Pawn pawn = props.pawn; + Pawn partner = props.partner; + + if (pawn == partner) return; + if (pawn.IsAnimal() || partner.IsAnimal()) return; + if (pawn.genes == null || partner.genes == null) return; + // No Infections on Condom Use + if (props.usedCondom) return; + + // Exit early if settings require penetrative sex, but this is not penetrative sex + if (!DiseaseHelper.IsPenetrativeSex(props) && RJW_Genes_Settings.rjw_genes_genetic_disease_spread_only_on_penetrative_sex) return; + + TryApplyGeneticInfections(pawn, partner); + TryApplyGeneticInfections(partner, pawn); + } + + private static void TryApplyGeneticInfections(Pawn infector, Pawn partner) + { + foreach (GeneDef infectorGeneDef in DiseaseHelper.GetGeneticInfectorGenes(infector)) + { + GeneticInfectorExtension diseaseExt = infectorGeneDef.GetModExtension(); + if (diseaseExt == null) continue; + float application_chance = diseaseExt.infectionChance; + + foreach (GeneDef diseaseGeneDef in DiseaseHelper.LookupInfectionGeneDefs(diseaseExt)) + { + if (DiseaseHelper.IsImmuneAgainstGeneticDisease(partner, diseaseGeneDef)) + continue; + + if ((new Random()).NextDouble() < application_chance) + partner.genes.AddGene(diseaseGeneDef, !RJW_Genes_Settings.rjw_genes_genetic_disease_as_endogenes); + } + } + } + + } +} diff --git a/Source/Genes/Diseases/Patches/Patch_AftersexUtility_TransferGeneticDiseases.cs b/Source/Genes/Diseases/Patches/Patch_AftersexUtility_TransferGeneticDiseases.cs index 6bf8e88..e33079f 100644 --- a/Source/Genes/Diseases/Patches/Patch_AftersexUtility_TransferGeneticDiseases.cs +++ b/Source/Genes/Diseases/Patches/Patch_AftersexUtility_TransferGeneticDiseases.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading.Tasks; using Verse; -namespace RJW_Genes.Genes.Diseases.Patches +namespace RJW_Genes { [HarmonyPatch(typeof(SexUtility), "Aftersex")] public class Patch_AftersexUtility_TransferGeneticDiseases @@ -29,83 +29,28 @@ namespace RJW_Genes.Genes.Diseases.Patches if (props.usedCondom) return; // Exit early if settings require penetrative sex, but this is not penetrative sex - if (!IsPenetrativeSex(props) && RJW_Genes_Settings.rjw_genes_genetic_disease_spread_only_on_penetrative_sex) return; + if (!DiseaseHelper.IsPenetrativeSex(props) && RJW_Genes_Settings.rjw_genes_genetic_disease_spread_only_on_penetrative_sex) return; - ModLog.Debug($"Firing Patch_TransferGeneticDiseases for {pawn} and {partner}"); + //ModLog.Debug($"Firing Patch_TransferGeneticDiseases for {pawn} and {partner}"); TryTransferGeneticDiseases(pawn, partner, props); TryTransferGeneticDiseases(partner, pawn, props); } private static void TryTransferGeneticDiseases(Pawn infector, Pawn infected, SexProps props) { - if (infected.genes.HasActiveGene(GeneDefOf.rjw_genes_genetic_disease_immunity)) - { - ModLog.Debug($"{infected} is immune to genetic diseases"); - return; - } - if (infected.Dead) - { - // Dead people can spread, but not receive, diseases. - return; - } - - foreach (GeneDef disease in GetGeneticDiseaseGenes(infector)) { + + foreach (GeneDef disease in DiseaseHelper.GetGeneticDiseaseGenes(infector)) { ModLog.Debug($"Found genetic disease {disease} in {infector}, trying to infect {infected}"); - if (infected.genes.HasActiveGene(disease)) + if (DiseaseHelper.IsImmuneAgainstGeneticDisease(infected,disease)) continue; - if ((new Random()).NextDouble() <= LookupDiseaseInfectionChance(disease)) + if ((new Random()).NextDouble() <= DiseaseHelper.LookupDiseaseInfectionChance(disease)) { infected.genes.AddGene(disease, !RJW_Genes_Settings.rjw_genes_genetic_disease_as_endogenes); } } } - private static List GetGeneticDiseaseGenes(Pawn pawn) - { - if (pawn != null && pawn.genes != null) - { - return pawn.genes - .GenesListForReading - .ConvertAll(gene => gene.def) - .Where(genedef => pawn.genes.HasActiveGene(genedef)) - .Where(IsGeneticDiseaseGene) - .ToList(); - } - - return new List() { }; - } - - private static bool IsPenetrativeSex(SexProps props) - { - if (props == null) return false; - - return props.sexType == - xxx.rjwSextype.Vaginal - || props.sexType == xxx.rjwSextype.Anal - || props.sexType == xxx.rjwSextype.Oral - || props.sexType == xxx.rjwSextype.DoublePenetration - || props.sexType == xxx.rjwSextype.Fellatio - || props.sexType == xxx.rjwSextype.Sixtynine; - } - - private static bool IsGeneticDiseaseGene(GeneDef geneDef) - { - if (geneDef == null) return false; - GeneticDiseaseExtension diseaseExt = geneDef.GetModExtension(); - return diseaseExt != null; - } - - private static float LookupDiseaseInfectionChance(GeneDef geneDef) - { - if (IsGeneticDiseaseGene(geneDef)) - { - GeneticDiseaseExtension diseaseExt = geneDef.GetModExtension(); - return diseaseExt != null ? diseaseExt.infectionChance : 0.0f; - } - else - return 0.0f; - } } } diff --git a/Source/Genes/Diseases/Thoughts/ThoughtWorker_HasMoreDiseasesThanMe_Social.cs b/Source/Genes/Diseases/Thoughts/ThoughtWorker_HasMoreDiseasesThanMe_Social.cs new file mode 100644 index 0000000..7a85eb3 --- /dev/null +++ b/Source/Genes/Diseases/Thoughts/ThoughtWorker_HasMoreDiseasesThanMe_Social.cs @@ -0,0 +1,59 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + public class ThoughtWorker_HasMoreDiseasesThanMe_Social : ThoughtWorker + { + protected override ThoughtState CurrentSocialStateInternal(Pawn pawn, Pawn other) + { + // Return for trivial errors + if (pawn == null || other == null || pawn == other) + return (ThoughtState)false; + // Check for position-existance + if (pawn.Position == null || other.Position == null || pawn.Map == null || other.Map == null) + return (ThoughtState)false; + // Do nothing if pawn is carried + if (pawn.CarriedBy != null) + return (ThoughtState)false; + // Do nothing if Pawn is Baby or Child (#25) + if (!pawn.ageTracker.Adult) + return (ThoughtState)false; + if (!other.ageTracker.Adult) + return (ThoughtState)false; + // Only check if they are spawned humans + if (!pawn.Spawned || !other.Spawned) + return (ThoughtState)false; + if (!pawn.RaceProps.Humanlike) + return (ThoughtState)false; + if (!other.RaceProps.Humanlike) + return (ThoughtState)false; + + // Pawns that have not "met" wont give each other Mali + // Known-Each-Other is a key-word for Rimworld that shows they have had any interaction and stored each other in relations. + if (!RelationsUtility.PawnsKnowEachOther(pawn, other)) + return (ThoughtState)false; + // If the pawn is not on Map (e.g. caravan), no mali + if (!MapUtility.PawnIsOnHomeMap(pawn)) + return (ThoughtState)false; + + int pawn_diseases = DiseaseHelper.GetGeneticDiseaseGenes(pawn).Count(); + int other_diseases = DiseaseHelper.GetGeneticDiseaseGenes(other).Count(); + int disease_diff = other_diseases - pawn_diseases; + + if (disease_diff >= 5) + return ThoughtState.ActiveAtStage(2); + else if (disease_diff >= 2) + return ThoughtState.ActiveAtStage(1); + else if (disease_diff >= 1) + return ThoughtState.ActiveAtStage(0); + else + return (ThoughtState)false; + } + } +} diff --git a/Source/Genes/Diseases/Thoughts/ThoughtWorker_SizeBlinded_Social.cs b/Source/Genes/Diseases/Thoughts/ThoughtWorker_SizeBlinded_Social.cs index 871b276..61199a6 100644 --- a/Source/Genes/Diseases/Thoughts/ThoughtWorker_SizeBlinded_Social.cs +++ b/Source/Genes/Diseases/Thoughts/ThoughtWorker_SizeBlinded_Social.cs @@ -44,7 +44,6 @@ namespace RJW_Genes if (!MapUtility.PawnIsOnHomeMap(pawn)) return (ThoughtState)false; - //ModLog.Debug($"ThoughtWorker Checks Size Blinded {pawn} -> {other}"); // Do nothing if there is no size-blinded involved if (!GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_size_blinded)) diff --git a/Source/Genes/Special/Patches/Patch_GeneticSexSwap.cs b/Source/Genes/Special/Patches/Patch_GeneticSexSwap.cs new file mode 100644 index 0000000..377b71d --- /dev/null +++ b/Source/Genes/Special/Patches/Patch_GeneticSexSwap.cs @@ -0,0 +1,69 @@ +using HarmonyLib; +using rjw; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + + [HarmonyPatch(typeof(SexUtility), "Aftersex")] + public class Patch_GeneticSexSwap + { + public static void Postfix(SexProps props) + { + if (props == null || props.pawn == null || props.partner == null || props.partner.IsAnimal()) + { + return; + } + + Pawn pawn = props.pawn; + Pawn partner = props.partner; + + if (pawn.genes == null || partner.genes == null) return; + + // If both have the swap gene, nothing happens + if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_sexual_genetic_swap) + && GeneUtility.HasGeneNullCheck(partner, GeneDefOf.rjw_genes_sexual_genetic_swap)) + return; + // If neither has the swap gene, nothing happens + if (!GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_sexual_genetic_swap) + && !GeneUtility.HasGeneNullCheck(partner, GeneDefOf.rjw_genes_sexual_genetic_swap)) + return; + + ChanceExtension chanceExt = GeneDefOf.rjw_genes_sexual_genetic_swap.GetModExtension(); + if (chanceExt != null && (new Random()).NextDouble() < chanceExt.chance) + SwapOneRandomGene(pawn, partner); + } + + /// + /// Removes a random gene from one pawn and adds it too the other as xenogene. + /// The "gene swap" gene cannot be swapped! + /// + private static void SwapOneRandomGene(Pawn a, Pawn b, bool AddAsXenogene = true) + { + + var geneFromA = a.genes.GenesListForReading + .Where(gene => a.genes.HasActiveGene(gene.def)) + .Where(gene => gene.def != GeneDefOf.rjw_genes_sexual_genetic_swap) + .RandomElement(); + var geneFromB = b.genes.GenesListForReading + .Where(gene => b.genes.HasActiveGene(gene.def)) + .Where(gene => gene.def != GeneDefOf.rjw_genes_sexual_genetic_swap) + .RandomElement(); + + if (geneFromA == null || geneFromB == null) return; + + ModLog.Debug($"Sexual Genetic Swap: Swapping {geneFromA.def} from {a} with {geneFromB.def} from {b}"); + + a.genes.AddGene(geneFromB.def, AddAsXenogene); + b.genes.AddGene(geneFromA.def, AddAsXenogene); + a.genes.RemoveGene(geneFromA); + b.genes.RemoveGene(geneFromB); + } + + } +} diff --git a/Source/Genes/Special/Patches/Patch_GeneticSexThief.cs b/Source/Genes/Special/Patches/Patch_GeneticSexThief.cs new file mode 100644 index 0000000..ecc2201 --- /dev/null +++ b/Source/Genes/Special/Patches/Patch_GeneticSexThief.cs @@ -0,0 +1,68 @@ +using HarmonyLib; +using rjw; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace RJW_Genes +{ + + [HarmonyPatch(typeof(SexUtility), "Aftersex")] + public class Patch_GeneticSexThief + { + public static void Postfix(SexProps props) + { + if (props == null || props.pawn == null || props.partner == null || props.partner.IsAnimal()) + { + return; + } + + Pawn pawn = props.pawn; + Pawn partner = props.partner; + + if (pawn.genes == null || partner.genes == null) return; + + // If both have the swap gene, nothing happens + if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_sexual_genetic_thief) + && GeneUtility.HasGeneNullCheck(partner, GeneDefOf.rjw_genes_sexual_genetic_thief)) + return; + + if (GeneUtility.HasGeneNullCheck(pawn,GeneDefOf.rjw_genes_sexual_genetic_thief) && + !GeneUtility.HasGeneNullCheck(partner, GeneDefOf.rjw_genes_genetic_disease_immunity)) + { + ChanceExtension chanceExt = GeneDefOf.rjw_genes_sexual_genetic_thief.GetModExtension(); + if (chanceExt != null && (new Random()).NextDouble() < chanceExt.chance) + StealRandomGene(pawn, partner); + } + + if (GeneUtility.HasGeneNullCheck(partner, GeneDefOf.rjw_genes_sexual_genetic_thief) && + !GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_genetic_disease_immunity)) + { + ChanceExtension chanceExt = GeneDefOf.rjw_genes_sexual_genetic_thief.GetModExtension(); + if (chanceExt != null && (new Random()).NextDouble() < chanceExt.chance) + StealRandomGene(partner, pawn); + } + } + + /// + /// Removes a random gene from one pawn and adds it too the other as xenogene. + /// + private static void StealRandomGene(Pawn stealer, Pawn victim, bool AddAsXenogene = true) + { + var stolenGene = victim.genes.GenesListForReading + .Where(gene => victim.genes.HasActiveGene(gene.def)) + .RandomElement(); + + if (stolenGene == null) return; + + ModLog.Debug($"Sexual Gene Thief: {stealer} steals {stolenGene.def} from {victim}"); + + stealer.genes.AddGene(stolenGene.def, AddAsXenogene); + victim.genes.RemoveGene(stolenGene); + } + + } +} diff --git a/Source/Genes/Special/Patches/Patch_HormonalSaliva.cs b/Source/Genes/Special/Patches/Patch_HormonalSaliva.cs index 150eeec..fed69d7 100644 --- a/Source/Genes/Special/Patches/Patch_HormonalSaliva.cs +++ b/Source/Genes/Special/Patches/Patch_HormonalSaliva.cs @@ -12,7 +12,6 @@ namespace RJW_Genes [HarmonyPatch(typeof(SexUtility), "Aftersex")] public class Patch_HormonalSaliva { - // TODO: Reduce to 0.02 after debug. const float SIZE_INCREMENT_FALLBACK = 0.02f; const float MAX_BODY_SIZE_FALLBACK = 2.5f; const float CUM_MULTIPLIER_FALLBACK = 1.05f; diff --git a/Source/Rjw-Genes.csproj b/Source/Rjw-Genes.csproj index 51b80a1..306bcb7 100644 --- a/Source/Rjw-Genes.csproj +++ b/Source/Rjw-Genes.csproj @@ -56,6 +56,7 @@ + @@ -71,14 +72,22 @@ + + + + + + + + @@ -180,6 +189,8 @@ + + diff --git a/Source/ThoughtDefOf.cs b/Source/ThoughtDefOf.cs index 50f2a00..596ecc0 100644 --- a/Source/ThoughtDefOf.cs +++ b/Source/ThoughtDefOf.cs @@ -16,6 +16,7 @@ namespace RJW_Genes public static readonly ThoughtDef rjw_genes_pheromone_carrier_nearby; public static readonly ThoughtDef rjw_genes_appealing_cock; + public static readonly ThoughtDef rjw_genes_has_more_diseases; //Others with same names but other defs than in genedefof public static readonly InteractionDef rjw_genes_flirt;