rimworld-animations/Source/Comps/CompBodyAnimator.cs

484 lines
18 KiB
C#
Raw Normal View History

2020-04-09 00:43:01 +00:00
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) {
2020-04-26 15:03:57 +00:00
SexUtility.DrawNude(pawn);
2020-04-09 00:43:01 +00:00
} else {
pawn.Drawer.renderer.graphics.ResolveAllGraphics();
2020-10-29 03:21:19 +00:00
actorsInCurrentAnimation = null;
2020-04-09 00:43:01 +00:00
}
PortraitsCache.SetDirty(pawn);
}
}
2020-05-29 21:54:48 +00:00
private bool Animating = false;
2020-04-21 04:59:09 +00:00
private bool mirror = false, quiver = false, shiver = false;
2020-04-09 00:43:01 +00:00
private int actor;
private int lastDrawFrame = -1;
2020-04-09 00:43:01 +00:00
private int animTicks = 0, stageTicks = 0, clipTicks = 0;
private int curStage = 0;
private float clipPercent = 0;
public Vector3 anchor = Vector3.zero, deltaPos = Vector3.zero, headBob = Vector3.zero;
public float bodyAngle = 0, headAngle = 0, genitalAngle = 0;
public Rot4 headFacing = Rot4.North, bodyFacing = Rot4.North;
2020-04-09 00:43:01 +00:00
2020-10-29 03:21:19 +00:00
public List<Pawn> actorsInCurrentAnimation;
public bool controlGenitalAngle = false;
2020-04-09 00:43:01 +00:00
private AnimationDef anim;
private AnimationStage stage => anim.animationStages[curStage];
private PawnAnimationClip clip => (PawnAnimationClip)stage.animationClips[actor];
public bool Mirror {
get {
return mirror;
}
}
2020-04-09 00:43:01 +00:00
public void setAnchor(IntVec3 pos)
{
2020-04-12 19:22:35 +00:00
anchor = pos.ToVector3Shifted();
2020-04-09 00:43:01 +00:00
}
public void setAnchor(Thing thing) {
2020-04-12 19:22:35 +00:00
2020-04-09 00:43:01 +00:00
//center on bed
if(thing is Building_Bed) {
2020-04-12 19:22:35 +00:00
anchor = thing.Position.ToVector3();
if (((Building_Bed)thing).SleepingSlotsCount == 2) {
2020-04-09 00:43:01 +00:00
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;
}
}
}
2020-04-12 19:22:35 +00:00
else {
anchor = thing.Position.ToVector3Shifted();
}
2020-04-09 00:43:01 +00:00
}
2020-10-29 03:21:19 +00:00
public void StartAnimation(AnimationDef anim, List<Pawn> actors, int actor, bool mirror = false, bool shiver = false, bool fastAnimForQuickie = false) {
2020-04-09 00:43:01 +00:00
2020-10-29 03:21:19 +00:00
actorsInCurrentAnimation = actors;
2020-04-28 03:34:30 +00:00
AlienRaceOffset raceOffset = anim?.actors[actor]?.raceOffsets?.Find(x => x.defName == pawn.def.defName);
2020-04-18 16:49:03 +00:00
2020-04-28 03:34:30 +00:00
if (raceOffset != null) {
anchor.x += mirror ? raceOffset.offset.x * -1f : raceOffset.offset.x;
anchor.z += raceOffset.offset.y;
2020-04-18 16:49:03 +00:00
}
2020-05-09 01:19:38 +00:00
//change the offset based on pawn body type
if(pawn?.story?.bodyType != null) {
if (pawn.story.bodyType == BodyTypeDefOf.Fat && anim?.actors[actor]?.bodyTypeOffset?.Fat != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Fat.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Fat.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Female && anim?.actors[actor]?.bodyTypeOffset?.Female != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Female.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Female.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Male && anim?.actors[actor]?.bodyTypeOffset?.Male != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Male.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Male.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Thin && anim?.actors[actor]?.bodyTypeOffset?.Thin != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Thin.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Thin.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Hulk && anim?.actors[actor]?.bodyTypeOffset?.Hulk != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Hulk.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Hulk.Value.y;
}
}
2020-04-09 00:43:01 +00:00
pawn.jobs.posture = PawnPosture.Standing;
this.actor = actor;
this.anim = anim;
this.mirror = mirror;
curStage = fastAnimForQuickie ? 1 : 0;
2020-04-09 00:43:01 +00:00
animTicks = 0;
stageTicks = 0;
clipTicks = 0;
2020-04-21 03:07:15 +00:00
quiver = false;
2020-04-21 04:59:09 +00:00
this.shiver = shiver && AnimationSettings.rapeShiver;
2020-04-21 03:07:15 +00:00
controlGenitalAngle = anim.actors[actor].controlGenitalAngle;
isAnimating = true;
2020-04-09 00:43:01 +00:00
//tick once for initialization
tickAnim();
}
2020-04-09 00:43:01 +00:00
public override void CompTick() {
2020-04-09 00:43:01 +00:00
base.CompTick();
2020-04-20 02:23:36 +00:00
if(isAnimating) {
2020-05-29 20:12:35 +00:00
if (pawn.Dead || pawn?.jobs?.curDriver == null || (pawn?.jobs?.curDriver != null && !(pawn?.jobs?.curDriver is rjw.JobDriver_Sex))) {
2020-04-10 00:03:56 +00:00
isAnimating = false;
2020-04-09 00:43:01 +00:00
}
2020-04-19 18:16:49 +00:00
else {
tickAnim();
}
2020-04-09 00:43:01 +00:00
}
}
public void animatePawn(ref Vector3 rootLoc, ref float angle, ref Rot4 bodyFacing, ref Rot4 headFacing) {
2020-04-20 02:23:36 +00:00
if(!isAnimating) {
2020-04-09 00:43:01 +00:00
return;
}
rootLoc = anchor + deltaPos;
angle = bodyAngle;
bodyFacing = this.bodyFacing;
headFacing = this.headFacing;
2020-04-09 00:43:01 +00:00
}
public void tickGraphics(PawnGraphicSet graphics) {
this.Graphics = graphics;
}
public void tickAnim() {
2020-04-20 02:23:36 +00:00
if (!isAnimating) return;
2020-04-09 00:43:01 +00:00
if (anim == null) {
isAnimating = false;
return;
}
2020-04-09 00:43:01 +00:00
animTicks++;
2020-04-09 00:43:01 +00:00
if (animTicks < anim.animationTimeTicks) {
tickStage();
} else {
if(LoopNeverending())
{
ResetOnLoop();
} else
{
isAnimating = false;
}
2020-10-29 03:21:19 +00:00
2020-04-09 00:43:01 +00:00
}
}
public void tickStage()
{
stageTicks++;
if(stageTicks >= stage.playTimeTicks) {
2020-04-20 02:23:36 +00:00
2020-04-09 00:43:01 +00:00
curStage++;
2020-04-20 02:23:36 +00:00
2020-04-09 00:43:01 +00:00
stageTicks = 0;
clipTicks = 0;
clipPercent = 0;
}
if(curStage >= anim.animationStages.Count) {
if (LoopNeverending())
{
ResetOnLoop();
}
else
{
isAnimating = false;
pawn.jobs.curDriver.ReadyForNextToil();
}
} else {
tickClip();
}
2020-04-09 00:43:01 +00:00
}
public void tickClip() {
clipTicks++;
//play sound effect
2020-04-22 18:12:06 +00:00
if(rjw.RJWSettings.sounds_enabled && clip.SoundEffects.ContainsKey(clipTicks) && AnimationSettings.soundOverride) {
2021-03-21 00:07:29 +00:00
2021-03-21 00:07:29 +00:00
SoundInfo sound = new TargetInfo(pawn.Position, pawn.Map);
string soundEffectName = clip.SoundEffects[clipTicks];
2021-03-21 00:07:29 +00:00
if ((pawn.jobs.curDriver as JobDriver_Sex).isAnimalOnAnimal)
{
sound.volumeFactor *= RJWSettings.sounds_animal_on_animal_volume;
}
if(soundEffectName.StartsWith("Voiceline_"))
{
sound.volumeFactor *= RJWSettings.sounds_voice_volume;
}
if (clip.SoundEffects[clipTicks] == "Cum") {
sound.volumeFactor *= RJWSettings.sounds_cum_volume;
considerApplyingSemen();
2020-04-29 06:32:40 +00:00
2021-03-21 00:07:29 +00:00
} else
{
sound.volumeFactor *= RJWSettings.sounds_sex_volume;
}
2021-03-21 00:07:29 +00:00
SoundDef.Named(soundEffectName).PlayOneShot(sound);
2020-04-09 00:43:01 +00:00
}
2020-04-21 03:07:15 +00:00
if(AnimationSettings.orgasmQuiver && clip.quiver.ContainsKey(clipTicks)) {
quiver = clip.quiver[clipTicks];
}
2020-04-09 00:43:01 +00:00
//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();
}
2021-03-21 00:07:29 +00:00
public void considerApplyingSemen()
{
if(AnimationSettings.applySemenOnAnimationOrgasm && (pawn?.jobs?.curDriver is JobDriver_Sex))
{
Pawn partner = (pawn.jobs.curDriver as JobDriver_Sex)?.Partner;
if (anim.sexTypes.Contains((pawn.jobs.curDriver as JobDriver_Sex).sexType))
{
SemenHelper.calculateAndApplySemen(pawn, partner, (pawn.jobs.curDriver as JobDriver_Sex).sexType);
}
}
}
2020-04-09 00:43:01 +00:00
public void calculateDrawValues() {
/*if(Find.TickManager.TickRateMultiplier > 1 && (lastDrawFrame + 1 >= RealTime.frameCount || RealTime.deltaTime < 0.05f)) {
return;
}*/
2020-04-09 00:43:01 +00:00
deltaPos = new Vector3(clip.BodyOffsetX.Evaluate(clipPercent) * (mirror ? -1 : 1), clip.layer.AltitudeFor(), clip.BodyOffsetZ.Evaluate(clipPercent));
2020-05-31 19:03:30 +00:00
if (AnimationSettings.offsets != null && AnimationSettings.offsets.ContainsKey(CurrentAnimation.defName + pawn.def.defName + ActorIndex)) {
deltaPos.x += AnimationSettings.offsets[CurrentAnimation.defName + pawn.def.defName + ActorIndex].x * (mirror ? -1 : 1);
deltaPos.z += AnimationSettings.offsets[CurrentAnimation.defName + pawn.def.defName + ActorIndex].y;
}
2020-04-21 04:59:09 +00:00
bodyAngle = (clip.BodyAngle.Evaluate(clipPercent) + (quiver || shiver ? ((Rand.Value * AnimationSettings.shiverIntensity) - (AnimationSettings.shiverIntensity / 2f)) : 0f)) * (mirror ? -1 : 1);
headAngle = clip.HeadAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
if (controlGenitalAngle) {
genitalAngle = clip.GenitalAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
}
2020-05-31 19:03:30 +00:00
if (AnimationSettings.rotation != null && AnimationSettings.rotation.ContainsKey(CurrentAnimation.defName + pawn.def.defName + ActorIndex)) {
float offsetRotation = AnimationSettings.rotation[CurrentAnimation.defName + pawn.def.defName + ActorIndex] * (Mirror ? -1 : 1);
genitalAngle += offsetRotation;
bodyAngle += offsetRotation;
headAngle += offsetRotation;
}
2020-04-22 18:12:06 +00:00
//don't go past 360 or less than 0
if (bodyAngle < 0) bodyAngle = 360 - ((-1f*bodyAngle) % 360);
if (bodyAngle > 360) bodyAngle %= 360;
2020-04-22 18:12:06 +00:00
if (headAngle < 0) headAngle = 360 - ((-1f * headAngle) % 360);
if (headAngle > 360) headAngle %= 360;
if (genitalAngle < 0) genitalAngle = 360 - ((-1f * genitalAngle) % 360);
if (genitalAngle > 360) genitalAngle %= 360;
2020-04-22 18:12:06 +00:00
2020-04-09 00:43:01 +00:00
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));
lastDrawFrame = RealTime.frameCount;
2020-04-09 00:43:01 +00:00
}
public Vector3 getPawnHeadPosition() {
2020-05-30 06:10:31 +00:00
Vector3 headPos = anchor + deltaPos + Quaternion.AngleAxis(bodyAngle, Vector3.up) * (pawn.Drawer.renderer.BaseHeadOffsetAt(headFacing) + headBob);
2020-04-09 00:43:01 +00:00
2020-05-30 06:10:31 +00:00
return headPos;
}
public AnimationDef CurrentAnimation {
get {
return anim;
}
}
public int ActorIndex {
get {
return actor;
}
2020-04-09 00:43:01 +00:00
}
public override void PostExposeData() {
base.PostExposeData();
Scribe_Defs.Look(ref anim, "RJWAnimations-Anim");
2020-04-09 00:43:01 +00:00
Scribe_Values.Look(ref animTicks, "RJWAnimations-animTicks", 1);
Scribe_Values.Look(ref stageTicks, "RJWAnimations-stageTicks", 1);
Scribe_Values.Look(ref clipTicks, "RJWAnimations-clipTicks", 1);
Scribe_Values.Look(ref clipPercent, "RJWAnimations-clipPercent", 1);
2020-04-09 00:43:01 +00:00
Scribe_Values.Look(ref mirror, "RJWAnimations-mirror");
2020-04-09 00:43:01 +00:00
Scribe_Values.Look(ref curStage, "RJWAnimations-curStage", 0);
Scribe_Values.Look(ref actor, "RJWAnimations-actor");
2020-04-09 00:43:01 +00:00
Scribe_Values.Look(ref Animating, "RJWAnimations-Animating");
Scribe_Values.Look(ref anchor, "RJWAnimations-anchor");
Scribe_Values.Look(ref deltaPos, "RJWAnimations-deltaPos");
Scribe_Values.Look(ref headBob, "RJWAnimations-headBob");
Scribe_Values.Look(ref bodyAngle, "RJWAnimations-bodyAngle");
Scribe_Values.Look(ref headAngle, "RJWAnimations-headAngle");
2020-04-09 00:43:01 +00:00
Scribe_Values.Look(ref genitalAngle, "RJWAnimations-GenitalAngle");
Scribe_Values.Look(ref controlGenitalAngle, "RJWAnimations-controlGenitalAngle");
2020-05-29 20:12:35 +00:00
Scribe_Values.Look(ref headFacing, "RJWAnimations-headFacing");
Scribe_Values.Look(ref headFacing, "RJWAnimations-bodyFacing");
2020-04-21 03:07:15 +00:00
Scribe_Values.Look(ref quiver, "RJWAnimations-orgasmQuiver");
2020-04-09 00:43:01 +00:00
}
2020-10-29 03:21:19 +00:00
public void shiftActorPositionAndRestartAnimation() {
actor = (actor == anim.actors.Count - 1 ? 0 : actor + 1);
2020-10-29 03:47:16 +00:00
if (pawn?.story?.bodyType != null) {
if (pawn.story.bodyType == BodyTypeDefOf.Fat && anim?.actors[actor]?.bodyTypeOffset?.Fat != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Fat.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Fat.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Female && anim?.actors[actor]?.bodyTypeOffset?.Female != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Female.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Female.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Male && anim?.actors[actor]?.bodyTypeOffset?.Male != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Male.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Male.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Thin && anim?.actors[actor]?.bodyTypeOffset?.Thin != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Thin.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Thin.Value.y;
}
else if (pawn.story.bodyType == BodyTypeDefOf.Hulk && anim?.actors[actor]?.bodyTypeOffset?.Hulk != null) {
anchor.x += anim.actors[actor].bodyTypeOffset.Hulk.Value.x * (mirror ? -1f : 1f);
anchor.z += anim.actors[actor].bodyTypeOffset.Hulk.Value.y;
}
}
2020-10-29 03:21:19 +00:00
curStage = 0;
animTicks = 0;
stageTicks = 0;
clipTicks = 0;
controlGenitalAngle = anim.actors[actor].controlGenitalAngle;
tickAnim();
}
public bool LoopNeverending()
{
if(pawn?.jobs?.curDriver != null &&
(pawn.jobs.curDriver is JobDriver_Sex) && (pawn.jobs.curDriver as JobDriver_Sex).neverendingsex ||
(pawn.jobs.curDriver is JobDriver_SexBaseReciever) && (pawn.jobs.curDriver as JobDriver_Sex).Partner?.jobs?.curDriver != null && ((pawn.jobs.curDriver as JobDriver_Sex).Partner.jobs.curDriver as JobDriver_Sex).neverendingsex)
{
return true;
}
return false;
}
public void ResetOnLoop()
{
curStage = 1;
animTicks = 0;
stageTicks = 0;
clipTicks = 0;
tickAnim();
}
2020-04-09 00:43:01 +00:00
}
}