Induce lactation

This commit is contained in:
c0ffee12 2021-07-29 18:33:08 -07:00
parent 616d83ba08
commit f21812a3eb
11 changed files with 382 additions and 0 deletions

View file

@ -70,10 +70,14 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Source\CompProperties\CompProperties_InduceLactation.cs" />
<Compile Include="Source\Comps\CompInduceLactation.cs" />
<Compile Include="Source\HarmonyPatches\HarmonyPatch_Ideo.cs" />
<Compile Include="Source\HarmonyPatches\Harmony_PatchAll.cs" />
<Compile Include="Source\HarmonyPatches\RJW\HarmonyPatch_Milk_HumanCompHasGatherableBodyResource.cs" />
<Compile Include="Source\HediffDefOf\HediffDefOf_Milk.cs" />
<Compile Include="Source\JobDefOf\JobDefOf_CRIALactation.cs" />
<Compile Include="Source\JobDrivers\JobDriver_MassageBreasts.cs" />
<Compile Include="Source\LactationUtility.cs" />
<Compile Include="Source\PreceptDefOf\PreceptDefOf_Lactation.cs" />
<Compile Include="Source\Precepts\Precept_Lactation.cs" />
@ -81,12 +85,16 @@
<Compile Include="Source\RoleRequirements\RoleRequirement_Lactating.cs" />
<Compile Include="Source\Thoughts\ThoughtWorker_Precept_Lactating_Essential.cs" />
<Compile Include="Source\Thoughts\ThoughtWorker_Precept_Lactating_Essential_Social.cs" />
<Compile Include="Source\WorkGivers\WorkGiver_MassageBreasts.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Defs\JobDefs\Jobs_CRIALactation.xml" />
<Content Include="Defs\MemeDefs\Memes_Hucow.xml" />
<Content Include="Defs\PreceptDefs\Precepts_Lactating.xml" />
<Content Include="Defs\PreceptDefs\Precepts_Lactating_Role.xml" />
<Content Include="Defs\WorkGiverDefs\WorkGiver_MassageBreasts.xml" />
<Content Include="Patches\Patch_LactationInduction.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<JobDef>
<defName>MassageBreasts</defName>
<driverClass>CRIALactation.JobDriver_MassageBreasts</driverClass>
<reportString>stimulating TargetA's breasts.</reportString>
<allowOpportunisticPrefix>true</allowOpportunisticPrefix>
</JobDef>
</Defs>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<WorkGiverDef>
<defName>MassageBreasts</defName>
<label>massage TargetA's breasts</label>
<giverClass>CRIALactation.WorkGiver_MassageBreasts</giverClass>
<workType>Handling</workType>
<verb>massage</verb>
<gerund>stimulating the breasts of</gerund>
<priorityInType>91</priorityInType>
<requiredCapacities>
<li>Manipulation</li>
</requiredCapacities>
</WorkGiverDef>
</Defs>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationConditional">
<xpath>/Defs/ThingDef/comps</xpath>
<success>Always</success>
<nomatch Class="PatchOperationAdd">
<xpath>/Defs/ThingDef</xpath>
<value>
<comps />
</value>
</nomatch>
</li>
<li Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[@Name="BasePawn"]/comps</xpath>
<value>
<li Class="CRIALactation.CompProperties_InduceLactation">
<DaysToLactating>15</DaysToLactating>
<TimesMassagedADay>2.5</TimesMassagedADay>
</li>
</value>
</li>
</operations>
</Operation>
</Patch>

View file

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using rjw;
using Milk;
using UnityEngine;
namespace CRIALactation
{
public class CompProperties_InduceLactation : CompProperties
{
public CompProperties_InduceLactation()
{
this.compClass = typeof(CompInduceLactation);
}
public float DaysToLactating = 15;
public float TimesMassagedADay = 2.5f;
}
}

View file

@ -0,0 +1,131 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using rjw;
using Milk;
using UnityEngine;
namespace CRIALactation
{
public class CompInduceLactation : ThingComp
{
private readonly int OneDayInTicks = 60000;
private int TicksSinceLastMassage = -60000;
private float InductionCompletionPercent = 0f;
public bool isActive = false;
public bool CanMassage = true;
public override void CompTick()
{
base.CompTick();
Pawn p = this.parent as Pawn;
if (!isActive)
{
return;
}
if (!p.IsHashIntervalTick(100))
{
return;
}
if (LactationUtility.IsLactating(p))
{
isActive = false;
return;
}
if(TicksSinceLastMassage + OneDayInTicks / Props.TimesMassagedADay < GenTicks.TicksGame)
{
CanMassage = true;
}
if(InductionCompletionPercent >= 1)
{
string main = p.Name.ToStringShort + "'s breasts have been stimulated enough to induce lactation! They can now begin producing milk for their colony's consumption.";
var letter = LetterMaker.MakeLetter(p.Name.ToStringShort + " induced lactation", main, LetterDefOf.PositiveEvent);
Find.LetterStack.ReceiveLetter(letter);
LactationUtility.StartLactating(p, true);
isActive = false;
InductionCompletionPercent = 0.6f; //start at 60% in case they ever lose lactating again
}
}
public void MassageBreasts()
{
InductionCompletionPercent += (float)1 / (Props.DaysToLactating * (Props.TimesMassagedADay + Rand.Value));
TicksSinceLastMassage = GenTicks.TicksGame;
CanMassage = false;
}
public CompProperties_InduceLactation Props
{
get
{
return (CompProperties_InduceLactation)props;
}
}
public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn pawn)
{
if (pawn != this.parent as Pawn) yield break;
if (LactationUtility.IsLactating(pawn)) yield break;
if(LactationUtility.HasMilkableBreasts(this.parent as Pawn))
{
if(isActive)
{
//stop trying to induce lactation
yield return new FloatMenuOption("Undesignate induce lactation", () =>
{
isActive = false;
});
}
else
{
//induce lactation
yield return new FloatMenuOption("Designate induce lactation", () =>
{
isActive = true;
});
}
}
else
{
yield return new FloatMenuOption("Designate induce lactation (no milkable breasts)", null);
}
yield break;
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look<float>(ref this.InductionCompletionPercent, "InductionCompletionPercent", 0f);
Scribe_Values.Look<int>(ref this.TicksSinceLastMassage, "TicksSinceLastMassage", -60000);
Scribe_Values.Look<bool>(ref this.isActive, "IsActive", false);
Scribe_Values.Look<bool>(ref this.CanMassage, "CanMassage", false);
}
public override string CompInspectStringExtra()
{
if (!isActive) return null;
string result = "Induce lactation completion: " + InductionCompletionPercent.ToStringPercent();
if(CanMassage)
{
result += "\nReady to massage.";
}
return result;
}
}
}

