using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace RimWorldAnimationStudio { public class KeyframeSlider : Slider, IPointerClickHandler, IBeginDragHandler, IEndDragHandler { public AnimationTimeline timeline; public Transform ghostSliders; public Slider ghostSliderPrefab; public Image handleImage; public GameObject soundIcon; public int maxGhosts = 4; public int actorID; public int keyframeID; private PawnAnimationClip clip; private PawnKeyframe keyframe; private float dragTimeStart = -1f; public void Initialize(AnimationTimeline timeline, int actorID, int keyframeID) { this.timeline = timeline; this.clip = Workspace.Instance.GetPawnAnimationClip(actorID); this.keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID); this.actorID = actorID; this.keyframeID = keyframeID; PawnKeyframe keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID); maxValue = Workspace.StageWindowSize; value = keyframe.atTick.Value; OnValueChanged(); onValueChanged.AddListener(delegate (float value) { OnValueChanged(); }); } public void OnValueChanged() { keyframe.atTick = (int)value; clip.BuildSimpleCurves(); //AnimationController.Instance.stageTick = keyframe.atTick.Value; timeline.InitiateUpdateOfGhostFrames(); } // Ghost sliders are non-interactable slider handle public void UpdateGhostFrames() { if (maxGhosts == 0) { return; } int requiredGhosts = GetGhostFramesRequired(); int currentGhostCount = ghostSliders.childCount; for (int i = 0; i < Mathf.Max(requiredGhosts, currentGhostCount); i++) { int targetTick = (int)(i * clip.duration + keyframe.atTick); if (ghostSliders.childCount <= i) { Instantiate(ghostSliderPrefab, ghostSliders); } GameObject ghostSliderObject = ghostSliders.GetChild(i).gameObject; ghostSliderObject.SetActive(i < requiredGhosts); Slider ghostSlider = ghostSliderObject.GetComponent(); ghostSlider.maxValue = Workspace.StageWindowSize; ghostSlider.value = targetTick; if (targetTick > ghostSlider.maxValue) { ghostSlider.gameObject.SetActive(false); } } } public int GetGhostFramesRequired() { if (Workspace.animationDef.animationStages[Workspace.stageID].isLooping == false) { return 0; } if (clip.duration <= 1) { return 0; } return Math.Min(Mathf.CeilToInt((float)Workspace.StageWindowSize / clip.duration), maxGhosts); } public void OnPointerClick(PointerEventData eventData) { Workspace.actorID = actorID; Workspace.keyframeID = keyframeID; if (eventData.clickCount >= 2) { AnimationController.Instance.stageTick = keyframe.atTick.Value; } //Workspace.Instance.RecordEvent("Keyframe selected"); } public void OnBeginDrag(PointerEventData eventData) { //AnimationController.Instance.stageTick = keyframe.atTick.Value; Workspace.actorID = actorID; Workspace.keyframeID = keyframeID; dragTimeStart = Time.unscaledTime; } public override void OnDrag(PointerEventData eventData) { if (keyframe.atTick == 1) { value = 1; return; } if (Time.unscaledTime - dragTimeStart < 0.05f) return; interactable = true; base.OnDrag(eventData); //AnimationController.Instance.stageTick = keyframe.atTick.Value; Workspace.actorID = actorID; } public void OnEndDrag(PointerEventData eventData) { if (keyframe.atTick == 1) { value = 1; return; } interactable = false; Workspace.Instance.RecordEvent("Keyframe tick"); } protected override void Update() { base.Update(); if (keyframe.atTick.HasValue && Workspace.keyframeID == keyframeID && AnimationController.Instance.stageTick == keyframe.atTick.Value) { handleImage.color = Constants.ColorPurple; } else if (Workspace.keyframeID == keyframeID) { handleImage.color = Constants.ColorCyan; } else if (keyframe.atTick.HasValue && AnimationController.Instance.stageTick == keyframe.atTick.Value) { handleImage.color = Constants.ColorPink; } else { handleImage.color = Constants.ColorWhite; } string soundDef = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID)?.soundEffect; if (soundDef != null && soundDef != "" && soundDef != "None") { soundIcon.SetActive(true); } else { soundIcon.SetActive(false); } } } }