1.5 update

This commit is contained in:
Jaaldabaoth 2024-05-24 00:33:37 +02:00
parent 7d9e399a47
commit e6db43f31d
231 changed files with 95 additions and 6968 deletions

View file

@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse.Sound;
using Verse;
using RimWorld;
using rjw;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Enums;
namespace RJW_Genes
{
public class AbilityUtility
{
public static void PussyHeal(SexProps props)
{
if (InteractionHelper.GetWithExtension(props.dictionaryKey).DominantHasFamily(GenitalFamily.Vagina) || InteractionHelper.GetWithExtension(props.dictionaryKey).SubmissiveHasFamily(GenitalFamily.Vagina))
{
Pawn pawn = props.pawn;
Pawn partner = props.partner;
FloatRange tendQualityRange;
tendQualityRange.min = 0.4f;
tendQualityRange.max = 0.8f;
if (GeneUtility.IsPussyHealer(pawn))
{
Heal(partner, tendQualityRange);
}
if (GeneUtility.IsPussyHealer(partner))
{
Heal(pawn, tendQualityRange);
}
}
}
public static bool Heal(Pawn pawn, FloatRange tendQualityRange)
{
bool any_wound_tended = false;
List<Hediff> hediffs = pawn.health.hediffSet.hediffs;
for (int i = hediffs.Count - 1; i >= 0; i--)
{
if ((hediffs[i] is Hediff_Injury || hediffs[i] is Hediff_MissingPart) && hediffs[i].TendableNow(false))
{
hediffs[i].Tended(tendQualityRange.RandomInRange, tendQualityRange.TrueMax, 1);
any_wound_tended = true;
}
}
return any_wound_tended;
}
public static float LifeForceCost(Ability ability)
{
if (ability.comps != null)
{
using (List<AbilityComp>.Enumerator enumerator = ability.comps.GetEnumerator())
{
while (enumerator.MoveNext())
{
CompAbilityEffect_LifeForceCost compAbilityEffect_HemogenCost;
if ((compAbilityEffect_HemogenCost = (enumerator.Current as CompAbilityEffect_LifeForceCost)) != null)
{
return compAbilityEffect_HemogenCost.Props.fertilinCost;
}
}
}
}
return 0f;
}
}
}

View file

@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
//Summary//
//Returns invalid if a pawn is not naked
//Summary//
public class CompAbilityEffect_CasterIsNaked : CompAbilityEffect_WithDest
{
private new CompProperties_CasterIsNaked Props
{
get
{
return (CompProperties_CasterIsNaked)this.props;
}
}
public override bool GizmoDisabled(out string reason)
{
Pawn pawn = this.CasterPawn;
if (pawn != null)
{
//Copied from ThoughtWorker_NudistNude.CurrentStateInternal
List<Apparel> wornApparel = pawn.apparel.WornApparel;
for (int i = 0; i < wornApparel.Count; i++)
{
Apparel apparel = wornApparel[i];
if (apparel.def.apparel.countsAsClothingForNudity)
{
for (int j = 0; j < apparel.def.apparel.bodyPartGroups.Count; j++)
{
if (apparel.def.apparel.bodyPartGroups[j] == BodyPartGroupDefOf.Torso)
{
reason = pawn.Name + " is not naked";
return true;
}
if (apparel.def.apparel.bodyPartGroups[j] == BodyPartGroupDefOf.Legs)
{
reason = pawn.Name + " is not naked";
return true;
}
}
}
}
}
reason = null;
return false;
}
}
}

View file

@ -1,121 +0,0 @@
using Verse;
using RimWorld;
using rjw;
namespace RJW_Genes
{
/// <summary>
/// The CockEater Ability bites off the first found non-artifical cock of an target pawn.
/// It will restore {MINIMUM_LIFEFORCE_GAIN} multiplied by up to 2-times the Cock-Size.
/// Consuming a "towering" cock will give 2*{MINIMUM_LIFEFORCE_GAIN}, resulting in default 0.5f LifeForce.
/// This number is reduced for consuming animals by Settings.
///
/// Balancing note: With the Cock-Eaters a drain of 0.08 is normal per day. This means 1 average cock should hold for 3-4 days of fertilin-fuel and half a day for an animal.
/// </summary>
public class CompAbilityEffect_CockEater : CompAbilityEffect
{
private new CompProperties_AbilityCockEater Props
{
get
{
return (CompProperties_AbilityCockEater)this.props;
}
}
public const float MINIMUM_LIFEFORCE_GAIN = 0.25f;
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn CockBiter = this.parent.pawn;
Pawn CockBittenPawn = target.Pawn;
if (CockBittenPawn == null)
{
return;
}
var partBPR = Genital_Helper.get_genitalsBPR(CockBittenPawn);
var parts = Genital_Helper.get_PartsHediffList(CockBittenPawn, partBPR);
if (!parts.NullOrEmpty())
{
foreach (Hediff part in parts)
{
if (GenitaliaChanger.IsArtificial(part))
continue;
if (Genital_Helper.is_penis(part))
{
float gained_lifeforce = MINIMUM_LIFEFORCE_GAIN * (1 + part.Severity);
if (CockBittenPawn.IsAnimal())
{
gained_lifeforce *= RJW_Genes_Settings.rjw_genes_fertilin_from_animals_factor;
}
// Increase LifeForce for Biter
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(CockBiter), gained_lifeforce);
// Handle Damage for Bitten
CockBittenPawn.TakeDamage(new DamageInfo(DamageDefOf.Bite, 99999f, 999f, hitPart: Genital_Helper.get_genitalsBPR(CockBittenPawn)));
//CockBittenPawn.health.RemoveHediff(part);
CockBittenPawn.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.rjw_genes_cock_eaten, CockBittenPawn, null);
//Only one penis at the time
break;
}
}
}
}
/// <summary>
/// For validity, there are a few checks:
/// 1. Target has Penis
/// 2. Target is either Colonist / Prisoner
/// 3. If the Target is an enemy, it must be downed.
/// </summary>
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
Pawn CockBiteTarget = target.Pawn;
if (CockBiteTarget != null)
{
bool CockBiteTargetIsColonistOrPrisoner = CockBiteTarget.Faction == this.parent.pawn.Faction || CockBiteTarget.IsPrisonerOfColony;
bool CockBiteTargetIsHostile = CockBiteTarget.HostileTo(this.parent.pawn);
bool CockBiteTargetIsDowned = CockBiteTarget.Downed;
if (!CockBiteTargetIsColonistOrPrisoner && !(CockBiteTargetIsHostile && CockBiteTargetIsDowned))
{
if (throwMessages)
{
if(CockBiteTargetIsHostile && !CockBiteTargetIsDowned)
{
Messages.Message(CockBiteTarget.Name + " is hostile, but not downed.", CockBiteTarget, MessageTypeDefOf.RejectInput, false);
}
else if (!CockBiteTargetIsColonistOrPrisoner)
{
Messages.Message(CockBiteTarget.Name + " is not a part of the colony or hostile.", CockBiteTarget, MessageTypeDefOf.RejectInput, false);
}
}
return false;
}
if (!Genital_Helper.has_penis_fertile(CockBiteTarget))
{
if (throwMessages)
{
Messages.Message(CockBiteTarget.Name + " has no penis", CockBiteTarget, MessageTypeDefOf.RejectInput, false);
}
return false;
}
}
return base.Valid(target, throwMessages);
}
public override bool GizmoDisabled(out string reason)
{
Pawn_GeneTracker genes = this.parent.pawn.genes;
Gene_LifeForce gene_LifeForce = (genes != null) ? genes.GetFirstGeneOfType<Gene_LifeForce>() : null;
if (gene_LifeForce == null)
{
reason = "AbilityDisabledNoFertilinGene".Translate(this.parent.pawn);
return true;
}
reason = null;
return false;
}
}
}

View file

@ -1,107 +0,0 @@
using Verse;
using Verse.AI;
using RimWorld;
namespace RJW_Genes
{
public class CompAbilityEffect_LifeForceCost : CompAbilityEffect
{
public new CompProperties_AbilityLifeForceCost Props
{
get
{
return (CompProperties_AbilityLifeForceCost)this.props;
}
}
private bool HasEnoughFertilin
{
get
{
Pawn_GeneTracker genes = this.parent.pawn.genes;
Gene_LifeForce gene_lifeforce = (genes != null) ? genes.GetFirstGeneOfType < Gene_LifeForce>() : null;
return gene_lifeforce != null && gene_lifeforce.Value >= this.Props.fertilinCost;
}
}
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(this.parent.pawn), -this.Props.fertilinCost);
}
public override bool GizmoDisabled(out string reason)
{
Pawn_GeneTracker genes = this.parent.pawn.genes;
Gene_LifeForce gene_LifeForce = (genes != null) ? genes.GetFirstGeneOfType<Gene_LifeForce>() : null;
if (gene_LifeForce == null)
{
reason = "AbilityDisabledNoFertilinGene".Translate(this.parent.pawn);
return true;
}
if (gene_LifeForce.Value < this.Props.fertilinCost)
{
reason = "AbilityDisabledNoFertilin".Translate(this.parent.pawn);
return true;
}
float num = this.TotalLifeForceCostOfQueuedAbilities();
float num2 = this.Props.fertilinCost + num;
if (this.Props.fertilinCost > 1E-45f && num2 > gene_LifeForce.Value)
{
reason = "AbilityDisabledNoFertilin".Translate(this.parent.pawn);
return true;
}
reason = null;
return false;
}
public override bool AICanTargetNow(LocalTargetInfo target)
{
return this.HasEnoughFertilin;
}
private float TotalLifeForceCostOfQueuedAbilities()
{
Pawn_JobTracker jobs = this.parent.pawn.jobs;
object obj;
if (jobs == null)
{
obj = null;
}
else
{
Job curJob = jobs.curJob;
obj = ((curJob != null) ? curJob.verbToUse : null);
}
Verb_CastAbility verb_CastAbility = obj as Verb_CastAbility;
float num;
if (verb_CastAbility == null)
{
num = 0f;
}
else
{
Ability ability = verb_CastAbility.ability;
num = ((ability != null) ? AbilityUtility.LifeForceCost(ability) : 0f);
}
float num2 = num;
if (this.parent.pawn.jobs != null)
{
for (int i = 0; i < this.parent.pawn.jobs.jobQueue.Count; i++)
{
Verb_CastAbility verb_CastAbility2;
if ((verb_CastAbility2 = (this.parent.pawn.jobs.jobQueue[i].job.verbToUse as Verb_CastAbility)) != null)
{
float num3 = num2;
Ability ability2 = verb_CastAbility2.ability;
num2 = num3 + ((ability2 != null) ? AbilityUtility.LifeForceCost(ability2) : 0f);
}
}
}
return num2;
}
}
}

