This commit is contained in:
Vegapnk 2024-07-01 14:32:53 +02:00
parent 92e917f473
commit 0190be4392
12 changed files with 396 additions and 7 deletions

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<DamageDef>
<defName>rjw_genes_dangerous_genitalia_damage</defName>
<label>stretch</label>
<workerClass>DamageWorker_AddInjury</workerClass>
<hediff>rjw_genes_dangerous_genitalia_damage_hediff</hediff>
<impactSoundType>Slice</impactSoundType>
<hasForcefulImpact>false</hasForcefulImpact>
<externalViolence>true</externalViolence>
<canInterruptJobs>false</canInterruptJobs>
<deathMessage>{0} was fucked to death by dangerous genitalia.</deathMessage>
<harmAllLayersUntilOutside>false</harmAllLayersUntilOutside>
<overkillPctToDestroyPart>0.5~1</overkillPctToDestroyPart>
</DamageDef>
<HediffDef ParentName="InjuryBase">
<defName>rjw_genes_dangerous_genitalia_damage_hediff</defName>
<initialSeverity>0.1</initialSeverity>
<displayWound>false</displayWound>
<stages>
<li>
<label>minor</label>
</li>
<li>
<minSeverity>1</minSeverity>
</li>
</stages>
<label>tear</label>
<labelNoun>an internal tear</labelNoun>
<description>Damage caused by close interaction with dangerous genitalia.</description>
<comps>
<li Class="HediffCompProperties_TendDuration">
<labelTendedWell>bandaged</labelTendedWell>
<labelTendedWellInner>sutured</labelTendedWellInner>
<labelSolidTendedWell>set</labelSolidTendedWell>
</li>
<li Class="HediffCompProperties_Infecter">
<infectionChance>0.15</infectionChance>
</li>
<li Class="HediffCompProperties_GetsPermanent">
<permanentLabel>rupture scar</permanentLabel>
</li>
</comps>
<injuryProps>
<painPerSeverity>0.025</painPerSeverity>
<averagePainPerSeverityPermanent>0.0125</averagePainPerSeverityPermanent>
<bleedRate>0.1</bleedRate>
<canMerge>true</canMerge>
<destroyedLabel>Torn off</destroyedLabel>
<destroyedOutLabel>Torn out</destroyedOutLabel>
</injuryProps>
</HediffDef>
</Defs>

View file

@ -26,4 +26,23 @@
</modExtensions>
</GeneDef>
<GeneDef >
<defName>rjw_genes_dangerous_genitalia</defName>
<label>Dangerous Genitalia</label>
<displayCategory>rjw_genes_damage</displayCategory>
<description>Carriers of this genes have spikes, hooks, rings, and other nasty things at their genitalia. They will harm partners, unless the partners are similiarly equipped. The damage is based on the difference in body- and part-size.</description>
<iconPath>UI/Icons/Rituals/TrialDefend</iconPath>
<displayOrderInCategory>5</displayOrderInCategory>
<biostatMet>-2</biostatMet>
<biostatCpx>1</biostatCpx>
<!-- Note: This Gene is mostly handled by a Patch "Patch_DangerousGenitalia.cs" -->
<modExtensions>
<li MayRequire="OskarPotocki.VanillaFactionsExpanded.Core" Class="VanillaGenesExpanded.GeneExtension">
<backgroundPathEndogenes>Genes/Icons/RJW_Genes_Endogene_Background</backgroundPathEndogenes>
<backgroundPathXenogenes>Genes/Icons/RJW_Genes_Xenogene_Background</backgroundPathXenogenes>
</li>
</modExtensions>
</GeneDef>
</Defs>

View file

@ -94,6 +94,7 @@ namespace RJW_Genes
// Damage & Side Effects
[MayRequire("LustLicentia.RJWLabs")] public static readonly GeneDef rjw_genes_elasticity;
public static readonly GeneDef rjw_genes_unbreakable;
public static readonly GeneDef rjw_genes_dangerous_genitalia;
// Special
public static readonly GeneDef rjw_genes_orgasm_rush;

