using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using Verse; using RimWorld; using HarmonyLib; using Rimworld_Animations; namespace Rimworld_Animations_Patch { public static class HandAnimationUtility { public static BodyPartDef handDef; public static bool BodyPartIsBeingTouched(Pawn pawn, string bodypartFilePath, out List handAnimationData) { handAnimationData = new List(); ActorAnimationData actorAnimationData = pawn.GetAnimationData(); HandAnimationDef handAnimationDef = DefDatabase.AllDefs.FirstOrDefault(x => x.animationDefName == actorAnimationData.animationDef.defName); if (handAnimationDef == null) { return false; } foreach (HandAnimationData datum in handAnimationDef.handAnimationData) { if (datum.stageID != actorAnimationData.currentStage || datum.actorID != actorAnimationData.actorID) { continue; } if (datum.bodySide.NullOrEmpty() == false && bodypartFilePath.ToLower().Contains(datum.bodySide) == false) { continue; } if (datum.targetBodyPart.NullOrEmpty() == false && bodypartFilePath.ToLower().Contains(datum.targetBodyPart.ToLower())) { handAnimationData.Add(datum); } else if (datum.targetBodyParts.Any(x => bodypartFilePath.ToLower().Contains(x.ToLower()))) { handAnimationData.Add(datum); } } return handAnimationData.NullOrEmpty() == false; } public static Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, Vector3 basePosition, float baseAngle) { var methodInfo = AccessTools.Method(typeof(HandAnimationUtility), handAnimationData.motion, null, null); if (methodInfo == null) { Debug.LogWarning("Hand anaimation motion '" + handAnimationData.motion + "' was not found"); return default; } Vector3 handPosition = (Vector3)methodInfo.Invoke(null, new object[] { pawn, handAnimationData, baseAngle }); return handPosition * pawn.RaceProps.baseBodySize + basePosition; } public static float GetGenitalSize(Pawn pawn, string genitalName) { switch(genitalName.ToLower()) { case "penis": return pawn.health.hediffSet.hediffs.First(x => x.def.defName.ToLower().Contains("penis")).Severity; case "breasts": return pawn.health.hediffSet.hediffs.First(x => x.def.defName.ToLower().Contains("breasts")).Severity; case "vagina": return 0.1f; case "anus": return 0.1f; } return 0.1f; } public static Vector3 Motion_StrokeGenitalsUpAndDownShort_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float p = (Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime); float length = 0.035f; handPosition.x = 0; handPosition.z = length * p; handPosition = handPosition.RotatedBy(baseAngle); return handPosition; } public static Vector3 Motion_StrokeGenitalsUpAndDown_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float p = (Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime); float size = GetGenitalSize(pawn, handAnimationData.targetBodyPart) * 0.2f; float m = (data.actorFacing == Rot4.North ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f) * (pawn.TryGetComp().Mirror ? -1f : 1f); handPosition.x = 0.025f * m; handPosition.z = size * p; handPosition = handPosition.RotatedBy(baseAngle); return handPosition; } public static Vector3 Motion_StrokeGenitalsUpAndDown_FacingEW(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float p = Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime; float size = GetGenitalSize(pawn, handAnimationData.targetBodyPart) * 0.2f; float m = (data.actorFacing == Rot4.West ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f); handPosition.x = Mathf.Sin(m * (baseAngle + 45f) / 180f * Mathf.PI) * size * p; handPosition.z = Mathf.Cos(m * (baseAngle + 45f) / 180f * Mathf.PI) * size * p; return handPosition; } public static Vector3 Motion_RubGenitals_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f; float m = (data.actorFacing == Rot4.North ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f) * (pawn.TryGetComp().Mirror ? -1f : 1f); handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.05f - 0.025f) * m; handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f + 0.03f; handPosition = handPosition.RotatedBy(baseAngle); return handPosition; } public static Vector3 Motion_RubGenitals_FacingEW(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f; float m = (data.actorFacing == Rot4.West ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f); handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.005f - 0.05f) * m; handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f; //handPosition.y = -0.1f; handPosition = handPosition.RotatedBy(baseAngle); return handPosition; } public static Vector3 Motion_RubBreasts_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f; float m = (data.actorFacing == Rot4.North ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f) * (pawn.TryGetComp().Mirror ? -1f : 1f); float size = GetGenitalSize(pawn, "breasts"); handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.05f * size - size * 0.25f) * m; handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f - size * 0.125f; handPosition = handPosition.RotatedBy(baseAngle); return handPosition; } public static Vector3 Motion_RubBreasts_FacingEW(Pawn pawn, HandAnimationData handAnimationData, float baseAngle) { Vector3 handPosition = new Vector3(); ActorAnimationData data = pawn.GetAnimationData(); float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f; float m = (data.actorFacing == Rot4.West ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f); float size = GetGenitalSize(pawn, "breasts"); handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.005f - size * 0.25f) * m; handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f - size * 0.125f; handPosition = handPosition.RotatedBy(baseAngle); return handPosition; } public static Graphic GetHandGraphic(Pawn touchingPawn, string touchedBodyAddonName, HandAnimationData handAnimationData) { string handGraphicPath = "Hands/HandClean"; Color skinColour = touchingPawn.story.SkinColor; float handSize = 0.6667f * touchingPawn.RaceProps.baseBodySize; return GraphicDatabase.Get(handGraphicPath, ShaderDatabase.Cutout, new Vector2(handSize, handSize), skinColour); } public static bool TryToDrawHand(Pawn pawn, string bodyAddonName, Vector3 bodyAddonPosition, float bodyAddonAngle, Rot4 bodyAddonRotation, PawnRenderFlags renderFlags) { if (pawn.TryGetComp() != null && pawn.TryGetComp().isAnimating && BodyPartIsBeingTouched(pawn, bodyAddonName, out List handAnimationData)) { foreach (HandAnimationData datum in handAnimationData) { Pawn touchingPawn = datum.touchingActorID >= 0 && pawn.GetAllSexParticipants().Count > datum.touchingActorID ? pawn.GetAllSexParticipants()[datum.touchingActorID] : pawn; Graphic handgraphic = GetHandGraphic(touchingPawn, bodyAddonName, datum); Vector3 handPosition = GetHandPosition(pawn, datum, bodyAddonPosition, bodyAddonAngle); GenDraw.DrawMeshNowOrLater(mesh: handgraphic.MeshAt(rot: bodyAddonRotation), loc: handPosition + new Vector3(0f, 0.022f, 0f), quat: Quaternion.identity, mat: handgraphic.MatAt(rot: bodyAddonRotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow)); return true; } } return false; } } }