mirror of
https://gitgud.io/c0ffeeeeeeee/rimworld-animations.git
synced 2024-08-15 00:43:45 +00:00
commit
This commit is contained in:
parent
74a34b6f8d
commit
55ae1c5d10
29 changed files with 1387 additions and 28 deletions
|
@ -62,33 +62,33 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Actors\Actor.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\AnimationStage.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\Clips\BaseAnimationClip.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\Clips\PawnAnimationClip.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\Clips\ThingAnimationClip.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\Keyframes\Keyframe.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\Keyframes\PawnKeyframe.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Animations\Keyframes\ThingKeyframe.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\AnimationUtility.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Comps\CompBodyAnimator.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Comps\CompProperties_BodyAnimator.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Comps\CompProperties_ThingAnimator.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Comps\CompThingAnimator.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Defs\AnimationDef.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\JobDrivers\JobDriver_SexBaseRecieverLovedForAnimation.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\JobDrivers\JobDriver_SexCasualForAnimation.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\MainTabWindows\MainTabWindow_SexAnimator.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_AlienRace.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_FacialAnimation.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_PawnRenderer.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_Pawn_DrawTracker.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_ShowHairWithHats.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\Harmony_PatchAll.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_JoinInBedGiveJob.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_SexTick.cs" />
|
||||
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_WorkGiverSex.cs" />
|
||||
<Compile Include="Source\Actors\Actor.cs" />
|
||||
<Compile Include="Source\Animations\AnimationStage.cs" />
|
||||
<Compile Include="Source\Animations\Clips\BaseAnimationClip.cs" />
|
||||
<Compile Include="Source\Animations\Clips\PawnAnimationClip.cs" />
|
||||
<Compile Include="Source\Animations\Clips\ThingAnimationClip.cs" />
|
||||
<Compile Include="Source\Animations\Keyframes\Keyframe.cs" />
|
||||
<Compile Include="Source\Animations\Keyframes\PawnKeyframe.cs" />
|
||||
<Compile Include="Source\Animations\Keyframes\ThingKeyframe.cs" />
|
||||
<Compile Include="Source\AnimationUtility.cs" />
|
||||
<Compile Include="Source\Comps\CompBodyAnimator.cs" />
|
||||
<Compile Include="Source\Comps\CompProperties_BodyAnimator.cs" />
|
||||
<Compile Include="Source\Comps\CompProperties_ThingAnimator.cs" />
|
||||
<Compile Include="Source\Comps\CompThingAnimator.cs" />
|
||||
<Compile Include="Source\Defs\AnimationDef.cs" />
|
||||
<Compile Include="Source\JobDrivers\JobDriver_SexBaseRecieverLovedForAnimation.cs" />
|
||||
<Compile Include="Source\JobDrivers\JobDriver_SexCasualForAnimation.cs" />
|
||||
<Compile Include="Source\MainTabWindows\MainTabWindow_SexAnimator.cs" />
|
||||
<Compile Include="Source\Patches\HarmonyPatch_AlienRace.cs" />
|
||||
<Compile Include="Source\Patches\HarmonyPatch_FacialAnimation.cs" />
|
||||
<Compile Include="Source\Patches\HarmonyPatch_PawnRenderer.cs" />
|
||||
<Compile Include="Source\Patches\HarmonyPatch_Pawn_DrawTracker.cs" />
|
||||
<Compile Include="Source\Patches\HarmonyPatch_ShowHairWithHats.cs" />
|
||||
<Compile Include="Source\Patches\Harmony_PatchAll.cs" />
|
||||
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
|
||||
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_JoinInBedGiveJob.cs" />
|
||||
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_SexTick.cs" />
|
||||
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_WorkGiverSex.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
|
18
Source/Actors/Actor.cs
Normal file
18
Source/Actors/Actor.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class Actor {
|
||||
public List<string> defNames;
|
||||
public List<string> requiredGenitals;
|
||||
public bool activeRole = false;
|
||||
public string gender;
|
||||
public bool isFucking = false;
|
||||
public bool isFucked = false;
|
||||
public Vector3 offset = new Vector3(0, 0, 0);
|
||||
}
|
||||
}
|
114
Source/AnimationUtility.cs
Normal file
114
Source/AnimationUtility.cs
Normal file
|
@ -0,0 +1,114 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public static class AnimationUtility {
|
||||
/*
|
||||
Note: always make the list in this order:
|
||||
Female pawns, animal female pawns, male pawns, animal male pawns
|
||||
*/
|
||||
public static AnimationDef tryFindAnimation(ref List<Pawn> participants) {
|
||||
|
||||
//aggressors first
|
||||
participants = participants.OrderByDescending(p => p.jobs.curDriver is rjw.JobDriver_SexBaseInitiator).ToList();
|
||||
|
||||
//fucked first, fucking second
|
||||
participants = participants.OrderBy(p => rjw.xxx.can_fuck(p)).ToList();
|
||||
|
||||
List<Pawn> localParticipants = new List<Pawn>(participants);
|
||||
|
||||
IEnumerable<AnimationDef> options = DefDatabase<AnimationDef>.AllDefs.Where((AnimationDef x) => {
|
||||
|
||||
if (x.actors.Count != localParticipants.Count) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < x.actors.Count; i++) {
|
||||
|
||||
|
||||
if(x.actors[i].defNames.Contains("Human")) {
|
||||
if (!rjw.xxx.is_human(localParticipants[i])) {
|
||||
if(rjw.RJWSettings.DevMode) {
|
||||
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not human");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!x.actors[i].defNames.Contains(localParticipants[i].def.defName)) {
|
||||
|
||||
if (rjw.RJWSettings.DevMode) {
|
||||
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not ");
|
||||
foreach(String defname in x.actors[i].defNames) {
|
||||
Log.Message(defname + ", ");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(x.actors[i].requiredGenitals != null && x.actors[i].requiredGenitals.Contains("Vagina")) {
|
||||
|
||||
if (!rjw.Genital_Helper.has_vagina(localParticipants[i])) {
|
||||
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " doesn't have vagina");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TESTING ANIMATIONS ONLY REMEMBER TO COMMENT OUT BEFORE PUSH
|
||||
/*
|
||||
if (x.defName != "Tribadism")
|
||||
return false;
|
||||
*/
|
||||
|
||||
if (x.actors[i].isFucking && !rjw.xxx.can_fuck(localParticipants[i])) {
|
||||
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't fuck");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x.actors[i].isFucked && !rjw.xxx.can_be_fucked(localParticipants[i])) {
|
||||
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't be fucked");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (options != null && options.Any()) {
|
||||
Log.Message("Randomly selecting animation...");
|
||||
return options.RandomElement();
|
||||
}
|
||||
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RenderPawnHeadMeshInAnimation(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool portrait, Pawn pawn) {
|
||||
|
||||
if(pawn == null) {
|
||||
GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
|
||||
return;
|
||||
}
|
||||
|
||||
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
|
||||
|
||||
if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) {
|
||||
GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
|
||||
} else {
|
||||
Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
|
||||
pawnHeadPosition.y = loc.y;
|
||||
GenDraw.DrawMeshNowOrLater(mesh, pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, portrait);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
Source/Animations/AnimationStage.cs
Normal file
27
Source/Animations/AnimationStage.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class AnimationStage
|
||||
{
|
||||
public string stageName;
|
||||
public int stageIndex;
|
||||
public int playTimeTicks = 0;
|
||||
public bool isLooping;
|
||||
public List<BaseAnimationClip> animationClips;
|
||||
|
||||
|
||||
public void initialize() {
|
||||
foreach (BaseAnimationClip clip in animationClips) {
|
||||
clip.buildSimpleCurves();
|
||||
//select playTimeTicks as longest playtime of all the animations
|
||||
if(clip.duration > playTimeTicks) {
|
||||
playTimeTicks = clip.duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
Source/Animations/Clips/BaseAnimationClip.cs
Normal file
19
Source/Animations/Clips/BaseAnimationClip.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public abstract class BaseAnimationClip
|
||||
{
|
||||
public List<ThingDef> types; //types of participants
|
||||
public int duration;
|
||||
public abstract void buildSimpleCurves();
|
||||
public string soundDef = null; //for playing sounds
|
||||
public int actor;
|
||||
|
||||
}
|
||||
}
|
101
Source/Animations/Clips/PawnAnimationClip.cs
Normal file
101
Source/Animations/Clips/PawnAnimationClip.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class PawnAnimationClip : BaseAnimationClip {
|
||||
|
||||
public List<PawnKeyframe> keyframes;
|
||||
public AltitudeLayer layer = AltitudeLayer.Pawn;
|
||||
|
||||
public Dictionary<int, string> SoundEffects = new Dictionary<int, string>();
|
||||
public SimpleCurve BodyAngle = new SimpleCurve();
|
||||
public SimpleCurve HeadAngle = new SimpleCurve();
|
||||
public SimpleCurve HeadBob = new SimpleCurve();
|
||||
public SimpleCurve BodyOffsetX = new SimpleCurve();
|
||||
public SimpleCurve BodyOffsetZ = new SimpleCurve();
|
||||
public SimpleCurve HeadFacing = new SimpleCurve();
|
||||
public SimpleCurve BodyFacing = new SimpleCurve();
|
||||
|
||||
|
||||
public override void buildSimpleCurves() {
|
||||
|
||||
|
||||
int duration = 0;
|
||||
//getting the length of the whole clip
|
||||
foreach(PawnKeyframe frame in keyframes) {
|
||||
duration += frame.tickDuration;
|
||||
}
|
||||
|
||||
//guarantees loops don't get cut off mid-anim
|
||||
this.duration = duration;
|
||||
|
||||
int keyframePosition = 0;
|
||||
foreach (PawnKeyframe frame in keyframes) {
|
||||
|
||||
if (frame.atTick.HasValue) {
|
||||
if (frame.bodyAngle.HasValue)
|
||||
BodyAngle.Add((float)frame.atTick / (float)duration, frame.bodyAngle.Value, true);
|
||||
|
||||
if (frame.headAngle.HasValue)
|
||||
HeadAngle.Add((float)frame.atTick / (float)duration, frame.headAngle.Value, true);
|
||||
|
||||
if (frame.bodyOffsetX.HasValue)
|
||||
BodyOffsetX.Add((float)frame.atTick / (float)duration, frame.bodyOffsetX.Value, true);
|
||||
|
||||
if (frame.bodyOffsetZ.HasValue)
|
||||
BodyOffsetZ.Add((float)frame.atTick / (float)duration, frame.bodyOffsetZ.Value, true);
|
||||
|
||||
if (frame.headFacing.HasValue)
|
||||
HeadFacing.Add((float)frame.atTick / (float)duration, frame.headFacing.Value, true);
|
||||
|
||||
if (frame.bodyFacing.HasValue)
|
||||
BodyFacing.Add((float)frame.atTick / (float)duration, frame.bodyFacing.Value, true);
|
||||
|
||||
if (frame.headBob.HasValue)
|
||||
HeadBob.Add((float)frame.atTick / (float)duration, frame.headBob.Value, true);
|
||||
|
||||
if (frame.soundEffect != null) {
|
||||
SoundEffects.Add((int)frame.atTick, frame.soundEffect);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (frame.bodyAngle.HasValue)
|
||||
BodyAngle.Add((float)keyframePosition / (float)duration, frame.bodyAngle.Value, true);
|
||||
|
||||
if (frame.headAngle.HasValue)
|
||||
HeadAngle.Add((float)keyframePosition / (float)duration, frame.headAngle.Value, true);
|
||||
|
||||
if (frame.bodyOffsetX.HasValue)
|
||||
BodyOffsetX.Add((float)keyframePosition / (float)duration, frame.bodyOffsetX.Value, true);
|
||||
|
||||
if (frame.bodyOffsetZ.HasValue)
|
||||
BodyOffsetZ.Add((float)keyframePosition / (float)duration, frame.bodyOffsetZ.Value, true);
|
||||
|
||||
if (frame.headFacing.HasValue)
|
||||
HeadFacing.Add((float)keyframePosition / (float)duration, frame.headFacing.Value, true);
|
||||
|
||||
if (frame.bodyFacing.HasValue)
|
||||
BodyFacing.Add((float)keyframePosition / (float)duration, frame.bodyFacing.Value, true);
|
||||
|
||||
if (frame.headBob.HasValue)
|
||||
HeadBob.Add((float)keyframePosition / (float)duration, frame.headBob.Value, true);
|
||||
|
||||
if (frame.soundEffect != null) {
|
||||
SoundEffects.Add(keyframePosition, frame.soundEffect);
|
||||
}
|
||||
|
||||
keyframePosition += frame.tickDuration;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
15
Source/Animations/Clips/ThingAnimationClip.cs
Normal file
15
Source/Animations/Clips/ThingAnimationClip.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class ThingAnimationClip : BaseAnimationClip
|
||||
{
|
||||
public List<ThingKeyframe> keyframes;
|
||||
|
||||
public override void buildSimpleCurves() {
|
||||
}
|
||||
}
|
||||
}
|
14
Source/Animations/Keyframes/Keyframe.cs
Normal file
14
Source/Animations/Keyframes/Keyframe.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public abstract class Keyframe
|
||||
{
|
||||
public int tickDuration = 1;
|
||||
|
||||
|
||||
}
|
||||
}
|
27
Source/Animations/Keyframes/PawnKeyframe.cs
Normal file
27
Source/Animations/Keyframes/PawnKeyframe.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class PawnKeyframe : Keyframe
|
||||
{
|
||||
public float? bodyAngle;
|
||||
public float? headAngle;
|
||||
|
||||
public float? bodyOffsetZ;
|
||||
public float? bodyOffsetX;
|
||||
|
||||
public float? headBob;
|
||||
//todo: add headOffsets l/r?
|
||||
|
||||
public int? bodyFacing;
|
||||
public int? headFacing;
|
||||
|
||||
public string soundEffect;
|
||||
|
||||
public float? atTick;
|
||||
}
|
||||
}
|
11
Source/Animations/Keyframes/ThingKeyframe.cs
Normal file
11
Source/Animations/Keyframes/ThingKeyframe.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class ThingKeyframe : Keyframe
|
||||
{
|
||||
}
|
||||
}
|
238
Source/Comps/CompBodyAnimator.cs
Normal file
238
Source/Comps/CompBodyAnimator.cs
Normal file
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using RimWorld;
|
||||
using rjw;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
class CompBodyAnimator : ThingComp
|
||||
{
|
||||
public Pawn pawn => base.parent as Pawn;
|
||||
public PawnGraphicSet Graphics;
|
||||
|
||||
public CompProperties_BodyAnimator Props => (CompProperties_BodyAnimator)(object)base.props;
|
||||
|
||||
public bool isAnimating {
|
||||
get {
|
||||
return Animating;
|
||||
}
|
||||
set {
|
||||
Animating = value;
|
||||
|
||||
if(value == true) {
|
||||
xxx.DrawNude(pawn);
|
||||
} else {
|
||||
pawn.Drawer.renderer.graphics.ResolveAllGraphics();
|
||||
}
|
||||
|
||||
PortraitsCache.SetDirty(pawn);
|
||||
}
|
||||
}
|
||||
private bool Animating;
|
||||
private bool mirror = false;
|
||||
private int actor;
|
||||
|
||||
private int animTicks = 0, stageTicks = 0, clipTicks = 0;
|
||||
private int curStage = 0;
|
||||
private float clipPercent = 0;
|
||||
|
||||
public Vector3 anchor, deltaPos, headBob;
|
||||
public float bodyAngle, headAngle;
|
||||
public Rot4 headFacing, bodyFacing;
|
||||
|
||||
private AnimationDef anim;
|
||||
private AnimationStage stage => anim.animationStages[curStage];
|
||||
private PawnAnimationClip clip => (PawnAnimationClip)stage.animationClips[actor];
|
||||
|
||||
public void setAnchor(IntVec3 pos)
|
||||
{
|
||||
anchor = pos.ToVector3();
|
||||
}
|
||||
public void setAnchor(Thing thing) {
|
||||
anchor = thing.Position.ToVector3();
|
||||
//center on bed
|
||||
if(thing is Building_Bed) {
|
||||
if(((Building_Bed)thing).SleepingSlotsCount == 2) {
|
||||
if (thing.Rotation.AsInt == 0) {
|
||||
anchor.x += 1;
|
||||
anchor.z += 1;
|
||||
}
|
||||
else if (thing.Rotation.AsInt == 1) {
|
||||
anchor.x += 1;
|
||||
}
|
||||
else if(thing.Rotation.AsInt == 3) {
|
||||
anchor.z += 1;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if(thing.Rotation.AsInt == 0) {
|
||||
anchor.x += 0.5f;
|
||||
anchor.z += 1f;
|
||||
}
|
||||
else if(thing.Rotation.AsInt == 1) {
|
||||
anchor.x += 1f;
|
||||
anchor.z += 0.5f;
|
||||
}
|
||||
else if(thing.Rotation.AsInt == 2) {
|
||||
anchor.x += 0.5f;
|
||||
} else {
|
||||
anchor.z += 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void StartAnimation(AnimationDef anim, int actor, bool mirror = false) {
|
||||
|
||||
isAnimating = true;
|
||||
|
||||
pawn.jobs.posture = PawnPosture.Standing;
|
||||
|
||||
this.actor = actor;
|
||||
this.anim = anim;
|
||||
this.mirror = mirror;
|
||||
|
||||
curStage = 0;
|
||||
animTicks = 0;
|
||||
stageTicks = 0;
|
||||
clipTicks = 0;
|
||||
|
||||
//tick once for initialization
|
||||
tickAnim();
|
||||
|
||||
}
|
||||
public override void CompTick() {
|
||||
base.CompTick();
|
||||
|
||||
//maybe this is causing anim to stop mid?
|
||||
|
||||
|
||||
if(Animating) {
|
||||
|
||||
tickAnim();
|
||||
|
||||
if (pawn?.jobs?.curDriver == null || (pawn?.jobs?.curDriver != null && !(pawn?.jobs?.curDriver is rjw.JobDriver_Sex))) {
|
||||
Animating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void animatePawn(ref Vector3 rootLoc, ref float angle, ref Rot4 bodyFacing, ref Rot4 headFacing) {
|
||||
|
||||
if(!Animating) {
|
||||
return;
|
||||
}
|
||||
rootLoc = anchor + deltaPos;
|
||||
angle = bodyAngle;
|
||||
bodyFacing = this.bodyFacing;
|
||||
headFacing = this.headFacing;
|
||||
|
||||
}
|
||||
|
||||
public void tickGraphics(PawnGraphicSet graphics) {
|
||||
this.Graphics = graphics;
|
||||
}
|
||||
|
||||
public void tickAnim() {
|
||||
|
||||
if (!Animating) return;
|
||||
|
||||
animTicks++;
|
||||
if (animTicks < anim.animationTimeTicks) {
|
||||
tickStage();
|
||||
} else {
|
||||
Animating = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void tickStage()
|
||||
{
|
||||
stageTicks++;
|
||||
|
||||
if(stageTicks >= stage.playTimeTicks) {
|
||||
curStage++;
|
||||
stageTicks = 0;
|
||||
clipTicks = 0;
|
||||
clipPercent = 0;
|
||||
}
|
||||
|
||||
tickClip();
|
||||
|
||||
}
|
||||
|
||||
public void tickClip() {
|
||||
|
||||
clipTicks++;
|
||||
|
||||
//play sound effect
|
||||
if(rjw.RJWSettings.sounds_enabled && clip.SoundEffects.ContainsKey(clipTicks)) {
|
||||
SoundDef.Named(clip.SoundEffects[clipTicks]).PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
|
||||
}
|
||||
|
||||
//loop animation if possible
|
||||
if (clipPercent >= 1 && stage.isLooping) {
|
||||
clipTicks = 1;//warning: don't set to zero or else calculations go wrong
|
||||
}
|
||||
clipPercent = (float)clipTicks / (float)clip.duration;
|
||||
|
||||
calculateDrawValues();
|
||||
}
|
||||
|
||||
public void calculateDrawValues() {
|
||||
|
||||
deltaPos = new Vector3(clip.BodyOffsetX.Evaluate(clipPercent) * (mirror ? -1 : 1), clip.layer.AltitudeFor(), clip.BodyOffsetZ.Evaluate(clipPercent));
|
||||
bodyAngle = clip.BodyAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
|
||||
headAngle = clip.HeadAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
|
||||
bodyFacing = mirror ? new Rot4((int)clip.BodyFacing.Evaluate(clipPercent)).Opposite : new Rot4((int)clip.BodyFacing.Evaluate(clipPercent));
|
||||
|
||||
bodyFacing = new Rot4((int)clip.BodyFacing.Evaluate(clipPercent));
|
||||
if(bodyFacing.IsHorizontal && mirror) {
|
||||
bodyFacing = bodyFacing.Opposite;
|
||||
}
|
||||
|
||||
headFacing = new Rot4((int)clip.HeadFacing.Evaluate(clipPercent));
|
||||
if(headFacing.IsHorizontal && mirror) {
|
||||
headFacing = headFacing.Opposite;
|
||||
}
|
||||
headBob = new Vector3(0, 0, clip.HeadBob.Evaluate(clipPercent));
|
||||
}
|
||||
|
||||
public Vector3 getPawnHeadPosition() {
|
||||
|
||||
return anchor + deltaPos + Quaternion.AngleAxis(bodyAngle, Vector3.up) * (pawn.Drawer.renderer.BaseHeadOffsetAt(headFacing) + headBob);
|
||||
|
||||
}
|
||||
|
||||
public override void PostExposeData() {
|
||||
base.PostExposeData();
|
||||
|
||||
Scribe_Defs.Look(ref anim, "anim");
|
||||
|
||||
Scribe_Values.Look(ref animTicks, "animTicks", 1);
|
||||
Scribe_Values.Look(ref stageTicks, "stageTicks", 1);
|
||||
Scribe_Values.Look(ref clipTicks, "clipTicks", 1);
|
||||
Scribe_Values.Look(ref clipPercent, "clipPercent", 1);
|
||||
|
||||
Scribe_Values.Look(ref mirror, "mirror");
|
||||
|
||||
Scribe_Values.Look(ref curStage, "curStage", 0);
|
||||
Scribe_Values.Look(ref actor, "actor");
|
||||
|
||||
Scribe_Values.Look(ref Animating, "Animating");
|
||||
Scribe_Values.Look(ref anchor, "anchor");
|
||||
Scribe_Values.Look(ref deltaPos, "deltaPos");
|
||||
Scribe_Values.Look(ref headBob, "headBob");
|
||||
Scribe_Values.Look(ref bodyAngle, "bodyAngle");
|
||||
Scribe_Values.Look(ref headAngle, "headAngle");
|
||||
|
||||
Scribe_Values.Look(ref headFacing, "headFacing");
|
||||
Scribe_Values.Look(ref headFacing, "bodyFacing");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
17
Source/Comps/CompProperties_BodyAnimator.cs
Normal file
17
Source/Comps/CompProperties_BodyAnimator.cs
Normal 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 {
|
||||
class CompProperties_BodyAnimator : CompProperties
|
||||
{
|
||||
public CompProperties_BodyAnimator() {
|
||||
|
||||
base.compClass = typeof(CompBodyAnimator);
|
||||
}
|
||||
}
|
||||
}
|
11
Source/Comps/CompProperties_ThingAnimator.cs
Normal file
11
Source/Comps/CompProperties_ThingAnimator.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
class CompProperties_ThingAnimator
|
||||
{
|
||||
}
|
||||
}
|
11
Source/Comps/CompThingAnimator.cs
Normal file
11
Source/Comps/CompThingAnimator.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
class CompThingAnimator
|
||||
{
|
||||
}
|
||||
}
|
25
Source/Defs/AnimationDef.cs
Normal file
25
Source/Defs/AnimationDef.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class AnimationDef : Def
|
||||
{
|
||||
public List<AnimationStage> animationStages;
|
||||
public List<Actor> actors;
|
||||
public int animationTimeTicks = 0; //do not set manually
|
||||
public bool sounds = false;
|
||||
|
||||
public override void PostLoad() {
|
||||
base.PostLoad();
|
||||
foreach(AnimationStage stage in animationStages) {
|
||||
stage.initialize();
|
||||
animationTimeTicks += stage.playTimeTicks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using System;
|
||||
using rjw;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
public class JobDriver_SexBaseRecieverLovedForAnimation : JobDriver_SexBaseReciever {
|
||||
|
||||
public readonly TargetIndex ipartner = TargetIndex.A;
|
||||
public readonly TargetIndex ibed = TargetIndex.B;
|
||||
|
||||
public Pawn Partner => (Pawn)(job.GetTarget(ipartner));
|
||||
public new Building_Bed Bed => (Building_Bed)(job.GetTarget(ibed));
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils() {
|
||||
setup_ticks();
|
||||
parteners.Add(Partner);// add job starter, so this wont fail, before Initiator starts his job
|
||||
//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils is called");
|
||||
|
||||
float partner_ability = xxx.get_sex_ability(Partner);
|
||||
|
||||
// More/less hearts based on partner ability.
|
||||
if (partner_ability < 0.8f)
|
||||
ticks_between_thrusts += 100;
|
||||
else if (partner_ability > 2.0f)
|
||||
ticks_between_thrusts -= 25;
|
||||
|
||||
// More/less hearts based on opinion.
|
||||
if (pawn.relations.OpinionOf(Partner) < 0)
|
||||
ticks_between_hearts += 50;
|
||||
else if (pawn.relations.OpinionOf(Partner) > 60)
|
||||
ticks_between_hearts -= 25;
|
||||
|
||||
this.FailOnDespawnedOrNull(ipartner);
|
||||
this.FailOn(() => !Partner.health.capacities.CanBeAwake);
|
||||
this.FailOn(() => pawn.Drafted);
|
||||
this.KeepLyingDown(ibed);
|
||||
yield return Toils_Reserve.Reserve(ipartner, 1, 0);
|
||||
yield return Toils_Reserve.Reserve(ibed, Bed.SleepingSlotsCount, 0);
|
||||
|
||||
Toil get_loved = Toils_LayDown.LayDown(ibed, true, false, false, false);
|
||||
get_loved.FailOn(() => Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true));
|
||||
get_loved.defaultCompleteMode = ToilCompleteMode.Never;
|
||||
get_loved.socialMode = RandomSocialMode.Off;
|
||||
get_loved.AddPreTickAction(delegate {
|
||||
if (pawn.IsHashIntervalTick(ticks_between_hearts))
|
||||
MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
|
||||
if (pawn.IsHashIntervalTick(ticks_between_thrusts))
|
||||
xxx.sexTick(pawn, Partner, false);
|
||||
});
|
||||
get_loved.AddFinishAction(delegate {
|
||||
if (xxx.is_human(pawn))
|
||||
pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
|
||||
});
|
||||
yield return get_loved;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
92
Source/JobDrivers/JobDriver_SexCasualForAnimation.cs
Normal file
92
Source/JobDrivers/JobDriver_SexCasualForAnimation.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using RimWorld;
|
||||
using rjw;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
class JobDriver_SexCasualForAnimation : JobDriver_SexBaseInitiator {
|
||||
|
||||
public readonly TargetIndex ipartner = TargetIndex.A;
|
||||
public readonly TargetIndex ibed = TargetIndex.B;
|
||||
|
||||
public Pawn Partner => (Pawn)job.GetTarget(ipartner);
|
||||
public new Building_Bed Bed => (Building_Bed)job.GetTarget(ibed);
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed) {
|
||||
return ReservationUtility.Reserve(base.pawn, Partner, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils() {
|
||||
setup_ticks();
|
||||
this.FailOnDespawnedOrNull(ipartner);
|
||||
this.FailOnDespawnedOrNull(ibed);
|
||||
this.FailOn(() => !Partner.health.capacities.CanBeAwake);
|
||||
|
||||
yield return Toils_Reserve.Reserve(ipartner, xxx.max_rapists_per_prisoner, 0, null);
|
||||
|
||||
Toil goToPawnInBed = Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
|
||||
goToPawnInBed.FailOn(() => !RestUtility.InBed(Partner) && !xxx.in_same_bed(Partner, pawn));
|
||||
|
||||
yield return goToPawnInBed;
|
||||
|
||||
|
||||
Toil startPartnerSex = new Toil();
|
||||
startPartnerSex.initAction = delegate {
|
||||
|
||||
Log.Message("Attempting to start job...");
|
||||
Job gettinLovedJob = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("GettinLovedAnimation"), pawn, Bed); // new gettin loved toil that wakes up the pawn goes here
|
||||
|
||||
Partner.jobs.jobQueue.EnqueueFirst(gettinLovedJob);
|
||||
Partner.jobs.EndCurrentJob(JobCondition.InterruptForced);
|
||||
};
|
||||
yield return startPartnerSex;
|
||||
|
||||
Toil sexToil = new Toil();
|
||||
sexToil.FailOn(() => (Partner.CurJobDef == null) || Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("GettinLovedAnimation", true)); //partner jobdriver is not sexbaserecieverlovedforanim
|
||||
sexToil.socialMode = RandomSocialMode.Off;
|
||||
sexToil.defaultCompleteMode = ToilCompleteMode.Never;
|
||||
sexToil.initAction = delegate {
|
||||
|
||||
usedCondom = (CondomUtility.TryUseCondom(base.pawn) || CondomUtility.TryUseCondom(Partner));
|
||||
Start();
|
||||
};
|
||||
|
||||
sexToil.AddPreTickAction(delegate {
|
||||
|
||||
ticks_left--;
|
||||
if(Gen.IsHashIntervalTick(pawn, ticks_between_hearts)) {
|
||||
MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
|
||||
}
|
||||
PawnUtility.GainComfortFromCellIfPossible(pawn, false);
|
||||
PawnUtility.GainComfortFromCellIfPossible(Partner, false);
|
||||
xxx.reduce_rest(Partner);
|
||||
xxx.reduce_rest(pawn, 2);
|
||||
if (ticks_left <= 0)
|
||||
ReadyForNextToil();
|
||||
|
||||
});
|
||||
sexToil.AddFinishAction(delegate {
|
||||
|
||||
End();
|
||||
if(xxx.is_human(pawn)) {
|
||||
pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
|
||||
}
|
||||
|
||||
});
|
||||
yield return sexToil;
|
||||
|
||||
Toil finish = new Toil();
|
||||
finish.initAction = delegate {
|
||||
SexUtility.ProcessSex(pawn, Partner, usedCondom);
|
||||
};
|
||||
finish.defaultCompleteMode = ToilCompleteMode.Instant;
|
||||
yield return finish;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
14
Source/MainTabWindows/MainTabWindow_SexAnimator.cs
Normal file
14
Source/MainTabWindows/MainTabWindow_SexAnimator.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
class MainTabWindow_SexAnimator : MainTabWindow
|
||||
{
|
||||
//todo: add animation maker window
|
||||
}
|
||||
}
|
35
Source/Patches/HarmonyPatch_AlienRace.cs
Normal file
35
Source/Patches/HarmonyPatch_AlienRace.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
[StaticConstructorOnStartup]
|
||||
public static class HarmonyPatch_AlienRace {
|
||||
static HarmonyPatch_AlienRace () {
|
||||
try {
|
||||
((Action)(() => {
|
||||
if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "Humanoid Alien Races 2.0")) {
|
||||
(new Harmony("rjw")).Patch(AccessTools.Method(typeof(PawnGraphicSet), "ResolveApparelGraphics"),
|
||||
prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_StopResolveAllGraphicsWhileSex")));
|
||||
}
|
||||
}))();
|
||||
}
|
||||
catch (TypeLoadException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Prefix_StopResolveAllGraphicsWhileSex(ref Pawn ___pawn) {
|
||||
|
||||
if(___pawn.TryGetComp<CompBodyAnimator>() != null && ___pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
44
Source/Patches/HarmonyPatch_FacialAnimation.cs
Normal file
44
Source/Patches/HarmonyPatch_FacialAnimation.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
[StaticConstructorOnStartup]
|
||||
public static class Patch_FacialAnimation {
|
||||
|
||||
static Patch_FacialAnimation() {
|
||||
try {
|
||||
((Action)(() => {
|
||||
if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[NL] Facial Animation - WIP")) {
|
||||
(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("FacialAnimation.DrawFaceGraphicsComp"), "DrawGraphics"),
|
||||
prefix: new HarmonyMethod(AccessTools.Method(typeof(Patch_FacialAnimation), "Prefix")));
|
||||
}
|
||||
}))();
|
||||
}
|
||||
catch (TypeLoadException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Prefix(ref Pawn ___pawn, ref Rot4 headFacing, ref Vector3 headOrigin, ref Quaternion quaternion, ref bool portrait) {
|
||||
|
||||
CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>();
|
||||
|
||||
if (bodyAnim.isAnimating && !portrait) {
|
||||
|
||||
headFacing = bodyAnim.headFacing;
|
||||
headOrigin = new Vector3(bodyAnim.getPawnHeadPosition().x, headOrigin.y, bodyAnim.getPawnHeadPosition().z);
|
||||
quaternion = Quaternion.AngleAxis(bodyAnim.headAngle, Vector3.up);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
86
Source/Patches/HarmonyPatch_PawnRenderer.cs
Normal file
86
Source/Patches/HarmonyPatch_PawnRenderer.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
|
||||
[HarmonyPatch(typeof(PawnRenderer), "RenderPawnInternal", new Type[]
|
||||
{
|
||||
typeof(Vector3),
|
||||
typeof(float),
|
||||
typeof(bool),
|
||||
typeof(Rot4),
|
||||
typeof(Rot4),
|
||||
typeof(RotDrawMode),
|
||||
typeof(bool),
|
||||
typeof(bool),
|
||||
typeof(bool)
|
||||
})]
|
||||
|
||||
public static class HarmonyPatch_PawnRenderer
|
||||
{
|
||||
|
||||
[HarmonyBefore(new string[] { "showhair.kv.rw", "erdelf.HumanoidAlienRaces", "Nals.FacialAnimation" })]
|
||||
public static void Prefix(PawnRenderer __instance, ref Vector3 rootLoc, ref float angle, bool renderBody, ref Rot4 bodyFacing, ref Rot4 headFacing, RotDrawMode bodyDrawType, bool portrait, bool headStump, bool invisible)
|
||||
{
|
||||
PawnGraphicSet graphics = __instance.graphics;
|
||||
Pawn pawn = graphics.pawn;
|
||||
CompBodyAnimator bodyAnim = pawn.TryGetComp<CompBodyAnimator>();
|
||||
|
||||
if (!graphics.AllResolved) {
|
||||
graphics.ResolveAllGraphics();
|
||||
}
|
||||
|
||||
|
||||
if (bodyAnim != null && bodyAnim.isAnimating && !portrait) {
|
||||
bodyAnim.tickGraphics(graphics);
|
||||
pawn.TryGetComp<CompBodyAnimator>().animatePawn(ref rootLoc, ref angle, ref bodyFacing, ref headFacing);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyAfter(new string[] { "showhair.kv.rw", "erdelf.HumanoidAlienRaces", "Nals.FacialAnimation" })]
|
||||
[HarmonyReversePatch(HarmonyReversePatchType.Snapshot)]
|
||||
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
|
||||
|
||||
MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");//typeof(GenDraw).GetMethod("DrawMeshNowOrLater", BindingFlags.Static | BindingFlags.Public);
|
||||
FieldInfo headGraphic = AccessTools.Field(typeof(PawnGraphicSet), "headGraphic");
|
||||
|
||||
|
||||
List<CodeInstruction> codes = instructions.ToList();
|
||||
bool forHead = true;
|
||||
for(int i = 0; i < codes.Count(); i++) {
|
||||
|
||||
//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
|
||||
if (codes[i].OperandIs(drawMeshNowOrLater) && forHead) {
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_0);
|
||||
yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
|
||||
yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation)));
|
||||
|
||||
}
|
||||
//checking for if(graphics.headGraphic != null)
|
||||
else if (codes[i].opcode == OpCodes.Ldfld && codes[i].OperandIs(headGraphic)) {
|
||||
forHead = true;
|
||||
yield return codes[i];
|
||||
}
|
||||
//checking for if(renderbody)
|
||||
else if(codes[i].opcode == OpCodes.Ldarg_3) {
|
||||
forHead = false;
|
||||
yield return codes[i];
|
||||
}
|
||||
else {
|
||||
yield return codes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
Source/Patches/HarmonyPatch_Pawn_DrawTracker.cs
Normal file
25
Source/Patches/HarmonyPatch_Pawn_DrawTracker.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
|
||||
[HarmonyPatch(typeof(Pawn_DrawTracker), "DrawPos", MethodType.Getter)]
|
||||
public static class HarmonyPatch_Pawn_DrawTracker {
|
||||
public static bool Prefix(ref Pawn ___pawn, ref Vector3 __result) {
|
||||
|
||||
if(___pawn.TryGetComp<CompBodyAnimator>() != null && ___pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
|
||||
__result = ___pawn.TryGetComp<CompBodyAnimator>().anchor + ___pawn.TryGetComp<CompBodyAnimator>().deltaPos;
|
||||
___pawn.Position = __result.ToIntVec3();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
50
Source/Patches/HarmonyPatch_ShowHairWithHats.cs
Normal file
50
Source/Patches/HarmonyPatch_ShowHairWithHats.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Verse;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
[StaticConstructorOnStartup]
|
||||
public static class Patch_ShowHairWithHats {
|
||||
|
||||
static Patch_ShowHairWithHats() {
|
||||
try {
|
||||
((Action)(() =>
|
||||
{
|
||||
if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[KV] Show Hair With Hats or Hide All Hats - 1.1")) {
|
||||
(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("ShowHair.Patch_PawnRenderer_RenderPawnInternal"), "Postfix"), //typeof(ShowHair.Patch_PawnRenderer_RenderPawnInternal), nameof(ShowHair.Patch_PawnRenderer_RenderPawnInternal.Postfix)),
|
||||
transpiler: new HarmonyMethod(AccessTools.Method(typeof(Patch_ShowHairWithHats), "Transpiler")));
|
||||
}
|
||||
}))();
|
||||
}
|
||||
catch (TypeLoadException ex) { }
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
|
||||
|
||||
MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");
|
||||
|
||||
List<CodeInstruction> codes = instructions.ToList();
|
||||
for (int i = 0; i < codes.Count(); i++) {
|
||||
|
||||
//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
|
||||
if (codes[i].OperandIs(drawMeshNowOrLater)) {
|
||||
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_0);
|
||||
yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
|
||||
yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation)));
|
||||
|
||||
}
|
||||
else {
|
||||
yield return codes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
Source/Patches/Harmony_PatchAll.cs
Normal file
22
Source/Patches/Harmony_PatchAll.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Verse;
|
||||
using HarmonyLib;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
|
||||
[StaticConstructorOnStartup]
|
||||
public static class Harmony_PatchAll {
|
||||
|
||||
static Harmony_PatchAll() {
|
||||
|
||||
Harmony val = new Harmony("rimworldanim");
|
||||
val.PatchAll(Assembly.GetExecutingAssembly());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using rjw;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
|
||||
[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
|
||||
static class HarmonyPatch_JobDriver_SexBaseInitiator_Start {
|
||||
public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {
|
||||
|
||||
if(__instance is JobDriver_JoinInBed) {
|
||||
Log.Warning("Tried to start wrong JobDriver with Rimworld-Animations installed. If you see this warning soon after installing this mod, it's fine and animated sex will start soon. If you see this a long time after installing, that's a problem.");
|
||||
return;
|
||||
}
|
||||
|
||||
Pawn Target = __instance.Target;
|
||||
Pawn pawn = __instance.pawn;
|
||||
|
||||
Building_Bed bed = __instance.Bed;
|
||||
|
||||
if (__instance is JobDriver_BestialityForFemale)
|
||||
bed = (__instance as JobDriver_BestialityForFemale).Bed;
|
||||
else if (__instance is JobDriver_WhoreIsServingVisitors) {
|
||||
bed = (__instance as JobDriver_WhoreIsServingVisitors).Bed;
|
||||
}
|
||||
else if(__instance is JobDriver_SexCasualForAnimation) {
|
||||
bed = (__instance as JobDriver_SexCasualForAnimation).Bed;
|
||||
}
|
||||
|
||||
if (__instance.Target.jobs?.curDriver is JobDriver_SexBaseReciever) {
|
||||
|
||||
if (!(Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Contains(pawn)) {
|
||||
(Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Add(pawn);
|
||||
}
|
||||
|
||||
if (bed != null) {
|
||||
RerollAnimations(Target, __instance.duration, bed as Thing);
|
||||
}
|
||||
else {
|
||||
RerollAnimations(Target, __instance.duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RerollAnimations(Pawn pawn, int duration, Thing bed = null) {
|
||||
|
||||
if(pawn == null || !(pawn.jobs?.curDriver is JobDriver_SexBaseReciever)) {
|
||||
Log.Message("Error: Tried to reroll animations when pawn isn't sexing");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Pawn> pawnsToAnimate = (pawn.jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList();
|
||||
|
||||
if (!pawnsToAnimate.Contains(pawn)) {
|
||||
pawnsToAnimate = pawnsToAnimate.Append(pawn).ToList();
|
||||
}
|
||||
|
||||
AnimationDef anim = AnimationUtility.tryFindAnimation(ref pawnsToAnimate);
|
||||
|
||||
if (anim != null) {
|
||||
|
||||
Log.Message("Now playing " + anim.defName);
|
||||
|
||||
bool mirror = GenTicks.TicksGame % 2 == 0;
|
||||
for (int i = 0; i < pawnsToAnimate.Count; i++) {
|
||||
|
||||
if (bed != null)
|
||||
pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().setAnchor(bed);
|
||||
else
|
||||
pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().setAnchor(pawn);
|
||||
|
||||
pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().StartAnimation(anim, i, mirror);
|
||||
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_left = anim.animationTimeTicks;
|
||||
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticksLeftThisToil = anim.animationTimeTicks;
|
||||
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).duration = anim.animationTimeTicks;
|
||||
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_remaining = anim.animationTimeTicks;
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log.Message("Anim not found");
|
||||
//if pawn isn't already animating,
|
||||
if (!pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
|
||||
(pawn.jobs.curDriver as JobDriver_SexBaseReciever).increase_time(duration);
|
||||
//they'll just do the thrusting anim
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "End")]
|
||||
static class HarmonyPatch_JobDriver_SexBaseInitiator_End {
|
||||
|
||||
public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {
|
||||
|
||||
if (__instance.Target.jobs?.curDriver is JobDriver_SexBaseReciever) {
|
||||
if (__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
|
||||
|
||||
List<Pawn> parteners = (__instance.Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners;
|
||||
|
||||
for (int i = 0; i < parteners.Count; i++) {
|
||||
|
||||
//prevents pawns who started a new anim from stopping their new anim
|
||||
if (!((parteners[i].jobs.curDriver as JobDriver_SexBaseInitiator) != null && (parteners[i].jobs.curDriver as JobDriver_SexBaseInitiator).Target != __instance.pawn))
|
||||
parteners[i].TryGetComp<CompBodyAnimator>().isAnimating = false;
|
||||
|
||||
if (xxx.is_human(parteners[i])) {
|
||||
parteners[i].Drawer.renderer.graphics.ResolveApparelGraphics();
|
||||
PortraitsCache.SetDirty(parteners[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__instance.Target.TryGetComp<CompBodyAnimator>().isAnimating = false;
|
||||
|
||||
if (xxx.is_human(__instance.Target)) {
|
||||
__instance.Target.Drawer.renderer.graphics.ResolveApparelGraphics();
|
||||
PortraitsCache.SetDirty(__instance.Target);
|
||||
}
|
||||
}
|
||||
|
||||
(__instance.Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Remove(__instance.pawn);
|
||||
|
||||
}
|
||||
|
||||
if (xxx.is_human(__instance.pawn)) {
|
||||
__instance.pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
|
||||
PortraitsCache.SetDirty(__instance.pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
Source/Patches/rjwPatches/HarmonyPatch_JoinInBedGiveJob.cs
Normal file
71
Source/Patches/rjwPatches/HarmonyPatch_JoinInBedGiveJob.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using rjw;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using RimWorld;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
[HarmonyPatch(typeof(JobGiver_JoinInBed), "TryGiveJob")]
|
||||
public static class HarmonyPatch_JoinInBedGiveJob {
|
||||
|
||||
public static bool Prefix(ref Job __result, ref Pawn pawn) {
|
||||
|
||||
__result = null;
|
||||
|
||||
if (!RJWHookupSettings.HookupsEnabled)
|
||||
return false;
|
||||
|
||||
if (pawn.Drafted)
|
||||
return false;
|
||||
|
||||
if (!SexUtility.ReadyForHookup(pawn))
|
||||
return false;
|
||||
|
||||
// We increase the time right away to prevent the fairly expensive check from happening too frequently
|
||||
SexUtility.IncreaseTicksToNextHookup(pawn);
|
||||
|
||||
// If the pawn is a whore, or recently had sex, skip the job unless they're really horny
|
||||
if (!xxx.is_frustrated(pawn) && (xxx.is_whore(pawn) || !SexUtility.ReadyForLovin(pawn)))
|
||||
return false;
|
||||
|
||||
// This check attempts to keep groups leaving the map, like guests or traders, from turning around to hook up
|
||||
if (pawn.mindState?.duty?.def == DutyDefOf.TravelOrLeave) {
|
||||
// TODO: Some guest pawns keep the TravelOrLeave duty the whole time, I think the ones assigned to guard the pack animals.
|
||||
// That's probably ok, though it wasn't the intention.
|
||||
if (RJWSettings.DebugLogJoinInBed) Log.Message($"[RJW] JoinInBed.TryGiveJob:({xxx.get_pawnname(pawn)}): has TravelOrLeave, no time for lovin!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pawn.CurJob == null || pawn.CurJob.def == JobDefOf.LayDown) {
|
||||
//--Log.Message(" checking pawn and abilities");
|
||||
if (xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn)) {
|
||||
//--Log.Message(" finding partner");
|
||||
Pawn partner = JobGiver_JoinInBed.find_pawn_to_fuck(pawn, pawn.Map);
|
||||
|
||||
//--Log.Message(" checking partner");
|
||||
if (partner == null)
|
||||
return false;
|
||||
|
||||
// Can never be null, since find checks for bed.
|
||||
Building_Bed bed = partner.CurrentBed();
|
||||
|
||||
// Interrupt current job.
|
||||
if (pawn.CurJob != null && pawn.jobs.curDriver != null)
|
||||
pawn.jobs.curDriver.EndJobWith(JobCondition.InterruptForced);
|
||||
|
||||
__result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), partner, bed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
42
Source/Patches/rjwPatches/HarmonyPatch_SexTick.cs
Normal file
42
Source/Patches/rjwPatches/HarmonyPatch_SexTick.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
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 Verse.Sound;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
|
||||
[HarmonyPatch(typeof(xxx), "sexTick")]
|
||||
public static class HarmonyPatch_SexTick {
|
||||
|
||||
public static bool Prefix(ref Pawn pawn, ref Pawn partner, ref bool enablerotation, ref bool pawnnude, ref bool partnernude) {
|
||||
|
||||
|
||||
if (enablerotation) {
|
||||
pawn.rotationTracker.Face(((Thing)partner).DrawPos);
|
||||
partner.rotationTracker.Face(((Thing)pawn).DrawPos);
|
||||
}
|
||||
if (RJWSettings.sounds_enabled && !pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
|
||||
SoundDef.Named("Sex").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
|
||||
}
|
||||
pawn.Drawer.Notify_MeleeAttackOn((Thing)(object)partner);
|
||||
if (enablerotation) {
|
||||
pawn.rotationTracker.FaceCell(partner.Position);
|
||||
}
|
||||
if (pawnnude && !xxx.has_quirk(pawn, "Endytophile")) {
|
||||
xxx.DrawNude(pawn);
|
||||
}
|
||||
if (partnernude && !xxx.has_quirk(pawn, "Endytophile")) {
|
||||
xxx.DrawNude(partner);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
29
Source/Patches/rjwPatches/HarmonyPatch_WorkGiverSex.cs
Normal file
29
Source/Patches/rjwPatches/HarmonyPatch_WorkGiverSex.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using rjw;
|
||||
using HarmonyLib;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
using Verse.AI;
|
||||
|
||||
namespace Rimworld_Animations {
|
||||
|
||||
[HarmonyPatch(typeof(WorkGiver_Sex), "JobOnThing")]
|
||||
public static class HarmonyPatch_WorkGiverSex {
|
||||
|
||||
public static bool Prefix(ref Job __result, ref Thing t) {
|
||||
|
||||
Building_Bed bed = RestUtility.CurrentBed(t as Pawn);
|
||||
if (bed == null) {
|
||||
return false;
|
||||
}
|
||||
__result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), t as Pawn, bed);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
Subproject commit ab9a1d98883cb78d0d40a6bd93d85de7e34b132b
|
Loading…
Reference in a new issue