View file

@ -0,0 +1,292 @@
using HarmonyLib;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using static rjw.Dialog_Sexcard;
namespace RJW_Genes
{
[HarmonyPatch(typeof(JobDriver_Sex), "SexTick")]
public static class Patch_DangerousGenitalia
{
const float BASE_DAMAGE = 1.5f;
const float BASE_APPLICATION_CHANCE = 1.0f;
const float BASE_CHANCE_FOR_INNER_DAMAGE = 0.3f;
const float THRESHOLD_FOR_INNER_DAMAGE = 2.0f;
public static void Postfix(JobDriver_Sex __instance)
{
if (__instance == null || __instance.Sexprops == null) return;
// Below automatically checks that not both pawns have dangerous genitalia
Pawn dangerousGenitaliaPawn = ExactlyOnePawnHasDangerousGenitalia(__instance.Sexprops.pawn, __instance.Sexprops.partner);
if (dangerousGenitaliaPawn == null) return;
Pawn fuckedPawn = GetPawnThatHasNotTheDangerousGenitalia(__instance.Sexprops.pawn, __instance.Sexprops.partner);
if (dangerousGenitaliaPawn.IsHashIntervalTick(__instance.ticks_between_thrusts)) {
if ((new System.Random()).NextDouble() < BASE_APPLICATION_CHANCE)
{
// TODO: Setting only for Rape
// TODO: Testing
ModLog.Message($"Test - Tick {__instance.Sexprops.pawn} <> {__instance.Sexprops.partner} --- Thrust");
ApplyDangerousGenitaliaDamage(dangerousGenitaliaPawn, fuckedPawn, __instance.Sexprops);
}
}
}
private static void ApplyDangerousGenitaliaDamage(Pawn damager, Pawn damaged, SexProps props)
{
DamageWorker DamageWorker = new DamageWorker_AddInjury();
bool damagerIsMaleOrFuta = GenderUtility.IsMale(damager) || Genital_Helper.has_penis_fertile(damager) || Genital_Helper.has_penis_infertile(damager);
float penetrator_genitalsize = GetBiggestGenitalSize(damager);
float penetrator_bodysize = GetBodySize(damager, GetBiggestGenital(damager, penis: damagerIsMaleOrFuta));
switch (props.sexType)
{
case xxx.rjwSextype.Oral:
{
float penetrated_bodysize = GetBodySize(damaged);
float damage = CalculateSizeRelatedDamage(penetrator_bodysize, penetrator_genitalsize, penetrated_bodysize, 0.0f);
bool allow_for_inner_damage = damage >= THRESHOLD_FOR_INNER_DAMAGE;
DamageInfo dInfo = new DamageInfo(
VariousDefOf.rjw_genes_dangerous_genitalia_damage,
damage,
instigator: damager, category: DamageInfo.SourceCategory.ThingOrUnknown,
hitPart: GetRandomOralBodyPartRecord(damaged, allow_for_inner_damage));
DamageWorker.Apply(dInfo, damaged);
} break;
case xxx.rjwSextype.Vaginal:
{
Hediff vagina = Genital_Helper.get_AllPartsHediffList(damaged).First(part => Genital_Helper.is_vagina(part));
if (vagina == null) return;
CompHediffBodyPart comps = vagina.TryGetComp<rjw.CompHediffBodyPart>();
float penetrated_bodysize = GetBodySize(damaged, vagina);
float damage = CalculateSizeRelatedDamage(penetrator_bodysize, penetrator_genitalsize, penetrated_bodysize, vagina.Severity);
bool allow_for_inner_damage = damage >= THRESHOLD_FOR_INNER_DAMAGE;
DamageInfo dInfo = new DamageInfo(
VariousDefOf.rjw_genes_dangerous_genitalia_damage,
damage,
instigator: damager, category: DamageInfo.SourceCategory.ThingOrUnknown,
hitPart: GetRandomGenitalBodyPartRecord(damaged, allow_for_inner_damage));
DamageWorker.Apply(dInfo, damaged);
} break;
case xxx.rjwSextype.Anal:
{
Hediff anus = Genital_Helper.get_AllPartsHediffList(damaged).First(part => Genital_Helper.is_anus(part));
if (anus == null) return;
CompHediffBodyPart comps = anus.TryGetComp<rjw.CompHediffBodyPart>();
float penetrated_bodysize = GetBodySize(damaged, anus);
float damage = CalculateSizeRelatedDamage(penetrator_bodysize, penetrator_genitalsize, penetrated_bodysize, anus.Severity);
bool allow_for_inner_damage = damage >= THRESHOLD_FOR_INNER_DAMAGE;
DamageInfo dInfo = new DamageInfo(
VariousDefOf.rjw_genes_dangerous_genitalia_damage,
damage,
instigator: damager, category: DamageInfo.SourceCategory.ThingOrUnknown,
hitPart: GetRandomAnalBodyPartRecord(damaged, allow_for_inner_damage));
DamageWorker.Apply(dInfo, damaged);
} break;
default: return;
}
}
/// <summary>
/// Checks both pawns for their genes,
/// and returns the pawn that has the rjw_genes_dangerous_genitalia def.
/// Returns null if both or none have it.
/// </summary>
private static Pawn ExactlyOnePawnHasDangerousGenitalia(Pawn pawnA, Pawn pawnB)
{
if (pawnA == null) return null;
if (pawnB == null) return null;
bool pawnAHasDangerousGen = pawnA.genes != null && pawnA.genes.HasActiveGene(GeneDefOf.rjw_genes_dangerous_genitalia);
bool pawnBHasDangerousGen = pawnB.genes != null && pawnB.genes.HasActiveGene(GeneDefOf.rjw_genes_dangerous_genitalia);
if (pawnAHasDangerousGen && pawnBHasDangerousGen)
return null;
else if (!pawnAHasDangerousGen && !pawnBHasDangerousGen)
return null;
else if (pawnAHasDangerousGen)
{
return pawnA;
} else if(pawnBHasDangerousGen)
{
return pawnB;
}
return null;
}
// This method assumes that the pawn is the penetrator - the biggest penis-severity is returned.
private static float GetBiggestGenitalSize(Pawn pawn)
{
float best = 0.0f;
var parts = Genital_Helper.get_AllPartsHediffList(pawn);
foreach (var part in parts)
{
if (Genital_Helper.is_sex_part(part) && Genital_Helper.is_penis(part)) {
best = part.Severity > best ? part.Severity : best;
}
}
return best;
}
/// <summary>
/// Calculates the damage that will be applied when penetrated based on the genital and body sizes.
/// This is done by a simple multiplier on the base damaged, based on the absolute differences in sizes.
/// </summary>
/// <param name="penetrator_bodysize">The bodysize of the pawn, or, if applicable, the bodysize of the damaging genital.</param>
/// <param name="penetrator_genitalsize">The severity of the penetrating body-part</param>
/// <param name="penetrated_bodysize">The bodysize of the penetrated, or, if applicable, the bodysize of the damaged genital.</param>
/// <param name="penetrated_genitalsize">The severity of the penetrated body-part, 0 for oral.</param>
/// <returns>The damage applied to a bodypart-record, 0.0 if there is no damage to be done.</returns>
private static float CalculateSizeRelatedDamage(float penetrator_bodysize, float penetrator_genitalsize, float penetrated_bodysize, float penetrated_genitalsize=0.0f)
{
float diff_in_bodysize = penetrator_bodysize - penetrated_bodysize;
float diff_in_genital_size = penetrator_genitalsize - penetrated_genitalsize;
float damage_multiplier = 1.0f + diff_in_bodysize + diff_in_genital_size;
if (damage_multiplier > 0.5f)
return BASE_DAMAGE * damage_multiplier;
// If the size differences are too big, but the penetrator is the small one, do not deal damage.
else
return 0.0f;
}
/// <summary>
/// Returns the pawn that does not have the gene for dangerous genitalia.
/// Returns null if neither or both have it.
/// </summary>
private static Pawn GetPawnThatHasNotTheDangerousGenitalia(Pawn a, Pawn b)
{
Pawn withGenitalia = ExactlyOnePawnHasDangerousGenitalia(a,b);
if (withGenitalia == null) return null;
else if (a == withGenitalia) return b;
else if (b == withGenitalia) return a;
else return null;
}
/// <summary>
/// Returns the biggest (by bodysize, then by severity) Genital.
/// Biggest mean highest severity for penisses, and lowest for vaginas.
/// </summary>
/// <param name="pawn">The pawn for whom to look up.</param>
/// <param name="penis">Whether to look for penisses - this should be true for males and futas.</param>
/// <returns>Largest Penis or Tightest Vagina. Null in case of errors / non genitals. </returns>
private static Hediff GetBiggestGenital(Pawn pawn, bool penis = true) {
Hediff best = null;
var parts = Genital_Helper.get_AllPartsHediffList(pawn);
if (penis)
{
foreach (var part in parts)
{
if (Genital_Helper.is_penis(part))
{
if (best == null) best = part;
CompHediffBodyPart CompHediff = part.TryGetComp<rjw.CompHediffBodyPart>();
CompHediffBodyPart CompHediffBest = best.TryGetComp<rjw.CompHediffBodyPart>();
if (CompHediffBest.SizeOwner > CompHediff.SizeOwner)
{
best = part;
} else if (CompHediffBest.SizeOwner == CompHediff.SizeOwner) {
best = part.Severity > best.Severity? part : best;
}
}
}
} else
{
foreach (var part in parts)
{
if (Genital_Helper.is_vagina(part))
{
if (best == null) best = part;
CompHediffBodyPart CompHediff = part.TryGetComp<rjw.CompHediffBodyPart>();
CompHediffBodyPart CompHediffBest = best.TryGetComp<rjw.CompHediffBodyPart>();
if (CompHediffBest.SizeOwner < CompHediff.SizeOwner)
{
best = part;
}
else if (CompHediffBest.SizeOwner == CompHediff.SizeOwner)
{
best = part.Severity < best.Severity ? part : best;
}
}
}
}
return best;
}
/// <summary>
/// Gets the (relevant) bodysize of a pawn or it's genital.
/// "Null" as genital input is meant for a fallback, or for oral. It will return the pawns bodysize.
/// </summary>
private static float GetBodySize(Pawn pawn, Hediff genital = null)
{
if (pawn == null) return 0;
if (genital == null) return pawn.BodySize;
if(rjw.Genital_Helper.is_sex_part(genital)){
CompHediffBodyPart CompHediff = genital.TryGetComp<rjw.CompHediffBodyPart>();
if (CompHediff != null)
{
return CompHediff.SizeOwner;
}
}
// Fallback!
return 0.0f;
}
private static BodyPartRecord GetRandomAnalBodyPartRecord(Pawn pawn, bool InnerOrgansPossible = false)
{
if (pawn == null) return null;
if (InnerOrgansPossible && (new System.Random()).NextDouble() < 1 - BASE_CHANCE_FOR_INNER_DAMAGE)
return rjw.Genital_Helper.get_torsoBPR(pawn);
return Genital_Helper.get_anusBPR(pawn);
}
private static BodyPartRecord GetRandomOralBodyPartRecord(Pawn pawn, bool InnerOrgansPossible = false)
{
if (pawn == null) return null;
if (InnerOrgansPossible && (new System.Random()).NextDouble() < 1 - BASE_CHANCE_FOR_INNER_DAMAGE)
return rjw.Genital_Helper.get_torsoBPR(pawn);
return Genital_Helper.get_mouthBPR(pawn);
}
private static BodyPartRecord GetRandomGenitalBodyPartRecord(Pawn pawn, bool InnerOrgansPossible = false)
{
if (pawn == null) return null;
if (InnerOrgansPossible && (new System.Random()).NextDouble() < 1 - BASE_CHANCE_FOR_INNER_DAMAGE)
return rjw.Genital_Helper.get_torsoBPR(pawn);
return Genital_Helper.get_genitalsBPR(pawn);
}
}
}

