rimworld-animations/1.5/Source/Comps/CompExtendedAnimator.cs

289 lines
8.0 KiB
C#

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
{
// CompExtendedAnimator
// Helps manage AnimationQueue, AbsolutePosition
//ticks of current animation
private int animationTicks;
private List<AnimationDef> animationQueue;
private BaseExtendedAnimatorAnchor anchor;
private int? rotation;
private Vector3? offset;
private bool isAnimating = false;
public bool IsAnimating
{
get
{
return isAnimating;
}
}
public bool IsAnchored
{
get
{
return anchor != null;
}
}
public Vector3? Offset
{
get
{
return offset;
}
set
{
this.offset = value;
}
}
public int? Rotation
{
get
{
return rotation;
}
set
{
this.rotation = value;
}
}
public int AnimationLength
{
get
{
if (!IsAnimating) return 0;
int groupAnimLength = 0;
foreach(AnimationDef anim in animationQueue)
{
groupAnimLength += anim.durationTicks;
}
return groupAnimLength;
}
}
public AnimationDef CurrentAnimation {
get
{
return IsAnimating ? animationQueue[0] : null;
}
}
public Vector3 getAnchor()
{
return anchor.getDrawPos();
}
public override void CompTick()
{
if (isAnimating)
{
animationTicks++;
//if animationticks is equal to cur. anim duration,
if (animationTicks >= animationQueue[0].durationTicks)
{
//dequeue; returns false if more animations
if (!PopAnimationQueue())
{
//play next if more anims still
PlayNextAnimation();
}
else
{
StopAnimating();
}
}
CheckAndPlaySounds();
}
base.CompTick();
}
//returns false if still more animations
public bool PopAnimationQueue()
{
if (!animationQueue.Empty())
{
//pop queue
animationQueue.RemoveAt(0);
}
return animationQueue.Empty();
}
public void PlayNextAnimation()
{
if (!animationQueue.Empty())
{
isAnimating = true;
animationTicks = 0;
pawn.Drawer.renderer.SetAnimation(animationQueue[0]);
}
}
public void StopAnimating()
{
isAnimating = false;
animationQueue = null;
anchor = null;
offset = null;
pawn.Drawer.renderer.SetAnimation(null);
}
public void PlayGroupAnimation(List<AnimationDef> groupAnimation, Vector3? positionOffset, int? rotationOffset)
{
this.Offset = positionOffset;
this.Rotation = rotationOffset;
animationQueue = groupAnimation;
PlayNextAnimation();
}
public void PlayGroupAnimation(List<AnimationDef> groupAnimation, Vector3? positionOffset, int? rotationOffset, BaseExtendedAnimatorAnchor anchor)
{
this.anchor = anchor;
PlayGroupAnimation(groupAnimation, positionOffset, rotationOffset);
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look<bool>(ref this.isAnimating, "animations_isAnimating", false);
Scribe_Values.Look<int>(ref this.animationTicks, "animations_ticks", 0);
Scribe_Collections.Look<AnimationDef>(ref animationQueue, "animations_queue");
Scribe_Deep.Look<BaseExtendedAnimatorAnchor>(ref this.anchor, "animations_anchor");
}
public override List<PawnRenderNode> CompRenderNodes()
{
//only if pawn is animating for performance
if (IsAnimating)
{
List<PawnRenderNode> animRenderNodes = new List<PawnRenderNode>();
// for all animationpropdefs,
foreach (AnimationPropDef animationProp in DefDatabase<AnimationPropDef>.AllDefsListForReading)
{
//if animation makes use of prop,
if (AnimationMakesUseOfProp(animationProp))
{
PawnRenderNodeProperties props = animationProp.animPropProperties;
if (props.texPath.NullOrEmpty())
{
props.texPath = "AnimationProps/MissingTexture/MissingTexture";
}
//create new render node
PawnRenderNode animRenderNode = (PawnRenderNode)Activator.CreateInstance(props.nodeClass, new object[] {
this.pawn,
props,
pawn.Drawer.renderer.renderTree
});
animRenderNodes.Add(animRenderNode);
}
}
//return list of rendernodes that should animate
return animRenderNodes;
}
else
{
return null;
}
}
public void CheckAndPlaySounds()
{
PawnRenderNode rootNode = pawn.Drawer?.renderer?.renderTree?.rootNode;
//check if the rootnode has sounds; if so play it
if (rootNode?.AnimationWorker is AnimationWorker_KeyframesExtended animWorker)
{
SoundDef sound = animWorker.soundAtTick(rootNode.tree.AnimationTick);
if (sound != null)
{
sound.PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
}
}
if (rootNode?.children != null)
{
foreach (PawnRenderNode node in rootNode?.children)
{
if (node?.AnimationWorker is AnimationWorker_KeyframesExtended childrenAnimWorker)
{
SoundDef sound = childrenAnimWorker.soundAtTick(node.tree.AnimationTick);
if (sound != null)
{
sound.PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
}
}
}
}
//do the same for all the child nodes
}
public bool AnimationMakesUseOfProp(AnimationPropDef animationProp)
{
// never true if not animating; anim props shouldn't be attached
if (!IsAnimating) return false;
//for ONLY THE CURRENT animation,
foreach (PawnRenderNodeTagDef propTag in animationQueue[0].animationParts.Keys)
{
// if that proptag is the same as the one for animationProp,
if (propTag == animationProp.animPropProperties.tagDef)
{
//that prop is being used in the animation
return true;
}
}
return false;
}
private Pawn pawn => base.parent as Pawn;
}
}