View file

@ -1,95 +0,0 @@
using System.Collections.Generic;
using Verse;
using RimWorld;
using rjw;
namespace RJW_Genes
{
public class CompAbilityEffect_PussyHeal : CompAbilityEffect
{
private new CompProperties_AbilityPussyHeal Props
{
get
{
return (CompProperties_AbilityPussyHeal)this.props;
}
}
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn pawn = target.Pawn;
if (pawn == null)
{
return;
}
bool any_wound_tended = AbilityUtility.Heal(pawn, this.Props.tendQualityRange);
if (any_wound_tended)
{
MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, "Sex tended wounds", 3.65f);
}
}
//Not yet implemented, but the heal should also trigger after normal sex
public void AfterSex(Pawn pawn, Pawn target)
{
List<Hediff> hediffs = target.health.hediffSet.hediffs;
for (int i = 0; i < hediffs.Count; i++)
{
if ((hediffs[i] is Hediff_Injury || hediffs[i] is Hediff_MissingPart) && hediffs[i].TendableNow(false))
{
//target.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.Pussy_Healed, pawn, null);
break;
}
}
//InteractionHelper.GetWithExtension(dictionaryKey).DominantHasTag("CanBePenetrated")
}
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
Pawn pawn = target.Pawn;
if (pawn != null)
{
//to be replaced with severel checks to make it clear why target is unable to have sex
if (!CasualSex_Helper.CanHaveSex(pawn))
{
if (throwMessages)
{
Messages.Message(pawn.Name + " is unable to have sex", pawn, MessageTypeDefOf.RejectInput, false);
}
return false;
}
else if (pawn.IsAnimal() && !RJWSettings.bestiality_enabled)
{
if (throwMessages)
{
Messages.Message("bestiality is disabled", pawn, MessageTypeDefOf.RejectInput, false);
}
return false;
}
//TODO: Only make pawns targetable that have tendable wounds
}
return base.Valid(target, throwMessages);
}
public override bool GizmoDisabled(out string reason)
{
reason = null;
if (!Genital_Helper.has_vagina(this.parent.pawn))
{
reason = this.parent.pawn.Name + " has no vagina to use.";
return true;
}
else if (!RJWSettings.rape_enabled)
{
reason = "Rape is disabled";
return true;
}
return false;
}
}
}

View file

@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
public class CompAbilityEffect_Seduce : CompAbilityEffect_WithDest
{
private new CompProperties_Seduce Props
{
get
{
return (CompProperties_Seduce)this.props;
}
}
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Pawn pawn = target.Thing as Pawn;
Pawn pawn2 = this.parent.pawn;
if (pawn != null && pawn2 != null && !pawn.Downed)
{
Job job = JobMaker.MakeJob(JobDefOf.rjw_genes_lifeforce_seduced, pawn2);
job.mote = MoteMaker.MakeThoughtBubble(pawn, this.parent.def.iconPath, true);
pawn.jobs.StopAll(false, true);
pawn.jobs.StartJob(job, JobCondition.InterruptForced, null, false, true, null, null, false, false, null, false, true);
}
}
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
Pawn pawn = target.Pawn;
if (pawn != null)
{
if (!xxx.can_be_fucked(pawn))
{
if (throwMessages)
{
Messages.Message(pawn.Name + " is unable to have sex", pawn, MessageTypeDefOf.RejectInput, false);
}
return false;
}
else if (pawn.IsAnimal() && !RJWSettings.bestiality_enabled)
{
if (throwMessages)
{
Messages.Message("bestiality is disabled", pawn, MessageTypeDefOf.RejectInput, false);
}
return false;
}
else if (GeneUtility.HasSeduce(pawn))
{
if (throwMessages)
{
Messages.Message(pawn.Name + " cannot be seduced, as they also have the Seduce-Ability", pawn, MessageTypeDefOf.RejectInput, false);
}
return false;
}
else if (pawn.Downed)
{
if (throwMessages)
{
Messages.Message(pawn.Name + " is unable to move", pawn, MessageTypeDefOf.RejectInput, false);
}
return false;
}
}
return base.Valid(target, throwMessages);
}
public override bool GizmoDisabled(out string reason)
{
reason = null;
if (!RJWSettings.rape_enabled)
{
reason = "Rape is disabled";
return true;
}
return false;
}
}
}

View file

@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class CompProperties_AbilityCockEater : CompProperties_AbilityEffect
{
public CompProperties_AbilityCockEater()
{
this.compClass = typeof(CompAbilityEffect_CockEater);
}
}
}

View file

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using RimWorld;
namespace RJW_Genes
{
// Token: 0x02000F65 RID: 3941
public class CompProperties_AbilityLifeForceCost : CompProperties_AbilityEffect
{
// Token: 0x06005D16 RID: 23830 RVA: 0x001FA73F File Offset: 0x001F893F
public CompProperties_AbilityLifeForceCost()
{
this.compClass = typeof(CompAbilityEffect_LifeForceCost);
}
// Token: 0x06005D17 RID: 23831 RVA: 0x001FA757 File Offset: 0x001F8957
public override IEnumerable<string> ExtraStatSummary()
{
yield return "AbilityFertilinCost" + ": " + Mathf.RoundToInt(this.fertilinCost * 100f);
yield break;
}
// Token: 0x040038CD RID: 14541
public float fertilinCost;
}
}

View file

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class CompProperties_AbilityPussyHeal : CompProperties_AbilityEffect
{
public CompProperties_AbilityPussyHeal()
{
this.compClass = typeof(CompAbilityEffect_PussyHeal);
}
public FloatRange tendQualityRange;
}
}

View file

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class CompProperties_CasterIsNaked : CompProperties_EffectWithDest
{
public CompProperties_CasterIsNaked()
{
this.compClass = typeof(CompAbilityEffect_CasterIsNaked);
}
}
}

View file

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class CompProperties_Seduce : CompProperties_EffectWithDest
{
public CompProperties_Seduce()
{
this.compClass = typeof(CompAbilityEffect_Seduce);
}
public StatDef durationMultiplier;
}
}

View file

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace RJW_Genes
{
public class LifeForceEmpathExtension : DefModExtension
{
public float aheagoIncrement;
public float satisfactionIncrement;
public float frustratedDecrement;
}
}

View file

@ -1,135 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
using Verse.AI.Group;
using RimWorld;
using rjw;
namespace RJW_Genes
{
public class IncidentWorker_SuccubusDreamVisit : IncidentWorker
{
//This incidint will only fire if there is a pawn asleep and sexneed is lower than 0.25
protected override bool CanFireNowSub(IncidentParms parms)
{
if (!base.CanFireNowSub(parms))
{
return false;
}
Map map = (Map)parms.target;
if (!map.mapTemperature.SeasonAcceptableFor(ThingDefOf.Human))
{
return false;
}
if (!RJW_Genes_Settings.rjw_genes_sexdemon_visit_incubi && !RJW_Genes_Settings.rjw_genes_sexdemon_visit_succubi)
{
return false;
}
foreach (Pawn pawn in map.mapPawns.FreeColonistsAndPrisonersSpawned)
{
if (pawn.jobs.curDriver.asleep && xxx.need_some_sex(pawn) > 1f)
{
return true;
}
}
return false;
}
protected override bool TryExecuteWorker(IncidentParms parms)
{
Map map = (Map)parms.target;
List < Pawn > victims = ValidVictims(map).ToList();
if(victims.NullOrEmpty())
{
return false;
}
Faction faction;
if (!this.TryFindFormerFaction(out faction))
{
return false;
}
int pawn_amount = RJW_Genes_Settings.rjw_genes_sexdemon_visit_groups ? Rand.Range(1, victims.Count) : 1;
List<Pawn> new_sexdemons = new List<Pawn>();
for (int i = 0; i < pawn_amount; i++)
{
Pawn victim = victims.RandomElement();
IntVec3 loc = victim.Position;
PawnKindDef pawnKindDef;
Gender gender;
if (victim.gender == Gender.Male || !RJW_Genes_Settings.rjw_genes_sexdemon_visit_incubi)
{
}
if ((Rand.Bool && RJW_Genes_Settings.rjw_genes_sexdemon_visit_succubi) || !RJW_Genes_Settings.rjw_genes_sexdemon_visit_incubi)
{
pawnKindDef = PawnKindDef.Named("rjw_genes_succubus");
gender = Gender.Female;
}
else
{
pawnKindDef = PawnKindDef.Named("rjw_genes_incubus");
gender = Gender.Male;
}
//Spawn succubus at pawn
Pawn sexdemon = PawnGenerator.GeneratePawn(new PawnGenerationRequest(pawnKindDef, faction, PawnGenerationContext.NonPlayer, -1,
false, false, false, true, false, 1f, false, true, false, true, true, false, false, false, false, 0f, 0f, null, 1f, null, null,
null, null, null, null, null, gender, null, null, null, null, false, false, false, false, null, null, null, null, null, 0f,
DevelopmentalStage.Adult, null, null, null, false));
sexdemon.SetFaction(null, null);
GenSpawn.Spawn(sexdemon, loc, map, WipeMode.Vanish);
List<Pawn> sexdemons = new List<Pawn> { sexdemon };
new_sexdemons.Add(sexdemon);
LordMaker.MakeNewLord(Faction.OfPlayer, this.CreateLordJob(parms, sexdemon, victim), map, sexdemons);
//Make succubus rape victim.
if (RJWSettings.rape_enabled)
{
//follow rjw rules
if (SexAppraiser.would_fuck(sexdemon,victim) > 0f)
{
sexdemon.pather.StopDead();
sexdemon.jobs.StopAll();
Job newJob = JobMaker.MakeJob(xxx.RapeRandom, victim);
sexdemon.jobs.StartJob(newJob, JobCondition.InterruptForced, null, false, true, null, null, false, false, null, false, true);
}
}
}
Find.LetterStack.ReceiveLetter("rjw_genes_sexdemon_visit_incident_label".Translate(), "rjw_genes_sexdemon_visit_incident_description".Translate(), LetterDefOf.PositiveEvent, new_sexdemons, null, null, null, null);
//base.SendStandardLetter(baseLetterLabel, baseLetterText, this.def.letterDef, parms, succubus, Array.Empty<NamedArgument>());
return true;
}
private IEnumerable<Pawn> ValidVictims(Map map)
{
foreach (Pawn pawn in map.mapPawns.FreeColonistsAndPrisonersSpawned)
{
if (pawn.jobs.curDriver.asleep && xxx.need_some_sex(pawn) > 1f)
{
yield return pawn;
}
}
yield break;
}
private bool TryFindFormerFaction(out Faction formerFaction)
{
return Find.FactionManager.TryGetRandomNonColonyHumanlikeFaction(out formerFaction, false, true, TechLevel.Undefined, false);
}
protected virtual LordJob_SuccubusVisit CreateLordJob(IncidentParms parms, Pawn succubus, Pawn target)
{
return new LordJob_SuccubusVisit(target);
}
}
}