View file

@ -0,0 +1,21 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace CRIALactation
{
[DefOf]
public static class JobDefOf_CRIALactation
{
static JobDefOf_CRIALactation()
{
DefOfHelper.EnsureInitializedInCtor(typeof(HediffDefOf_Milk));
}
public static JobDef MassageBreasts;
}
}

View file

@ -0,0 +1,79 @@
using Milk;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using Verse.AI;
namespace CRIALactation
{
public class JobDriver_MassageBreasts : JobDriver
{
private readonly float WorkTotal = 300f;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
LocalTargetInfo Target = job.GetTarget(TargetIndex.A);
return pawn.Reserve(Target, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);
Toil massage = new Toil();
massage.FailOnDespawnedOrNull(TargetIndex.A);
massage.FailOnAggroMentalStateAndHostile(TargetIndex.A);
massage.initAction = delegate
{
Pawn p = job.GetTarget(TargetIndex.A).Thing as Pawn;
pawn.pather.StopDead();
PawnUtility.ForceWait(p, 15000, null, true);
};
massage.tickAction = delegate ()
{
pawn.skills.Learn(SkillDefOf.Animals, 0.13f, false);
massageProgress += pawn.GetStatValue(StatDefOf.AnimalGatherSpeed, true);
};
massage.AddEndCondition(delegate
{
Pawn p = job.GetTarget(TargetIndex.A).Thing as Pawn;
if (massageProgress >= WorkTotal)
{
p.TryGetComp<CompInduceLactation>().MassageBreasts();
return JobCondition.Succeeded;
}
if (!(p.TryGetComp<CompInduceLactation>().isActive && p.TryGetComp<CompInduceLactation>().CanMassage))
{
return JobCondition.Incompletable;
}
return JobCondition.Ongoing;
});
massage.AddFinishAction(delegate {
Pawn pawn = this.job.GetTarget(TargetIndex.A).Thing as Pawn;
if (pawn != null && pawn.CurJobDef == JobDefOf.Wait_MaintainPosture)
{
pawn.jobs.EndCurrentJob(JobCondition.InterruptForced, true, true);
}
});
massage.defaultCompleteMode = ToilCompleteMode.Never;
massage.WithProgressBar(TargetIndex.A, () => massageProgress / WorkTotal);
massage.activeSkill = (() => SkillDefOf.Animals);
yield return massage;
yield break;
}
float massageProgress = 0f;
}
}

View file

@ -38,5 +38,17 @@ namespace CRIALactation
lactating.Severity = Rand.Value;
p.health.AddHediff(lactating, Genital_Helper.get_breastsBPR(p));
}
public static bool isMassageable(Pawn p)
{
CompInduceLactation c = p.TryGetComp<CompInduceLactation>();
if (c != null && c.isActive && c.CanMassage)
{
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,56 @@
using Milk;
using RimWorld;
using RimWorld.Planet;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
namespace CRIALactation
{
public class WorkGiver_MassageBreasts : WorkGiver_Scanner
{
public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
{
return pawn.Map.mapPawns.SpawnedPawnsInFaction(pawn.Faction);
}
public override bool ShouldSkip(Pawn pawn, bool forced = false)
{
List<Pawn> list = pawn.Map.mapPawns.SpawnedPawnsInFaction(pawn.Faction);
for(int i = 0; i < list.Count; i++)
{
if(LactationUtility.isMassageable(list[i]))
{
return false;
}
}
return true;
}
public override bool HasJobOnThing(Pawn p, Thing t, bool forced = false)
{
Pawn pawn2 = t as Pawn;
if(pawn2?.TryGetComp<CompInduceLactation>() == null)
{
return false;
}
CompInduceLactation c = pawn2.TryGetComp<CompInduceLactation>();
return p != pawn2 && c.isActive && c.CanMassage && !pawn2.Downed && !pawn2.Drafted && !pawn2.InAggroMentalState && !pawn2.IsFormingCaravan() && pawn2.CanCasuallyInteractNow(false, true, false) && p.CanReserve(pawn2, 1, -1, null, forced);
}
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
return JobMaker.MakeJob(JobDefOf_CRIALactation.MassageBreasts, t);
}
}
}