Added animation selecting based on defined contexts

This commit is contained in:
c0ffee 2024-04-17 16:57:18 -07:00
parent 16af5bb63a
commit db4c2d3833
29 changed files with 506 additions and 259 deletions

View file

@ -4,13 +4,8 @@
<defName>TestAnimation1</defName>
<durationTicks>400</durationTicks>
<playWhenDowned>False</playWhenDowned>
<modExtensions>
<li Class="Rimworld_Animations.AltitudeLayer">
<PawnAltitudeLayer>Pawn</PawnAltitudeLayer>
</li>
</modExtensions>
<animationParts>
<!--Note: Rotate animal body by setting body rotation, not head rotation; different for animals-->
<li>
<key>Root</key>
<value>
@ -19,58 +14,52 @@
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>30</tick>
<angle>0</angle>
<rotation>North</rotation>
<visible>true</visible>
</li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>100</tick>
<angle>0</angle>
<rotation>East</rotation>
<visible>true</visible>
</li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>200</tick>
<angle>0</angle>
<rotation>South</rotation>
<visible>true</visible>
</li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>300</tick>
<angle>0</angle>
<rotation>West</rotation>
<visible>true</visible>
</li>
</keyframes>
</value>
</li>
<!--
<li>
<key>Body</key>
<value>
<workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
<keyframes>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>30</tick>
<angle>30</angle>
<tick>0</tick>
<angle>0</angle>
<rotation>North</rotation>
<visible>false</visible>
</li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>300</tick>
<angle>150</angle>
<tick>50</tick>
<angle>0</angle>
<rotation>South</rotation>
<visible>true</visible>
</li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>600</tick>
<tick>100</tick>
<angle>30</angle>
<rotation>North</rotation>
<rotation>East</rotation>
<visible>false</visible>
</li>
</keyframes>
</value>
</li>
-->
<li>
<key>Head</key>
<value>

View file

@ -10,23 +10,31 @@
<li>
<key>Root</key>
<value>
<workerClass>AnimationWorker_Keyframes</workerClass>
<workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
<keyframes>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<offset>(0, -1, 0)</offset>
<tick>0</tick>
<angle>23</angle>
<rotation>North</rotation>
</li>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<offset>(0, -1, 0)</offset>
<tick>6</tick>
<angle>-5</angle>
<rotation>North</rotation>
</li>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<offset>(0, -1, 0)</offset>
<tick>12</tick>
<angle>4</angle>
<rotation>North</rotation>
</li>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<offset>(0, -1, 0)</offset>
<tick>188</tick>
<angle>-1</angle>
<rotation>North</rotation>
</li>
</keyframes>
</value>
@ -34,23 +42,27 @@
<li>
<key>Head</key>
<value>
<workerClass>AnimationWorker_Keyframes</workerClass>
<workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
<keyframes>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>0</tick>
<angle>5</angle>
<angle>0</angle>
<rotation>North</rotation>
</li>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>6</tick>
<angle>-5</angle>
<rotation>North</rotation>
</li>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>12</tick>
<angle>4</angle>
<rotation>North</rotation>
</li>
<li>
<li Class="Rimworld_Animations.ExtendedKeyframe">
<tick>128</tick>
<angle>-1</angle>
<angle>0</angle>
<rotation>North</rotation>
</li>
</keyframes>
</value>

View file

