Improved undo redo function and timelines

This commit is contained in:
AbstractConcept 2022-09-21 00:40:58 -05:00
parent 1af7f41d63
commit e36ef6a368
372 changed files with 4086 additions and 211 deletions

View file

@ -91,7 +91,7 @@ namespace RimWorldAnimationStudio
public bool MakeNew()
{
PawnKeyframe keyframeA = new PawnKeyframe();
keyframeA.tickDuration = 60;
keyframeA.tickDuration = Constants.defaultAnimationClipLength - 1;
keyframes.Add(keyframeA);
PawnKeyframe keyframeB = new PawnKeyframe();

View file

@ -6,7 +6,7 @@ using UnityEngine.EventSystems;
namespace RimWorldAnimationStudio
{
public class ActorBody : MonoBehaviour, IPointerClickHandler, IDragHandler
public class ActorBody : MonoBehaviour, IPointerClickHandler, IDragHandler, IEndDragHandler
{
public int actorID;
public string bodyType = "Male";
@ -78,6 +78,11 @@ namespace RimWorldAnimationStudio
clip.BuildSimpleCurves();
}
public void OnEndDrag(PointerEventData eventData)
{
Workspace.Instance.RecordEvent("Actor position / orientation");
}
public void Activate()
{
Workspace.actorID = actorID;

View file

@ -6,7 +6,7 @@ using UnityEngine.EventSystems;
namespace RimWorldAnimationStudio
{
public class ActorBodyPart : MonoBehaviour, IDragHandler, IPointerClickHandler
public class ActorBodyPart : MonoBehaviour, IPointerClickHandler, IDragHandler, IEndDragHandler
{
public SpriteRenderer bodyPartRenderer;
public ActorBody parent;
@ -82,6 +82,11 @@ namespace RimWorldAnimationStudio
clip.BuildSimpleCurves();
}
public void OnEndDrag(PointerEventData eventData)
{
Workspace.Instance.RecordEvent("Actor position / orientation");
}
public void Activate()
{
Workspace.actorID = parent.actorID;

View file

@ -43,7 +43,7 @@ namespace RimWorldAnimationStudio
bodyOffsetZField.text = actor.bodyTypeOffset.GetOffset(bodyType).z.ToString();
}
public void UpdateAnimationDef()
public void OnValueChanged()
{
if (Workspace.animationDef == null || isDirty) return;
@ -65,10 +65,10 @@ namespace RimWorldAnimationStudio
default: actor.requiredGender = null; break;
}
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor body type offset data");
}
public void OpenSelectBodyPartsDialog()
/*public void OpenSelectBodyPartsDialog()
{
if (Workspace.animationDef == null) return;
@ -99,7 +99,7 @@ namespace RimWorldAnimationStudio
if (dialog != null)
{ dialog[0].Initialize(actor); dialog[0].Pop(); }
}
}*/
public void Update()
{

View file

@ -75,7 +75,7 @@ namespace RimWorldAnimationStudio
Workspace.Instance.GetPawnAnimationClip(Workspace.actorID).BuildSimpleCurves();
isDirty = true;
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor position / orientation");
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 329a2ecc35813f94fa879c7f434c8fb7
guid: b7e9565ba13ac2e469767bcfad7da5d3
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -68,5 +68,37 @@ namespace RimWorldAnimationStudio
{
BroadcastMessage("UpdateGhostFrames");
}
public void OnMoveTimeline(int delta)
{
int? siblingIndex = transform.parent.GetComponentsInChildren<AnimationTimeline>()?.ToList()?.IndexOf(this);
int? siblingCount = transform.parent.GetComponentsInChildren<AnimationTimeline>()?.ToList()?.Count();
if (siblingIndex != null && siblingCount != null && MoveAnimationTimeline(siblingIndex.Value, delta))
{
//siblingIndex = Mathf.Clamp(siblingCount.Value + delta, 0, siblingCount.Value - 1);
transform.SetSiblingIndex(transform.GetSiblingIndex() + delta);
}
}
public bool MoveAnimationTimeline(int startIndex, int delta)
{
if (startIndex + delta < 0 || startIndex + delta >= Workspace.animationDef.animationStages[Workspace.stageID].animationClips.Count)
{ return false; }
Actor actor = Workspace.animationDef.actors[startIndex];
Workspace.animationDef.actors[startIndex] = Workspace.animationDef.actors[startIndex + delta];
Workspace.animationDef.actors[startIndex + delta] = actor;
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(startIndex);
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[startIndex] = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[startIndex + delta];
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[startIndex + delta] = clip;
Workspace.actorID = startIndex + delta;
Workspace.Instance.RecordEvent("Timeline move");
return true;
}
}
}

View file

@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class ConsoleMessagesDialog : MonoBehaviour
{
public bool isExpanded = false;
public GameObject consoleWindow;
public Transform logContent;
public Text stackTrace;
public Text currentMessage;
public Text logMessagePrefab;
public int maxMessages = 500;
private int currentMessages = 0;
public void Start()
{
Application.logMessageReceived += LogMessage;
}
public void ToggleExpand()
{
isExpanded = !isExpanded;
consoleWindow.SetActive(isExpanded);
}
public void LogMessage(string logString, string stackTrace, LogType type)
{
if (currentMessages > maxMessages)
{ return; }
currentMessages++;
currentMessage.text = logString;
Text logMessage = Instantiate(logMessagePrefab, logContent);
logMessage.text = logString;
logMessage.GetComponent<Button>().onClick.AddListener(delegate { OpenStackTrace(stackTrace); });
if (type == LogType.Warning)
{
currentMessage.color = Constants.ColorGoldYellow;
logMessage.color = Constants.ColorGoldYellow;
}
else if (type == LogType.Exception || type == LogType.Error)
{
currentMessage.color = Constants.ColorRed;
logMessage.color = Constants.ColorRed;
}
else
{
currentMessage.color = Constants.ColorDarkGrey;
logMessage.color = Constants.ColorDarkGrey;
}
}
public void OpenStackTrace(string stackTrace)
{
this.stackTrace.text = stackTrace;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c62f9942e9d933848913ee2b28f65791
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class SelectActorLayerDialog : DialogBox
{
public void OnEnable()
{
Initialize();
}
public void Initialize()
{
if (Workspace.animationDef == null) return;
Transform contentWindow = transform.FindDeepChild("Content");
Reset();
for (int i = 0; i < Workspace.actorLayers.Count; i++)
{
string actorLayer = Workspace.actorLayers[i];
Transform _optionToggle = AddCloneObjectToParent(contentWindow).transform;
_optionToggle.Find("Text").GetComponent<Text>().text = actorLayer;
Toggle toggleComp = _optionToggle.GetComponent<Toggle>();
toggleComp.isOn = Workspace.Instance.GetCurrentPawnAnimationClip().layer == actorLayer;
toggleComp.onValueChanged.AddListener(delegate {
PawnAnimationClip clip = Workspace.Instance.GetCurrentPawnAnimationClip();
if (clip != null)
{ clip.layer = actorLayer; }
Workspace.Instance.RecordEvent("Actor layer set");
});
toggleComp.group = contentWindow.GetComponent<ToggleGroup>();
}
}
public void Reset()
{
Transform contentWindow = transform.FindDeepChild("Content");
RemoveCloneObjectsFromParent(contentWindow);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce4b68a13ad01b14f97697ab310a74d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -10,9 +10,7 @@ namespace RimWorldAnimationStudio
{
public class SelectBodyDefTypesDialog : DialogBox
{
private Actor actor;
public void Start()
public void OnEnable()
{
Initialize();
}
@ -27,18 +25,12 @@ namespace RimWorldAnimationStudio
Workspace.bodyDefTypes.Add(field.text);
Initialize(null, true);
Initialize(true);
}
public void Initialize(Actor actor = null, bool addedNewTag = false)
public void Initialize(bool addedNewTag = false)
{
if (actor != null)
{ this.actor = actor; }
if (actor == null)
{ actor = this.actor; }
if (actor == null) return;
Actor actor = Workspace.animationDef.actors[Workspace.actorID];
foreach (string bodyDefType in actor.bodyDefTypes)
{
@ -66,7 +58,7 @@ namespace RimWorldAnimationStudio
else if (toggleComp.isOn == false && actor.bodyDefTypes.Contains(bodyDefType))
{ actor.bodyDefTypes.Remove(bodyDefType); }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor bodyDef type");
});
if (addedNewTag && i == Workspace.sexTypes.Count - 1)

View file

@ -10,9 +10,7 @@ namespace RimWorldAnimationStudio
{
public class SelectBodyPartsDialog : DialogBox
{
private Actor actor;
public void Start()
public void OnEnable()
{
Initialize();
}
@ -30,18 +28,12 @@ namespace RimWorldAnimationStudio
Debug.Log("Add new body part: " + field.text);
Workspace.bodyParts.Add(field.text);
Initialize(null, true);
Initialize(true);
}
public void Initialize(Actor actor = null, bool addedNewTag = false)
public void Initialize(bool addedNewTag = false)
{
if (actor != null)
{ this.actor = actor; }
if (actor == null)
{ actor = this.actor; }
if (actor == null) return;
Actor actor = Workspace.animationDef.actors[Workspace.actorID];
foreach (string bodyPart in actor.requiredGenitals)
{
@ -69,7 +61,7 @@ namespace RimWorldAnimationStudio
else if (toggleComp.isOn == false && actor.requiredGenitals.Contains(bodyPart))
{ actor.requiredGenitals.Remove(bodyPart); }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor required body part");
});
if (addedNewTag && i == Workspace.sexTypes.Count - 1)

View file

@ -10,10 +10,9 @@ namespace RimWorldAnimationStudio
{
public class SelectDefNamesDialog : DialogBox
{
private Actor actor;
public void Start()
public void OnEnable()
{
Debug.Log("enabled");
Initialize();
}
@ -27,18 +26,12 @@ namespace RimWorldAnimationStudio
Workspace.defNames.Add(field.text);
Initialize(null, true);
Initialize(true);
}
public void Initialize(Actor actor = null, bool addedNewTag = false)
public void Initialize(bool addedNewTag = false)
{
if (actor != null)
{ this.actor = actor; }
if (actor == null)
{ actor = this.actor; }
if (actor == null) return;
Actor actor = Workspace.animationDef.actors[Workspace.actorID];
foreach (string defName in actor.defNames)
{
@ -66,7 +59,7 @@ namespace RimWorldAnimationStudio
else if (toggleComp.isOn == false && actor.defNames.Contains(defName))
{ actor.defNames.Remove(defName); }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor def name");
});
if (addedNewTag && i == Workspace.sexTypes.Count - 1)

View file

@ -10,7 +10,7 @@ namespace RimWorldAnimationStudio
{
public class SelectInteractionDefsDialog : DialogBox
{
public void Start()
public void OnEnable()
{
Initialize();
}
@ -58,7 +58,7 @@ namespace RimWorldAnimationStudio
else if (toggleComp.isOn == false && Workspace.animationDef.interactionDefTypes.Contains(interactionDefType))
{ Workspace.animationDef.interactionDefTypes.Remove(interactionDefType); }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Animation InteractionDef");
});
if (addedNewTag && i == Workspace.sexTypes.Count - 1)

View file

@ -10,7 +10,7 @@ namespace RimWorldAnimationStudio
{
public class SelectSexTypesDialog : DialogBox
{
public void Start()
public void OnEnable()
{
Initialize();
}
@ -58,7 +58,7 @@ namespace RimWorldAnimationStudio
else if (toggleComp.isOn == false && Workspace.animationDef.sexTypes.Contains(sexType))
{ Workspace.animationDef.sexTypes.Remove(sexType); }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Animation sex type");
});
if (addedNewTag && i == Workspace.sexTypes.Count - 1)

View file

@ -10,7 +10,7 @@ namespace RimWorldAnimationStudio
{
public class SelectSoundDefDialog : DialogBox
{
public void Start()
public void OnEnable()
{
Initialize();
}
@ -63,7 +63,7 @@ namespace RimWorldAnimationStudio
if (keyframe != null)
{ keyframe.soundEffect = soundDef; }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Keyframe sound effect");
});
toggleComp.group = contentWindow.GetComponent<ToggleGroup>();

View file

@ -99,7 +99,7 @@ namespace RimWorldAnimationStudio
if (eventData.clickCount >= 2)
{ AnimationController.Instance.stageTick = keyframe.atTick.Value; }
Workspace.Instance.MakeDirty();
//Workspace.Instance.RecordEvent("Keyframe selected");
}
public void OnBeginDrag(PointerEventData eventData)
@ -126,7 +126,7 @@ namespace RimWorldAnimationStudio
{
interactable = false;
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Keyframe tick");
}
protected override void Update()
@ -145,7 +145,7 @@ namespace RimWorldAnimationStudio
else
{ handleImage.color = Constants.ColorWhite; }
string soundDef = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID).soundEffect;
string soundDef = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID)?.soundEffect;
if (soundDef != null && soundDef != "" && soundDef != "None")
{ soundIcon.SetActive(true); }

View file

@ -19,6 +19,8 @@ namespace RimWorldAnimationStudio
if (keyframe != null)
{ keyframe.quiver = GetComponent<Toggle>().isOn; }
Workspace.Instance.RecordEvent("Actor quiver");
}
}
}

View file

@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class SelectActorLayerButton : MonoBehaviour
{
private Text text;
public void Start()
{
text = GetComponentInChildren<Text>();
}
void Update()
{
if (Workspace.animationDef == null) return;
PawnAnimationClip clip = Workspace.Instance.GetCurrentPawnAnimationClip();
if (clip != null)
{ text.text = clip.layer; }
else
{ text.text = "Pawn"; }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 329a2ecc35813f94fa879c7f434c8fb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -19,7 +19,7 @@ namespace RimWorldAnimationStudio
Workspace.animationDef.animationStages[Workspace.stageID].stageName = stageName.text;
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Stage renamed");
}
public void OnMoveStage(int delta)
@ -62,9 +62,10 @@ namespace RimWorldAnimationStudio
stageNameField.gameObject.SetActive(true);
}
Workspace.stageID = transform.GetSiblingIndex();
if (Workspace.stageID != transform.GetSiblingIndex())
{ Workspace.Instance.RecordEvent("Stage selected"); }
Workspace.Instance.MakeDirty();
Workspace.stageID = transform.GetSiblingIndex();
}
}
}

