Bug fixes plus extra features

- Insert adds a new keyframe to the selected timeline
- New stages have frames cloned from the last frame of current stage
- Existing key are now replaced when another key is dropped on them
- Fixed bug where starting a new animation could result in errors
This commit is contained in:
AbstractConcept 2022-10-18 21:57:43 -05:00
parent ca22fa0c18
commit 2989d9a72c
137 changed files with 527 additions and 668 deletions

View file

@ -121,5 +121,12 @@ namespace RimWorldAnimationStudio
return true;
}
public int GetActorID()
{
if (Workspace.animationDef == null) return -1;
return Workspace.animationDef.actors.IndexOf(this);
}
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
@ -89,17 +90,46 @@ namespace RimWorldAnimationStudio
public override void ValidateData() { }
public bool MakeNew()
public int GetOwningActorID()
{
PawnKeyframe keyframeA = new PawnKeyframe();
keyframeA.tickDuration = Constants.defaultAnimationClipLength - 1;
keyframes.Add(keyframeA);
if (Workspace.animationDef == null) return -1;
PawnKeyframe keyframeB = new PawnKeyframe();
keyframes.Add(keyframeB);
return Workspace.animationDef.animationStages[Workspace.stageID].animationClips.IndexOf(this);
}
public bool MakeNew(int actorID = -1)
{
PawnKeyframe lastkeyframe = null;
if (actorID >= 0)
{ lastkeyframe = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID]?.keyframes?.Last(); }
if (lastkeyframe != null)
{
PawnKeyframe keyframeA = lastkeyframe.Copy();
keyframeA.atTick = null;
keyframeA.tickDuration = Constants.defaultAnimationClipLength - 1;
keyframeA.GenerateKeyframeID(actorID);
keyframes.Add(keyframeA);
PawnKeyframe keyframeB = lastkeyframe.Copy();
keyframeB.atTick = null;
keyframeB.tickDuration = 1;
keyframeB.GenerateKeyframeID(actorID);
keyframes.Add(keyframeB);
}
else
{
PawnKeyframe keyframeA = new PawnKeyframe();
keyframeA.tickDuration = Constants.defaultAnimationClipLength - 1;
keyframes.Add(keyframeA);
PawnKeyframe keyframeB = new PawnKeyframe();
keyframes.Add(keyframeB);
}
BuildSimpleCurves();
return true;
}
}

View file

@ -36,23 +36,31 @@ namespace RimWorldAnimationStudio
{ clip.keyframes = clip.keyframes.OrderBy(x => x.atTick).ToList(); }
}
public int GetStageID()
{
if (Workspace.animationDef == null) return -1;
return Workspace.animationDef.animationStages.IndexOf(this);
}
public bool MakeNew()
{
if (Workspace.animationDef == null)
{ Debug.LogWarning("Cannot make new animation stage - there is no AnimationDef"); return false; }
foreach(Actor actor in Workspace.animationDef.actors)
Workspace.animationDef.animationStages.Add(this);
foreach (Actor actor in Workspace.animationDef.actors)
{
PawnAnimationClip clip = new PawnAnimationClip();
if (clip.MakeNew())
if (clip.MakeNew(actor.GetActorID()))
{ animationClips.Add(clip); }
}
Initialize();
playTimeTicksQuick = playTimeTicks;
Workspace.animationDef.animationStages.Add(this);
return true;
}
}

View file

@ -31,10 +31,8 @@ namespace RimWorldAnimationStudio
public void LogMessage(string logString, string stackTrace, LogType type)
{
if (currentMessages > maxMessages)
{ return; }
currentMessages++;
if (currentMessages > maxMessages) return;
currentMessages++;
currentMessage.text = logString;

View file

@ -17,7 +17,7 @@ namespace RimWorldAnimationStudio
public void Start()
{
keybindLabel = GetComponent<Text>();
keybindLabel.text = KeybindConfig.Instance.GetKeybindLabel(command);
keybindLabel.text = KeybindConfig.GetKeybindLabel(command);
}
}
}

View file