View file

@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using Verse;
using Verse.AI.Group;
using RimWorld;
using rjw;
namespace RJW_Genes
{
//Based on LordJob_VisitColony
public class LordJob_SuccubusVisit : LordJob
{
public LordJob_SuccubusVisit(){}
public LordJob_SuccubusVisit(Pawn target)
{
this.target = target;
}
//
//Stategraph has lordtoils which say what a pawn should be doing
//Transitions say when active lordtoil for pawn should change
//
public override StateGraph CreateGraph()
{
StateGraph stateGraph = new StateGraph();
//Flirt
LordToil_Flirt lordToil_Flirt = new LordToil_Flirt(this.target, 7f);
stateGraph.AddToil(lordToil_Flirt);
stateGraph.StartingToil = lordToil_Flirt;
//Leave
LordToil_ExitMapRandom lordToil_ExitMapRandom = new LordToil_ExitMapRandom();
stateGraph.AddToil(lordToil_ExitMapRandom);
LordToil_ExitMapAndDefendSelf lordToil_ExitMapAndDefendSelf = new LordToil_ExitMapAndDefendSelf();
stateGraph.AddToil(lordToil_ExitMapAndDefendSelf);
//Leave after some time
Transition transition1 = new Transition(lordToil_Flirt, lordToil_ExitMapRandom, false, true);
int tickLimit;
if (this.durationTicks != null)
{
tickLimit = this.durationTicks.Value;
}
else
{
tickLimit = Rand.Range(60000, 180000); //~1-3 days
}
transition1.AddTrigger(new Trigger_TicksPassed(tickLimit));
transition1.AddPreAction(new TransitionAction_Custom(new Action(this.SuccubiLeave))); //Join or leave colony
stateGraph.AddTransition(transition1);
//If they become hostile
Transition transition3 = new Transition(lordToil_Flirt, lordToil_ExitMapAndDefendSelf, false, true);
transition3.AddSource(lordToil_ExitMapRandom); //Not sure what this does
transition3.AddTrigger(new Trigger_BecamePlayerEnemy());
transition3.AddTrigger(new Trigger_PawnKilled());
transition3.AddPostAction(new TransitionAction_EndAllJobs());
stateGraph.AddTransition(transition3, false);
Transition transition4 = new Transition(lordToil_ExitMapRandom, lordToil_ExitMapAndDefendSelf, false, true);
transition4.AddSource(lordToil_Flirt); //Not sure what this does
transition4.AddTrigger(new Trigger_PawnHarmed(1f, true, Faction.OfPlayer));
stateGraph.AddTransition(transition4, false);
return stateGraph;
}
//add toggleable gizmo to allow playes to have colonists sex the succubus into joining your colony
//comfort pawn? cooldown?
public override IEnumerable<Gizmo> GetPawnGizmos(Pawn p)
{
return base.GetPawnGizmos(p);
}
public override void ExposeData()
{
Scribe_Values.Look<int?>(ref this.durationTicks, "durationTicks", null, false);
Scribe_References.Look<Pawn>(ref this.target, "target", false);
}
public void SuccubiLeave()
{
foreach (Pawn pawn in this.lord.ownedPawns)
{
if(colonyJoiners.Contains(pawn))
{
RecruitUtility.Recruit(pawn, Faction.OfPlayer);
Find.LetterStack.ReceiveLetter("rjw_genes_succubus_joins_letter_label".Translate(), string.Format("rjw_genes_succubus_joins_letter_description".Translate(), xxx.get_pawnname(pawn)), LetterDefOf.PositiveEvent, pawn, null, null, null, null);
}
else
{
Messages.Message("SuccubusLeaving".Translate(xxx.get_pawnname(pawn)), pawn, MessageTypeDefOf.NeutralEvent, true);
}
}
}
public Pawn target;
private int? durationTicks;
public List<Pawn> colonyJoiners = new List<Pawn>();
}
}

View file

@ -1,140 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using RimWorld;
using rjw;
namespace RJW_Genes
{
public class Gene_LifeForce : Gene_Resource, IGeneResourceDrain
{
//Gene should only be active if sex is allowed for this pawn
public override bool Active
{
get
{
if (this.Overridden)
{
return false;
}
Pawn pawn = this.pawn;
return ((pawn != null) ? pawn.ageTracker : null) == null ||
((float)this.pawn.ageTracker.AgeBiologicalYears >= this.def.minAgeActive);
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look<bool>(ref this.StoredCumAllowed, "StoredCumAllowed", true, false);
}
public bool ShouldConsumeLifeForceNow()
{
return this.Value < this.targetValue;
}
//Same as Gene_Hemogen
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (Gizmo gizmo in base.GetGizmos())
{
yield return gizmo;
}
yield break;
}
//every tick it decreases fertilin value and everyday if fertilin is below alert minimum there a ~50 chance for mental break
public override void Tick()
{
base.Tick();
if (this.CanOffset && this.Resource != null)
{
GeneUtility.OffsetLifeForce(this, -this.ResourceLossPerDay / 60000f);
}
}
public bool StoredCumAllowed = true;
public Gene_Resource Resource
{
get
{
return this;
}
}
public Pawn Pawn
{
get
{
return this.pawn;
}
}
public bool CanOffset
{
get
{
return this.pawn.Spawned && this.Active;
}
}
public float ResourceLossPerDay
{
get
{
return this.def.resourceLossPerDay;
}
}
public string DisplayLabel
{
get
{
return this.def.resourceLabel;
}
}
public override float InitialResourceMax
{
get
{
return 1f;
}
}
public override float MinLevelForAlert
{
get
{
return 0.2f;
}
}
public override float MaxLevelOffset
{
get
{
return base.MaxLevelOffset;
}
}
protected override Color BarColor
{
get
{
return Color.grey;
}
}
protected override Color BarHighlightColor
{
get
{
return Color.white;
}
}
}
}

View file

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class Gene_LifeForceDrain : Gene, IGeneResourceDrain
{
public Gene_Resource Resource
{
get
{
if (this.cachedLifeForceGene == null || !this.cachedLifeForceGene.Active)
{
this.cachedLifeForceGene = this.pawn.genes.GetFirstGeneOfType<Gene_LifeForce>();
}
return this.cachedLifeForceGene;
}
}
public bool CanOffset
{
get
{
return this.Active && this.Resource != null && this.Resource.Active;
}
}
public float ResourceLossPerDay
{
get
{
return this.def.resourceLossPerDay;
}
}
public Pawn Pawn
{
get
{
return this.pawn;
}
}
public string DisplayLabel
{
get
{
return this.Label + " (" + "Gene".Translate() + ")";
}
}
public override void Tick()
{
base.Tick();
if (this.CanOffset && this.Resource != null)
{
GeneUtility.OffsetLifeForce(this, -this.ResourceLossPerDay / 60000);
}
}
[Unsaved(false)]
private Gene_LifeForce cachedLifeForceGene;
private const float MinAgeForDrain = 3f;
}
}

View file

