Adding Lifeforce and succubus wings gene

Adds lifeforce gene and functions for gaining lifeforce from oral sex and mental break if lifeforce reaches zero. Also add succubus wings drawn by me
This commit is contained in:
Shabakur 2022-12-27 13:48:38 +01:00
parent f0a9cce9c9
commit 1f865320b0
25 changed files with 690 additions and 39 deletions

View file

@ -33,5 +33,35 @@ namespace RJW_Genes
}
}
//Get total fluidamount a persom has.
public static float GetTotalFluidAmount(Pawn pawn, float multiplier = 1f)
{
var partBPR = Genital_Helper.get_genitalsBPR(pawn);
var parts = Genital_Helper.get_PartsHediffList(pawn, partBPR);
float total_cum = 0;
if (!parts.NullOrEmpty())
{
CompHediffBodyPart CompHediff;
foreach (Hediff part in parts)
{
if (GenitaliaChanger.IsArtificial(part))
continue;
if (rjw.Genital_Helper.is_penis(part))
{
CompHediff = part.TryGetComp<rjw.CompHediffBodyPart>();
if (CompHediff != null)
{
total_cum += CompHediff.FluidAmmount * multiplier;
}
}
}
}
return total_cum;
}
}
}

View file

@ -14,6 +14,41 @@ namespace RJW_Genes
return pawn.genes.HasGene(GeneDefOf.rjw_genes_mechbreeder);
}
public static bool HasLifeForce(Pawn pawn)
{
if (pawn.genes == null)
{
return false;
}
return pawn.genes.HasGene(GeneDefOf.rjw_genes_lifeforce);
}
public static bool HasLowLifeForce(Pawn pawn)
{
if (HasLifeForce(pawn))
{
Gene_LifeForce gene = pawn.genes.GetFirstGeneOfType<Gene_LifeForce>();
if (gene.Resource.Value < gene.targetValue)
{
return true;
}
}
return false;
}
public static bool HasCriticalLifeForce(Pawn pawn)
{
if (HasLifeForce(pawn))
{
Gene_LifeForce gene = pawn.genes.GetFirstGeneOfType<Gene_LifeForce>();
if (gene.Resource.Value < gene.MinLevelForAlert)
{
return true;
}
}
return false;
}
public static bool IsInsectIncubator(Pawn pawn)
{
if (pawn.genes == null)

View file

@ -0,0 +1,83 @@
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 += "NeverConsumeCum";
}
else
{
text = text + ("ConsumeCumBelow" + ": ") + 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>>();
}
}

View file

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using RimWorld;
namespace RJW_Genes
{
public class Gene_LifeForce : Gene_Resource, IGeneResourceDrain
{
public override void ExposeData()
{
base.ExposeData();
}
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;
}
IEnumerator<Gizmo> enumerator = null;
foreach (Gizmo gizmo2 in GeneResourceDrainUtility.GetResourceDrainGizmos(this))
{
yield return gizmo2;
}
enumerator = null;
yield break;
yield break;
}
//Depending on how low the value is it will increase sexdrive and if it reaches zero it will create a random rape mental break.
//Not using base.Tick() as it is used to start mental breaks, but we have another way to do it.
public override void Tick()
{
//base.Tick();
if (this.CanOffset && this.Resource != null)
{
this.Resource.Value -= this.ResourceLossPerDay / 60000;
if (this.Resource.Value <= 0 && this.pawn.IsHashIntervalTick(300))
{
if (ModsConfig.BiotechActive && this.def.mentalBreakDef != null &&
this.pawn.Spawned && !this.pawn.InMentalState && !this.pawn.Downed &&
this.def.mentalBreakDef.Worker.BreakCanOccur(this.pawn))
{
this.def.mentalBreakDef.Worker.TryStart(this.pawn, "MentalStateReason_Gene".Translate() + ": " + this.LabelCap, false);
}
}
}
//GeneResourceDrainUtility.TickResourceDrain(this);
}
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.15f;
}
}
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

@ -0,0 +1,38 @@
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

@ -0,0 +1,32 @@
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

@ -0,0 +1,23 @@
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

@ -0,0 +1,15 @@
using System;
using Verse;
using Verse.AI;
using rjw;
namespace RJW_Genes
{
// Token: 0x020000FB RID: 251
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

@ -0,0 +1,36 @@
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
{
[HarmonyPatch(typeof(SexUtility), nameof(SexUtility.SatisfyPersonal))]
public static class Patch_LifeForce
{
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;
if (GeneUtility.HasLifeForce(props.pawn))
{
if (props.sexType == xxx.rjwSextype.Oral || props.sexType == xxx.rjwSextype.Fellatio || props.sexType == xxx.rjwSextype.Sixtynine)
{
Pawn_GeneTracker genes = props.pawn.genes;
Gene_LifeForce gene = genes.GetFirstGeneOfType<Gene_LifeForce>();
gene.Resource.Value += CumUtility.GetTotalFluidAmount(props.partner); //total amount may need to be modified to be balanced
}
}
}
}
}