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 float? headFacing; public float? bodyFacing; public float? genitalAngle; public bool? quiver; public int? tickDuration; public string soundEffect; public List tags; [XmlArray("addonKeyframes"), XmlArrayItem("li")] public List 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 : (float)(bodyAngle = 0f); } set { bodyAngle = value; } } [XmlIgnore] public float HeadAngle { get { return headAngle.HasValue ? headAngle.Value : (float)(headAngle = 0f); } set { headAngle = value; } } [XmlIgnore] public float HeadBob { get { return headBob.HasValue ? headBob.Value : (float)(headBob = 0f); } set { headBob = value; } } [XmlIgnore] public float BodyOffsetX { get { return bodyOffsetX.HasValue ? bodyOffsetX.Value : (float)(bodyOffsetX = 0f); } set { bodyOffsetX = value; } } [XmlIgnore] public float BodyOffsetZ { get { return bodyOffsetZ.HasValue ? bodyOffsetZ.Value : (float)(bodyOffsetZ = 0f); } set { bodyOffsetZ = value; } } [XmlIgnore] public int HeadFacing { get { return headFacing.HasValue ? (int)headFacing.Value : (int)(headFacing = 2); } set { headFacing = (int)value; } } [XmlIgnore] public int BodyFacing { get { return bodyFacing.HasValue ? (int)bodyFacing.Value : (int)(bodyFacing = 2); } set { bodyFacing = (int)value; } } [XmlIgnore] public float GenitalAngle { get { return genitalAngle.HasValue ? genitalAngle.Value : (float)(genitalAngle = 0f); } set { genitalAngle = value; } } [XmlIgnore] public bool Quiver { get { return quiver == true; } set { quiver = value; } } [XmlIgnore] public int TickDuration { get { return tickDuration.HasValue ? tickDuration.Value : (int)(tickDuration = 0); } set { tickDuration = value; } } [XmlIgnore] public string SoundEffect { get { return soundEffect; } set { soundEffect = value; } } [XmlIgnore] public List Tags { get { return tags.NullOrEmpty() ? tags = new List() : tags; } set { tags = value.NotNullOrEmpty() ? value : null; } } [XmlIgnore] public List AddonKeyframes { get { return addonKeyframes.NullOrEmpty() ? addonKeyframes = new List() : addonKeyframes; } set { addonKeyframes = value.NotNullOrEmpty()? value : null; } } // 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()?.keyframeID == keyframeID)?.GetComponent< KeyframeSlider>(); } public AddonKeyframe GetAddonKeyframe(string addonName) { return AddonKeyframes.FirstOrDefault(x => x.AddonName == addonName); } 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"); } public PawnKeyframe GetClone() { PawnKeyframe clone = this.Copy(); clone.GenerateKeyframeID(actorID); return clone; } // Pre-save / post-load public void OnPreSave() { SoundEffect = DefaultTags.soundDefs.Concat(CustomTags.soundDefs).Contains(SoundEffect) ? SoundEffect : null; if (addonKeyframes.NotNullOrEmpty()) { var temp = AddonKeyframes.Copy(); addonKeyframes.Clear(); foreach (AddonKeyframe addonKeyframe in temp) { ActorAddon addon = Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID).GetActorAddon(addonKeyframe.AddonName); if (addon.Render) { addonKeyframes.Add(addonKeyframe.Copy()); } } } } public void OnPostLoad() { } } }