View file

@ -27,7 +27,7 @@ namespace RimWorldAnimationStudio
public Transform animationTimelines;
public Transform actorBodies;
public Toggle stretchkeyframesToggle;
public InputField playBackSpeedField;
[Header("Prefabs")]
public ActorBody actorBodyPrefab;
@ -39,7 +39,8 @@ namespace RimWorldAnimationStudio
private int cycleIndex = 0;
private bool isDirty = true;
private bool isTimelineDirty = true;
private float playBackSpeed = 1f;
public void MakeDirty()
{ isDirty = true; }
@ -53,17 +54,17 @@ namespace RimWorldAnimationStudio
// Dirty animation, reset
if (Workspace.animationDef != null && isDirty)
{ Initialize(); return; }
{ Initialize(); }
// Update tick if animating
if (isAnimating)
{
timeSinceLastUpdate += Time.deltaTime;
if (timeSinceLastUpdate < 1f / 60f)
if (timeSinceLastUpdate < 1 / (playBackSpeed * 60f))
{ return; }
timeSinceLastUpdate -= 1f / 60f;
timeSinceLastUpdate -= 1 / (playBackSpeed * 60f);
stageTick += 1;
if (stageTick > Workspace.StageWindowSize)
@ -237,7 +238,7 @@ namespace RimWorldAnimationStudio
Actor actor = new Actor();
actor.MakeNew();
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor addition");
}
public void RemoveActor()
@ -254,7 +255,7 @@ namespace RimWorldAnimationStudio
Workspace.animationDef.actors.RemoveAt(Workspace.actorID);
Workspace.actorID = Workspace.actorID >= Workspace.animationDef.actors.Count ? Workspace.actorID = Workspace.animationDef.actors.Count - 1 : Workspace.actorID;
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Actor deletion");
}
public void AddPawnKeyframe()
@ -294,14 +295,12 @@ namespace RimWorldAnimationStudio
animationTimelines.GetComponentsInChildren<AnimationTimeline>()[Workspace.actorID].AddPawnKeyFrame(keyframe.keyframeID);
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Keyframe addition");
}
public void RemovePawnKeyframe()
{
RemovePawnKeyframe(Workspace.actorID, Workspace.keyframeID);
Workspace.Instance.MakeDirty();
}
public void RemovePawnKeyframe(int actorID, int keyframeID)
@ -318,9 +317,9 @@ namespace RimWorldAnimationStudio
PawnAnimationClip clip = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID];
clip.keyframes.Remove(keyframe);
clip.BuildSimpleCurves();
}
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Keyframe deletion");
}
}
public void ToggleAnimation()
@ -351,8 +350,6 @@ namespace RimWorldAnimationStudio
int.TryParse(animationClipTimeField.text, out int newStageTick);
stageTick = Mathf.Clamp(newStageTick, 1, Workspace.StageWindowSize);
stageTimelineSlider.value = stageTick;
Workspace.Instance.MakeDirty();
}
public void OnAnimationClipLengthFieldChange()
@ -390,7 +387,7 @@ namespace RimWorldAnimationStudio
animationClipLengthField.text = newStageWindowSize.ToString();
Workspace.animationDef.animationStages[Workspace.stageID].stageWindowSize = newStageWindowSize;
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Stage length");
}
public void StretchKeyframes(int newStageWindowSize)
@ -418,7 +415,7 @@ namespace RimWorldAnimationStudio
if (int.TryParse(cyclesNormalField.text, out int cycles))
{ Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks = cycles * Workspace.StageWindowSize; }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Cycle count (normal)");
}
public void OnCycleFastFieldChange()
@ -428,7 +425,13 @@ namespace RimWorldAnimationStudio
if (int.TryParse(cyclesFastField.text, out int cycles))
{ Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicksQuick = cycles * Workspace.StageWindowSize; }
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Cycle count (fast)");
}
public void OnPlayBackSpeedChange()
{
if (float.TryParse(playBackSpeedField.text, out playBackSpeed))
{ playBackSpeed = Mathf.Clamp(playBackSpeed, 0.01f, 4f); }
}
private int lastactorCount = 0;

