diff --git a/Common/Assemblies/Rjw-Genes.dll b/Common/Assemblies/Rjw-Genes.dll
index df5976d..706a831 100644
Binary files a/Common/Assemblies/Rjw-Genes.dll and b/Common/Assemblies/Rjw-Genes.dll differ
diff --git a/Common/Defs/AbilityDefs/Ability_PussyHeal.xml b/Common/Defs/AbilityDefs/Ability_PussyHeal.xml
new file mode 100644
index 0000000..6837a39
--- /dev/null
+++ b/Common/Defs/AbilityDefs/Ability_PussyHeal.xml
@@ -0,0 +1,47 @@
+
+
+
+ rjw_genes_pussyheal
+
+ Rape another pawn, so you can heal them with your vagina's special healing power.
+ Things/Mote/Heart
+ false
+ true
+ false
+ 60000
+ Mote_CoagulateStencil
+ Coagulate
+ Coagulate_Cast
+ rjw_genes_lifeforce_healpussy
+ 401
+
+ Verb_CastAbilityTouch
+ false
+ -1
+ 0
+
+ true
+ false
+ false
+ false
+ true
+
+
+
+
+ 0.4~0.8
+
+
+
+
+ Rape
+
+
+
+ Vagina
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Common/Defs/Genes/GeneDefs_Cosmetic.xml b/Common/Defs/Genes/GeneDefs_Cosmetic.xml
index acff4a5..5a65039 100644
--- a/Common/Defs/Genes/GeneDefs_Cosmetic.xml
+++ b/Common/Defs/Genes/GeneDefs_Cosmetic.xml
@@ -18,7 +18,7 @@
rjw_genes_Succubus_Wings
Carriers of this gene grow succubus wings.
- UI/Icons/Genes/Gene_TailFurry
+ Genes/Icons/Succubus_Wings
(0.75, 0.75, 0.75)
1000
1
diff --git a/Common/Defs/Genes/GeneDefs_LifeForce.xml b/Common/Defs/Genes/GeneDefs_LifeForce.xml
index 1428ec2..69db900 100644
--- a/Common/Defs/Genes/GeneDefs_LifeForce.xml
+++ b/Common/Defs/Genes/GeneDefs_LifeForce.xml
@@ -33,4 +33,28 @@
1
1
+
+
+ rjw_genes_pussyhealer
+
+ pussyhealer
+ Carriers of this gene are able use vaginal sex to tend to other's wounds.
+ Things/Mote/Heart
+ Ability
+
+ rjw_genes_pussyheal
+
+
+ rjw_genes_pussyheal
+
+ 1
+ 13
+
+
+ life
+ clotter
+ tender
+
+
+
\ No newline at end of file
diff --git a/Common/Defs/JobDefs/Jobs_LifeForce.xml b/Common/Defs/JobDefs/Jobs_LifeForce.xml
index 274ade7..babb547 100644
--- a/Common/Defs/JobDefs/Jobs_LifeForce.xml
+++ b/Common/Defs/JobDefs/Jobs_LifeForce.xml
@@ -7,4 +7,11 @@
Raping
false
+
+
+ rjw_genes_lifeforce_healpussy
+ RJW_Genes.JobDriver_CastAbilityAfterSex
+ Healing someone with sex.
+ false
+
\ No newline at end of file
diff --git a/Common/Textures/Genes/Icons/Succubus_Wings.png b/Common/Textures/Genes/Icons/Succubus_Wings.png
new file mode 100644
index 0000000..5ca976f
Binary files /dev/null and b/Common/Textures/Genes/Icons/Succubus_Wings.png differ
diff --git a/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_east.png b/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_east.png
index 19ecc3e..71e3f53 100644
Binary files a/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_east.png and b/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_east.png differ
diff --git a/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_north.png b/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_north.png
index 92c4a01..331add5 100644
Binary files a/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_north.png and b/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_north.png differ
diff --git a/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_south.png b/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_south.png
index 92c4a01..331add5 100644
Binary files a/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_south.png and b/Common/Textures/Things/Pawn/Humanlike/BodyAttachments/rjw_genes_Succubus_Wings/Succubus_Wings_south.png differ
diff --git a/Source/GeneDefOf.cs b/Source/GeneDefOf.cs
index 726bb53..5ac85dd 100644
--- a/Source/GeneDefOf.cs
+++ b/Source/GeneDefOf.cs
@@ -74,5 +74,6 @@ namespace RJW_Genes
// LifeForce
public static readonly GeneDef rjw_genes_lifeforce;
+ public static readonly GeneDef rjw_genes_pussyhealer;
}
}
diff --git a/Source/Genes/GeneUtility.cs b/Source/Genes/GeneUtility.cs
index 3bea9a6..0a8d571 100644
--- a/Source/Genes/GeneUtility.cs
+++ b/Source/Genes/GeneUtility.cs
@@ -102,5 +102,14 @@ namespace RJW_Genes
}
return pawn.genes.HasGene(GeneDefOf.rjw_genes_generous_donor);
}
+
+ public static bool isPussyHealer(Pawn pawn)
+ {
+ if (pawn.genes == null)
+ {
+ return false;
+ }
+ return pawn.genes.HasGene(GeneDefOf.rjw_genes_pussyhealer);
+ }
}
}
\ No newline at end of file
diff --git a/Source/Genes/Life_Force/AbilityUtility.cs b/Source/Genes/Life_Force/AbilityUtility.cs
new file mode 100644
index 0000000..896694e
--- /dev/null
+++ b/Source/Genes/Life_Force/AbilityUtility.cs
@@ -0,0 +1,52 @@
+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 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Genes/Life_Force/CompAbilityEffect_PussyHeal.cs b/Source/Genes/Life_Force/CompAbilityEffect_PussyHeal.cs
new file mode 100644
index 0000000..5ddb6af
--- /dev/null
+++ b/Source/Genes/Life_Force/CompAbilityEffect_PussyHeal.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using UnityEngine;
+using RimWorld;
+using rjw;
+using rjw.Modules.Interactions.Helpers;
+
+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 healed wounds", 3.65f);
+ //pawn.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.Pussy_Healed, pawn, null);
+ }
+ //this.AfterSex(any_wound_tended);
+ //FleckMaker.AttachedOverlay(pawn, FleckDefOf.FlashHollow, Vector3.zero, 1.5f, -1f);
+ }
+
+ public void AfterSex(Pawn pawn, Pawn target)
+ {
+ List 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;
+ }
+ //AbilityUtility.ValidateHasTendableWound(pawn, throwMessages, this.parent);
+
+ }
+ 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;
+ }
+
+
+ }
+}
diff --git a/Source/Genes/Life_Force/CompAbility_SexInteractionRequirements.cs b/Source/Genes/Life_Force/CompAbility_SexInteractionRequirements.cs
new file mode 100644
index 0000000..5e926c5
--- /dev/null
+++ b/Source/Genes/Life_Force/CompAbility_SexInteractionRequirements.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using RimWorld;
+using rjw;
+using rjw.Modules.Interactions.Objects;
+using rjw.Modules.Interactions.Helpers;
+using rjw.Modules.Interactions.Enums;
+using rjw.Modules.Interactions.Implementation;
+using rjw.Modules.Interactions.Defs.DefFragment;
+namespace RJW_Genes
+{
+ public class CompAbility_SexInteractionRequirements : AbilityComp
+ {
+ public CompProperties_SexInteractionRequirements Props
+ {
+ get
+ {
+ return (CompProperties_SexInteractionRequirements)this.props;
+ }
+ }
+
+ public static List GenerateInteractionDefList(Pawn pawn, Pawn pawn2, CompProperties_SexInteractionRequirements sexpropsreq)
+ {
+ List tags = new List();
+ if (pawn2.IsAnimal())
+ {
+ tags.Add(InteractionTag.Animal);
+
+ }
+ else
+ {
+ tags = sexpropsreq.tags;
+ }
+
+ InteractionRequirement dominantRequirement = sexpropsreq.dominantRequirement;
+ InteractionRequirement submissiveRequirement = sexpropsreq.submissiveRequirement;
+ List sexinteractions = SexUtility.SexInterractions;
+ List list = new List();
+ //List a = from interaction in sexinteractions
+ //where InteractionHelper.GetWithExtension(interaction).DominantHasFamily(dominantRequirement.families.)
+ // select interaction;
+
+ //should use where select but dont fully understand that, so I am using this.
+ foreach (InteractionDef interactionDef in SexUtility.SexInterractions)
+ {
+ //Use rjw function to check if the interaction would be valid
+ if (!LewdInteractionValidatorService.Instance.IsValid(interactionDef, pawn, pawn2))
+ {
+ continue;
+ }
+ InteractionWithExtension withExtension = InteractionHelper.GetWithExtension(interactionDef);
+ bool add_interaction = false;
+ //only add interactions which have a correct tag
+ foreach (InteractionTag tag in tags)
+ {
+ if (withExtension.HasInteractionTag(tag))
+ {
+ add_interaction = true;
+ break;
+ }
+ }
+ //In case of failure go to next interaction
+ if (!add_interaction)
+ {
+ continue;
+ }
+ //goes to next interaction if it doesn't have the required genitals
+ if (dominantRequirement != null)
+ {
+ foreach (GenitalFamily genitalFamily in dominantRequirement.families)
+ {
+ if (!withExtension.DominantHasFamily(genitalFamily))
+ {
+ add_interaction = false;
+ break;
+
+ }
+ }
+ if (!add_interaction)
+ {
+ continue;
+ }
+ foreach (GenitalTag tag in dominantRequirement.tags)
+ {
+ if (!withExtension.DominantHasTag(tag))
+ {
+ add_interaction = false;
+ break;
+
+ }
+ }
+ }
+ //goes to next interaction if it doesn't have the required genitals
+ if (submissiveRequirement != null)
+ {
+ foreach (GenitalFamily genitalFamily in submissiveRequirement.families)
+ {
+ if (!withExtension.SubmissiveHasFamily(genitalFamily))
+ {
+ add_interaction = false;
+ break;
+
+ }
+ }
+ if (!add_interaction)
+ {
+ continue;
+ }
+ foreach (GenitalTag tag in submissiveRequirement.tags)
+ {
+ if (!withExtension.SubmissiveHasTag(tag))
+ {
+ add_interaction = false;
+ break;
+
+ }
+
+ }
+ }
+ if (add_interaction)
+ {
+ list.Add(interactionDef);
+ }
+
+ }
+ return list;
+ }
+
+ //Generates a valid interaction for the requirements and assigns sexprops based on that
+ public static SexProps GenerateSexProps(Pawn pawn, Pawn pawn2, CompProperties_SexInteractionRequirements sexpropsreq)
+ {
+ List interactionlist = GenerateInteractionDefList(pawn, pawn2, sexpropsreq);
+ if (!interactionlist.Any())
+ {
+ return null;
+ }
+ InteractionDef dictionaryKey = interactionlist.RandomElement();
+ bool rape = InteractionHelper.GetWithExtension(dictionaryKey).HasInteractionTag(InteractionTag.Rape);
+ SexProps sexProps = new SexProps();
+ sexProps.pawn = pawn;
+ sexProps.partner = pawn2;
+ sexProps.sexType = SexUtility.rjwSextypeGet(dictionaryKey);
+ sexProps.isRape = rape;
+ sexProps.isRapist = rape;
+ sexProps.canBeGuilty = false;
+ sexProps.dictionaryKey = dictionaryKey;
+ sexProps.rulePack = SexUtility.SexRulePackGet(dictionaryKey);
+ return sexProps;
+ }
+ }
+}
diff --git a/Source/Genes/Life_Force/CompProperties_AbilityPussyHeal.cs b/Source/Genes/Life_Force/CompProperties_AbilityPussyHeal.cs
new file mode 100644
index 0000000..16ba74d
--- /dev/null
+++ b/Source/Genes/Life_Force/CompProperties_AbilityPussyHeal.cs
@@ -0,0 +1,20 @@
+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;
+ }
+}
diff --git a/Source/Genes/Life_Force/CompProperties_SexInteractionRequirements.cs b/Source/Genes/Life_Force/CompProperties_SexInteractionRequirements.cs
new file mode 100644
index 0000000..3756d7b
--- /dev/null
+++ b/Source/Genes/Life_Force/CompProperties_SexInteractionRequirements.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using RimWorld;
+using rjw;
+using rjw.Modules.Interactions.Defs.DefFragment;
+using rjw.Modules.Interactions.Enums;
+
+namespace RJW_Genes
+{
+ public class CompProperties_SexInteractionRequirements : AbilityCompProperties
+ {
+ public CompProperties_SexInteractionRequirements()
+ {
+ this.compClass = typeof(CompAbility_SexInteractionRequirements);
+ }
+
+ public List tags = new List();
+ public InteractionRequirement dominantRequirement;
+ public InteractionRequirement submissiveRequirement;
+ }
+}
diff --git a/Source/Genes/Life_Force/Interactionchances.cs b/Source/Genes/Life_Force/Interactionchances.cs
new file mode 100644
index 0000000..fd7a43d
--- /dev/null
+++ b/Source/Genes/Life_Force/Interactionchances.cs
@@ -0,0 +1,16 @@
+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 Interaction_weights
+ {
+ public InteractionDef interaction;
+ public int weight = 1;
+
+ }
+}
diff --git a/Source/Genes/Life_Force/JobDriver_CastAbilityAfterSex.cs b/Source/Genes/Life_Force/JobDriver_CastAbilityAfterSex.cs
new file mode 100644
index 0000000..7198890
--- /dev/null
+++ b/Source/Genes/Life_Force/JobDriver_CastAbilityAfterSex.cs
@@ -0,0 +1,90 @@
+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 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 = CompAbility_SexInteractionRequirements.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;
+ }
+ }
+}
diff --git a/Source/Rjw-Genes.csproj b/Source/Rjw-Genes.csproj
index 3f24101..26c21bf 100644
--- a/Source/Rjw-Genes.csproj
+++ b/Source/Rjw-Genes.csproj
@@ -114,7 +114,14 @@
+
+
+
+
+
+
+