@ -1,118 +0,0 @@
using System.Collections.Generic;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class Gene_LifeForce_Empath : Gene
{
const int EMPATH_DISTANCE_FALLBACK = 25;
const int TICK_INTERVAL_FALLBACK = 60000 / 48;
const float AHEAGO_FALLBACK = 0.02f, SATISFIED_FALLBACK = 0.01f, FRUSTRATED_FALLBACK = -0.01f;
int empathDistance = 25;
int tickInterval = 60000 / 48 ; // 60k = 1 day, we want 0.5h which is 1/48th of 1 day.
float aheagoIncrement = 0.02f;
float satisfiedIncrement = 0.01f;
float frustratedDecrement = -0.01f;
public Gene_LifeForce_Empath() : base()
{
SetValuesFromExtension();
}
private void SetValuesFromExtension()
{
LifeForceEmpathExtension empathExt = GeneDefOf.rjw_genes_lifeforce_empath.GetModExtension<LifeForceEmpathExtension>();
tickInterval = ModExtensionHelper.GetTickIntervalFromModExtension(GeneDefOf.rjw_genes_lifeforce_empath, TICK_INTERVAL_FALLBACK);
empathDistance = ModExtensionHelper.GetTickIntervalFromModExtension(GeneDefOf.rjw_genes_lifeforce_empath, EMPATH_DISTANCE_FALLBACK);
aheagoIncrement = empathExt?.aheagoIncrement ?? AHEAGO_FALLBACK;
satisfiedIncrement = empathExt?.satisfactionIncrement ?? SATISFIED_FALLBACK;
frustratedDecrement = empathExt?.frustratedDecrement ?? FRUSTRATED_FALLBACK;
}
public override void Tick()
{
base.Tick();
if (this.pawn.IsHashIntervalTick(tickInterval) && this.pawn.Map != null)
{
foreach (Pawn pawn in this.AffectedPawns(this.pawn.Position, this.pawn.Map))
{
this.FarmLifeForce(pawn);
}
}
}
/// <summary>
/// Creates an IEnumerable of all pawns which are closeby and in lineofsight, self and other pawns with lifeforce gene are skipped (to prevent loops).
/// </summary>
/// <param name="pos">The position of the empath on the map</param>
/// <param name="map">The map the empath is on</param>
/// <returns>A list of all pawns that are close enough for the empath to connect.</returns>
private IEnumerable<Pawn> AffectedPawns(IntVec3 pos, Map map)
{
foreach (Pawn pawn in map.mapPawns.AllPawns)
{
// Return for trivial errors
if (pawn == null || this.pawn == null || pawn == this.pawn)
continue;
// Check for position-existance
if (pawn.Position == null || pos == null || pawn.Map == null)
continue;
// Do nothing if pawn is carried
if (pawn.CarriedBy != null)
continue;
// Do nothing if Pawn is Baby or Child (#25)
if (!pawn.ageTracker.Adult)
continue;
// Do nothing for pawns that also have lifeforce
if (GeneUtility.HasGeneNullCheck(pawn, GeneDefOf.rjw_genes_lifeforce))
continue;
// Actual Logic:
// Pawn qualifies in right distance and needs line of sight.
if (pos.DistanceTo(pawn.Position) < empathDistance && GenSight.LineOfSight(pos, pawn.Position, pawn.Map))
{
yield return pawn;
}
}
yield break;
}
/// <summary>
/// Adjust the empaths lifeforce depending on the farmed pawns sexneed.
/// </summary>
/// <param name="farmedPawn">The pawn affecting the empath, increasing or decreasing his lifeforce. </param>
private void FarmLifeForce(Pawn farmedPawn)
{
// Short rename to make rest more obvious.
Pawn empath = pawn;
if (farmedPawn == null)
return;
var sexneed = farmedPawn.needs.TryGetNeed<rjw.Need_Sex>();
// Shortwire: do nothing on no sexneed.
if (sexneed == null)
return;
if (sexneed.CurLevel >= sexneed.thresh_ahegao())
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(empath), aheagoIncrement);
else if (sexneed.CurLevel >= sexneed.thresh_satisfied())
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(empath), satisfiedIncrement);
else if (sexneed.CurLevel <= sexneed.thresh_frustrated())
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(empath), frustratedDecrement);
}
}
}

View file

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class HediffCompProperties_SeverityFromFertilin : HediffCompProperties
{
public HediffCompProperties_SeverityFromFertilin()
{
this.compClass = typeof(HediffComp_SeverityFromFertilin);
}
// Token: 0x04001162 RID: 4450
public float severityPerHourEmpty;
// Token: 0x04001163 RID: 4451
public float severityPerHourHemogen;
}
}

View file

@ -1,66 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class HediffComp_SeverityFromFertilin : HediffComp
{
public HediffCompProperties_SeverityFromFertilin Props
{
get
{
return (HediffCompProperties_SeverityFromFertilin)this.props;
}
}
public override bool CompShouldRemove
{
get
{
Pawn_GeneTracker genes = base.Pawn.genes;
return ((genes != null) ? genes.GetFirstGeneOfType<Gene_LifeForce>() : null) == null;
}
}
private Gene_LifeForce LifeForce
{
get
{
if (this.cachedLifeForceGene == null)
{
this.cachedLifeForceGene = base.Pawn.genes.GetFirstGeneOfType<Gene_LifeForce>();
}
return this.cachedLifeForceGene;
}
}
public override void CompPostTick(ref float severityAdjustment)
{
base.CompPostTick(ref severityAdjustment);
severityAdjustment += ((this.LifeForce.Value > 0f) ? this.Props.severityPerHourHemogen : this.Props.severityPerHourEmpty) / 2500f;
this.MentalBreak();
}
public void MentalBreak()
{
if (cachedLifeForceGene.Resource.Value <= cachedLifeForceGene.Resource.MinLevelForAlert && this.Pawn.IsHashIntervalTick(2500) && Rand.Chance(0.03f)) //~50% chance each day for mental break
{
if (this.Pawn.genes.HasGene(GeneDefOf.rjw_genes_cum_eater)
|| this.Pawn.genes.HasGene(GeneDefOf.rjw_genes_fertilin_absorber) || this.Pawn.genes.HasGene(GeneDefOf.rjw_genes_drainer))
{
//TODO: use mentalstatedef instead of mentalbreakdef
MentalBreakDef randomrape = GeneDefOf.rjw_genes_lifeforce_randomrape;
if (ModsConfig.BiotechActive &&
this.Pawn.Spawned && !this.Pawn.InMentalState && !this.Pawn.Downed &&
randomrape.Worker.BreakCanOccur(this.Pawn))
{
randomrape.Worker.TryStart(this.Pawn, "MentalBreakNoFertilin".Translate(), false);
}
}
}
}
private Gene_LifeForce cachedLifeForceGene;
}
}

View file

@ -1,24 +0,0 @@
using RimWorld;
using Verse;
namespace RJW_Genes
{
/// <summary>
/// This class checks for pawns with LifeForce and Cumeater Gene to add Fertilin when eating cum (the Item from RJW-Sexperience).
/// </summary>
public class IngestionOutcomeDoer_LifeForceOffset : IngestionOutcomeDoer
{
public const float DEFAULT_FERTILIN_PER_UNIT = 1f;
public float FertilinPerUnit = 1f;
protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
{
if (GeneUtility.HasLifeForce(pawn) && GeneUtility.IsCumEater(pawn))
{
float num = ingested.stackCount * this.FertilinPerUnit / 100;
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(pawn), num);
}
}
}
}

View file

@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
public class JobDriver_CastAbilityAfterSex : JobDriver_SexBaseInitiator
{
//Summary//
//Similar to jobdriver rape, but it cast an ability after sex and tries to limit what kind of sexinteractions are allowed.
protected override IEnumerable<Toil> MakeNewToils()
{
base.setup_ticks();
//this.FailOnDespawnedOrNull(TargetIndex.A);
//this.FailOnCannotTouch(TargetIndex.B, PathEndMode.OnCell);
this.FailOnDespawnedNullOrForbidden(this.iTarget);
//this.FailOn(() => !target.health.capacities.CanBeAwake);
JobDef PartnerJob = xxx.gettin_raped;
yield return Toils_Goto.Goto(TargetIndex.A, PathEndMode.OnCell);
yield return new Toil
{
defaultCompleteMode = ToilCompleteMode.Instant,
socialMode = RandomSocialMode.Off,
initAction = delegate ()
{
Job newJob = JobMaker.MakeJob(PartnerJob, this.pawn, this.Partner);
this.Partner.jobs.StartJob(newJob, JobCondition.InterruptForced, null, false, true, null, null, false, false, null, false, true);
}
};
Toil toil = new Toil();
toil.defaultCompleteMode = ToilCompleteMode.Never;
toil.socialMode = RandomSocialMode.Off;
toil.defaultDuration = this.duration;
toil.handlingFacing = true;
toil.FailOn(() => this.Partner.CurJob.def != PartnerJob);
toil.initAction = delegate ()
{
this.Partner.pather.StopDead();
this.Partner.jobs.curDriver.asleep = false;
//Tries to find CompProperties_SexInteractionRequirements and if it finds it it will try and generate sexprops based on the sexpropsrequirements.
foreach (AbilityComp comp in this.job.ability.comps)
{
if (comp.props is CompProperties_SexInteractionRequirements)
{
CompProperties_SexInteractionRequirements sexpropsreq = comp.props as CompProperties_SexInteractionRequirements;
this.Sexprops = CustomSexInteraction_Helper.GenerateSexProps(this.pawn, this.Partner, sexpropsreq);
}
}
this.Start();
this.Sexprops.usedCondom = (CondomUtility.TryUseCondom(this.pawn) || CondomUtility.TryUseCondom(this.Partner));
};
toil.AddPreTickAction(delegate
{
if (this.pawn.IsHashIntervalTick(this.ticks_between_hearts))
{
this.ThrowMetaIconF(this.pawn.Position, this.pawn.Map, FleckDefOf.Heart);
}
this.SexTick(this.pawn, this.Partner, true, true);
SexUtility.reduce_rest(this.Partner, 1f);
SexUtility.reduce_rest(this.pawn, 1f);
if (this.ticks_left <= 0)
{
this.ReadyForNextToil();
}
});
toil.AddFinishAction(delegate
{
this.End();
});
yield return toil;
yield return new Toil
{
initAction = delegate ()
{
SexUtility.ProcessSex(this.Sexprops);
},
defaultCompleteMode = ToilCompleteMode.Instant
};
yield return Toils_Combat.CastVerb(TargetIndex.A, TargetIndex.B, false);
yield break;
}
}
}

View file