View file

@ -54,7 +54,7 @@ namespace RimWorldAnimationStudio
animationDef.Initialize();
Workspace.Instance.ClearHistory();
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("AnimationDef loaded");
AnimationController.Instance.MakeDirty();

View file

@ -42,7 +42,7 @@ namespace RimWorldAnimationStudio
{
AnimationStage stage = new AnimationStage();
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Stage addition");
return stage.MakeNew();
}
@ -64,7 +64,7 @@ namespace RimWorldAnimationStudio
Workspace.animationDef.animationStages.Insert(Workspace.stageID + 1, stage);
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Stage clone");
return true;
}
@ -78,7 +78,9 @@ namespace RimWorldAnimationStudio
Workspace.animationDef.animationStages[startIndex] = Workspace.animationDef.animationStages[startIndex + delta];
Workspace.animationDef.animationStages[startIndex + delta] = stage;
Workspace.Instance.MakeDirty();
Workspace.stageID = startIndex + delta;
Workspace.Instance.RecordEvent("Stage move");
return true;
}
@ -100,7 +102,7 @@ namespace RimWorldAnimationStudio
Workspace.animationDef.animationStages.RemoveAt(Workspace.stageID);
Workspace.stageID = Workspace.stageID >= Workspace.animationDef.animationStages.Count ? Workspace.stageID = Workspace.animationDef.animationStages.Count - 1 : Workspace.stageID;
Workspace.Instance.MakeDirty();
Workspace.Instance.RecordEvent("Stage deletion");
return true;
}

