rimworld-animations-patch_m.../Source/Scripts/Utilities/AnimationPatchUtility.cs

216 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using UnityEngine;
using HarmonyLib;
using Verse;
using Rimworld_Animations;
using rjw;
namespace Rimworld_Animations_Patch
{
public static class AnimationPatchUtility
{
public static int FindTrueAnimationLength(Pawn pawn, out int orgasmTick)
{
orgasmTick = int.MaxValue;
ActorAnimationData actorAnimationData = pawn.GetAnimationData();
CompBodyAnimator compBodyAnimator = pawn.TryGetComp<CompBodyAnimator>();
// No data
if (actorAnimationData == null || compBodyAnimator == null)
{
DebugMode.Message("There is no actor animation data for " + pawn.NameShortColored);
orgasmTick = 1500 + (int)(Rand.Value * 1000f);
return orgasmTick;
}
AnimationDef anim = actorAnimationData.animationDef;
int actorId = actorAnimationData.actorID;
bool isQuickie = compBodyAnimator.fastAnimForQuickie;
int ticks = 0;
foreach (AnimationStage animStage in anim.animationStages)
{
// Legacy: skip the first stage of quickies if there's no playTimeTicksQuick values declared
if (anim.animationStages.IndexOf(animStage) == 0 && isQuickie && anim.animationStages.Any(x => x.playTimeTicksQuick >= 0) == false)
{ continue; }
int curr_tick = 0;
foreach (PawnKeyframe keyframe in (animStage.animationClips[actorId] as PawnAnimationClip).keyframes)
{
curr_tick += keyframe.tickDuration;
if (keyframe.soundEffect != null && keyframe.soundEffect == "Cum" && orgasmTick > (ticks + curr_tick))
{ orgasmTick = ticks + curr_tick; }
if (isQuickie && animStage.playTimeTicksQuick > 0 && curr_tick >= animStage.playTimeTicksQuick)
{ break; }
}
ticks += isQuickie && animStage.playTimeTicksQuick > 0 && animStage.playTimeTicksQuick < animStage.playTimeTicks ? animStage.playTimeTicksQuick : animStage.playTimeTicks;
}
// Orgasm tick not found
if (orgasmTick > ticks)
{
// Safeguard for penial, vaginal and anal sex
if (anim.actors[actorId].isFucked || anim.actors[actorId].isFucking || (anim.actors[actorId].requiredGenitals.NullOrEmpty() == false && anim.actors[actorId].requiredGenitals.Any(x => x.ContainsAny("penis", "vagina", "anus", "Penis", "Vagina", "Anus"))))
{ orgasmTick = Mathf.Clamp(ticks - 5, 0, int.MaxValue); }
// Actor does not orgasm
else
{ orgasmTick = (int)(ticks * (2f + Rand.Value)); }
}
return ticks;
}
// Extended version of PawnHeadRotInAnimation (prevents pawn hair from getting messed up when draw in portraits)
public static Rot4 PawnHeadRotInAnimation(Pawn pawn, Rot4 regularPos, PawnRenderFlags renderFlags)
{
if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawn?.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating)
{ return pawn.TryGetComp<CompBodyAnimator>().headFacing; }
return regularPos;
}
public static Rot4 PawnBodyRotInAnimation(Pawn pawn, Rot4 regularPos, PawnRenderFlags renderFlags)
{
if (!renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawn?.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating)
{ return pawn.TryGetComp<CompBodyAnimator>().bodyFacing; }
return regularPos;
}
public static BodyPartRecord GetBodyPartRecord(Pawn pawn, string bodyPart)
{
if (bodyPart.NullOrEmpty())
{ return null; }
return pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Undefined, null, null).FirstOrDefault(x => x.untranslatedCustomLabel == bodyPart || x.def.defName == bodyPart);
}
public static Vector3 GetAnchorPosition(Pawn pawn, Thing thing = null)
{
Vector3 anchor;
if (thing == null)
{ return pawn.Position.ToVector3Shifted(); }
int numOfSleepingSlots = 0;
if (thing is Building_Bed)
{ numOfSleepingSlots = BedUtility.GetSleepingSlotsCount(thing.def.size); }
// Anchor to the pawn's sleeping slot when masturbating in own bed
if (thing is Building_Bed && (pawn.ownership.OwnedBed == thing || pawn.CurrentBed() == thing) && pawn.IsMasturbating())
{
anchor = RestUtility.GetBedSleepingSlotPosFor(pawn, thing as Building_Bed).ToVector3();
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;
anchor.z += 0.5f;
}
else if (thing.Rotation.AsInt == 3)
{
anchor.x += 0f;
anchor.z += 0.5f;
}
}
// Anchor to the center of the bed (should work for beds of any size?)
else if (thing is Building_Bed && numOfSleepingSlots > 0)
{
anchor = thing.Position.ToVector3();
float halfSlots = numOfSleepingSlots / 2f;
if (thing.Rotation.AsInt == 0)
{
anchor.x += halfSlots;
anchor.z += 1f;
}
else if (thing.Rotation.AsInt == 1)
{
anchor.x += 1f;
anchor.z += (1.0f - halfSlots);
}
else if (thing.Rotation.AsInt == 2)
{
anchor.x += (1f - halfSlots);
anchor.z += 0f;
}
else if (thing.Rotation.AsInt == 3)
{
anchor.x += 0f;
anchor.z += halfSlots;
}
}
// Anchor to the centre of the thing
else
{
anchor = thing.Position.ToVector3Shifted();
}
return anchor;
}
public static bool ShouldNotAnimatePawn(Pawn pawn)
{
return pawn.ageTracker.CurLifeStage.developmentalStage == DevelopmentalStage.Baby || pawn.ageTracker.CurLifeStage.developmentalStage == DevelopmentalStage.Child;
}
public static float GetBodySize(Pawn pawn)
{
return 1f;
}
private static Dictionary<string, Vector3> raceSpecificChildMultipliers = new Dictionary<string, Vector3>()
{
{ "Alien_Orassan", new Vector3(1.4f, 1.4f, 1.4f) },
{ "Alien_Cutebold", new Vector3(1.2f, 1f, 1f) },
{ "Rabbie", new Vector3(-0.5f, 1f, 1f) },
};
public static Vector2 GetRaceSpecificOffsetMultipliers(Pawn pawn, BodyPartDef bodypart)
{
Vector2 multiplierVector = new Vector2();
if (GetBodySize(pawn) == 1f || raceSpecificChildMultipliers.TryGetValue(pawn.def.defName, out Vector3 raceVector) == false)
{ raceVector = new Vector3(1f, 1f, 1f); }
if (bodypart?.defName == "tail" || bodypart?.defName == "Tail")
{
multiplierVector.x = raceVector.z;
multiplierVector.y = raceVector.x;
}
else
{
multiplierVector.x = raceVector.y;
multiplierVector.y = raceVector.x;
}
return multiplierVector;
}
}
}