@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;
namespace RJW_Genes
{
public class JobDriver_Flirt : JobDriver
{
private Pawn Target
{
get
{
return (Pawn)((Thing)this.pawn.CurJob.GetTarget(TargetIndex.A));
}
}
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
return true;
}
//Some wait toils to induce delay
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDespawnedOrNull(TargetIndex.A);
yield return Toils_Interpersonal.GotoInteractablePosition(TargetIndex.A);
yield return Toils_General.Wait(300, TargetIndex.A);
yield return Toils_Interpersonal.WaitToBeAbleToInteract(this.pawn);
Toil toil = Toils_Interpersonal.GotoInteractablePosition(TargetIndex.A);
toil.socialMode = RandomSocialMode.Off;
yield return toil;
yield return this.InteractToil();
Toil toil1 = Toils_General.Wait(300, TargetIndex.A);
toil1.socialMode = RandomSocialMode.Off;
yield return toil1;
yield break;
}
private Toil InteractToil()
{
return Toils_General.Do(delegate
{
if (this.pawn.interactions.TryInteractWith(this.Target, ThoughtDefOf.rjw_genes_flirt))
{
Need_Sex need_Sex = this.Target.needs.TryGetNeed<Need_Sex>();
need_Sex.CurLevel += -0.01f;
}
});
}
private const TargetIndex TargetInd = TargetIndex.A;
}
}

View file

@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;
using rjw.Modules.Interactions.Enums;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Objects;
using rjw.Modules.Interactions.Contexts;
using rjw.Modules.Interactions.Implementation;
namespace RJW_Genes
{
public class JobDriver_Seduced : JobDriver
{
//Summary//
//Makes a pawn move to seducing pawn and then tries to rape them.
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
this.FailOn(() => !this.pawn.CanReserve(TargetA, xxx.max_rapists_per_prisoner, 0, null, false));
this.FailOn(() => this.pawn.IsFighting());
this.FailOn(() => this.pawn.Drafted);
Pawn partner = this.job.GetTarget(TargetIndex.A).Pawn;
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);
yield return new Toil
{
defaultCompleteMode = ToilCompleteMode.Instant,
socialMode = RandomSocialMode.Off,
initAction = delegate ()
{
if(partner != null)
{
partner.drafter.Drafted = false;
this.pawn.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.rjw_genes_seduced, partner, null);
Job newJob = JobMaker.MakeJob(JobDefOf.sex_on_spot, pawn);
partner.jobs.StartJob(newJob, JobCondition.InterruptForced, null, false, true, null, null, false, false, null, false, true);
}
}
};
yield break;
}
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
return this.pawn.Reserve(TargetA, this.job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
}
}
}

View file

@ -1,108 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;
using rjw.Modules.Interactions.Enums;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Objects;
using rjw.Modules.Interactions.Contexts;
using rjw.Modules.Interactions.Implementation;
namespace RJW_Genes
{
public class JobDriver_SexOnSpot : JobDriver_SexBaseInitiator
{
protected override IEnumerable<Toil> MakeNewToils()
{
if (RJWSettings.DebugRape)
{
ModLog.Message(base.GetType().ToString() + "::MakeNewToils() called");
}
base.setup_ticks();
JobDef PartnerJob = JobDefOf.sex_on_spot_reciever;
this.FailOnDespawnedNullOrForbidden(this.iTarget);
this.FailOn(() => !this.pawn.CanReserve(this.Partner, xxx.max_rapists_per_prisoner, 0, null, false));
this.FailOn(() => this.pawn.IsFighting());
this.FailOn(() => this.Partner.IsFighting());
this.FailOn(() => this.pawn.Drafted);
yield return Toils_Goto.GotoThing(this.iTarget, PathEndMode.Touch);
if (this.pawn.HostileTo(this.Partner))
{
Partner.health.AddHediff(xxx.submitting);
}
yield return Toils_Goto.GotoThing(this.iTarget, PathEndMode.OnCell);
//Give thought malus to partner (I was seduced into having sex against my will)
yield return new Toil
{
defaultCompleteMode = ToilCompleteMode.Instant,
socialMode = RandomSocialMode.Off,
initAction = delegate ()
{
if (!(this.Partner.jobs.curDriver is JobDriver_SexOnSpotReciever))
{
Job newJob = JobMaker.MakeJob(PartnerJob, this.pawn);
Building_Bed building_Bed = null;
if (this.Partner.GetPosture() == PawnPosture.LayingInBed)
{
building_Bed = this.Partner.CurrentBed();
}
this.Partner.jobs.StartJob(newJob, JobCondition.InterruptForced, null, false, true, null, null, false, false, null, false, true);
if (building_Bed != null)
{
JobDriver_SexOnSpotReciever jobDriver_SexOnSpotReciever = this.Partner.jobs.curDriver as JobDriver_SexOnSpotReciever;
if (jobDriver_SexOnSpotReciever == null)
{
return;
}
jobDriver_SexOnSpotReciever.Set_bed(building_Bed);
}
}
}
};
Toil toil = new Toil();
toil.defaultCompleteMode = ToilCompleteMode.Never;
toil.defaultDuration = this.duration;
toil.handlingFacing = true;
toil.FailOn(() => this.Partner.CurJob.def != PartnerJob);
toil.initAction = delegate ()
{
this.Partner.pather.StopDead();
this.Partner.jobs.curDriver.asleep = false;
this.Start();
};
toil.tickAction = delegate ()
{
if (this.pawn.IsHashIntervalTick(this.ticks_between_hearts))
{
this.ThrowMetaIconF(this.pawn.Position, this.pawn.Map, FleckDefOf.Heart);
}
this.SexTick(this.pawn, this.Partner, true, true);
SexUtility.reduce_rest(this.Partner, 1f);
SexUtility.reduce_rest(this.pawn, 2f);
if (this.ticks_left <= 0)
{
this.ReadyForNextToil();
}
};
toil.AddFinishAction(delegate
{
this.End();
});
yield return toil;
yield return new Toil
{
initAction = delegate ()
{
SexUtility.ProcessSex(this.Sexprops);
},
defaultCompleteMode = ToilCompleteMode.Instant
};
yield break;
}
}
}

View file

@ -1,83 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;
using rjw.Modules.Interactions.Enums;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Objects;
using rjw.Modules.Interactions.Contexts;
using rjw.Modules.Interactions.Implementation;
namespace RJW_Genes
{
//Modified JobDriver_SexBaseRecieverLoved from rjw
public class JobDriver_SexOnSpotReciever : JobDriver_SexBaseReciever
{
protected override IEnumerable<Toil> MakeNewToils()
{
base.setup_ticks();
this.parteners.Add(base.Partner);
if (this.pawn.relations.OpinionOf(base.Partner) < 0)
{
this.ticks_between_hearts += 50;
}
else if (this.pawn.relations.OpinionOf(base.Partner) > 60)
{
this.ticks_between_hearts -= 25;
}
this.FailOnDespawnedOrNull(this.iTarget);
this.FailOn(() => !base.Partner.health.capacities.CanBeAwake);
this.FailOn(() => this.pawn.Drafted);
this.FailOn(() => base.Partner.Drafted);
yield return Toils_Reserve.Reserve(this.iTarget, 1, 0, null);
Toil toil2 = this.MakeSexToil();
toil2.handlingFacing = false;
yield return toil2;
yield break;
}
private Toil MakeSexToil()
{
Toil toil = new Toil();
toil.defaultCompleteMode = ToilCompleteMode.Never;
toil.socialMode = RandomSocialMode.Off;
toil.handlingFacing = true;
toil.tickAction = delegate ()
{
if (this.pawn.IsHashIntervalTick(this.ticks_between_hearts))
{
base.ThrowMetaIconF(this.pawn.Position, this.pawn.Map, FleckDefOf.Heart);
}
};
toil.AddEndCondition(delegate
{
if (this.parteners.Count <= 0)
{
return JobCondition.Succeeded;
}
return JobCondition.Ongoing;
});
toil.AddFinishAction(delegate
{
if (xxx.is_human(this.pawn))
{
this.pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
}
GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(this.pawn);
Hediff submitting = this.pawn.health.hediffSet.GetFirstHediffOfDef(xxx.submitting);
if (submitting != null)
{
this.pawn.health.RemoveHediff(submitting);
this.pawn.stances.stunner.StunFor(60, this.pawn, true, true);
}
});
toil.socialMode = RandomSocialMode.Off;
return toil;
}
}
}

View file

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
using RimWorld;
namespace RJW_Genes
{
public class JobGiver_Flirt : ThinkNode_JobGiver
{
// Token: 0x0600405A RID: 16474 RVA: 0x0017271C File Offset: 0x0017091C
protected override Job TryGiveJob(Pawn pawn)
{
Pawn target = pawn.mindState.duty.focus.Pawn;
if (pawn.CanReach(target, PathEndMode.InteractionCell, Danger.Deadly) && !target.jobs.curDriver.asleep)
{
return JobMaker.MakeJob(JobDefOf.rjw_genes_flirt, target);
}
return null;
}
}
}

