2022-10-27 05:56:04 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using System.Xml.Serialization;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
|
|
|
|
namespace RimWorldAnimationStudio
|
|
|
|
|
{
|
|
|
|
|
public class PawnKeyframe
|
|
|
|
|
{
|
|
|
|
|
// Data to/from animationDef
|
|
|
|
|
public float? bodyAngle;
|
|
|
|
|
public float? headAngle;
|
|
|
|
|
public float? headBob;
|
|
|
|
|
public float? bodyOffsetX;
|
|
|
|
|
public float? bodyOffsetZ;
|
|
|
|
|
public int? headFacing;
|
|
|
|
|
public int? bodyFacing;
|
|
|
|
|
public float? genitalAngle;
|
|
|
|
|
public bool? quiver;
|
|
|
|
|
public int? tickDuration;
|
|
|
|
|
public string soundEffect;
|
|
|
|
|
public List<string> tags;
|
|
|
|
|
[XmlArray("addonKeyframes"), XmlArrayItem("li")] public List<AddonKeyframe> addonKeyframes;
|
|
|
|
|
|
|
|
|
|
// Data serialization control
|
|
|
|
|
public bool ShouldSerializebodyAngle() { return bodyAngle.HasValue && bodyAngle.Value != 0f; }
|
|
|
|
|
public bool ShouldSerializeheadAngle() { return headAngle.HasValue && headAngle.Value != 0f; }
|
|
|
|
|
public bool ShouldSerializeheadBob() { return headBob.HasValue && headBob.Value != 0f; }
|
|
|
|
|
public bool ShouldSerializebodyOffsetX() { return bodyOffsetX.HasValue && bodyOffsetX.Value != 0f; }
|
|
|
|
|
public bool ShouldSerializebodyOffsetZ() { return bodyOffsetZ.HasValue && bodyOffsetZ.Value != 0f; }
|
|
|
|
|
public bool ShouldSerializegenitalAngle() { return genitalAngle.HasValue && genitalAngle.Value != 0f; }
|
|
|
|
|
public bool ShouldSerializequiver() { return quiver == true; }
|
|
|
|
|
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
|
|
|
|
|
public bool ShouldSerializeaddonKeyframes() { return addonKeyframes.NotNullOrEmpty(); }
|
|
|
|
|
|
|
|
|
|
// Data helper functions
|
|
|
|
|
[XmlIgnore] public float BodyAngle
|
|
|
|
|
{
|
|
|
|
|
get { return bodyAngle.HasValue ? bodyAngle.Value : 0f; }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { bodyAngle = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public float HeadAngle
|
|
|
|
|
{
|
|
|
|
|
get { return headAngle.HasValue ? headAngle.Value : (float)(headAngle = 0f); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { headAngle = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public float HeadBob
|
|
|
|
|
{
|
|
|
|
|
get { return headBob.HasValue ? headBob.Value : (float)(headBob = 0f); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { headBob = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public float BodyOffsetX
|
|
|
|
|
{
|
|
|
|
|
get { return bodyOffsetX.HasValue ? bodyOffsetX.Value : (float)(bodyOffsetX = 0f); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { bodyOffsetX = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public float BodyOffsetZ
|
|
|
|
|
{
|
|
|
|
|
get { return bodyOffsetZ.HasValue ? bodyOffsetZ.Value : (float)(bodyOffsetZ = 0f); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { bodyOffsetZ = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public int HeadFacing
|
|
|
|
|
{
|
|
|
|
|
get { return headFacing.HasValue ? headFacing.Value : (int)(headFacing = 2); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { headFacing = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public int BodyFacing
|
|
|
|
|
{
|
|
|
|
|
get { return bodyFacing.HasValue ? bodyFacing.Value : (int)(bodyFacing = 2); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { bodyFacing = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public float GenitalAngle
|
|
|
|
|
{
|
|
|
|
|
get { return genitalAngle.HasValue ? genitalAngle.Value : (float)(genitalAngle = 0f); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { genitalAngle = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public bool Quiver
|
|
|
|
|
{
|
|
|
|
|
get { return quiver == true; }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { quiver = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public int TickDuration
|
|
|
|
|
{
|
|
|
|
|
get { return tickDuration.HasValue ? tickDuration.Value : (int)(tickDuration = 0); }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { tickDuration = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public string SoundEffect
|
|
|
|
|
{
|
|
|
|
|
get { return soundEffect; }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { soundEffect = value; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public List<string> Tags
|
|
|
|
|
{
|
|
|
|
|
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { tags = value.NotNullOrEmpty() ? value : null; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[XmlIgnore] public List<AddonKeyframe> AddonKeyframes
|
|
|
|
|
{
|
|
|
|
|
get { return addonKeyframes.NullOrEmpty() ? addonKeyframes = new List<AddonKeyframe>() : addonKeyframes; }
|
2022-10-28 05:28:51 +00:00
|
|
|
|
set { addonKeyframes = value.NotNullOrEmpty()? value : null; }
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Local data
|
|
|
|
|
[XmlIgnore] public int keyframeID;
|
|
|
|
|
[XmlIgnore] public int actorID = -1;
|
|
|
|
|
[XmlIgnore] public int? atTick;
|
|
|
|
|
|
|
|
|
|
// Methods
|
|
|
|
|
public void GenerateKeyframeID(int actorID)
|
|
|
|
|
{
|
|
|
|
|
this.actorID = actorID;
|
|
|
|
|
int _keyframeID = UnityEngine.Random.Range(100000, 1000000);
|
|
|
|
|
|
|
|
|
|
if (Workspace.animationDef.AnimationStages.Any(x => x.AnimationClips.Any(y => y.Keyframes.Any(z => z.keyframeID == _keyframeID))))
|
|
|
|
|
{
|
|
|
|
|
GenerateKeyframeID(actorID);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyframeID = _keyframeID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasValidKeyframeID()
|
|
|
|
|
{ return keyframeID >= 100000 && keyframeID < 1000000; }
|
|
|
|
|
|
|
|
|
|
public KeyframeSlider GetKeyframeSlider()
|
|
|
|
|
{
|
|
|
|
|
return Selectable.allSelectablesArray.FirstOrDefault(x => x.GetComponent<KeyframeSlider>()?.keyframeID == keyframeID)?.GetComponent< KeyframeSlider>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public AddonKeyframe GetAddonKeyframe(string addonName)
|
|
|
|
|
{
|
|
|
|
|
return AddonKeyframes.FirstOrDefault(x => x.AddonName == addonName);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 05:28:51 +00:00
|
|
|
|
public void AdjustActor(Vector2 deltaOffset)
|
|
|
|
|
{
|
|
|
|
|
float deltaAngle = -deltaOffset.x * 33.3333f + deltaOffset.y * 33.3333f;
|
|
|
|
|
int facing = deltaOffset.x < 0 ? 3 : deltaOffset.y < 0 ? 2 : deltaOffset.x > 0 ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
switch (Workspace.actorManipulationMode)
|
|
|
|
|
{
|
|
|
|
|
case ActorManipulationMode.Pan: MoveActor(deltaOffset); break;
|
|
|
|
|
case ActorManipulationMode.Rotate: RotateActor(deltaAngle); break;
|
|
|
|
|
case ActorManipulationMode.Face: FaceActor(facing); break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MoveActor(Vector2 deltaOffset)
|
|
|
|
|
{
|
|
|
|
|
if (Workspace.selectedBodyPart == null)
|
|
|
|
|
{
|
|
|
|
|
BodyOffsetX += deltaOffset.x;
|
|
|
|
|
BodyOffsetZ += deltaOffset.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
|
|
|
|
|
{ HeadBob += deltaOffset.y; }
|
|
|
|
|
|
|
|
|
|
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
|
|
|
|
Workspace.RecordEvent("Actor position / orientation");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RotateActor(float deltaAngle)
|
|
|
|
|
{
|
|
|
|
|
if (Workspace.selectedBodyPart == null)
|
|
|
|
|
{ BodyAngle += deltaAngle; }
|
|
|
|
|
|
|
|
|
|
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
|
|
|
|
|
{ HeadAngle += deltaAngle; }
|
|
|
|
|
|
|
|
|
|
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "appendage")
|
|
|
|
|
{ GenitalAngle -= deltaAngle; }
|
|
|
|
|
|
|
|
|
|
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
|
|
|
|
Workspace.RecordEvent("Actor position / orientation");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void FaceActor(int facing)
|
|
|
|
|
{
|
|
|
|
|
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
|
|
|
|
|
|
|
|
|
|
if (Workspace.selectedBodyPart == null)
|
|
|
|
|
{ keyframe.BodyFacing = facing; }
|
|
|
|
|
|
|
|
|
|
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
|
|
|
|
|
{ keyframe.HeadFacing = facing; }
|
|
|
|
|
|
|
|
|
|
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
|
|
|
|
Workspace.RecordEvent("Actor position / orientation");
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 05:56:04 +00:00
|
|
|
|
// Pre-save / post-load
|
|
|
|
|
public void OnPreSave()
|
|
|
|
|
{
|
|
|
|
|
SoundEffect = DefaultTags.soundDefs.Concat(CustomTags.soundDefs).Contains(SoundEffect) ? SoundEffect : null;
|
|
|
|
|
|
2022-10-29 00:52:58 +00:00
|
|
|
|
if (addonKeyframes.NotNullOrEmpty())
|
2022-10-27 05:56:04 +00:00
|
|
|
|
{
|
2022-10-29 00:52:58 +00:00
|
|
|
|
var temp = AddonKeyframes.Copy();
|
|
|
|
|
addonKeyframes.Clear();
|
|
|
|
|
|
|
|
|
|
foreach (AddonKeyframe addonKeyframe in temp)
|
|
|
|
|
{
|
|
|
|
|
ActorAddon addon = Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID).GetActorAddon(addonKeyframe.AddonName);
|
2022-10-27 05:56:04 +00:00
|
|
|
|
|
2022-10-29 00:52:58 +00:00
|
|
|
|
if (addon.Render)
|
|
|
|
|
{ addonKeyframes.Add(addonKeyframe.Copy()); }
|
|
|
|
|
}
|
2022-10-27 05:56:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnPostLoad()
|
|
|
|
|
{
|
|
|
|
|
AddonKeyframes.Clear();
|
|
|
|
|
|
|
|
|
|
foreach (AddonKeyframe addonKeyframe in addonKeyframes)
|
|
|
|
|
{
|
|
|
|
|
AddonKeyframes.Add(addonKeyframe.Copy());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|