@ -1,41 +1,41 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<GroupAnimationDef>
<Rimworld_Animations.GroupAnimationDef>
<defName>TestGroupAnimation1</defName>
<numActors>2</numActors>
<AnimationStages>
<animationStages>
<li Class="Rimworld_Animations.AnimationStage_TicksDuration">
<ticks>200</ticks>
<animationDefs>
<li>Pawn1_Stage1_TestAnimation1</li>
<li>Pawn2_Stage1_TestAnimation2</li>
<li>TestAnimation1</li>
<li>TestAnimation2</li>
</animationDefs>
</li>
<li Class="Rimworld_Animations.AnimationStage_LoopRandomSelectChance">
<loops>10</loops>
<animationOptions>
<loopOptions>
<li>
<probability>3</probability>
<animationDefs>
<li>Pawn1_Stage2_Variant1</li>
<li>Pawn2_Stage2_Variant1</li>
<li>TestAnimation1</li>
<li>TestAnimation2</li>
</animationDefs>
</li>
<li>
<probability>1</probability>
<animationDefs>
<li>Pawn1_Stage2_Variant2</li>
<li>Pawn2_Stage2_Variant2</li>
<li>TestAnimation1</li>
<li>TestAnimation2</li>
</animationDefs>
</li>
</animationOptions>
</loopOptions>
</li>
<!--
<li Class="Rimworld_Animations.AnimationStage_Branch">
<paths>
<li Class="Rimworld_Animations.AnimationStage_LoopRandomSelectChance">
<numberOfLoops>10</numberOfLoops>
<loops>10</loops>
<animationOptions>
<li>
<probability>3</probability>
@ -52,7 +52,7 @@
</li>
<li Class="Rimworld_Animations.AnimationStage_LoopRandomSelectChance">
<numberOfLoops>10</numberOfLoops>
<loops>10</loops>
<animationOptions>
<li>
<probability>3</probability>
@ -70,42 +70,25 @@
</li>
</paths>
</li>
</AnimationStages>
-->
</animationStages>
<contexts>
<li Class="Rimworld_Animations.Actor_Sextype">
<sexTypes>
<li>Sex</li>
</sexTypes>
<actorShift>0</actorShift>
<li Class="Rimworld_Animations.GroupAnimationContext_RJWSex">
<interactionDefs>
<li>Sex_Anal</li>
<li>Sex_Vaginal</li>
</interactionDefs>
</li>
<li Class="Rimworld_Animations.Actor_Sextype">
<sexTypes>
<li>Sex_Reverse</li>
</sexTypes>
<actorShift>1</actorShift>
</li>
<li Class="Rimworld_Animations.Actor_Custom_Races">
<sexTypes>
<li>Sex</li>
</sexTypes>
<races>
<li>Human</li>
<li>Dog</li>
</races>
<actorShift>0</actorShift>
</li>
<li Class="Rimworld_Animations.Actor_Custom_Races">
<sexTypes>
<li>Sex_Reverse</li>
</sexTypes>
<races>
<li>Dog</li>
<li>Human</li>
</races>
<li Class="Rimworld_Animations.GroupAnimationContext_RJWSex">
<actorShift>1</actorShift>
<interactionDefs>
<li>Sex_Reverse_Anal</li>
<li>Sex_Reverse_Vaginal</li>
</interactionDefs>
</li>
</contexts>
</GroupAnimationDef>
</Rimworld_Animations.GroupAnimationDef>
</Defs>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Core SK</li>
</mods>
<match Class="PatchOperationSequence">
<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="BaseAnimalPawn" or @Name="SK_BasePawn" or @Name="BasePawnSkynet"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</li>
</operations>
</match>
</Operation>
</Patch>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAdd">
<success>Always</success>
<xpath>Defs/ThingDef[race][not(comps)]</xpath>
<value>
<comps />
</value>
</li>
<li Class="PatchOperationAdd">
<success>Always</success>
<xpath>Defs/AlienRace.ThingDef_AlienRace[not(comps)]</xpath>
<value>
<comps />
</value>
</li>
<li Class="PatchOperationAdd">
<xpath>Defs/ThingDef[@Name="BasePawn"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</li>
<li Class="PatchOperationAdd">
<xpath>Defs/AlienRace.ThingDef_AlienRace/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</li>
</operations>
</Operation>
</Patch>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationConditional">
<xpath>/Defs/ThingDef[@Name="BaseBaseAutocleaner"]/comps</xpath>
<success>Always</success>
<match Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[@Name="BaseBaseAutocleaner"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</match>
</li>
</operations>
</Operation>
</Patch>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationConditional">
<xpath>/Defs/ThingDef[@Name="BasePawnSimple"]/comps</xpath>
<success>Always</success>
<match Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[@Name="BasePawnSimple"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</match>
</li>
</operations>
</Operation>
</Patch>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationConditional">
<xpath>/Defs/ThingDef[@Name="BaseZombie"]/comps</xpath>
<success>Always</success>
<match Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[@Name="BaseZombie"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</match>
</li>
</operations>
</Operation>
</Patch>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<!-- Patch for HCSK, to attach to differently written thingdefs -->
<Operation Class="PatchOperationFindMod">
<mods>
<li>Core SK</li>
</mods>
<match 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="SK_BasePawn"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</li>
<li Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[@Name="BaseAnimalPawn"]/comps</xpath>
<value>
<li Class="Rimworld_Animations.CompProperties_ExtendedAnimator" />
</value>
</li>
</operations>
</match>
</Operation>
</Patch>

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using rjw;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Sound;
namespace Rimworld_Animations {
public class CompExtendedAnimator : ThingComp
{
List<Tuple<int, AnimationDef>> animationQueue;
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace Rimworld_Animations {
public class CompProperties_ExtendedAnimator : CompProperties
{
public CompProperties_ExtendedAnimator()
{
base.compClass = typeof(CompExtendedAnimator);
}
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations
{
public abstract class BaseGroupAnimationContext
{
public int actorShift = 0;
public abstract bool CanAnimationBeUsed(List<Pawn> actors, out int reorder);
//cool class for designating contexts for animations
// configure CanAnimationBeUsed to test whether it can be used
}
}

View file

@ -0,0 +1,29 @@
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using static rjw.xxx;
namespace Rimworld_Animations
{
public class GroupAnimationContext_RJWSex : BaseGroupAnimationContext
{
public List<InteractionDef> interactionDefs;
public override bool CanAnimationBeUsed(List<Pawn> actors, out int reorder)
{
Log.Message("Testing this animation");
JobDriver_SexBaseInitiator latestSexBaseInitiator = (actors.FindLast(x => x.jobs?.curDriver is JobDriver_SexBaseInitiator).jobs.curDriver as JobDriver_SexBaseInitiator);
reorder = base.actorShift;
return interactionDefs.Contains(latestSexBaseInitiator.Sexprops.dictionaryKey);
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations
{
public class GroupAnimationDef : Def
{
public int numActors;
public List<AnimationStage> animationStages;
public List<BaseGroupAnimationContext> contexts;
public bool canAnimationBeUsed(List<Pawn> actors, out int reorder)
{
foreach (BaseGroupAnimationContext context in contexts)
{
if (context.CanAnimationBeUsed(actors, out reorder))
{
return true;
}
}
reorder = 0;
return false;
}
}
}

View file

@ -9,7 +9,7 @@ namespace Rimworld_Animations
{
public class AnimationStage_Branch : AnimationStage
{
List<AnimationStage> paths;
public List<AnimationStage> paths;
public override List<Tuple<int, AnimationDef>> GetAnimations(int actor, int seed)
{
return paths[(seed * 59) % paths.Count].GetAnimations(actor, seed);

View file

@ -10,11 +10,11 @@ namespace Rimworld_Animations
public class AnimationStage_LoopRandomSelectChance : AnimationStage
{
public int loops;
public List<AnimationLoopOption> animationOptions;
public List<AnimationLoopOption> loopOptions;
public override List<Tuple<int, AnimationDef>> GetAnimations(int actor, int seed)
{
int numberOfActors = animationOptions[0].animationDefs.Count;
int numberOfActors = loopOptions[0].animationDefs.Count;
List<Tuple<int, AnimationDef>> animations = new List<Tuple<int, AnimationDef>>();
for (int i = 0; i < loops; i++)
@ -37,11 +37,11 @@ namespace Rimworld_Animations
//select random element from loop options by weight; also calculate the longest anim length
public AnimationLoopOption getAnimationLoopOptionByWeight(int seed, out int longestAnimLength)
{
int totalWeight = animationOptions.Sum(x => x.probability);
int totalWeight = loopOptions.Sum(x => x.probability);
int randomNumber = (seed * 56) % totalWeight;
int cumulativeWeight = 0;
foreach(AnimationLoopOption option in animationOptions) {
foreach(AnimationLoopOption option in loopOptions) {
cumulativeWeight += option.probability;
if (randomNumber <= cumulativeWeight)
{
@ -50,8 +50,8 @@ namespace Rimworld_Animations
}
}
longestAnimLength = animationOptions[0].animationDefs.Max(x => x.durationTicks);
return animationOptions[0];
longestAnimLength = loopOptions[0].animationDefs.Max(x => x.durationTicks);
return loopOptions[0];
}

View file

@ -13,7 +13,7 @@ namespace Rimworld_Animations
public Rot4 rotation;
public SoundDef sound = null;
public bool visible;
public bool visible = true;
}
}

View file

@ -40,8 +40,14 @@ namespace Rimworld_Animations {
int AnimationTimeTicks = 0;
List<Pawn> participants = partnerSexBaseReceiver.parteners.Append(partner).ToList();
RerollAnimations(participants);
GroupAnimationDef groupAnimation = AnimationUtility.FindGroupAnimation(participants, out int reorder);
if (groupAnimation != null)
{
AnimationUtility.StartGroupAnimation(participants, groupAnimation, reorder);
}
//Modify Orgasm ticks to only orgasm as many times as RJW stock orgasm allows
if (AnimationTimeTicks != 0)
@ -53,14 +59,6 @@ namespace Rimworld_Animations {
}
}
public static void RerollAnimations(List<Pawn> participants) {
participants[0].Drawer.renderer.SetAnimation(AnimationDefOf.TestAnimation1);
participants[1].Drawer.renderer.SetAnimation(AnimationDefOf.TestAnimation2);
}
static IEnumerable<String> NonSexActRulePackDefNames = new String[]
{
"MutualHandholdingRP",
@ -77,12 +75,14 @@ namespace Rimworld_Animations {
}
}
[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "End")]
static class HarmonyPatch_JobDriver_SexBaseInitiator_End {
public static void Postfix(ref JobDriver_SexBaseInitiator __instance)
public static void Prefix(ref JobDriver_SexBaseInitiator __instance)
{
//stop animation
AnimationUtility.StopGroupAnimation(__instance.pawn);
}
}
}

View file

@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using rjw;
using System.Reflection.Emit;
using Verse.AI;
namespace Rimworld_Animations
{
[HarmonyPatch(typeof(JobDriver_SexBaseRecieverLoved), "MakeSexToil")]
public static class HarmonyPatch_JobDriver_SexBaseReceiverLoved
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> codeInstructions)
{
var ins = codeInstructions.ToList();
for(int i = 0; i < ins.Count; i++)
{
if(i < ins.Count && ins[i].opcode == OpCodes.Call && ins[i].OperandIs(AccessTools.DeclaredMethod(typeof(Toils_LayDown), "LayDown"))) {
ins[i].operand = AccessTools.DeclaredMethod(typeof(HarmonyPatch_JobDriver_SexBaseReceiverLoved), "DoNotLayDown");
yield return ins[i];
}
else
{
yield return ins[i];
}
}
}
public static Toil DoNotLayDown(TargetIndex bedOrRestSpotIndex, bool hasBed, bool lookForOtherJobs, bool canSleep = true, bool gainRestAndHealth = true, PawnPosture noBedLayingPosture = PawnPosture.LayingMask, bool deathrest = false)
{
return new Toil();
}
}
}

View file

@ -0,0 +1,23 @@
using HarmonyLib;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse.AI;
namespace Rimworld_Animations
{
[HarmonyPatch(typeof(JobDriver_SexBaseRecieverLoved), "MakeSexToil")]
public class HarmonyPatch_JobDriver_SexBaseReceiverLoved
{
public static void Postfix(JobDriver_SexBaseRecieverLoved __instance, ref Toil __result)
{
//added for sudden end of jobdriver
__result.AddFinishAction(delegate {
AnimationUtility.StopGroupAnimation(__instance.pawn);
});
}
}
}

View file

@ -0,0 +1,23 @@
using HarmonyLib;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse.AI;
namespace Rimworld_Animations
{
[HarmonyPatch(typeof(JobDriver_SexBaseRecieverRaped), "MakeNewToils")]
public class HarmonyPatch_JobDriver_SexBaseReceiverRaped
{
public static void Postfix(JobDriver_SexBaseRecieverRaped __instance, ref IEnumerable<Toil> __result)
{
//added for sudden end of jobdriver
__result.Last().AddFinishAction(delegate {
AnimationUtility.StopGroupAnimation(__instance.pawn);
});
}
}
}

View file

@ -27,7 +27,7 @@ namespace Rimworld_Animations
}
// HEAD ROTATION ADJUST FACING
// HEAD ROTATION ADJUST FACING get rotated textures
Rot4 animFacing = extendedAnimWorker.facingAtTick(__instance.tree.AnimationTick);

View file

@ -0,0 +1,35 @@
using HarmonyLib;
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations
{
[HarmonyPatch(typeof(PawnRenderTree), "TryGetMatrix")]
public class HarmonyPatch_PawnRenderTree
{
public static void Prefix(PawnRenderTree __instance, PawnRenderNode node, ref PawnDrawParms parms)
{
//Note: Maybe need to change this to check all ancestors if it's the head node
//in case of deeper nodes in the PawnRenderTree
//This code only checks for node and parent, if either are head
// Change facing for all head and apparelhead nodes
// So that the offset is based on body rotation, not head rotation
//fixes misaligned hairs and headaddons
if ((node.Props.tagDef == PawnRenderNodeTagDefOf.Head || node.Props.tagDef == PawnRenderNodeTagDefOf.ApparelHead
|| node?.parent?.Props?.tagDef == PawnRenderNodeTagDefOf.Head || node?.parent?.Props?.tagDef == PawnRenderNodeTagDefOf.ApparelHead)
&& node.tree.rootNode.AnimationWorker is AnimationWorker_KeyframesExtended rootNodeAnimationWorker)
{
parms.facing = rootNodeAnimationWorker.facingAtTick(node.tree.AnimationTick);
}
}
}
}

View file

@ -9,19 +9,23 @@ namespace Rimworld_Animations {
public static class HarmonyPatch_Pawn_DrawTracker {
public static bool Prefix(ref Pawn ___pawn, ref Vector3 __result) {
// If animating and is sexbaseinitiator jobdriver,
if (___pawn?.Drawer?.renderer?.renderTree?.rootNode?.AnimationWorker is AnimationWorker_KeyframesExtended
&& ___pawn.jobs?.curDriver is JobDriver_SexBaseInitiator sexdriver)
{
//align pos on top of partner
__result = sexdriver.Partner.Drawer.DrawPos;
//change height so that they're all layered properly
if (sexdriver.Partner.jobs.curDriver is JobDriver_SexBaseReciever)
for (int i = 0; i < )
//align pos on top of partner
if (sexdriver?.Partner?.Drawer?.DrawPos != null)
__result = sexdriver.Partner.Drawer.DrawPos;
return false;
}
return true;
}
}
}

View file

@ -15,120 +15,57 @@ namespace Rimworld_Animations {
public static class AnimationUtility {
public static bool GenitalCheckForPawn(List<string> requiredGenitals, Pawn pawn, out string failReason)
public static void StartAnimation(List<Pawn> participants)
{
participants[0].Drawer.renderer.SetAnimation(AnimationDefOf.TestAnimation1);
participants[1].Drawer.renderer.SetAnimation(AnimationDefOf.TestAnimation2);
}
failReason = null;
if (requiredGenitals != null)
public static void StartGroupAnimation(List<Pawn> participants, GroupAnimationDef groupAnimationDef, int reorder)
{
for(int i = 0; i < participants.Count; i++)
{
if (requiredGenitals.Contains("Vagina"))
{
if (!rjw.Genital_Helper.has_vagina(pawn))
{
failReason = "missing vagina";
return false;
}
}
if (requiredGenitals.Contains("Penis"))
{
if (!(rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn) || rjw.Genital_Helper.has_ovipositorM(pawn) || rjw.Genital_Helper.has_ovipositorF(pawn)))
{
failReason = "missing penis";
return false;
}
}
if (requiredGenitals.Contains("Mouth"))
{
if (!rjw.Genital_Helper.has_mouth(pawn))
{
failReason = "missing mouth";
return false;
}
}
if (requiredGenitals.Contains("Anus"))
{
if (!rjw.Genital_Helper.has_anus(pawn))
{
failReason = "missing anus";
return false;
}
}
if (requiredGenitals.Contains("Breasts"))
{
if (!rjw.Genital_Helper.can_do_breastjob(pawn))
{
failReason = "missing breasts";
return false;
}
}
if (requiredGenitals.Contains("NoVagina"))
{
if (rjw.Genital_Helper.has_vagina(pawn))
{
failReason = "has vagina";
return false;
}
}
if (requiredGenitals.Contains("NoPenis"))
{
if ((rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn)))
{
failReason = "has penis";
return false;
}
}
if (requiredGenitals.Contains("NoMouth"))
{
if (rjw.Genital_Helper.has_mouth(pawn))
{
failReason = "has mouth";
return false;
}
}
if (requiredGenitals.Contains("NoAnus"))
{
if (rjw.Genital_Helper.has_anus(pawn))
{
failReason = "has anus";
return false;
}
}
if (requiredGenitals.Contains("NoBreasts"))
{
if (rjw.Genital_Helper.can_do_breastjob(pawn))
{
failReason = "has breasts";
return false;
}
}
//todo: pass all animationstages to ExtendedAnimator, and queue animations
participants[i].Drawer.renderer.SetAnimation(
groupAnimationDef.animationStages[0]
.GetAnimations((i + reorder) % participants.Count, GenTicks.TicksGame)[0].Item2);
}
}
return true;
public static void StopGroupAnimation(List<Pawn> participants)
{
foreach(Pawn pawn in participants)
{
pawn.Drawer.renderer.SetAnimation(null);
}
}
public static void StopGroupAnimation(Pawn participant)
{
participant?.Drawer?.renderer?.SetAnimation(null);
}
public static GroupAnimationDef FindGroupAnimation(List<Pawn> participants, out int reorder)
{
// go through each context in each GroupAnimationDef
// if you find one where it returns canAnimationBeUsed (and reorders),
// return that animation
int reorder2 = 0;
Log.Message(DefDatabase<GroupAnimationDef>.AllDefsListForReading[0].defName);
DefDatabase<GroupAnimationDef>.AllDefsListForReading.TryRandomElement((GroupAnimationDef x) =>
x.canAnimationBeUsed(participants, out reorder2), out GroupAnimationDef result);
reorder = reorder2;
if (result != null) return result;
return null;
}
}

View file

@ -73,19 +73,25 @@
<Compile Include="1.5\Source\Actors\AlienRaceOffset.cs" />
<Compile Include="1.5\Source\Actors\BodyTypeOffset.cs" />
<Compile Include="1.5\Source\AnimationWorkers\AnimationWorker_KeyframesExtended.cs" />
<Compile Include="1.5\Source\Comps\CompExtendedAnimator.cs" />
<Compile Include="1.5\Source\Comps\CompProperties_ExtendedAnimator.cs" />
<Compile Include="1.5\Source\Comps\CompProperties_ThingAnimator.cs" />
<Compile Include="1.5\Source\Comps\CompThingAnimator.cs" />
<Compile Include="1.5\Source\Defs\AnimationDefOf.cs" />
<Compile Include="1.5\Source\GroupAnimationStages\AnimationStage.cs" />
<Compile Include="1.5\Source\GroupAnimationStages\AnimationStage_Branch.cs" />
<Compile Include="1.5\Source\GroupAnimationStages\AnimationStage_LoopRandomSelectChance.cs" />
<Compile Include="1.5\Source\GroupAnimationStages\AnimationStage_TicksDuration.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationContexts\BaseGroupAnimationContext.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationContexts\GroupAnimationContext_RJWSex.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationDef.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationStages\AnimationStage.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationStages\AnimationStage_Branch.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationStages\AnimationStage_LoopRandomSelectChance.cs" />
<Compile Include="1.5\Source\GroupAnimations\GroupAnimationStages\AnimationStage_TicksDuration.cs" />
<Compile Include="1.5\Source\Keyframes\ExtendedKeyframe.cs" />
<Compile Include="1.5\Source\MainTabWindows\MainTabWindow_OffsetConfigure.cs" />
<Compile Include="1.5\Source\MainTabWindows\OffsetMainButtonDefOf.cs" />
<Compile Include="1.5\Source\MainTabWindows\WorldComponent_UpdateMainTab.cs" />
<Compile Include="1.5\Source\Patches\Harmony_PatchAll.cs" />
<Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderNode.cs" />
<Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderTree.cs" />
<Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_Pawn_DrawTracker.cs" />
<Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_Thing.cs" />
<Compile Include="1.5\Source\Patches\RJWPatches\HarmonyPatch_PlaySexSounds.cs" />
@ -94,7 +100,8 @@
<Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_JoinInBed.cs" />
<Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_Sex.cs" />
<Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
<Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs" />
<Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\SexBaseReceivers\HarmonyPatch_JobDriver_SexBaseReceiverRaped.cs" />
<Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\SexBaseReceivers\HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs" />
<Compile Include="1.5\Source\Settings\AnimationSettings.cs" />
<Compile Include="1.5\Source\Utilities\AnimationUtility.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -108,7 +115,13 @@
<Content Include="1.5\Defs\GroupAnimationDefs\TestGroupAnimation1.xml" />
<Content Include="1.5\Defs\MainTabDefs\MainButtonDef.xml" />
<Content Include="1.5\Defs\SoundDefs\Sounds_Sex.xml" />
<Content Include="1.5\Patches\AnimationPatchHSK.xml" />
<Content Include="1.5\Patches\AnimationPatch_CompExtendedAnimator.xml" />
<Content Include="1.5\Patches\CompatibilityPatch_FacialAnimation.xml" />
<Content Include="1.5\Patches\CompatibilityPatch_HCSK.xml" />
<Content Include="1.5\Patches\CompPatches\AutoCleaner.xml" />
<Content Include="1.5\Patches\CompPatches\CombatExtended.xml" />
<Content Include="1.5\Patches\CompPatches\ZombieLand.xml" />
<Content Include="1.5\Patches\Patch_GenitaliaRenderNode.xml" />
<Content Include="1.5\Sounds\Sex\Clap_1.wav" />
<Content Include="1.5\Sounds\Sex\Clap_2.wav" />