View file

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;
namespace RJW_Genes
{
public class JobGiver_GetLifeForce : ThinkNode_JobGiver
{
protected override Job TryGiveJob(Pawn pawn)
{
Pawn_GeneTracker genes = pawn.genes;
Gene_LifeForce gene_lifeforce = (genes != null) ? genes.GetFirstGeneOfType<Gene_LifeForce>() : null;
if (gene_lifeforce == null)
{
return null;
}
if (!gene_lifeforce.ShouldConsumeLifeForceNow())
{
return null;
}
if (ModsConfig.IsActive("rjw.sexperience") && gene_lifeforce.StoredCumAllowed && genes.HasGene(GeneDefOf.rjw_genes_cum_eater))
{
Thing gatheredCum = this.GetStoredCum(pawn);
if (gatheredCum == null)
{
return null;
}
IngestionOutcomeDoer_LifeForceOffset ingestionOutcomeDoer = (IngestionOutcomeDoer_LifeForceOffset)gatheredCum.def.ingestible.outcomeDoers.First((IngestionOutcomeDoer x) => x is IngestionOutcomeDoer_LifeForceOffset);
if (ingestionOutcomeDoer == null)
{
return null;
}
int num = Mathf.RoundToInt(((gene_lifeforce.targetValue - gene_lifeforce.Value) * 100 + 10) / IngestionOutcomeDoer_LifeForceOffset.DEFAULT_FERTILIN_PER_UNIT);
if (gatheredCum != null && num > 0)
{
Job job = JobMaker.MakeJob(RimWorld.JobDefOf.Ingest, gatheredCum);
job.count = Mathf.Min(gatheredCum.stackCount, num);
job.ingestTotalCount = true;
return job;
}
}
return null;
}
//From JobGiver_GetHemogen, dont know exactly what this influences
public override float GetPriority(Pawn pawn)
{
if (!ModsConfig.BiotechActive)
{
return 0f;
}
Pawn_GeneTracker genes = pawn.genes;
if (((genes != null) ? genes.GetFirstGeneOfType<Gene_LifeForce>() : null) == null)
{
return 0f;
}
return 9.1f;
}
private Thing GetStoredCum(Pawn pawn)
{
Thing carriedThing = pawn.carryTracker.CarriedThing;
ThingDef gatheredCum = ThingDef.Named("GatheredCum");
if (carriedThing != null && carriedThing.def == gatheredCum)
{
return carriedThing;
}
for (int i = 0; i < pawn.inventory.innerContainer.Count; i++)
{
if (pawn.inventory.innerContainer[i].def == gatheredCum)
{
return pawn.inventory.innerContainer[i];
}
}
return GenClosest.ClosestThing_Global_Reachable(pawn.Position, pawn.Map, pawn.Map.listerThings.ThingsOfDef(gatheredCum), PathEndMode.OnCell, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false, false, false), 9999f, (Thing t) => pawn.CanReserve(t, 1, -1, null, false) && !t.IsForbidden(pawn), null);
}
}
}

View file

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;
namespace RJW_Genes
{
public class JobGiver_LifeForce_RandomRape : JobGiver_RandomRape
{
protected override Job TryGiveJob(Pawn pawn)
{
if (!can_rape(pawn, false))
{
return null;
}
Pawn pawn2 = this.find_victim(pawn, pawn.Map);
if (pawn2 == null)
{
return null;
}
return JobMaker.MakeJob(JobDefOf.rjw_genes_lifeforce_randomrape, pawn2);
}
//same as xxx.canrape from rjw, but without last requirements.
public static bool can_rape(Pawn pawn, bool forced = false)
{
return RJWSettings.rape_enabled && (xxx.is_mechanoid(pawn) || ((xxx.can_fuck(pawn) ||
(!xxx.is_male(pawn) && xxx.get_vulnerability(pawn) < RJWSettings.nonFutaWomenRaping_MaxVulnerability &&
xxx.can_be_fucked(pawn))) && (!xxx.is_human(pawn) || ((pawn.ageTracker.Growth >= 1f || pawn.ageTracker.CurLifeStage.reproductive)))));
}
}
}

View file

@ -1,188 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using rjw;
using RJWSexperience;
using RimWorld;
using Verse;
using Verse.AI;
using Verse.AI.Group;
using UnityEngine;
namespace RJW_Genes
{
public class JobGiver_TryQuickieWith : ThinkNode_JobGiver
{
protected override Job TryGiveJob(Pawn pawn)
{
Pawn target = pawn.mindState.duty.focus.Pawn;
Pawn_JobTracker jobs = target.jobs;
string pawn_name = xxx.get_pawnname(pawn);
string target_name = xxx.get_pawnname(target);
//can reserve eachother
if (pawn.CanReserveAndReach(target, PathEndMode.InteractionCell, Danger.Some) && target.CanReserve(pawn, 1, 0, null, false))
{
//Dont interrupt player
if (!(((jobs != null) ? jobs.curJob : null) != null && jobs.curJob.playerForced))
{
float willingness = TargetWillingness(pawn, target);
if (Rand.Chance(willingness))
{
Job newJob =JobMaker.MakeJob(xxx.quick_sex, target);
//Pawn joins faction when lordJob ends instead of leaving
//in the future determine the chance of this another way
if (Rand.Chance(JoinChance(pawn, target)))
{
Lord lord = pawn.GetLord();
LordJob_SuccubusVisit lordJob = lord == null? null : lord.LordJob as LordJob_SuccubusVisit;
if (lordJob != null)
{
if (!lordJob.colonyJoiners.Contains(pawn))
{
lordJob.colonyJoiners.Add(pawn);
}
}
}
return newJob;
}
else
{
if (RJWSettings.DebugLogJoinInBed) //change this when we have our own settigns
{
ModLog.Message(string.Format("{0} was not interested in having sex with {1}: ({2} chance)", pawn_name, target_name, willingness));
}
}
}
else
{
if (RJWSettings.DebugLogJoinInBed) //change this when we have our own settigns
{
//ModLog.Message(string.Format(" find_pawn_to_fuck({0}): lover has important job ({1}), skipping", pawn_name, target.jobs.curJob.def));
}
}
}
else
{
if (RJWSettings.DebugLogJoinInBed) //change this when we have our own settigns
{
ModLog.Message(" (" + pawn_name + "): cannot reach or reserve " + target_name);
}
}
return null;
}
public static float TargetWillingness(Pawn pawn, Pawn target)
{
string pawn_name = xxx.get_pawnname(pawn);
float willingness = SexAppraiser.would_fuck(target,pawn);
bool nymph = xxx.is_nympho(target);
bool loverelation = LovePartnerRelationUtility.LovePartnerRelationExists(pawn, target);
if (nymph || loverelation)
{
willingness *= 2;
}
if (xxx.HasNonPolyPartner(pawn, false) && !loverelation)
{
if (RJWHookupSettings.NymphosCanCheat && nymph && xxx.is_frustrated(pawn))
{
if (RJWSettings.DebugLogJoinInBed)
{
ModLog.Message(" find_partner(" + pawn_name + "): I'm a nympho and I'm so frustrated that I'm going to cheat");
}
}
else
{
if (!pawn.health.hediffSet.HasHediff(HediffDef.Named("AlcoholHigh"), false))
{
if (RJWSettings.DebugLogJoinInBed)
{
ModLog.Message(" find_partner(" + pawn_name + "): I interested in banging but that's cheating");
}
//Succubus has a small chance to seduce even if target is in relationship
willingness *= 0.1f;
}
else
{
if (RJWSettings.DebugLogJoinInBed)
{
ModLog.Message(" find_partner(" + pawn_name + "): I want to bang and im too drunk to care if its cheating");
}
//No change
}
}
}
return willingness;
}
public static float JoinChance(Pawn pawn ,Pawn target)
{
float chance = 0.1f;
//Sex satisfaction, how good the target is at sex
chance *= xxx.get_sex_satisfaction(target);
//Succubus mood
if (pawn.needs != null && pawn.needs.mood != null)
{
chance *= pawn.needs.mood.CurLevelPercentage + 0.5f;
}
//Size of genitals
bool size_matters = true; //To be placed in modsettings
if (size_matters)
{
//The larger the penis to greater the chance
if (RelationsUtility.AttractedToGender(pawn, Gender.Male))
{
chance *= GetGenitalSize(target, true) + 0.5f;
}
//The tighter the vagine the greater the chance, a size above 1 is considered as 1
if (RelationsUtility.AttractedToGender(pawn, Gender.Female))
{
chance *= 1f - Mathf.Min(GetGenitalSize(target, false),1f) + 0.5f;
}
}
//Sex ability from sexperience
if (ModsConfig.IsActive("rjw.sexperience"))
{
chance *= RJWSexperience.PawnExtensions.GetSexStat(pawn);
}
return Mathf.Max(chance,0f);
}
//Gets the size of the largest penis or the tightest vagina
public static float GetGenitalSize(Pawn pawn, bool penis_else_vagina)
{
List<Hediff> genitals = rjw.PawnExtensions.GetGenitalsList(pawn);
if(!genitals.NullOrEmpty())
{
if (penis_else_vagina)
{
List<Hediff> penises = genitals.Where(genital => Genital_Helper.is_penis(genital)).ToList();
{
if (!penises.NullOrEmpty())
{
return penises.Max(genital => genital.Severity);
}
}
}
else
{
List<Hediff> vaginas = genitals.Where(genital => Genital_Helper.is_vagina(genital)).ToList();
{
if (!vaginas.NullOrEmpty())
{
return vaginas.Min(genital => genital.Severity);
}
}
}
}
return 0f;
}
}
}

View file

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
using Verse.AI.Group;
using RimWorld;
namespace RJW_Genes
{
//Based on LordToil_EscortPawn
public class LordToil_Flirt : LordToil
{
public LordToil_Flirt(Pawn victim, float followRadius)
{
this.victim = victim;
this.followRadius = followRadius;
}
public override void UpdateAllDuties()
{
for (int i = 0; i < this.lord.ownedPawns.Count; i++)
{
PawnDuty duty = new PawnDuty(GeneDefOf.rjw_genes_flirt, this.victim, this.followRadius);
this.lord.ownedPawns[i].mindState.duty = duty;
}
}
public Pawn victim;
public float followRadius;
}
}

View file

@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using RimWorld;
using Verse.AI;
namespace RJW_Genes
{
public class LifeForceMentalBreakWorker : MentalBreakWorker
{
public override bool BreakCanOccur(Pawn pawn)
{
if (pawn.Spawned && base.BreakCanOccur(pawn))
{
if (!GeneUtility.HasLifeForce(pawn))
{
return false;
}
Gene_LifeForce gene = pawn.genes.GetFirstGeneOfType<Gene_LifeForce>();
if( gene.Resource.Value <= 0)
{
return true;
}
}
return false;
}
}
}

View file

@ -1,23 +0,0 @@
using System;
using Verse;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
public class LifeForceMentalState : MentalState
{
public override void MentalStateTick()
{
if (this.pawn.IsHashIntervalTick(150) && !GeneUtility.HasCriticalLifeForce(this.pawn))
{
Pawn_JobTracker jobs = this.pawn.jobs;
if (!(((jobs != null) ? jobs.curDriver : null) is JobDriver_Sex))
{
base.RecoverFromState();
return;
}
}
base.MentalStateTick();
}
}
}

