using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; namespace RimWorldAnimationStudio { public class Workspace : Singleton { public static AnimationDef animationDef; public static int stageID = 0; public static List keyframeID = new List(); [SerializeField] private List workspaceHistory = new List(); [SerializeField] private int maxHistoryDepth = 100; public static ActorManipulationMode actorManipulationMode = ActorManipulationMode.Pan; public static ActorBodyPart selectedBodyPart; public static List copiedKeyframes = new List(); public static string animationSavePath; private static int _actorID = 0; public static int actorID { get { return Mathf.Clamp(_actorID, 0, animationDef.actors.Count - 1); } set { _actorID = value; } } public static int StageWindowSize { get { if (animationDef == null) { return -1; } if (animationDef.animationStages[stageID].stageWindowSize < 0) { animationDef.animationStages[stageID].stageWindowSize = animationDef.animationStages[stageID].animationClips.Select(x => x.duration).Max(); } return animationDef.animationStages[stageID].stageWindowSize; } } public PawnKeyframe GetCurrentPawnKeyframe(bool makeKeyframe = false) { int stageTick = AnimationController.Instance.stageTick; PawnKeyframe keyframe = animationDef?.animationStages[stageID]?.animationClips[actorID]?.keyframes.FirstOrDefault(x => x.atTick == stageTick); if (keyframe != null || makeKeyframe == false) { return keyframe; } AnimationController.Instance.AddPawnKeyframe(); return animationDef?.animationStages[stageID]?.animationClips[actorID]?.keyframes.FirstOrDefault(x => x.atTick == stageTick); } public PawnAnimationClip GetCurrentPawnAnimationClip() { return animationDef.animationStages[stageID].animationClips[actorID]; } public PawnAnimationClip GetPawnAnimationClip(int actorID) { return animationDef.animationStages[stageID].animationClips[actorID]; } public PawnKeyframe GetPawnKeyframe(int actorID, int keyframeID) { if (stageID < 0) return null; if (actorID < 0) return null; if (stageID >= animationDef.animationStages.Count) return null; if (actorID >= animationDef.animationStages[stageID].animationClips.Count) return null; return animationDef.animationStages[stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == keyframeID); } public List GetPawnKeyframes(List keyframeIDs) { List pawnKeyframes = new List(); foreach (PawnAnimationClip clip in animationDef.animationStages[stageID].animationClips) { foreach (PawnKeyframe keyframe in clip.keyframes) { if (keyframeIDs.Contains(keyframe.keyframeID)) { pawnKeyframes.Add(keyframe); } } } return pawnKeyframes; } public PawnAnimationClip GetAnimationClipThatOwnsKeyframe(int keyframeID, out int clipID) { clipID = -1; for (int i = 0; i < animationDef.animationStages[stageID].animationClips.Count; i++) { PawnAnimationClip clip = animationDef.animationStages[stageID].animationClips[i]; if (clip.keyframes.Any(x => x.keyframeID == keyframeID)) { clipID = i; return clip; } } return null; } public bool DoesPawnKeyframeExistAtTick(int stageID, int actorID, int atTick) { return animationDef.animationStages[stageID].animationClips[actorID].keyframes.Any(x => x.atTick == atTick); } public PawnKeyframe GetNextKeyframe(int actorID) { PawnKeyframe pawnKeyframe = null; PawnAnimationClip clip = GetPawnAnimationClip(actorID); int stageTick = AnimationController.Instance.stageTick; foreach (PawnKeyframe keyframe in clip.keyframes) { if (keyframe.atTick > stageTick) { pawnKeyframe = keyframe; break; } } return pawnKeyframe; } public PawnKeyframe GetPreviousKeyframe(int actorID) { PawnKeyframe pawnKeyframe = null; PawnAnimationClip clip = GetPawnAnimationClip(actorID); int stageTick = AnimationController.Instance.stageTick; foreach (PawnKeyframe keyframe in clip.keyframes) { if (keyframe.atTick < stageTick) { pawnKeyframe = keyframe; } } return pawnKeyframe; } public PawnKeyframe GetCurrentOrPreviousKeyframe(int actorID) { PawnKeyframe pawnKeyframe = null; PawnAnimationClip clip = GetPawnAnimationClip(actorID); int stageTick = AnimationController.Instance.stageTick; foreach (PawnKeyframe keyframe in clip.keyframes) { if (keyframe.atTick <= stageTick) { pawnKeyframe = keyframe; } } return pawnKeyframe; } public static int FindClosestKeyFrameAtTick(int atTick, int searchDistance = int.MaxValue, int excludedActorID = -1) { List keyframesToCheck; if (excludedActorID >= 0) { keyframesToCheck = animationDef.animationStages[stageID].animationClips.Where(x => animationDef.animationStages[stageID].animationClips.IndexOf(x) != excludedActorID).SelectMany(x => x.keyframes)?.ToList(); } else { keyframesToCheck = animationDef.animationStages[stageID].animationClips.SelectMany(x => x.keyframes)?.ToList(); } keyframesToCheck = keyframesToCheck.Where(x => Mathf.Abs(atTick - x.atTick.Value) <= searchDistance)?.ToList(); if (keyframesToCheck.NullOrEmpty()) { return atTick; } int minDist = int.MaxValue; int bestAtTick = -1; foreach (PawnKeyframe keyframe_ in keyframesToCheck) { if (Mathf.Abs(keyframe_.atTick.Value - atTick) < minDist) { minDist = Mathf.Abs(keyframe_.atTick.Value - atTick); bestAtTick = keyframe_.atTick.Value; } } return bestAtTick; } public int GetEarliestAtTickInCopiedKeyframes(int actorID) { IEnumerable keyframes = copiedKeyframes.Where(x => x.actorID == actorID); if (keyframes == null || keyframes.Any() == false) return -1; return keyframes.Min(x => x.atTick).Value; } [SerializeField] public LinkedList pastSnapshots = new LinkedList(); public LinkedList futureSnapshots = new LinkedList(); public void MakeHistoricRecord(string eventDesc) { HistoricRecord record = new HistoricRecord(); record.recordID = pastSnapshots.Count; record.eventDesc = eventDesc; record.animationDef = animationDef.Copy(); record.stageID = stageID; futureSnapshots.Clear(); pastSnapshots.AddLast(record); if (pastSnapshots.Count > maxHistoryDepth) { pastSnapshots.RemoveFirst(); } } public void RestoreToHistoricRecord(HistoricRecord record) { animationDef = record.animationDef.Copy(); stageID = record.stageID; AnimationController.Instance.MakeTimelineDirty(); StageCardManager.Instance.Initialize(); } public void Undo() { HistoricRecord recordToRead = pastSnapshots.Last?.Previous?.Value; HistoricRecord recordToStore = pastSnapshots.Last?.Value; if (recordToRead == null || recordToStore == null) return; pastSnapshots.RemoveLast(); futureSnapshots.AddLast(recordToStore); RestoreToHistoricRecord(recordToRead); Debug.Log("Undoing : " + recordToStore.eventDesc); } public void Redo() { HistoricRecord recordToReadAndStore = futureSnapshots.Last?.Value; if (recordToReadAndStore == null) return; futureSnapshots.RemoveLast(); pastSnapshots.AddLast(recordToReadAndStore); RestoreToHistoricRecord(recordToReadAndStore); Debug.Log("Redoing : " + recordToReadAndStore.eventDesc); } public void ClearHistory() { pastSnapshots.Clear(); futureSnapshots.Clear(); } public void RecordEvent(string eventDesc) { //Debug.Log("Recording event: " + eventDesc + " (record ID: " + pastSnapshots.Count + ")"); MakeHistoricRecord(eventDesc); } } }