View file

@ -19,5 +19,6 @@ namespace RimWorldAnimationStudio
public static Color ColorCyan = new Color(0.0f, 1.0f, 1.0f);
public static Color ColorPurple = new Color(0.85f, 0.0f, 1.0f);
public static Color ColorGhost = new Color(0.5f, 0.5f, 0.5f, 0.5f);
public static Color ColorRed = new Color(0.9f, 0f, 0f);
}
}

View file

@ -7,8 +7,10 @@ using System.Threading.Tasks;
namespace RimWorldAnimationStudio
{
[Serializable]
public class WorkspaceSnapShot
public class HistoricRecord
{
public int recordID = 0;
public string eventDesc;
public AnimationDef animationDef;
public int stageID = 0;
public int actorID = 0;

View file

@ -11,8 +11,8 @@ namespace RimWorldAnimationStudio
{
public static AnimationDef animationDef;
public static int stageID = 0;
public static int actorID = 0;
public static int keyframeID = 0;
public static int keyframeID = 0;
public static List<string> defNames = new List<string>() { "Human" };
public static List<string> bodyParts = new List<string>() { "Any appendage", "Any orifice", "Penis", "Vagina", "Anus", "Breasts", "Mouth" };
@ -20,8 +20,9 @@ namespace RimWorldAnimationStudio
public static List<string> sexTypes = new List<string>() { "None", "Vaginal", "Anal", "Oral", "Masturbation", "DoublePenetration", "Boobjob", "Handjob", "Footjob", "Fingering", "Scissoring", "MutualMasturbation", "Fisting", "MechImplant", "Rimming", "Fellatio", "Cunnilingus", "Sixtynine" };
public static List<string> interactionDefTypes = new List<string>();
public static List<string> soundDefs = new List<string>() { "None", "Sex", "Fuck", "Slimy", "Suck", "Cum" };
public static List<string> actorLayers = new List<string>() { "Building", "BuildingOnTop", "MoteBelowThings", "Item", "ItemImportant", "LayingPawn", "PawnRope", "Projectile", "Pawn", "PawnUnused", "PawnState" };
[SerializeField] private List<WorkspaceSnapShot> workspaceHistory = new List<WorkspaceSnapShot>();
[SerializeField] private List<HistoricRecord> workspaceHistory = new List<HistoricRecord>();
[SerializeField] private int historyIndex = 0;
[SerializeField] private int maxHistoryDepth = 100;
private bool isDirty = false;
@ -29,9 +30,12 @@ namespace RimWorldAnimationStudio
public static ActorManipulationMode actorManipulationMode = ActorManipulationMode.Pan;
public static ActorBodyPart selectedBodyPart;
public static int StageWindowSize
{
get
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; }
@ -40,13 +44,13 @@ namespace RimWorldAnimationStudio
{ return animationDef.animationStages[stageID].animationClips.Select(x => x.duration).Max(); }
return animationDef.animationStages[stageID].stageWindowSize;
}
}
}
public void Update()
{
if (isDirty)
{ TrackChanges(); }
//if (isDirty)
//{ TrackChanges(); }
}
public PawnKeyframe GetCurrentPawnKeyframe(bool makeKeyframe = false)
@ -76,58 +80,70 @@ namespace RimWorldAnimationStudio
return animationDef.animationStages[stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == keyframeID);
}
public void TrackChanges()
[SerializeField]
public LinkedList<HistoricRecord> pastSnapshots = new LinkedList<HistoricRecord>();
public LinkedList<HistoricRecord> futureSnapshots = new LinkedList<HistoricRecord>();
public void MakeHistoricRecord(string eventDesc)
{
if (historyIndex < workspaceHistory.Count - 1)
{ workspaceHistory.RemoveRange(historyIndex + 1, workspaceHistory.Count - historyIndex - 1); }
HistoricRecord record = new HistoricRecord();
record.recordID = pastSnapshots.Count;
record.eventDesc = eventDesc;
record.animationDef = animationDef.Copy();
record.stageID = stageID;
if (workspaceHistory.Any() && workspaceHistory.Count >= maxHistoryDepth)
{ workspaceHistory.RemoveAt(0); }
futureSnapshots.Clear();
pastSnapshots.AddLast(record);
WorkspaceSnapShot workspaceSnapShot = new WorkspaceSnapShot();
workspaceSnapShot.animationDef = animationDef.Copy();
workspaceSnapShot.stageID = stageID;
workspaceSnapShot.actorID = actorID;
workspaceSnapShot.keyframeID = keyframeID;
if (pastSnapshots.Count > maxHistoryDepth)
{ pastSnapshots.RemoveFirst(); }
}
workspaceHistory.Add(workspaceSnapShot);
historyIndex = workspaceHistory.Count - 1;
public void RestoreToHistoricRecord(HistoricRecord record)
{
animationDef = record.animationDef.Copy();
stageID = record.stageID;
isDirty = false;
AnimationController.Instance.MakeDirty();
}
public void Undo()
{
historyIndex = Mathf.Clamp(historyIndex - 1, 0, workspaceHistory.Count - 1);
LoadHistoricState();
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("Undo : " + recordToStore.eventDesc + " (record ID: " + recordToStore.recordID + ")");
}
public void Redo()
{
historyIndex = Mathf.Clamp(historyIndex + 1, 0, workspaceHistory.Count - 1);
LoadHistoricState();
}
HistoricRecord recordToReadAndStore = futureSnapshots.Last?.Value;
public void LoadHistoricState()
{
if (workspaceHistory.NullOrEmpty())
{ Debug.LogWarning("Cannot load historic state - workspace history is empty"); return; }
if (recordToReadAndStore == null) return;
animationDef = workspaceHistory[historyIndex].animationDef.Copy();
stageID = workspaceHistory[historyIndex].stageID;
actorID = workspaceHistory[historyIndex].actorID;
keyframeID = workspaceHistory[historyIndex].keyframeID;
futureSnapshots.RemoveLast();
pastSnapshots.AddLast(recordToReadAndStore);
AnimationController.Instance.MakeTimelineDirty();
RestoreToHistoricRecord(recordToReadAndStore);
Debug.Log("Redo : " + recordToReadAndStore.eventDesc + " (record ID: " + recordToReadAndStore.recordID + ")");
}
public void ClearHistory()
{
workspaceHistory.Clear();
historyIndex = 0;
pastSnapshots.Clear();
futureSnapshots.Clear();
}
public void MakeDirty()
{ isDirty = true; }
public void RecordEvent(string eventDesc)
{
Debug.Log("Recording event: " + eventDesc + " (record ID: " + pastSnapshots.Count + ")");
MakeHistoricRecord(eventDesc);
}
}
}