2022-11-01 01:00:38 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Xml ;
using System.Xml.Serialization ;
using UnityEngine ;
namespace RimWorldAnimationStudio
{
public class PawnAnimationClip
{
// Data to/from animationDef
public string layer = "Pawn" ;
[XmlArray("addons"), XmlArrayItem("li")] public List < ActorAddon > addons ;
[XmlAttribute("Class")] public string className = "Rimworld_Animations.PawnAnimationClip" ;
[XmlArray("keyframes"), XmlArrayItem("li")] public List < PawnKeyframe > keyframes ;
[XmlArray("tags"), XmlArrayItem("li")] public List < string > tags ;
// Data serialization control
public bool ShouldSerializeaddons ( ) { return addons . Where ( x = > x . Render ) ? . Any ( ) = = true ; }
public bool ShouldSerializekeyframes ( ) { return keyframes . NotNullOrEmpty ( ) ; }
public bool ShouldSerializetags ( ) { return tags . NotNullOrEmpty ( ) ; }
// Data helper functions
[XmlIgnore] public string Layer
{
get { return layer ; }
set { layer = value ; EventsManager . OnPawnAnimationClipChanged ( this ) ; }
}
[XmlIgnore] public List < ActorAddon > Addons
{
get { return addons . NullOrEmpty ( ) ? addons = new List < ActorAddon > ( ) : addons ; }
set { addons = value . NotNullOrEmpty ( ) ? value : null ; }
}
[XmlIgnore] public List < PawnKeyframe > Keyframes
{
get { return keyframes . NullOrEmpty ( ) ? keyframes = new List < PawnKeyframe > ( ) : keyframes ; }
set { keyframes = value . NotNullOrEmpty ( ) ? value : null ; }
}
[XmlIgnore]
public List < string > Tags
{
get { return tags . NullOrEmpty ( ) ? tags = new List < string > ( ) : tags ; }
set { tags = value . NotNullOrEmpty ( ) ? value : null ; }
}
// Local data
[XmlIgnore] public int duration { get { return Keyframes . Max ( x = > x . atTick . Value ) ; } }
[XmlIgnore] public SimpleCurve GenitalAngle = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve BodyAngle = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve HeadAngle = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve HeadBob = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve BodyOffsetX = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve BodyOffsetZ = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve HeadFacing = new SimpleCurve ( ) ;
[XmlIgnore] public SimpleCurve BodyFacing = new SimpleCurve ( ) ;
// Methods
public void BuildSimpleCurves ( )
{
// Add addon data (if missing)
foreach ( ActorAddonDef actorAddonDef in ActorAddonDefs . allDefs )
{ AddActorAddon ( actorAddonDef ) ; }
// Clear simple curve data
BodyAngle . Clear ( ) ;
HeadAngle . Clear ( ) ;
BodyOffsetX . Clear ( ) ;
BodyOffsetZ . Clear ( ) ;
HeadFacing . Clear ( ) ;
BodyFacing . Clear ( ) ;
HeadBob . Clear ( ) ;
GenitalAngle . Clear ( ) ;
foreach ( ActorAddon addon in Addons )
{
addon . PosX . Clear ( ) ;
addon . PosZ . Clear ( ) ;
addon . Rotation . Clear ( ) ;
}
// Start building simple curves
int keyframePosition = 0 ;
int duration = 0 ;
Keyframes [ Keyframes . Count - 1 ] . TickDuration = 1 ;
foreach ( PawnKeyframe frame in Keyframes )
{ duration + = frame . TickDuration ; }
for ( int i = 0 ; i < Keyframes . Count ; i + + )
{
PawnKeyframe keyframe = Keyframes [ i ] ;
2022-11-01 02:19:52 +00:00
if ( keyframe . HasValidKeyframeID ( ) = = false )
{ keyframe . GenerateKeyframeID ( GetOwningActorID ( ) ) ; }
2022-11-01 01:00:38 +00:00
if ( keyframe . atTick . HasValue )
{
2022-11-10 00:03:52 +00:00
if ( i + 1 < Keyframes . Count )
{ Keyframes [ i ] . TickDuration = Keyframes [ i + 1 ] . atTick . Value - Keyframes [ i ] . atTick . Value ; }
// Safeguard - if two keys end up in having the same atTick, the duration of the first will be 0 and should be deleted
if ( Keyframes [ i ] . TickDuration = = 0 )
{
Keyframes . RemoveAt ( i ) ;
continue ;
}
2022-11-01 01:00:38 +00:00
BodyAngle . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . BodyAngle , true ) ;
HeadAngle . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . HeadAngle , true ) ;
BodyOffsetX . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . BodyOffsetX , true ) ;
BodyOffsetZ . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . BodyOffsetZ , true ) ;
HeadFacing . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . HeadFacing , true ) ;
BodyFacing . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . BodyFacing , true ) ;
HeadBob . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . HeadBob , true ) ;
GenitalAngle . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . GenitalAngle , true ) ;
foreach ( ActorAddon addon in Addons )
{
if ( keyframe . AddonKeyframes . Any ( x = > x . AddonName = = addon . AddonName ) = = false )
{ keyframe . AddonKeyframes . Add ( new AddonKeyframe ( addon . AddonName ) ) ; }
addon . PosX . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . GetAddonKeyframe ( addon . AddonName ) . PosX , true ) ;
addon . PosZ . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . GetAddonKeyframe ( addon . AddonName ) . PosZ , true ) ;
addon . Rotation . Add ( ( float ) keyframe . atTick / ( float ) duration , keyframe . GetAddonKeyframe ( addon . AddonName ) . Rotation , true ) ;
}
}
else
{
BodyAngle . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . BodyAngle , true ) ;
HeadAngle . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . HeadAngle , true ) ;
BodyOffsetX . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . BodyOffsetX , true ) ;
BodyOffsetZ . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . BodyOffsetZ , true ) ;
HeadFacing . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . HeadFacing , true ) ;
BodyFacing . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . BodyFacing , true ) ;
HeadBob . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . HeadBob , true ) ;
GenitalAngle . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . GenitalAngle , true ) ;
foreach ( ActorAddon addon in Addons )
{
if ( keyframe . AddonKeyframes . Any ( x = > x . AddonName = = addon . AddonName ) = = false )
{ keyframe . AddonKeyframes . Add ( new AddonKeyframe ( addon . AddonName ) ) ; }
addon . PosX . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . GetAddonKeyframe ( addon . AddonName ) . PosX , true ) ;
addon . PosZ . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . GetAddonKeyframe ( addon . AddonName ) . PosZ , true ) ;
addon . Rotation . Add ( ( float ) keyframePosition / ( float ) duration , keyframe . GetAddonKeyframe ( addon . AddonName ) . Rotation , true ) ;
}
keyframe . atTick = keyframePosition + Constants . minTick ;
keyframePosition + = keyframe . TickDuration ;
}
}
}
public void AddActorAddon ( ActorAddonDef actorAddonDef )
{
if ( Addons . Any ( x = > x . AddonName = = actorAddonDef . addonName ) = = false )
{ Addons . Add ( new ActorAddon ( actorAddonDef ) ) ; }
foreach ( PawnKeyframe keyframe in Keyframes )
{
if ( keyframe . AddonKeyframes . Any ( x = > x . AddonName = = actorAddonDef . addonName ) = = false )
{ keyframe . AddonKeyframes . Add ( new AddonKeyframe ( actorAddonDef . addonName ) ) ; }
}
}
public void ShowOrHideActorAddon ( string addonName , bool flag )
{
ActorAddon addon = GetActorAddon ( addonName ) ;
if ( addon ! = null )
{ addon . Render = flag ; }
}
public bool IsActorAddonVisible ( string addonName )
{
ActorAddon addon = GetActorAddon ( addonName ) ;
if ( addon ! = null )
{ return addon . Render ; }
return false ;
}
public ActorAddon GetActorAddon ( string addonName )
{
return Addons . FirstOrDefault ( x = > x . AddonName = = addonName ) ;
}
public int GetOwningActorID ( )
{
2023-01-13 04:55:47 +00:00
if ( Workspace . animationDef = = null )
{ Debug . Log ( "Cannot get actor ID - the AnimationDef is invalid" ) ; return - 1 ; }
2022-11-01 01:00:38 +00:00
2023-01-13 04:55:47 +00:00
foreach ( AnimationStage stage in Workspace . animationDef . animationStages )
{
if ( stage . AnimationClips . Contains ( this ) )
{ return stage . AnimationClips . IndexOf ( this ) ; }
}
Debug . Log ( "Cannot get actor ID - animation clip has been orphaned" ) ;
return - 1 ;
2022-11-01 01:00:38 +00:00
}
public void AddPawnKeyframe ( )
{
if ( Keyframes = = null )
{ Debug . LogWarning ( "Cannot add pawn keyframe - the AnimationDef is invalid" ) ; return ; }
if ( Keyframes . FirstOrDefault ( x = > x . atTick = = Workspace . StageTick ) ! = null )
{ Debug . LogWarning ( "Cannot add pawn keyframe - a keyframe already exists at this tick" ) ; return ; }
float clipPercent = ( float ) ( Workspace . StageTick % duration ) / duration ;
PawnKeyframe keyframe = new PawnKeyframe ( ) ;
keyframe . BodyAngle = BodyAngle . Evaluate ( clipPercent ) ;
keyframe . HeadAngle = HeadAngle . Evaluate ( clipPercent ) ;
keyframe . HeadBob = HeadBob . Evaluate ( clipPercent ) ;
keyframe . BodyOffsetX = BodyOffsetX . Evaluate ( clipPercent ) ;
keyframe . BodyOffsetZ = BodyOffsetZ . Evaluate ( clipPercent ) ;
keyframe . HeadFacing = ( int ) HeadFacing . Evaluate ( clipPercent ) ;
keyframe . BodyFacing = ( int ) BodyFacing . Evaluate ( clipPercent ) ;
keyframe . GenitalAngle = GenitalAngle . Evaluate ( clipPercent ) ;
keyframe . atTick = Workspace . StageTick ;
PawnKeyframe nextKeyframe = Keyframes . FirstOrDefault ( x = > x . atTick > Workspace . StageTick ) ;
if ( nextKeyframe ! = null )
{ keyframes . Insert ( keyframes . IndexOf ( nextKeyframe ) , keyframe ) ; }
else
{ keyframes . Add ( keyframe ) ; }
BuildSimpleCurves ( ) ;
EventsManager . OnKeyframeCountChanged ( this ) ;
Workspace . RecordEvent ( "Keyframe addition" ) ;
}
public void CopyPawnKeyframes ( )
{
Workspace . copiedKeyframes . Clear ( ) ;
List < PawnKeyframe > keyframesToClone = Workspace . GetPawnKeyframesByID ( Workspace . keyframeID ) ;
foreach ( PawnKeyframe keyframe in keyframesToClone )
2022-11-06 04:03:51 +00:00
{ Workspace . copiedKeyframes . Add ( keyframe . GetClone ( ) ) ; }
2022-11-01 01:00:38 +00:00
}
public void PastePawnKeyframes ( )
{
int originalWindowSize = Workspace . StageWindowSize ;
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 . GetEarliestAtTickInCopiedKeyframes ( actorsInvolved [ 0 ] ) : Workspace . GetEarliestAtTickInCopiedKeyframes ( Workspace . ActorID ) ;
if ( earliestTick < 1 ) { Debug . Log ( "Unknown error occured during keyframe paste operation" ) ; return ; }
foreach ( PawnKeyframe copiedKeyframe in Workspace . copiedKeyframes )
{
int tickToPasteAt = Workspace . StageTick + ( copiedKeyframe . atTick . Value - earliestTick ) ;
if ( tickToPasteAt < 1 ) continue ;
if ( tickToPasteAt > Workspace . StageWindowSize )
{
if ( Workspace . stretchKeyframes )
{ Workspace . GetCurrentAnimationStage ( ) . ResizeStageWindow ( tickToPasteAt ) ; }
else continue ;
}
int targetActorID = actorsInvolved . Count = = 1 ? Workspace . ActorID : copiedKeyframe . actorID ;
if ( Workspace . DoesPawnKeyframeExistAtTick ( Workspace . StageID , targetActorID , tickToPasteAt ) )
{
PawnKeyframe oldKeyframe = Workspace . GetPawnAnimationClip ( targetActorID ) . Keyframes . First ( x = > x . atTick = = tickToPasteAt ) ;
Workspace . GetAnimationClipThatOwnsKeyframe ( oldKeyframe . keyframeID ) . RemovePawnKeyframe ( oldKeyframe . keyframeID , true ) ;
}
2022-11-06 04:03:51 +00:00
PawnKeyframe clonedKeyframe = copiedKeyframe . GetClone ( ) ;
2022-11-01 01:00:38 +00:00
clonedKeyframe . GenerateKeyframeID ( targetActorID ) ;
clonedKeyframe . atTick = tickToPasteAt ;
PawnAnimationClip clip = Workspace . animationDef . AnimationStages [ Workspace . StageID ] . AnimationClips [ targetActorID ] ;
PawnKeyframe nextKeyframe = clip . Keyframes . FirstOrDefault ( x = > x . atTick > tickToPasteAt ) ;
if ( nextKeyframe ! = null )
{ clip . Keyframes . Insert ( clip . Keyframes . IndexOf ( nextKeyframe ) , clonedKeyframe ) ; }
else
{ clip . Keyframes . Add ( clonedKeyframe ) ; }
clip . BuildSimpleCurves ( ) ;
EventsManager . OnKeyframeCountChanged ( clip ) ;
}
if ( originalWindowSize ! = Workspace . StageWindowSize )
{
Workspace . GetCurrentAnimationStage ( ) . StretchStageWindow ( originalWindowSize ) ;
Workspace . GetCurrentAnimationStage ( ) . ResizeStageWindow ( originalWindowSize ) ;
}
Workspace . RecordEvent ( "Keyframe pasted" ) ;
}
public void RemovePawnKeyframe ( int keyframeID , bool force = false )
{
PawnKeyframe keyframe = Workspace . GetPawnKeyframe ( keyframeID ) ;
if ( keyframe = = null | | IsOwnerOfKeyframe ( keyframeID ) = = false ) return ;
2022-11-06 04:03:51 +00:00
Keyframes . Remove ( keyframe ) ;
2022-11-01 01:00:38 +00:00
2022-11-06 04:03:51 +00:00
if ( Workspace . GetAllPawnKeyframesAtTick ( GetOwningActorID ( ) , Constants . minTick ) . NullOrEmpty ( ) )
{
PawnKeyframe newKeyframe = new PawnKeyframe ( ) ;
newKeyframe . GenerateKeyframeID ( GetOwningActorID ( ) ) ;
newKeyframe . atTick = Constants . minTick ;
Keyframes . Insert ( 0 , newKeyframe ) ;
}
// Add missing second keyframe (if needed)
if ( Keyframes . Count = = 1 )
{
PawnKeyframe newKeyframe = Workspace . GetAllPawnKeyframesAtTick ( GetOwningActorID ( ) , Constants . minTick ) . First ( ) . GetClone ( ) ;
newKeyframe . atTick = 10 ;
Keyframes . Add ( newKeyframe ) ;
}
2022-11-01 01:00:38 +00:00
BuildSimpleCurves ( ) ;
EventsManager . OnKeyframeCountChanged ( this ) ;
Workspace . RecordEvent ( "Keyframe deletion" ) ;
}
public bool IsOwnerOfKeyframe ( int keyframeID )
{
return Keyframes . Any ( x = > x . keyframeID = = keyframeID ) ;
}
// Pre-save / post-load
public void OnPreSave ( )
{
var temp = Addons . Copy ( ) ;
Addons . Clear ( ) ;
foreach ( ActorAddon addon in temp )
{
if ( addon . Render )
{ addons . Add ( addon ) ; }
}
}
public void OnPostLoad ( )
{
foreach ( PawnKeyframe keyframe in Keyframes )
2022-11-01 02:19:52 +00:00
{ keyframe . OnPostLoad ( ) ; }
2022-11-01 01:00:38 +00:00
}
}
}