View file

@ -43,7 +43,7 @@ namespace RJW_Genes
{
return Toils_General.Do(delegate
{
if (this.pawn.interactions.TryInteractWith(this.Target, ThoughtDefOf.rjw_genes_flirt))
if (this.pawn.interactions.TryInteractWith(this.Target, VariousDefOf.rjw_genes_flirt))
{
Need_Sex need_Sex = this.Target.needs.TryGetNeed<Need_Sex>();
need_Sex.CurLevel += -0.01f;

View file

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using UnityEngine;
using Verse;
namespace RJW_Genes.Genes.Special
namespace RJW_Genes
{
[HarmonyPatch(typeof(SexUtility), "Aftersex")]
public static class Patch_AgeDrain

View file

@ -22,6 +22,8 @@ namespace RJW_Genes
[MayRequire("LustLicentia.RJWLabs")] public static readonly HediffDef rjw_genes_cumstuffed_counter;
[MayRequire("LustLicentia.RJWLabs")] public static readonly HediffDef rjw_genes_cumflation_counter;
public static readonly HediffDef rjw_genes_dangerous_genitalia_damage_hediff;
public static readonly HediffDef OvaryAgitator;
public static readonly HediffDef Bioscaffold;

View file

@ -73,7 +73,8 @@
<Compile Include="Genes\Breeding\Genes\Gene_FerventOvipositor.cs" />
<Compile Include="Genes\Breeding\Genes\Gene_InsectIncubator.cs" />
<Compile Include="Genes\Cum\Patch_LikesCumflation.cs" />
<Compile Include="Genes\Damage\Gene_Elasticity.cs" />
<Compile Include="Genes\Damage\Genes\Gene_Elasticity.cs" />
<Compile Include="Genes\Damage\Patches\Patch_DangerousGenitalia.cs" />
<Compile Include="Genes\Life_Force\Events\SuccubusVisit\IncidentWorker_SuccubusVisit.cs" />
<Compile Include="Genes\Life_Force\Events\SuccubusVisit\LordJob_SuccubusVisit.cs" />
<Compile Include="Genes\Life_Force\Interactions\SuccubusTailjob\DomSuccubusTailCustomRequirementHandler.cs" />
@ -94,8 +95,8 @@
<Compile Include="Genes\Cum\Gene_MuchCum.cs" />
<Compile Include="Genes\Cum\Gene_NoCum.cs" />
<Compile Include="Genes\Cum\Patch_TransferNutrition.cs" />
<Compile Include="Genes\Damage\Gene_Unbreakable.cs" />
<Compile Include="Genes\Cum\Patch_CumflationImmunity.cs" />
<Compile Include="Genes\Damage\Genes\Gene_Unbreakable.cs" />
<Compile Include="Genes\ExtraGenitalia\Gene_ExtraBreasts.cs" />
<Compile Include="Genes\ExtraGenitalia\Gene_ExtraAnus.cs" />
<Compile Include="Genes\ExtraGenitalia\Gene_Futa.cs" />
@ -196,6 +197,7 @@
<Compile Include="Settings\RJW_Genes_Settings.cs" />
<Compile Include="Settings\RJW_Genes_SettingsController.cs" />
<Compile Include="ThoughtDefOf.cs" />
<Compile Include="VariousDefOf.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="GeneDefs_GenitaliaTypeEndogenes.xml" />

View file

@ -14,8 +14,5 @@ namespace RJW_Genes
public static readonly ThoughtDef rjw_genes_seduced;
public static readonly ThoughtDef rjw_genes_pheromone_carrier_nearby;
//Others with same names but other defs than in genedefof
public static readonly InteractionDef rjw_genes_flirt;
}
}

19
Source/VariousDefOf.cs Normal file
View file

@ -0,0 +1,19 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace RJW_Genes
{
[DefOf]
public class VariousDefOf
{
public static readonly DamageDef rjw_genes_dangerous_genitalia_damage;
//Others with same names but other defs than in genedefof
public static readonly InteractionDef rjw_genes_flirt;
}
}