2022-09-13 05:36:34 +00:00
using System ;
2022-09-14 05:25:58 +00:00
using System.Text ;
2022-09-13 05:36:34 +00:00
using System.Collections.Generic ;
using System.Linq ;
using System.IO ;
using System.Xml.Serialization ;
using UnityEngine ;
using UnityEngine.UI ;
namespace RimWorldAnimationStudio
{
2022-09-15 05:17:44 +00:00
public class AnimationController : Singleton < AnimationController >
2022-09-13 05:36:34 +00:00
{
2022-09-18 00:06:33 +00:00
[Header("Animation settings")]
2022-09-13 05:36:34 +00:00
public bool isAnimating = false ;
2022-10-14 05:52:07 +00:00
public int stageTick = Constants . minTick ;
2022-09-13 05:36:34 +00:00
2022-09-18 00:06:33 +00:00
[Header("Object references")]
2022-09-15 05:17:44 +00:00
public Slider stageTimelineSlider ;
2022-09-18 00:06:33 +00:00
public Dropdown stageLoopDropdown ;
public InputField cyclesNormalField ;
public InputField cyclesFastField ;
2022-09-18 05:01:12 +00:00
public InputField animationClipTimeField ;
public InputField animationClipLengthField ;
2022-09-18 00:06:33 +00:00
public ActorCard actorCard ;
2022-09-19 05:35:34 +00:00
public Transform animationTimelines ;
public Transform actorBodies ;
2022-10-14 05:52:07 +00:00
public Toggle stretchKeyframesToggle ;
2022-09-21 05:40:58 +00:00
public InputField playBackSpeedField ;
2022-09-27 05:56:35 +00:00
public Button playToggleButton ;
2022-10-14 19:00:57 +00:00
public Text stageLengthText ;
public Text animationLengthText ;
2022-09-13 05:36:34 +00:00
2022-09-18 00:06:33 +00:00
[Header("Prefabs")]
public ActorBody actorBodyPrefab ;
2022-10-12 05:22:29 +00:00
public GameObject animationTimelinePrefab ;
2022-09-16 14:18:06 +00:00
2022-09-18 00:06:33 +00:00
// Private timing variables
2022-10-14 05:52:07 +00:00
private int lastStageTick = Constants . minTick ;
2022-09-18 00:06:33 +00:00
private float timeSinceLastUpdate = 0 ;
2022-09-16 22:50:15 +00:00
private int cycleIndex = 0 ;
2022-09-19 00:07:44 +00:00
private bool isDirty = true ;
private bool isTimelineDirty = true ;
2022-09-21 05:40:58 +00:00
private float playBackSpeed = 1f ;
2022-09-19 00:07:44 +00:00
public void MakeDirty ( )
2022-10-19 02:57:43 +00:00
{ isDirty = true ; isTimelineDirty = true ; }
2022-09-19 00:07:44 +00:00
public void MakeTimelineDirty ( )
{ isTimelineDirty = true ; }
2022-10-14 19:00:57 +00:00
public bool IsDirty ( )
{ return isDirty ; }
public bool IsTimelineDirty ( )
{ return isTimelineDirty ; }
2022-09-15 05:17:44 +00:00
public void Update ( )
2022-09-13 05:36:34 +00:00
{
2022-09-15 05:17:44 +00:00
// No animation, exit
if ( Workspace . animationDef = = null ) { return ; }
2022-09-13 05:36:34 +00:00
2022-09-15 05:17:44 +00:00
// Dirty animation, reset
2022-09-19 00:07:44 +00:00
if ( Workspace . animationDef ! = null & & isDirty )
2022-09-21 05:40:58 +00:00
{ Initialize ( ) ; }
2022-09-13 05:36:34 +00:00
2022-10-14 19:00:57 +00:00
// Update animation lengths
if ( stageLoopDropdown . value = = 3 )
{
stageLengthText . text = "Stage length (quickie): " + Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicksQuick + " (" + Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicksQuick / 60f + " s)" ;
animationLengthText . text = "Animation length (quickie): " + Workspace . animationDef . animationTimeTicksQuick + " (" + Workspace . animationDef . animationTimeTicksQuick / 60f + " s)" ;
LayoutRebuilder . ForceRebuildLayoutImmediate ( stageLengthText . GetComponent < RectTransform > ( ) ) ;
LayoutRebuilder . ForceRebuildLayoutImmediate ( animationLengthText . GetComponent < RectTransform > ( ) ) ;
}
else
{
stageLengthText . text = "Stage length (normal): " + Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicks + " (" + Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicks / 60f + " s)" ;
animationLengthText . text = "Animation length (normal): " + Workspace . animationDef . animationTimeTicks + " (" + Workspace . animationDef . animationTimeTicks / 60f + " s)" ;
LayoutRebuilder . ForceRebuildLayoutImmediate ( stageLengthText . GetComponent < RectTransform > ( ) ) ;
LayoutRebuilder . ForceRebuildLayoutImmediate ( animationLengthText . GetComponent < RectTransform > ( ) ) ;
}
2022-09-18 00:06:33 +00:00
// Update tick if animating
2022-10-14 05:52:07 +00:00
stageTick = Mathf . Clamp ( stageTick , Constants . minTick , Workspace . StageWindowSize ) ;
2022-10-12 05:22:29 +00:00
2022-09-18 00:06:33 +00:00
if ( isAnimating )
{
timeSinceLastUpdate + = Time . deltaTime ;
2022-09-13 05:36:34 +00:00
2022-09-21 05:40:58 +00:00
if ( timeSinceLastUpdate < 1 / ( playBackSpeed * 60f ) )
2022-09-18 00:06:33 +00:00
{ return ; }
2022-09-13 05:36:34 +00:00
2022-09-21 05:40:58 +00:00
timeSinceLastUpdate - = 1 / ( playBackSpeed * 60f ) ;
2022-09-18 00:06:33 +00:00
stageTick + = 1 ;
2022-09-13 05:36:34 +00:00
2022-09-18 05:01:12 +00:00
if ( stageTick > Workspace . StageWindowSize )
2022-09-16 22:50:15 +00:00
{
2022-09-18 00:06:33 +00:00
if ( stageLoopDropdown . value = = 1 )
2022-10-14 05:52:07 +00:00
{ stageTick = Constants . minTick ; }
2022-09-16 22:50:15 +00:00
2022-09-27 05:56:35 +00:00
else if ( stageLoopDropdown . value > = 2 )
2022-09-16 22:50:15 +00:00
{
2022-09-18 00:06:33 +00:00
+ + cycleIndex ;
2022-10-14 05:52:07 +00:00
stageTick = Constants . minTick ;
2022-09-18 00:06:33 +00:00
2022-09-19 00:07:44 +00:00
if ( ( stageLoopDropdown . value = = 2 & & cycleIndex > = int . Parse ( cyclesNormalField . text ) ) | |
( stageLoopDropdown . value = = 3 & & cycleIndex > = int . Parse ( cyclesFastField . text ) ) )
2022-09-18 00:06:33 +00:00
{
+ + Workspace . stageID ;
cycleIndex = 0 ;
}
2022-09-27 05:56:35 +00:00
if ( Workspace . stageID > Workspace . animationDef . animationStages . Count - 1 )
{
Workspace . stageID = Workspace . animationDef . animationStages . Count - 1 ;
stageTick = Workspace . StageWindowSize ;
isAnimating = false ;
}
2022-09-16 22:50:15 +00:00
}
2022-09-13 05:36:34 +00:00
2022-09-18 00:06:33 +00:00
else
2022-09-27 05:56:35 +00:00
{
2022-10-04 02:45:52 +00:00
//stageTick = Workspace.StageWindowSize;
//isAnimating = false;
2022-09-27 05:56:35 +00:00
}
2022-09-18 00:06:33 +00:00
}
2022-09-13 05:36:34 +00:00
}
2022-09-18 00:06:33 +00:00
// Update stage timeline
2022-09-18 06:52:54 +00:00
animationClipTimeField . interactable = isAnimating = = false ;
2022-09-18 05:01:12 +00:00
animationClipLengthField . interactable = isAnimating = = false ;
2022-09-13 05:36:34 +00:00
2022-09-18 06:52:54 +00:00
if ( lastStageTick ! = stageTick )
{
stageTimelineSlider . value = stageTick ;
animationClipTimeField . text = stageTick . ToString ( ) ;
lastStageTick = stageTick ;
}
2022-09-27 05:56:35 +00:00
playToggleButton . image . color = isAnimating ? Constants . ColorGoldYellow : Constants . ColorWhite ;
2022-09-18 00:06:33 +00:00
// Update animation
2022-09-15 05:17:44 +00:00
UpdateAnimation ( ) ;
2022-09-13 05:36:34 +00:00
}
public void UpdateAnimation ( )
{
2022-09-19 00:07:44 +00:00
if ( AnimationTimelinesNeedUpdate ( ) )
2022-10-14 19:00:57 +00:00
{ InitializeAnimationTimeline ( ) ; }
2022-09-18 06:52:54 +00:00
2022-09-19 05:35:34 +00:00
List < ActorBody > _actorBodies = actorBodies . GetComponentsInChildren < ActorBody > ( ) . ToList ( ) ;
2022-09-13 05:36:34 +00:00
2022-09-19 05:35:34 +00:00
for ( int actorID = 0 ; actorID < _actorBodies . Count ; actorID + + )
2022-09-13 05:36:34 +00:00
{
2022-09-19 00:07:44 +00:00
if ( Workspace . stageID > = Workspace . animationDef ? . animationStages . Count )
2022-10-04 02:45:52 +00:00
{ return ; }
2022-09-19 00:07:44 +00:00
if ( actorID > = Workspace . animationDef ? . animationStages [ Workspace . stageID ] ? . animationClips . Count )
2022-10-04 02:45:52 +00:00
{ return ; }
2022-09-19 00:07:44 +00:00
2022-09-27 05:56:35 +00:00
Actor actor = Workspace . animationDef . actors [ actorID ] ;
2022-09-18 00:06:33 +00:00
PawnAnimationClip clip = Workspace . animationDef ? . animationStages [ Workspace . stageID ] ? . animationClips [ actorID ] ;
if ( clip = = null )
{ continue ; }
2022-09-13 05:36:34 +00:00
2022-10-08 02:52:27 +00:00
bool quiver = isAnimating & & Workspace . Instance . GetCurrentOrPreviousKeyframe ( actorID ) . quiver = = true ;
2022-10-09 02:15:28 +00:00
bool requiresGenitals = actor . requiredGenitals . Any ( x = > x = = "Penis" ) | | Workspace . animationDef . actors [ actorID ] . isFucking ;
2022-10-08 02:52:27 +00:00
2022-09-13 05:36:34 +00:00
float clipPercent = ( float ) ( stageTick % clip . duration ) / clip . duration ;
2022-10-14 05:52:07 +00:00
if ( stageTick > Constants . minTick & & stageTick = = clip . duration ) clipPercent = 1f ;
2022-09-13 05:36:34 +00:00
2022-10-04 02:45:52 +00:00
if ( Workspace . animationDef . animationStages [ Workspace . stageID ] . isLooping = = false )
{ clipPercent = ( float ) stageTick / clip . duration ; }
2022-09-27 05:56:35 +00:00
AlienRaceDef alienRaceDef = actor . GetAlienRaceDef ( ) ;
2022-09-19 05:35:34 +00:00
ActorBody actorBody = _actorBodies [ actorID ] ;
2022-09-27 05:56:35 +00:00
string bodyType = alienRaceDef . isHumanoid ? actor . bodyType : "None" ;
2022-09-18 00:06:33 +00:00
2022-09-13 05:36:34 +00:00
Vector3 deltaPos = new Vector3 ( clip . BodyOffsetX . Evaluate ( clipPercent ) , 0 , clip . BodyOffsetZ . Evaluate ( clipPercent ) ) ;
float bodyAngle = clip . BodyAngle . Evaluate ( clipPercent ) ;
2022-10-08 02:52:27 +00:00
bodyAngle + = quiver ? UnityEngine . Random . value * 2f - 1f : 0f ;
2022-09-13 05:36:34 +00:00
float headAngle = clip . HeadAngle . Evaluate ( clipPercent ) ;
int bodyFacing = ( int ) clip . BodyFacing . Evaluate ( clipPercent ) ;
int headFacing = ( int ) clip . HeadFacing . Evaluate ( clipPercent ) ;
2022-09-24 19:12:18 +00:00
float headBob = clip . HeadBob . Evaluate ( clipPercent ) ;
Vector3 headOffset = new Vector3 ( 0 , 0 , headBob ) + PawnUtility . BaseHeadOffsetAt ( bodyType , bodyFacing ) ;
2022-09-13 05:36:34 +00:00
Vector3 bodyPos = new Vector3 ( deltaPos . x , deltaPos . z , 0 ) ;
2022-09-24 19:12:18 +00:00
Vector3 headPos = new Vector3 ( headOffset . x , headOffset . z , 0 ) ;
2022-09-13 05:36:34 +00:00
2022-09-20 06:03:55 +00:00
Vector3 appendagePos = PawnUtility . AppendageOffsetAt ( bodyType , bodyFacing ) ;
2022-09-21 21:15:25 +00:00
float appendageRotation = clip . GenitalAngle . Evaluate ( clipPercent ) ;
2022-09-20 06:03:55 +00:00
2022-09-27 23:29:08 +00:00
actorBody . transform . position = bodyPos + actor . GetFinalTransformOffset ( ) ;
2022-09-24 07:17:40 +00:00
actorBody . transform . eulerAngles = new Vector3 ( 0 , 0 , - bodyAngle ) ;
2022-09-13 05:36:34 +00:00
actorBody . headRenderer . transform . localPosition = headPos ;
2022-09-24 07:17:40 +00:00
actorBody . headRenderer . transform . eulerAngles = new Vector3 ( 0 , 0 , - headAngle ) ;
2022-09-13 05:36:34 +00:00
2022-09-20 06:03:55 +00:00
actorBody . appendageRenderer . transform . localPosition = new Vector3 ( appendagePos . x , appendagePos . z , 0f ) ;
2022-10-19 03:18:46 +00:00
actorBody . appendageRenderer . transform . eulerAngles = new Vector3 ( 0 , 0 , - appendageRotation ) ;
2022-09-20 06:03:55 +00:00
2022-09-24 07:17:40 +00:00
actorBody . bodyRenderer . sprite = alienRaceDef . GetBodyTypeGraphic ( ( CardinalDirection ) bodyFacing , bodyType ) ;
actorBody . headRenderer . sprite = alienRaceDef . isHumanoid ? alienRaceDef . GetHeadGraphic ( ( CardinalDirection ) headFacing ) : null ;
2022-10-09 02:15:28 +00:00
actorBody . appendageRenderer . sprite = requiresGenitals & & alienRaceDef . isHumanoid & & bodyFacing ! = 0 ? Resources . Load < Sprite > ( "Textures/Humanlike/Appendages/Appendage" + bodyFacing ) : null ;
2022-09-24 07:17:40 +00:00
actorBody . bodyRenderer . gameObject . SetActive ( actorBody . bodyRenderer . sprite ! = null ) ;
actorBody . headRenderer . gameObject . SetActive ( actorBody . headRenderer . sprite ! = null ) ;
actorBody . appendageRenderer . gameObject . SetActive ( actorBody . appendageRenderer . sprite ! = null ) ;
2022-09-13 05:36:34 +00:00
2022-09-15 05:17:44 +00:00
actorBody . bodyRenderer . sortingLayerName = clip . layer ;
actorBody . headRenderer . sortingLayerName = clip . layer ;
2022-09-19 05:35:34 +00:00
actorBody . headRenderer . sortingOrder = bodyFacing = = 0 ? - 1 : 1 ;
2022-09-21 21:15:25 +00:00
actorBody . appendageRenderer . sortingLayerName = clip . layer ;
2022-09-24 07:17:40 +00:00
actorBody . bodyRenderer . flipX = bodyFacing = = 3 ;
actorBody . headRenderer . flipX = headFacing = = 3 ;
//actorBody.appendageRenderer.flipX = bodyFacing == 3;
2022-09-24 19:12:18 +00:00
actorBody . transform . localScale = new Vector3 ( alienRaceDef . scale , alienRaceDef . scale , alienRaceDef . scale ) ;
2022-09-21 21:15:25 +00:00
// ActorKeyframeCard update
2022-09-27 05:56:35 +00:00
if ( actorID ! = Workspace . actorID ) continue ;
2022-09-21 21:15:25 +00:00
if ( ActorKeyframeCard . Instance . positionXField . isFocused = = false ) { ActorKeyframeCard . Instance . positionXField . text = bodyPos . x . ToString ( "0.000" ) ; }
if ( ActorKeyframeCard . Instance . positionZField . isFocused = = false ) { ActorKeyframeCard . Instance . positionZField . text = bodyPos . y . ToString ( "0.000" ) ; }
if ( ActorKeyframeCard . Instance . rotationField . isFocused = = false ) { ActorKeyframeCard . Instance . rotationField . text = bodyAngle . ToString ( "0.000" ) ; }
if ( ActorKeyframeCard . Instance . headBobField . isFocused = = false ) { ActorKeyframeCard . Instance . headBobField . text = headBob . ToString ( "0.000" ) ; }
if ( ActorKeyframeCard . Instance . headRotationField . isFocused = = false ) { ActorKeyframeCard . Instance . headRotationField . text = headAngle . ToString ( "0.000" ) ; }
if ( ActorKeyframeCard . Instance . appendageRotationField . isFocused = = false ) { ActorKeyframeCard . Instance . appendageRotationField . text = appendageRotation . ToString ( "0.000" ) ; }
2022-09-13 05:36:34 +00:00
}
}
2022-09-15 05:17:44 +00:00
2022-09-18 00:06:33 +00:00
public void Initialize ( )
2022-09-15 05:17:44 +00:00
{
2022-10-14 19:00:57 +00:00
isDirty = true ;
2022-09-18 00:06:33 +00:00
Debug . Log ( "Initializing animation preview" ) ;
2022-09-19 05:35:34 +00:00
foreach ( Transform child in transform )
{ child . gameObject . SetActive ( true ) ; }
2022-09-18 00:06:33 +00:00
InitializeAnimationTimeline ( ) ;
StageCardManager . Instance . Initialize ( ) ;
2022-09-15 05:17:44 +00:00
2022-09-19 00:07:44 +00:00
isDirty = false ;
2022-09-15 05:17:44 +00:00
}
2022-10-14 19:00:57 +00:00
public void Reset ( )
{
isAnimating = false ;
timeSinceLastUpdate = 0 ;
cycleIndex = 0 ;
2022-10-19 02:57:43 +00:00
MakeDirty ( ) ;
2022-10-14 19:00:57 +00:00
}
2022-09-18 00:06:33 +00:00
public void InitializeAnimationTimeline ( )
2022-09-15 05:17:44 +00:00
{
2022-10-14 19:00:57 +00:00
isTimelineDirty = true ;
2022-09-18 05:01:12 +00:00
cyclesNormalField . text = Mathf . Max ( Mathf . CeilToInt ( ( float ) Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicks / Workspace . StageWindowSize ) , 1 ) . ToString ( ) ;
2022-10-04 02:45:52 +00:00
cyclesFastField . text = Mathf . Max ( Mathf . CeilToInt ( ( float ) Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicksQuick / Workspace . StageWindowSize ) , 0 ) . ToString ( ) ;
Workspace . animationDef . animationStages [ Workspace . stageID ] . isLooping = int . Parse ( cyclesNormalField . text ) > 1 ? true : false ;
2022-09-16 22:50:15 +00:00
2022-10-14 19:00:57 +00:00
int actorCount = Workspace . animationDef . actors . Count ;
int childCount = animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) . Count ( ) ;
for ( int actorID = 0 ; actorID < Mathf . Max ( actorCount , childCount ) ; actorID + + )
2022-09-15 05:17:44 +00:00
{
2022-10-14 19:00:57 +00:00
// Add new actors as required
if ( actorID > = childCount )
{
Instantiate ( animationTimelinePrefab , animationTimelines ) ;
Instantiate ( actorBodyPrefab , actorBodies . transform ) ;
}
// Get objects to update
AnimationTimeline animationTimeline = animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) [ actorID ] ;
ActorBody actorBody = actorBodies . GetComponentsInChildren < ActorBody > ( ) [ actorID ] ;
2022-09-16 14:18:06 +00:00
2022-10-14 19:00:57 +00:00
// Update values
if ( actorID < actorCount )
{
animationTimeline . Initialize ( actorID ) ;
actorBody . Initialize ( actorID ) ;
}
// Remove excess objects as required
else
{
Destroy ( animationTimeline . transform . parent . gameObject ) ;
Destroy ( actorBody . gameObject ) ;
}
2022-09-15 05:17:44 +00:00
}
2022-09-18 05:01:12 +00:00
animationClipLengthField . text = Workspace . StageWindowSize . ToString ( ) ;
stageTimelineSlider . maxValue = Workspace . StageWindowSize ;
2022-09-19 00:07:44 +00:00
isTimelineDirty = false ;
2022-09-15 05:17:44 +00:00
2022-10-14 19:00:57 +00:00
foreach ( AnimationTimeline timeline in animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) )
{ timeline . InitiateUpdateOfGhostFrames ( ) ; }
2022-09-15 05:17:44 +00:00
}
2022-09-18 00:06:33 +00:00
public void AddActor ( )
2022-09-17 07:17:26 +00:00
{
2022-09-18 00:06:33 +00:00
Actor actor = new Actor ( ) ;
2022-09-19 00:07:44 +00:00
actor . MakeNew ( ) ;
2022-09-18 00:06:33 +00:00
2022-09-21 05:40:58 +00:00
Workspace . Instance . RecordEvent ( "Actor addition" ) ;
2022-09-17 07:17:26 +00:00
}
2022-09-18 00:06:33 +00:00
public void RemoveActor ( )
2022-09-17 07:17:26 +00:00
{
if ( Workspace . animationDef . actors . Count = = 1 )
{
Debug . LogWarning ( "Cannot delete actor - the animation must contain at least one actor." ) ;
2022-09-18 00:06:33 +00:00
return ;
2022-09-17 07:17:26 +00:00
}
foreach ( AnimationStage stage in Workspace . animationDef . animationStages )
{ stage . animationClips . RemoveAt ( Workspace . actorID ) ; }
Workspace . animationDef . actors . RemoveAt ( Workspace . actorID ) ;
Workspace . actorID = Workspace . actorID > = Workspace . animationDef . actors . Count ? Workspace . actorID = Workspace . animationDef . actors . Count - 1 : Workspace . actorID ;
2022-09-21 05:40:58 +00:00
Workspace . Instance . RecordEvent ( "Actor deletion" ) ;
2022-09-17 07:17:26 +00:00
}
2022-09-18 00:06:33 +00:00
public void AddPawnKeyframe ( )
{
PawnAnimationClip clip = Workspace . Instance . GetCurrentPawnAnimationClip ( ) ;
List < PawnKeyframe > keyframes = clip ? . keyframes ;
2022-09-17 07:17:26 +00:00
2022-09-18 00:06:33 +00:00
if ( clip = = null | | keyframes = = null )
{ Debug . LogWarning ( "Cannot add pawn keyframe - the AnimationDef is invalid" ) ; return ; }
if ( keyframes . FirstOrDefault ( x = > x . atTick = = stageTick ) ! = null )
{ Debug . LogWarning ( "Cannot add pawn keyframe - a keyframe already exists at this tick" ) ; return ; }
float clipPercent = ( float ) ( stageTick % clip . duration ) / clip . duration ;
PawnKeyframe keyframe = new PawnKeyframe ( ) ;
keyframe . bodyAngle = clip . BodyAngle . Evaluate ( clipPercent ) ;
keyframe . headAngle = clip . HeadAngle . Evaluate ( clipPercent ) ;
keyframe . headBob = clip . HeadBob . Evaluate ( clipPercent ) ;
keyframe . bodyOffsetX = clip . BodyOffsetX . Evaluate ( clipPercent ) ;
keyframe . bodyOffsetZ = clip . BodyOffsetZ . Evaluate ( clipPercent ) ;
keyframe . headFacing = clip . HeadFacing . Evaluate ( clipPercent ) ;
keyframe . bodyFacing = clip . BodyFacing . Evaluate ( clipPercent ) ;
keyframe . genitalAngle = clip . GenitalAngle . Evaluate ( clipPercent ) ;
keyframe . atTick = stageTick ;
PawnKeyframe nextKeyframe = keyframes . FirstOrDefault ( x = > x . atTick > stageTick ) ;
if ( nextKeyframe ! = null )
{ keyframes . Insert ( keyframes . IndexOf ( nextKeyframe ) , keyframe ) ; }
else
{ keyframes . Add ( keyframe ) ; }
clip . BuildSimpleCurves ( ) ;
2022-09-18 06:52:54 +00:00
animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) [ Workspace . actorID ] . AddPawnKeyFrame ( keyframe . keyframeID ) ;
2022-09-19 00:07:44 +00:00
2022-09-21 05:40:58 +00:00
Workspace . Instance . RecordEvent ( "Keyframe addition" ) ;
2022-09-18 00:06:33 +00:00
}
2022-09-24 19:12:18 +00:00
public void ClonePawnKeyframe ( )
{
2022-10-19 02:57:43 +00:00
List < PawnKeyframe > keyframesToClone = Workspace . Instance . GetPawnKeyframesByID ( Workspace . keyframeID ) ;
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
foreach ( PawnKeyframe keyframe in keyframesToClone )
{
PawnAnimationClip clip = Workspace . Instance . GetAnimationClipThatOwnsKeyframe ( keyframe . keyframeID , out int clipID ) ;
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
if ( clip = = null )
{ Debug . LogWarning ( "Cannot clone pawn keyframe - no clip owns this keyframe" ) ; continue ; }
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
if ( clip . keyframes . FirstOrDefault ( x = > x . atTick = = stageTick ) ! = null )
{ Debug . LogWarning ( "Cannot clone pawn keyframe - a keyframe already exists at this tick" ) ; return ; }
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
PawnKeyframe cloneFrame = keyframe . Copy ( ) ;
2022-10-09 02:15:28 +00:00
cloneFrame . GenerateKeyframeID ( clipID ) ;
2022-10-02 22:39:03 +00:00
cloneFrame . atTick = stageTick ;
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
PawnKeyframe nextKeyframe = clip . keyframes . FirstOrDefault ( x = > x . atTick > stageTick ) ;
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
if ( nextKeyframe ! = null )
{ clip . keyframes . Insert ( clip . keyframes . IndexOf ( nextKeyframe ) , cloneFrame ) ; }
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
else
{ clip . keyframes . Add ( cloneFrame ) ; }
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
clip . BuildSimpleCurves ( ) ;
2022-09-24 19:12:18 +00:00
2022-10-02 22:39:03 +00:00
animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) [ clipID ] . AddPawnKeyFrame ( cloneFrame . keyframeID ) ;
}
Workspace . Instance . RecordEvent ( "Keyframe clone" ) ;
2022-09-24 19:12:18 +00:00
}
2022-10-09 02:15:28 +00:00
public void CopyPawnKeyframes ( )
{
Workspace . copiedKeyframes . Clear ( ) ;
2022-10-19 02:57:43 +00:00
List < PawnKeyframe > keyframesToClone = Workspace . Instance . GetPawnKeyframesByID ( Workspace . keyframeID ) ;
2022-10-09 02:15:28 +00:00
foreach ( PawnKeyframe keyframe in keyframesToClone )
{ Workspace . copiedKeyframes . Add ( keyframe . Copy ( ) ) ; }
}
public void PastePawnKeyframes ( )
2022-10-13 05:33:18 +00:00
{
2022-10-15 06:00:47 +00:00
MakeTimelineDirty ( ) ;
int originalWindowSize = Workspace . StageWindowSize ;
2022-10-13 05:33:18 +00:00
List < int > actorsInvolved = Workspace . copiedKeyframes . Select ( x = > x . actorID ) ? . ToList ( ) ;
actorsInvolved = actorsInvolved ? . Distinct ( ) ? . ToList ( ) ;
if ( actorsInvolved . NullOrEmpty ( ) ) { Debug . Log ( "Cannot paste keyframes - there were no copied keyframes to paste" ) ; return ; }
if ( actorsInvolved . Count > 1 & & actorsInvolved . Contains ( Workspace . actorID ) = = false ) { Debug . Log ( "Cannot paste keyframes - keyframes copied across multiple timelines can only be pasted back into these source timelines" ) ; return ; }
int earliestTick = actorsInvolved . Count = = 1 ? Workspace . Instance . GetEarliestAtTickInCopiedKeyframes ( actorsInvolved [ 0 ] ) : Workspace . Instance . GetEarliestAtTickInCopiedKeyframes ( Workspace . actorID ) ;
if ( earliestTick < 1 ) { Debug . Log ( "Unknown error occured during keyframe paste operation" ) ; return ; }
foreach ( PawnKeyframe copiedKeyframe in Workspace . copiedKeyframes )
{
int tickToPasteAt = stageTick + ( copiedKeyframe . atTick . Value - earliestTick ) ;
if ( tickToPasteAt < 1 ) continue ;
if ( tickToPasteAt > Workspace . StageWindowSize )
{
2022-10-14 05:52:07 +00:00
if ( stretchKeyframesToggle . isOn )
2022-10-13 05:33:18 +00:00
{ ResizeStageWindowSize ( tickToPasteAt ) ; }
else continue ;
}
int targetActorID = actorsInvolved . Count = = 1 ? Workspace . actorID : copiedKeyframe . actorID ;
if ( Workspace . Instance . DoesPawnKeyframeExistAtTick ( Workspace . stageID , targetActorID , tickToPasteAt ) )
{
PawnKeyframe oldKeyframe = Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips [ targetActorID ] . keyframes . First ( x = > x . atTick = = tickToPasteAt ) ;
RemovePawnKeyframe ( targetActorID , oldKeyframe . keyframeID , true ) ;
}
PawnKeyframe clonedKeyframe = copiedKeyframe . Copy ( ) ;
clonedKeyframe . GenerateKeyframeID ( targetActorID ) ;
clonedKeyframe . atTick = tickToPasteAt ;
PawnAnimationClip clip = Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips [ targetActorID ] ;
2022-10-15 06:00:47 +00:00
PawnKeyframe nextKeyframe = clip . keyframes . FirstOrDefault ( x = > x . atTick > tickToPasteAt ) ;
2022-10-13 05:33:18 +00:00
if ( nextKeyframe ! = null )
{ clip . keyframes . Insert ( clip . keyframes . IndexOf ( nextKeyframe ) , clonedKeyframe ) ; }
else
{ clip . keyframes . Add ( clonedKeyframe ) ; }
clip . BuildSimpleCurves ( ) ;
animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) [ clonedKeyframe . actorID ] . AddPawnKeyFrame ( clonedKeyframe . keyframeID ) ;
}
2022-10-15 06:00:47 +00:00
if ( originalWindowSize ! = Workspace . StageWindowSize )
{
StretchKeyframes ( originalWindowSize ) ;
ResizeStageWindowSize ( originalWindowSize ) ;
}
2022-10-13 05:33:18 +00:00
Workspace . Instance . RecordEvent ( "Keyframe pasted" ) ;
}
2022-09-18 00:06:33 +00:00
public void RemovePawnKeyframe ( )
2022-09-15 05:17:44 +00:00
{
2022-10-02 22:39:03 +00:00
foreach ( int keyframeID in Workspace . keyframeID )
{
if ( Workspace . Instance . GetAnimationClipThatOwnsKeyframe ( keyframeID , out int clipID ) ! = null )
{ RemovePawnKeyframe ( clipID , keyframeID ) ; }
}
2022-09-18 05:01:12 +00:00
}
2022-10-09 02:15:28 +00:00
public void RemovePawnKeyframe ( int actorID , int keyframeID , bool force = false )
2022-09-18 05:01:12 +00:00
{
PawnKeyframe keyframe = Workspace . Instance . GetPawnKeyframe ( actorID , keyframeID ) ;
2022-09-26 05:50:26 +00:00
PawnAnimationClip clip = Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips [ actorID ] ;
2022-09-18 05:01:12 +00:00
2022-09-26 05:50:26 +00:00
if ( keyframe = = null | | clip = = null ) return ;
2022-09-18 00:06:33 +00:00
2022-10-14 05:52:07 +00:00
if ( keyframe . atTick = = Constants . minTick & & force = = false )
2022-09-26 05:50:26 +00:00
{ Debug . LogWarning ( "Cannot delete key frame - the first key frame of an animation clip cannot be deleted" ) ; return ; }
2022-09-18 00:06:33 +00:00
2022-10-09 02:15:28 +00:00
if ( clip . keyframes . Count < = 2 & & force = = false )
2022-09-26 05:50:26 +00:00
{ Debug . LogWarning ( "Cannot delete key frame - an animation clip must have two or more keyframes" ) ; return ; }
2022-09-19 00:07:44 +00:00
2022-10-02 22:39:03 +00:00
animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) [ actorID ] . RemovePawnKeyFrame ( keyframe . keyframeID ) ;
2022-09-26 05:50:26 +00:00
clip . keyframes . Remove ( keyframe ) ;
clip . BuildSimpleCurves ( ) ;
Workspace . Instance . RecordEvent ( "Keyframe deletion" ) ;
2022-09-15 05:17:44 +00:00
}
2022-09-18 00:06:33 +00:00
public void ToggleAnimation ( )
2022-09-15 05:17:44 +00:00
{
2022-09-18 00:06:33 +00:00
isAnimating = ! isAnimating ;
2022-09-15 05:17:44 +00:00
}
2022-09-18 05:01:12 +00:00
public void ToggleActorManipulationMode ( int mode )
{
Workspace . actorManipulationMode = ( ActorManipulationMode ) mode ;
}
public void OnStageTimelineSliderChange ( )
2022-09-15 05:17:44 +00:00
{
2022-09-18 05:01:12 +00:00
if ( Workspace . animationDef = = null ) return ;
2022-09-15 05:17:44 +00:00
2022-09-16 22:50:15 +00:00
if ( stageTick ! = ( int ) stageTimelineSlider . value )
2022-09-18 05:01:12 +00:00
{
stageTick = ( int ) stageTimelineSlider . value ;
animationClipTimeField . text = stageTick . ToString ( ) ;
}
2022-09-15 05:17:44 +00:00
}
2022-09-16 22:50:15 +00:00
2022-09-18 05:01:12 +00:00
public void OnAnimationClipTimeFieldChange ( )
2022-09-16 22:50:15 +00:00
{
2022-09-18 05:01:12 +00:00
if ( Workspace . animationDef = = null ) return ;
int . TryParse ( animationClipTimeField . text , out int newStageTick ) ;
2022-10-14 05:52:07 +00:00
stageTick = Mathf . Clamp ( newStageTick , Constants . minTick , Workspace . StageWindowSize ) ;
2022-09-18 05:01:12 +00:00
stageTimelineSlider . value = stageTick ;
}
public void OnAnimationClipLengthFieldChange ( )
{
if ( Workspace . animationDef = = null ) return ;
2022-09-20 06:03:55 +00:00
int . TryParse ( animationClipLengthField . text , out int newStageWindowSize ) ;
newStageWindowSize = Mathf . Clamp ( newStageWindowSize , Constants . minAnimationClipLength , Constants . maxAnimationClipLength ) ;
2022-09-18 05:01:12 +00:00
2022-09-20 06:03:55 +00:00
Debug . Log ( "Resizing animation clip length to " + newStageWindowSize . ToString ( ) + " ticks." ) ;
2022-09-18 05:01:12 +00:00
2022-10-14 05:52:07 +00:00
if ( stretchKeyframesToggle . isOn )
2022-10-14 19:00:57 +00:00
{ StretchKeyframes ( newStageWindowSize ) ; }
2022-09-20 06:03:55 +00:00
else
2022-09-18 05:01:12 +00:00
{
2022-09-20 06:03:55 +00:00
for ( int i = 0 ; i < Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips . Count ; i + + )
{
PawnAnimationClip clip = Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips [ i ] ;
List < PawnKeyframe > keyframes = clip . keyframes . Where ( x = > x . atTick > newStageWindowSize ) ? . ToList ( ) ;
2022-09-18 05:01:12 +00:00
2022-09-20 06:03:55 +00:00
if ( keyframes . NullOrEmpty ( ) )
{ continue ; }
2022-09-18 05:01:12 +00:00
2022-09-20 06:03:55 +00:00
foreach ( PawnKeyframe keyframe in keyframes )
{
RemovePawnKeyframe ( i , keyframe . keyframeID ) ;
2022-09-18 05:01:12 +00:00
2022-09-20 06:03:55 +00:00
if ( Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips [ i ] . keyframes . Count < = 2 )
{ break ; }
}
2022-09-18 05:01:12 +00:00
}
}
2022-10-13 05:33:18 +00:00
ResizeStageWindowSize ( newStageWindowSize ) ;
2022-10-15 06:00:47 +00:00
Workspace . Instance . RecordEvent ( "Stage length" ) ;
2022-09-18 05:01:12 +00:00
}
2022-10-14 19:00:57 +00:00
public void StretchKeyframes ( int newStageWindowSize )
2022-09-20 06:03:55 +00:00
{
float scale = ( float ) newStageWindowSize / Workspace . StageWindowSize ;
foreach ( PawnAnimationClip clip in Workspace . animationDef . animationStages [ Workspace . stageID ] . animationClips )
{
foreach ( PawnKeyframe keyframe in clip . keyframes )
{
keyframe . tickDuration = Mathf . RoundToInt ( keyframe . tickDuration * scale ) ;
keyframe . atTick = null ;
}
clip . BuildSimpleCurves ( ) ;
}
}
2022-10-13 05:33:18 +00:00
public void ResizeStageWindowSize ( int newStageWindowSize )
{
animationClipLengthField . text = newStageWindowSize . ToString ( ) ;
Workspace . animationDef . animationStages [ Workspace . stageID ] . stageWindowSize = newStageWindowSize ;
Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicks = newStageWindowSize * int . Parse ( cyclesNormalField . text ) ;
Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicksQuick = newStageWindowSize * int . Parse ( cyclesFastField . text ) ;
}
2022-09-18 05:01:12 +00:00
public void OnCycleNormalFieldChange ( )
{
if ( Workspace . animationDef = = null ) return ;
if ( int . TryParse ( cyclesNormalField . text , out int cycles ) )
2022-09-27 05:56:35 +00:00
{
cycles = cycles < = 0 ? 1 : cycles ;
2022-10-02 05:42:07 +00:00
cyclesNormalField . text = cycles . ToString ( ) ;
2022-09-27 05:56:35 +00:00
2022-09-26 05:50:26 +00:00
Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicks = cycles * Workspace . StageWindowSize ;
Workspace . animationDef . animationStages [ Workspace . stageID ] . isLooping = cycles > 1 ;
foreach ( AnimationTimeline animationTimeline in animationTimelines . GetComponentsInChildren < AnimationTimeline > ( ) )
{ animationTimeline . InitiateUpdateOfGhostFrames ( ) ; }
}
2022-09-19 00:07:44 +00:00
2022-09-21 05:40:58 +00:00
Workspace . Instance . RecordEvent ( "Cycle count (normal)" ) ;
2022-09-18 05:01:12 +00:00
}
public void OnCycleFastFieldChange ( )
{
if ( Workspace . animationDef = = null ) return ;
2022-09-26 05:50:26 +00:00
if ( int . TryParse ( cyclesFastField . text , out int fastCycles ) )
{
2022-09-27 05:56:35 +00:00
fastCycles = fastCycles < 0 ? 0 : fastCycles ;
2022-09-26 05:50:26 +00:00
int . TryParse ( cyclesNormalField . text , out int cycles ) ;
if ( fastCycles > cycles ) fastCycles = cycles ;
cyclesFastField . text = fastCycles . ToString ( ) ;
Workspace . animationDef . animationStages [ Workspace . stageID ] . playTimeTicksQuick = fastCycles * Workspace . StageWindowSize ;
}
2022-09-19 00:07:44 +00:00
2022-09-21 05:40:58 +00:00
Workspace . Instance . RecordEvent ( "Cycle count (fast)" ) ;
}
public void OnPlayBackSpeedChange ( )
{
if ( float . TryParse ( playBackSpeedField . text , out playBackSpeed ) )
2022-09-27 05:56:35 +00:00
{ playBackSpeed = Mathf . Clamp ( playBackSpeed , 0.01f , 16f ) ; }
playBackSpeedField . text = playBackSpeed . ToString ( ) ;
2022-09-19 00:07:44 +00:00
}
private int lastactorCount = 0 ;
private int lastStageID = 0 ;
private int lastStageCount = 0 ;
private int lastStageWindowSize = 0 ;
public bool AnimationTimelinesNeedUpdate ( )
{
if ( Workspace . animationDef = = null ) return false ;
bool update = isTimelineDirty ;
if ( lastStageID ! = Workspace . stageID )
{ update = true ; }
if ( lastStageCount ! = Workspace . animationDef . animationStages . Count )
{ update = true ; }
if ( lastactorCount ! = Workspace . animationDef . actors . Count )
{ update = true ; }
if ( lastStageWindowSize ! = Workspace . StageWindowSize )
{ update = true ; }
if ( update )
{
lastStageID = Workspace . stageID ;
lastStageCount = Workspace . animationDef . animationStages . Count ;
lastactorCount = Workspace . animationDef . actors . Count ;
lastStageWindowSize = Workspace . StageWindowSize ;
return true ;
}
return false ;
2022-09-16 22:50:15 +00:00
}
2022-09-13 05:36:34 +00:00
}
}