@ -117,7 +117,7 @@ namespace RimWorldAnimationStudio
if (Workspace.keyframeID.NullOrEmpty() || Workspace.keyframeID.Contains(keyframeID) == false)
{ Workspace.keyframeID = new List<int> { keyframeID }; }
List<PawnKeyframe> selectedKeyframes = Workspace.Instance.GetPawnKeyframes(Workspace.keyframeID).Except(new List<PawnKeyframe>() { keyframe })?.ToList();
List<PawnKeyframe> selectedKeyframes = Workspace.Instance.GetPawnKeyframesByID(Workspace.keyframeID).Except(new List<PawnKeyframe>() { keyframe })?.ToList();
// Link other slected keyframes to the movement of this one
if (selectedKeyframes.NotNullOrEmpty())
@ -170,12 +170,39 @@ namespace RimWorldAnimationStudio
if (keyframe.atTick == Constants.minTick)
{ value = Constants.minTick; return; }
foreach (Selectable linkedSlider in Selectable.allSelectablesArray)
List<PawnKeyframe> keyframesToCheck = Workspace.Instance.GetPawnKeyframesAtTick(actorID, keyframe.atTick.Value);
if (keyframesToCheck.NotNullOrEmpty())
{
if (linkedSlider is KeyframeSlider)
{
(linkedSlider as KeyframeSlider).linkedSlider = null;
(linkedSlider as KeyframeSlider).pivotKeyframe = null;
foreach (PawnKeyframe _keyframe in keyframesToCheck)
{
if (_keyframe != keyframe)
{ AnimationController.Instance.RemovePawnKeyframe(actorID, _keyframe.keyframeID); }
}
}
foreach (Selectable selectable in Selectable.allSelectablesArray)
{
if (selectable is KeyframeSlider)
{
KeyframeSlider linkedSlider = selectable.GetComponent<KeyframeSlider>();
PawnKeyframe linkedKeyframe = linkedSlider.keyframe;
if (linkedSlider.linkedSlider != null)
{
keyframesToCheck = Workspace.Instance.GetPawnKeyframesAtTick(actorID, linkedKeyframe.atTick.Value);
if (keyframesToCheck.NotNullOrEmpty() && keyframesToCheck.Count > 1)
{
foreach (PawnKeyframe _keyframe in keyframesToCheck)
{
if (_keyframe.keyframeID != linkedKeyframe.keyframeID)
{ AnimationController.Instance.RemovePawnKeyframe(actorID, _keyframe.keyframeID); Debug.Log("delete"); }
}
}
}
linkedSlider.linkedSlider = null;
linkedSlider.pivotKeyframe = null;
}
}

View file

@ -8,26 +8,30 @@ using UnityEngine;
namespace RimWorldAnimationStudio
{
public class KeybindConfig : Singleton<KeybindConfig>
public class KeybindConfig
{
private static List<Keybind> keybinds = new List<Keybind>();
private static bool initialized = false;
public void Awake()
public static void Initialize()
{
string path = Path.Combine(Application.streamingAssetsPath, "keybindConfig.xml");
keybinds = XmlUtility.ReadXML<List<Keybind>>(path);
initialized = true;
}
public List<Keybind> GetAllKeybinds()
public static List<Keybind> GetAllKeybinds()
{
if (initialized == false)
{ Initialize(); }
return keybinds;
}
public string GetKeybindLabel(string command)
public static string GetKeybindLabel(string command)
{
string label = "";
Keybind keybind = keybinds.FirstOrDefault(x => x.command == command);
Keybind keybind = GetAllKeybinds()?.FirstOrDefault(x => x.command == command);
if (keybind == null) return label;

View file

@ -44,7 +44,7 @@ namespace RimWorldAnimationStudio
private float playBackSpeed = 1f;
public void MakeDirty()
{ isDirty = true; }
{ isDirty = true; isTimelineDirty = true; }
public void MakeTimelineDirty()
{ isTimelineDirty = true; }
@ -255,10 +255,11 @@ namespace RimWorldAnimationStudio
public void Reset()
{
Workspace.stageID = 0;
isAnimating = false;
timeSinceLastUpdate = 0;
cycleIndex = 0;
MakeDirty();
}
public void InitializeAnimationTimeline()
@ -377,7 +378,7 @@ namespace RimWorldAnimationStudio
public void ClonePawnKeyframe()
{
List<PawnKeyframe> keyframesToClone = Workspace.Instance.GetPawnKeyframes(Workspace.keyframeID);
List<PawnKeyframe> keyframesToClone = Workspace.Instance.GetPawnKeyframesByID(Workspace.keyframeID);
foreach (PawnKeyframe keyframe in keyframesToClone)
{
@ -413,7 +414,7 @@ namespace RimWorldAnimationStudio
{
Workspace.copiedKeyframes.Clear();
List<PawnKeyframe> keyframesToClone = Workspace.Instance.GetPawnKeyframes(Workspace.keyframeID);
List<PawnKeyframe> keyframesToClone = Workspace.Instance.GetPawnKeyframesByID(Workspace.keyframeID);
foreach (PawnKeyframe keyframe in keyframesToClone)
{ Workspace.copiedKeyframes.Add(keyframe.Copy()); }

View file

@ -70,10 +70,9 @@ namespace RimWorldAnimationStudio
Workspace.animationDef = animationDef;
animationDef.Initialize();
Workspace.Instance.ClearHistory();
AnimationController.Instance.Reset();
Workspace.Instance.Reset();
Workspace.Instance.RecordEvent("AnimationDef loaded");
AnimationController.Instance.MakeDirty();
}
public void RunPostLoadOperations(AnimationDef animationDef)

View file

@ -61,7 +61,7 @@ namespace RimWorldAnimationStudio
bool canRepeatThisUpdate = CanRepeatThisUpdate();
// Check keybinds
foreach (Keybind keybind in KeybindConfig.Instance.GetAllKeybinds())
foreach (Keybind keybind in KeybindConfig.GetAllKeybinds())
{
if (IsModifierKeyHeld() && keybind.keyModifiers.NullOrEmpty()) goto nextKeybind;
@ -207,6 +207,12 @@ namespace RimWorldAnimationStudio
AnimationController.Instance.ToggleAnimation();
}
public void AddKeyframe()
{
if (Workspace.animationDef == null) return;
AnimationController.Instance.AddPawnKeyframe();
}
public void CopyKeyframes()
{
if (Workspace.animationDef == null) return;

View file

@ -14,6 +14,8 @@ namespace RimWorldAnimationStudio
public string message = "Undefined";
public string executedCommand;
public float delay = 0f;
public Vector2 offset = new Vector2(5f, -15f);
public bool flipX = false;
private GameObject tooltip;
private Text tooltipText;
@ -33,12 +35,13 @@ namespace RimWorldAnimationStudio
if (isDisplayed == false)
{
tooltip.GetComponent<RectTransform>().pivot = flipX ? new Vector2(1, 1) : new Vector2(0, 1);
tooltipText.text = message;
if (executedCommand != null && executedCommand != "")
{ tooltipText.text += " (" + KeybindConfig.Instance.GetKeybindLabel(executedCommand) + ")"; }
{ tooltipText.text += " (" + KeybindConfig.GetKeybindLabel(executedCommand) + ")"; }
tooltip.transform.position = (Vector2)transform.position + new Vector2(5f, -15f);
tooltip.transform.position = (Vector2)transform.position + offset;
tooltip.gameObject.SetActive(true);
LayoutRebuilder.ForceRebuildLayoutImmediate(tooltip.GetComponent<RectTransform>());

View file

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace RimWorldAnimationStudio
{
[Serializable]
public class HistoricRecord
public class WorkspaceRecord
{
public int recordID = 0;
public string eventDesc;

View file

@ -14,7 +14,7 @@ namespace RimWorldAnimationStudio
public static List<int> keyframeID = new List<int>();
[SerializeField] private List<HistoricRecord> workspaceHistory = new List<HistoricRecord>();
[SerializeField] private List<WorkspaceRecord> workspaceHistory = new List<WorkspaceRecord>();
[SerializeField] private int maxHistoryDepth = 100;
public static ActorManipulationMode actorManipulationMode = ActorManipulationMode.Pan;
@ -53,6 +53,11 @@ namespace RimWorldAnimationStudio
return animationDef?.animationStages[stageID]?.animationClips[actorID]?.keyframes.FirstOrDefault(x => x.atTick == stageTick);
}
public List<PawnKeyframe> GetPawnKeyframesAtTick(int actorID, int atTick)
{
return animationDef?.animationStages[stageID]?.animationClips[actorID]?.keyframes.Where(x => x.atTick == atTick)?.ToList();
}
public PawnAnimationClip GetCurrentPawnAnimationClip()
{
return animationDef.animationStages[stageID].animationClips[actorID];
@ -74,7 +79,7 @@ namespace RimWorldAnimationStudio
return animationDef.animationStages[stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == keyframeID);
}
public List<PawnKeyframe> GetPawnKeyframes(List<int> keyframeIDs)
public List<PawnKeyframe> GetPawnKeyframesByID(List<int> keyframeIDs)
{
List<PawnKeyframe> pawnKeyframes = new List<PawnKeyframe>();
@ -203,12 +208,23 @@ namespace RimWorldAnimationStudio
}
[SerializeField]
public LinkedList<HistoricRecord> pastSnapshots = new LinkedList<HistoricRecord>();
public LinkedList<HistoricRecord> futureSnapshots = new LinkedList<HistoricRecord>();
public LinkedList<WorkspaceRecord> pastSnapshots = new LinkedList<WorkspaceRecord>();
public LinkedList<WorkspaceRecord> futureSnapshots = new LinkedList<WorkspaceRecord>();
public void Reset()
{
actorID = 0;
stageID = 0;
keyframeID.Clear();
selectedBodyPart = null;
ClearHistory();
}
public void MakeHistoricRecord(string eventDesc)
{
HistoricRecord record = new HistoricRecord();
WorkspaceRecord record = new WorkspaceRecord();
record.recordID = pastSnapshots.Count;
record.eventDesc = eventDesc;
record.animationDef = animationDef.Copy();
@ -221,7 +237,7 @@ namespace RimWorldAnimationStudio
{ pastSnapshots.RemoveFirst(); }
}
public void RestoreToHistoricRecord(HistoricRecord record)
public void RestoreToHistoricRecord(WorkspaceRecord record)
{
animationDef = record.animationDef.Copy();
stageID = record.stageID;
@ -232,8 +248,8 @@ namespace RimWorldAnimationStudio
public void Undo()
{
HistoricRecord recordToRead = pastSnapshots.Last?.Previous?.Value;
HistoricRecord recordToStore = pastSnapshots.Last?.Value;
WorkspaceRecord recordToRead = pastSnapshots.Last?.Previous?.Value;
WorkspaceRecord recordToStore = pastSnapshots.Last?.Value;
if (recordToRead == null || recordToStore == null) return;
@ -246,7 +262,7 @@ namespace RimWorldAnimationStudio
public void Redo()
{
HistoricRecord recordToReadAndStore = futureSnapshots.Last?.Value;
WorkspaceRecord recordToReadAndStore = futureSnapshots.Last?.Value;
if (recordToReadAndStore == null) return;