View file

@ -1,14 +0,0 @@
using System;
using Verse;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
public class LifeForceMentalStateWorker : MentalStateWorker
{
public override bool StateCanOccur(Pawn pawn)
{
return base.StateCanOccur(pawn) && (xxx.is_human(pawn) && JobGiver_LifeForce_RandomRape.can_rape(pawn));
}
}
}

View file

@ -1,203 +0,0 @@
using HarmonyLib;
using rjw;
using RimWorld;
using Verse;
namespace RJW_Genes
{
/// <summary>
/// This Patch hooks after "SatisfyPersonal"(i.E. when the pawn finished fucking) and covers LifeForceGain.
/// If the pawn has LifeForce, all relevant Genes are checked and applied.
/// </summary>
[HarmonyPatch(typeof(SexUtility), nameof(SexUtility.SatisfyPersonal))]
public static class Patch_SatisfyPersonal_LifeForceGain
{
public const float LIFEFORCE_GAINED_FROM_DRAINER_GENE = 0.25f;
public static void Postfix(SexProps props)
{
// ShortCuts: Exit Early if Pawn or Partner are null (can happen with Animals or Masturbation)
if (props.pawn == null || !props.hasPartner())
return;
// Exit if pawn has fertilin themself, it won't give any if it has lifeforce themself.
if (GeneUtility.HasLifeForce(props.pawn))
{
return;
}
//Summary//
//We use the positions of the pawn (dom or sub) and based on that which interactions will transfer fertilin
//By checking isreceiver we know if the succubus is the dom or the sub and if the situation is reverse we also swap the function we use
float absorb_factor = 0f;
if (GeneUtility.HasLifeForce(props.partner))
{
Pawn PawnWithLifeForce = props.partner;
if (!props.isRevese)
{
if (props.isReceiver)
{
// Scenario Dom Succubus, normal
absorb_factor = BaseDom(props, PawnWithLifeForce);
}
else
{
// Scenario Sub Succubus, normal
absorb_factor = BaseSub(props, PawnWithLifeForce);
}
}
else
{
if (props.isReceiver)
{
// Scenario Dom Succubus, Reverse
absorb_factor = BaseSub(props, PawnWithLifeForce);
}
else
{
// Scenario Sub Succubus, Reverse
absorb_factor = BaseDom(props, PawnWithLifeForce);
}
}
// If we remove this check fertilin is always lost, but the succubus doesn't always gain any
if (absorb_factor != 0f)
{
TransferFertilin(props, absorb_factor);
}
// Handle Gene: Sexual_Drainer
// to be drained, a pawn must not-be-drained-already and drainers cannot be drained either.
if (GeneUtility.IsSexualDrainer(PawnWithLifeForce)
&& !props.pawn.health.hediffSet.HasHediff(HediffDefOf.rjw_genes_succubus_drained)
&& !GeneUtility.IsSexualDrainer(props.pawn))
{
if (GeneUtility.IsGenerousDonor(props.pawn) && RJW_Genes_Settings.rjw_genes_generous_donor_cheatmode)
{
// Cheatmode is on, do not drain but give life
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(PawnWithLifeForce), LIFEFORCE_GAINED_FROM_DRAINER_GENE);
if (RJW_Genes_Settings.rjw_genes_detailed_debug)
ModLog.Message($"{props.pawn.Name} was not (sexually) drained by {PawnWithLifeForce.Name}, because Cheatmode for Generous Donors is on");
} else
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug)
ModLog.Message($"{props.pawn.Name} has been (sexually) drained by {PawnWithLifeForce.Name}");
props.pawn.health.AddHediff(HediffDefOf.rjw_genes_succubus_drained);
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(PawnWithLifeForce), LIFEFORCE_GAINED_FROM_DRAINER_GENE);
}
}
}
}
public static void TransferFertilin(SexProps props, float absorb_percentage = 1f)
{
Pawn_GeneTracker genes = props.partner.genes;
Gene_LifeForce gene = genes.GetFirstGeneOfType<Gene_LifeForce>();
Hediff fertilin_lost = props.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.rjw_genes_fertilin_lost);
//Around quarter get ejected everytime pawn cums
float multiplier = Rand.Range(0.10f, 0.40f);
if (GeneUtility.IsGenerousDonor(props.pawn) && RJW_Genes_Settings.rjw_genes_generous_donor_cheatmode)
{
// Do nothing, Cheatmode is on
multiplier = 1;
}
else
{
//Create a new ferilin_lost hediff or increase it
if (fertilin_lost == null)
{
Hediff new_fertilin_lost = HediffMaker.MakeHediff(HediffDefOf.rjw_genes_fertilin_lost, props.pawn);
props.pawn.health.AddHediff(new_fertilin_lost);
new_fertilin_lost.Severity = multiplier;
} else
{
multiplier *= 1 - fertilin_lost.Severity;
fertilin_lost.Severity += multiplier;
}
}
multiplier *= absorb_percentage;
//Currently taking the sum of all penises, maybe I should just consider one at random
float valuechange = TotalFertilinAmount(props, multiplier);
if (props.partner.IsAnimal())
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug)
ModLog.Message($"Fertilin-Source of {props.pawn.Name} was an Animal, Fertilin-Gain is being adjusted by {RJW_Genes_Settings.rjw_genes_fertilin_from_animals_factor}%");
valuechange *= RJW_Genes_Settings.rjw_genes_fertilin_from_animals_factor;
}
GeneUtility.OffsetLifeForce(GeneUtility.GetLifeForceGene(props.partner), valuechange);
}
public static float TotalFertilinAmount(SexProps props, float multiplier)
{
float total_fluid = CumUtility.GetTotalFluidAmount(props.pawn) / 100;
//More in the tank means more to give
if (props.pawn.Has(Quirk.Messy))
{
total_fluid *= 2;
}
if (props.pawn.RaceProps.Animal)
{
total_fluid *= 0.1f; //Should make this settable in settings
}
return total_fluid;
}
/// <summary>
/// Handles the Case that the Life-Force wielder initiated the Sex (They are "Dom").
/// </summary>
/// <param name="props">The summary of the sex act, used for checking conditions.</param>
/// <param name="PawnWithLifeForce">The pawn that might gain LifeForce through this method.</param>
/// <returns>A factor between 0 and 1 how much of output-fertilin will be used for input-lifeforce</returns>
public static float BaseDom(SexProps props, Pawn PawnWithLifeForce)
{
float absorb_factor = 0f;
if (props.sexType == xxx.rjwSextype.Sixtynine && GeneUtility.IsCumEater(PawnWithLifeForce))
{
absorb_factor += 1f;
}
return absorb_factor;
}
/// <summary>
/// Handles the Case that the Life-Force wielder got initiated into sex (They are "Sub").
/// </summary>
/// <param name="props">The summary of the sex act, used for checking conditions.</param>
/// <param name="PawnWithLifeForce">The pawn that might gain LifeForce through this method.</param>
/// <returns>A factor between 0 and 1 how much of output-fertilin will be used for input-lifeforce</returns>
public static float BaseSub(SexProps props, Pawn PawnWithLifeForce)
{
float absorb_factor = 0f;
if ((props.sexType == xxx.rjwSextype.Oral || props.sexType == xxx.rjwSextype.Fellatio || props.sexType == xxx.rjwSextype.Sixtynine)
&& GeneUtility.IsCumEater(PawnWithLifeForce))
{
absorb_factor += 1f;
}
else if (props.sexType == xxx.rjwSextype.Vaginal && GeneUtility.HasGeneNullCheck(PawnWithLifeForce, GeneDefOf.rjw_genes_fertilin_absorber))
{
absorb_factor += 1f;
}
else if (props.sexType == xxx.rjwSextype.Anal && GeneUtility.HasGeneNullCheck(PawnWithLifeForce, GeneDefOf.rjw_genes_fertilin_absorber))
{
absorb_factor += 1f;
}
else if (props.sexType == xxx.rjwSextype.DoublePenetration && GeneUtility.HasGeneNullCheck(PawnWithLifeForce, GeneDefOf.rjw_genes_fertilin_absorber))
{
absorb_factor += 1f;
}
else if (props.sexType == xxx.rjwSextype.Scissoring || props.sexType == xxx.rjwSextype.Cunnilingus)
{
//with vaginal cum absorbtion
//absorb_factor += 1f;
}
return absorb_factor;
}
}
}

View file

@ -1,62 +0,0 @@
using HarmonyLib;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
namespace RJW_Genes
{
/// <summary>
/// This patch enables cum-eater pawns to drain cumflations for more fertilin drain by passively having sex.
/// It is hooked after RJWs Change-Psyfocus so that pawns that are having prolonged sex (e.g. by overdrive) can fully drain the cumflation over time.
///
/// It is conditionally loaded only when LicentiaLabs is enabled, as this is the necessary source for cumflation-hediffs.
/// The patched function is: [HarmonyPatch(typeof(JobDriver_Sex), nameof(JobDriver_Sex.ChangePsyfocus))]
/// </summary>
public static class Patch_SexTicks_ChangePsyfocus
{
public const float LIFEFORCE_GAIN_PER_TICK = 0.05f;
public const float CUMFLATION_SEVERITY_LOSS_PER_TICK = 0.1f;
//Using ChangePsyfocus as it is something that fires every 60 ticks
public static void Postfix(ref JobDriver_Sex __instance, ref Pawn pawn, ref Thing target)
{
SexProps props = __instance.Sexprops;
if (props != null && props.sexType == xxx.rjwSextype.Cunnilingus && props.partner != null && target != null)
{
Pawn pawn2 = target as Pawn;
// Case 1: Pawn is "drinking" and has CumEater Gene
if (props.isRevese && GeneUtility.IsCumEater(pawn))
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug)
ModLog.Message($"{pawn.Name} is draining {pawn2.Name}'s cumflation for additional fertilin (CumEater-Gene ChangePsyFocus-Trigger).");
DrinkCumflation(pawn2, pawn);
}
// Case 2: Pawn2 is "drinking" and has CumEater Gene
else if (GeneUtility.IsCumEater(pawn2))
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug)
ModLog.Message($"{pawn.Name} is draining {pawn2.Name}'s cumflation for additional fertilin (CumEater-Gene ChangePsyFocus-Trigger).");
DrinkCumflation(pawn, pawn2);
}
}
}
public static void DrinkCumflation(Pawn source, Pawn consumer)
{
if (GeneUtility.HasLifeForce(consumer) && GeneUtility.IsCumEater(consumer)
&& source.health.hediffSet.HasHediff(HediffDef.Named("Cumflation")))
{
Hediff cumflation = source.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Cumflation"));
Gene_LifeForce gene_LifeForce = consumer.genes.GetFirstGeneOfType<Gene_LifeForce>();
cumflation.Severity = Math.Max(0f,cumflation.Severity - CUMFLATION_SEVERITY_LOSS_PER_TICK);
gene_LifeForce.Resource.Value += LIFEFORCE_GAIN_PER_TICK;
}
}
}
}

View file

@ -1,79 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using RimWorld;
using Verse;
namespace RJW_Genes
{
/// <summary>
/// This Patch is applied to add a absorption gene for fertilin if it has none, but it does have the fertilin gene
/// First tries to get one from the parents else chooses one of them at random
/// the genes are determined and "simply added".
///
/// This fixes the potential problem that Pawns could inherit Fertilin, but no gene to gain Fertilin.
/// </summary>
[HarmonyPatch(typeof(PregnancyUtility), "GetInheritedGeneSet", new Type[]
{
typeof(Pawn),
typeof(Pawn)
}
)]
public static class Patch_Vanilla_Inheritance_Fertilin
{
[HarmonyPostfix]
public static void InheritedGenes(Pawn father, Pawn mother, ref GeneSet __result)
{
//Also make a setting for this
if (__result.GenesListForReading.Contains(GeneDefOf.rjw_genes_lifeforce))
{
List<GeneDef> babies_genes = __result.GenesListForReading;
//If there is no absorption gene get one from the parents, else a random one
if(!Has_Fertilin_Source_Gene(babies_genes))
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug)
ModLog.Message($"Child of ({father.Name};{mother.Name}) has Genes with LifeForce-Resource but no Source-Gene, adding one of parents random if possible or any random otherwise.");
// Gather Parents Source-Genes
List<GeneDef> absorption_genes_parents = new List<GeneDef>();
foreach (GeneDef geneDef in FertilinSourceGenes)
{
if(mother.genes != null && mother.genes.HasGene(geneDef))
absorption_genes_parents.Add(geneDef);
if (father.genes != null && father.genes.HasGene(geneDef))
absorption_genes_parents.Add(geneDef);
}
// Parents had Genes - Pick a random one of them
if (!absorption_genes_parents.NullOrEmpty())
__result.AddGene(absorption_genes_parents.RandomElement());
// Create a fully random one for your little Cumfueled missbreed
else
__result.AddGene(FertilinSourceGenes.RandomElement());
}
}
}
private static List<GeneDef> FertilinSourceGenes = new List<GeneDef>() {
GeneDefOf.rjw_genes_drainer,
GeneDefOf.rjw_genes_cum_eater,
GeneDefOf.rjw_genes_fertilin_absorber,
GeneDefOf.rjw_genes_cockeater
};
private static bool Has_Fertilin_Source_Gene(List<GeneDef> genes)
{
foreach (GeneDef gene in genes)
{
if (FertilinSourceGenes.Contains(gene))
{
return true;
}
}
return false;
}
}
}

View file

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
namespace RJW_Genes
{
public class ThinkNode_ConditionalCannotInteract : ThinkNode_Conditional
{
protected override bool Satisfied(Pawn pawn)
{
Pawn target = pawn.mindState.duty.focus.Pawn;
if (target == null)
{
return true;
}
return (target.jobs != null && target.jobs.curDriver.asleep) || !pawn.CanReach(target, PathEndMode.InteractionCell, Danger.Deadly);
}
}
}

View file

@ -1,14 +0,0 @@
using System;
using Verse;
using Verse.AI;
namespace RJW_Genes
{
public class ThinkNode_ConditionalCritcalLifeForce : ThinkNode_Conditional
{
protected override bool Satisfied(Pawn p)
{
return GeneUtility.HasCriticalLifeForce(p);
}
}
}

View file

@ -1,14 +0,0 @@
using System;
using Verse;
using Verse.AI;
namespace RJW_Genes
{
public class ThinkNode_ConditionalLowLifeForce : ThinkNode_Conditional
{
protected override bool Satisfied(Pawn p)
{
return GeneUtility.HasLowLifeForce(p);
}
}
}

View file

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
public class ThinkNode_NewFlirtTarget : ThinkNode
{
public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParams)
{
List<Pawn> validTargets = ValidTargets(pawn, pawn.Map).ToList();
Pawn new_target = validTargets.NullOrEmpty() ? null : validTargets.RandomElement();
if (new_target != null)
{
pawn.mindState.duty.focus = new_target;
}
return ThinkResult.NoJob;
}
private IEnumerable<Pawn> ValidTargets(Pawn pawn, Map map)
{
foreach (Pawn pawn2 in map.mapPawns.FreeAdultColonistsSpawned)
{
if (pawn != null && pawn2 != null && pawn != pawn2 && !pawn2.jobs.curDriver.asleep && SexAppraiser.would_fuck(pawn, pawn2) > 0.1f)
{
yield return pawn2;
}
}
//IEnumerator<Pawn> enumerator = null;
yield break;
}
}
}

View file

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld.Planet;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class Alert_CriticalFertilin : Alert
{
private List<GlobalTargetInfo> Targets
{
get
{
this.CalculateTargets();
return this.targets;
}
}
public override string GetLabel()
{
if (this.Targets.Count == 1)
{
return "AlertLowFertilin".Translate() + ": " + this.targetLabels[0];
}
return "AlertLowFertilin".Translate();
}
private void CalculateTargets()
{
this.targets.Clear();
this.targetLabels.Clear();
if (!ModsConfig.BiotechActive)
{
return;
}
foreach (Pawn pawn in PawnsFinder.AllMapsCaravansAndTravelingTransportPods_Alive)
{
if (pawn.RaceProps.Humanlike && pawn.Faction == Faction.OfPlayer)
{
Pawn_GeneTracker genes = pawn.genes;
Gene_LifeForce gene_Lifeforce = (genes != null) ? genes.GetFirstGeneOfType<Gene_LifeForce>() : null;
if (gene_Lifeforce != null && gene_Lifeforce.Active && gene_Lifeforce.Value < gene_Lifeforce.MinLevelForAlert)
{
this.targets.Add(pawn);
this.targetLabels.Add(pawn.NameShortColored.Resolve());
}
}
}
}
public override TaggedString GetExplanation()
{
return "AlertLowFertilinDesc".Translate() + ":\n" + this.targetLabels.ToLineList(" - ");
}
public override AlertReport GetReport()
{
return AlertReport.CulpritsAre(this.Targets);
}
private List<GlobalTargetInfo> targets = new List<GlobalTargetInfo>();
private List<string> targetLabels = new List<string>();
}
}

View file

@ -1,83 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using UnityEngine;
namespace RJW_Genes
{
//Copied from GeneGizmo_ResourceHemogen, with small modifications
public class GeneGizmo_ResourceLifeForce : GeneGizmo_Resource
{
public GeneGizmo_ResourceLifeForce(Gene_Resource gene, List<IGeneResourceDrain> drainGenes, Color barColor, Color barhighlightColor) : base(gene, drainGenes, barColor, barhighlightColor)
{
this.draggableBar = true;
}
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
{
return base.GizmoOnGUI(topLeft, maxWidth, parms);
}
protected override string GetTooltip()
{
this.tmpDrainGenes.Clear();
string text = string.Format("{0}: {1} / {2}\n", this.gene.ResourceLabel.CapitalizeFirst().Colorize(ColoredText.TipSectionTitleColor), this.gene.ValueForDisplay, this.gene.MaxForDisplay);
if (this.gene.pawn.IsColonistPlayerControlled || this.gene.pawn.IsPrisonerOfColony)
{
if (this.gene.targetValue <= 0f)
{
text += "NeverSeekFertilin";
}
else
{
text = text + ("SeekFertilinBelow" + ": ") + this.gene.PostProcessValue(this.gene.targetValue);
}
}
if (!this.drainGenes.NullOrEmpty<IGeneResourceDrain>())
{
float num = 0f;
foreach (IGeneResourceDrain geneResourceDrain in this.drainGenes)
{
if (geneResourceDrain.CanOffset)
{
this.tmpDrainGenes.Add(new Pair<IGeneResourceDrain, float>(geneResourceDrain, geneResourceDrain.ResourceLossPerDay));
num += geneResourceDrain.ResourceLossPerDay;
}
}
if (num != 0f)
{
string text2 = (num < 0f) ? "RegenerationRate".Translate() : "DrainRate".Translate();
text = string.Concat(new string[]
{
text,
"\n\n",
text2,
": ",
"PerDay".Translate(Mathf.Abs(this.gene.PostProcessValue(num))).Resolve()
});
foreach (Pair<IGeneResourceDrain, float> pair in this.tmpDrainGenes)
{
text = string.Concat(new string[]
{
text,
"\n - ",
pair.First.DisplayLabel.CapitalizeFirst(),
": ",
"PerDay".Translate(this.gene.PostProcessValue(-pair.Second).ToStringWithSign()).Resolve()
});
}
}
}
if (!this.gene.def.resourceDescription.NullOrEmpty())
{
text = text + "\n\n" + this.gene.def.resourceDescription.Formatted(this.gene.pawn.Named("PAWN")).Resolve();
}
return text;
}
private List<Pair<IGeneResourceDrain, float>> tmpDrainGenes = new List<Pair<IGeneResourceDrain, float>>();
}
}