Code refactor

This commit is contained in:
AbstractConcept 2022-10-27 00:56:04 -05:00
parent cd4711a8e5
commit 757badf4f6
517 changed files with 2534 additions and 2221 deletions

View file

@ -8,80 +8,143 @@ namespace RimWorldAnimationStudio
{
public class Actor
{
[XmlArray("defNames"), XmlArrayItem("li")] public List<string> defNames = new List<string>();
[XmlArray("bodyDefTypes"), XmlArrayItem("li")] public List<string> bodyDefTypes = new List<string>();
[XmlArray("requiredGender"), XmlArrayItem("li")] public List<string> requiredGender = new List<string>();
[XmlArray("requiredGenitals"), XmlArrayItem("li")] public List<string> requiredGenitals = new List<string>();
[XmlArray("raceOffsets"), XmlArrayItem("li")] public List<AlienRaceOffset> raceOffsets = new List<AlienRaceOffset>();
[XmlArray("blacklistedRaces"), XmlArrayItem("li")] public List<string> blacklistedRaces = new List<string>();
// Data to/from animationDef
[XmlArray("defNames"), XmlArrayItem("li")] public List<string> defNames;
[XmlArray("bodyDefTypes"), XmlArrayItem("li")] public List<string> bodyDefTypes;
[XmlArray("requiredGenitals"), XmlArrayItem("li")] public List<string> requiredGenitals;
[XmlArray("raceOffsets"), XmlArrayItem("li")] public List<PawnRaceOffset> raceOffsets;
[XmlArray("tags"), XmlArrayItem("li")] public List<string> tags;
public BodyTypeOffset bodyTypeOffset;
public bool? initiator = false;
public bool? controlGenitalAngle;
public bool? isFucking;
public bool? isFucked;
[XmlIgnore] public ActorGender gender;
[XmlIgnore] private AlienRaceDef alienRaceDef;
public BodyTypeOffset bodyTypeOffset = new BodyTypeOffset();
public bool initiator = false;
public bool controlGenitalAngle;
public bool isFucking;
public bool isFucked;
[XmlIgnore] public string bodyType = "Male";
// Data serialization control
public bool ShouldSerializedefNames() { return defNames.NotNullOrEmpty(); }
public bool ShouldSerializebodyDefTypes() { return bodyDefTypes.NotNullOrEmpty(); }
public bool ShouldSerializerequiredGender() { return requiredGender.NotNullOrEmpty(); }
public bool ShouldSerializerequiredGenitals() { return requiredGenitals.NotNullOrEmpty(); }
public bool ShouldSerializeraceOffsets() { return raceOffsets.NotNullOrEmpty(); }
public bool ShouldSerializeblacklistedRaces() { return blacklistedRaces.NotNullOrEmpty(); }
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
public bool ShouldSerializeinitiator() { return initiator; }
public bool ShouldSerializecontrolGenitalAngle() { return controlGenitalAngle; }
public bool ShouldSerializeisFucking() { return isFucking; }
public bool ShouldSerializeisFucked() { return isFucked; }
public bool ShouldSerializebodyTypeOffset() { return bodyTypeOffset?.AllOffsetsEmpty() == false; }
public bool ShouldSerializeinitiator() { return initiator == true; }
public bool ShouldSerializecontrolGenitalAngle() { return controlGenitalAngle == true; }
public bool ShouldSerializeisFucking() { return isFucking == true; }
public bool ShouldSerializeisFucked() { return isFucked == true; }
public AlienRaceDef GetAlienRaceDef()
{
if (alienRaceDef == null)
{ alienRaceDef = AlienRaceDefs.GetNamed("Human"); }
return alienRaceDef;
// Data helper functions
[XmlIgnore] public List<string> DefNames
{
get { return defNames.NullOrEmpty() ? defNames = new List<string>() : defNames; }
set { defNames = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
}
public void SetAlienRaceDef(string alienRaceDefName)
{
AlienRaceDef alienRaceDef = AlienRaceDefs.GetNamed(alienRaceDefName);
if (alienRaceDef != null)
{ this.alienRaceDef = alienRaceDef; }
[XmlIgnore] public List<string> BodyDefTypes
{
get { return bodyDefTypes.NullOrEmpty() ? bodyDefTypes = new List<string>() : bodyDefTypes; }
set { bodyDefTypes = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
}
public Vector3 GetAlienRaceOffset()
{
if (alienRaceDef == null)
{ alienRaceDef = AlienRaceDefs.GetNamed("Human"); }
[XmlIgnore] public List<string> RequiredGenitals
{
get { return requiredGenitals.NullOrEmpty() ? requiredGenitals = new List<string>() : requiredGenitals; }
set { requiredGenitals = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
}
AlienRaceOffset raceOffset = raceOffsets.FirstOrDefault(x => x.defName == alienRaceDef.defName);
[XmlIgnore] public List<PawnRaceOffset> RaceOffsets {
get { return raceOffsets.NullOrEmpty() ? raceOffsets = new List<PawnRaceOffset>() : raceOffsets; }
set { raceOffsets = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
}
[XmlIgnore] public List<string> Tags
{
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
set { tags = value.NotNullOrEmpty() ? value : null; EventsManager.OnActorChanged(this); }
}
[XmlIgnore] public BodyTypeOffset BodyTypeOffset
{
get { return bodyTypeOffset == null ? bodyTypeOffset = new BodyTypeOffset() : bodyTypeOffset; }
set { bodyTypeOffset = value; EventsManager.OnActorChanged(this); }
}
[XmlIgnore] public bool Initiator
{
get { return initiator == true; }
set { if (value) { initiator = true; } else initiator = null; EventsManager.OnActorChanged(this); }
}
[XmlIgnore] public bool ControlGenitalAngle
{
get { return controlGenitalAngle == true; }
set { if (value) { controlGenitalAngle = true; } else controlGenitalAngle = null; EventsManager.OnActorChanged(this); }
}
[XmlIgnore] public bool IsFucking
{
get { return isFucking == true; }
set { if (value) { isFucking = true; } else isFucking = null; EventsManager.OnActorChanged(this); }
}
[XmlIgnore] public bool IsFucked
{
get { return isFucked == true; }
set { if (value) { isFucked = true; } else isFucked = null; EventsManager.OnActorChanged(this); }
}
// Local data
[XmlIgnore] public string bodyType = "Male";
[XmlIgnore] private PawnRaceDef pawnRaceDef;
// Methods
public PawnRaceDef GetPawnRaceDef()
{
if (pawnRaceDef == null)
{ pawnRaceDef = PawnRaceDefs.GetNamed("Human"); }
return pawnRaceDef;
}
public void SetPawnRaceDef(string pawnRaceDefName)
{
PawnRaceDef pawnRaceDef = PawnRaceDefs.GetNamed(pawnRaceDefName);
if (pawnRaceDef != null)
{
this.pawnRaceDef = pawnRaceDef;
EventsManager.OnActorChanged(this);
}
}
public Vector3 GetPawnRaceOffset()
{
if (pawnRaceDef == null)
{ pawnRaceDef = PawnRaceDefs.GetNamed("Human"); }
PawnRaceOffset raceOffset = RaceOffsets.FirstOrDefault(x => x.defName == pawnRaceDef.defName);
if (raceOffset == null)
{
raceOffset = new AlienRaceOffset(alienRaceDef.defName);
raceOffsets.Add(raceOffset);
raceOffset = new PawnRaceOffset(pawnRaceDef.defName);
RaceOffsets.Add(raceOffset);
}
return raceOffset.GetOffset();
}
public void SetAlienRaceOffset(Vector2 offset)
public void SetPawnRaceOffset(Vector2 offset)
{
if (alienRaceDef == null)
if (pawnRaceDef == null)
{ return; }
AlienRaceOffset raceOffset = raceOffsets.FirstOrDefault(x => x.defName == alienRaceDef.defName);
PawnRaceOffset raceOffset = RaceOffsets.FirstOrDefault(x => x.defName == pawnRaceDef.defName);
if (raceOffset == null)
{
raceOffset = new AlienRaceOffset(alienRaceDef.defName);
raceOffsets.Add(raceOffset);
raceOffset = new PawnRaceOffset(pawnRaceDef.defName);
RaceOffsets.Add(raceOffset);
EventsManager.OnActorChanged(this);
}
raceOffset.SetOffset(offset);
@ -89,46 +152,25 @@ namespace RimWorldAnimationStudio
public Vector3 GetFinalTransformOffset()
{
Vector3 offset = GetAlienRaceOffset() + (GetAlienRaceDef().isHumanoid ? bodyTypeOffset.GetOffset(bodyType) : new Vector3());
Vector3 offset = GetPawnRaceOffset() + (GetPawnRaceDef().isHumanoid ? BodyTypeOffset.GetOffset(bodyType) : new Vector3());
return new Vector3(offset.x, offset.z, offset.y);
}
public void ValidateData()
{
bodyDefTypes = bodyDefTypes.Intersect(Tags.bodyDefTypes.Concat(CustomTags.bodyDefTypes))?.ToList();
requiredGenitals = requiredGenitals.Intersect(Tags.bodyParts.Concat(CustomTags.bodyParts))?.ToList();
raceOffsets = raceOffsets.Except(raceOffsets.Where(x => x.OffsetIsZero()))?.ToList();
}
public bool MakeNew()
{
if (Workspace.animationDef == null)
{ Debug.LogWarning("Cannot make new actor - there is no AnimationDef"); return false; }
Workspace.animationDef.actors.Add(this);
Workspace.actorID = Workspace.animationDef.actors.Count - 1;
foreach (AnimationStage stage in Workspace.animationDef.animationStages)
{
PawnAnimationClip clip = new PawnAnimationClip();
if (clip.MakeNew())
{
stage.animationClips.Add(clip);
stage.Initialize();
stage.OnPostLoad();
}
}
return true;
}
public int GetActorID()
{
if (Workspace.animationDef == null) return -1;
return Workspace.animationDef.actors.IndexOf(this);
return Workspace.animationDef.Actors.IndexOf(this);
}
// Pre-save / post-load
public void OnPreSave()
{
BodyDefTypes = BodyDefTypes.Intersect(DefaultTags.bodyDefTypes.Concat(CustomTags.bodyDefTypes))?.ToList();
RequiredGenitals = RequiredGenitals.Intersect(DefaultTags.bodyParts.Concat(CustomTags.bodyParts))?.ToList();
RaceOffsets = RaceOffsets.Except(RaceOffsets.Where(x => x.OffsetIsZero()))?.ToList();
}
public void OnPostLoad() { }
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ca7cd67490c5773499bff5c06907bdf7
guid: 63a9fd7a0256e9849bc2bc07403528e8
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -4,28 +4,69 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using UnityEngine;
namespace RimWorldAnimationStudio
{
public class ActorAddon
{
// Data to/from animationDef
public string addonName;
public int anchoringActor;
public int? anchoringActor;
public string anchorName;
public string layer = "Pawn";
public float scale;
public bool render;
public float? scale;
public bool? render;
// Data helper functions
[XmlIgnore] public string AddonName
{
get { return addonName; }
set { addonName = value; }
}
[XmlIgnore] public int AnchoringActor
{
get { return anchoringActor.HasValue ? anchoringActor.Value : 0; }
set { anchoringActor = value; EventsManager.OnActorAddonChanged(this); }
}
[XmlIgnore] public string AnchorName
{
get { return anchorName; }
set { anchorName = value; EventsManager.OnActorAddonChanged(this); }
}
[XmlIgnore] public string Layer
{
get { return layer; }
set { layer = value; EventsManager.OnActorAddonChanged(this); }
}
[XmlIgnore] public float Scale
{
get { return scale.HasValue ? scale.Value : 0f; }
set { scale = value; EventsManager.OnActorAddonChanged(this); }
}
[XmlIgnore] public bool Render
{
get { return render == true; }
set { render = value; EventsManager.OnActorAddonChanged(this); }
}
// Simple curves
[XmlIgnore] public SimpleCurve PosX = new SimpleCurve();
[XmlIgnore] public SimpleCurve PosZ = new SimpleCurve();
[XmlIgnore] public SimpleCurve Rotation = new SimpleCurve();
// Constructors
public ActorAddon() { }
public ActorAddon(string addonName, float scale = 1f)
{
this.addonName = addonName;
this.scale = scale;
this.AddonName = addonName;
this.Scale = scale;
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f4d87003a570d5241affe4170ae91045
guid: 3759e796f4f62b044b9a652e746d79a1
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
namespace RimWorldAnimationStudio
{
public class AddonKeyframe
{
// Data to/from animationDef
public string addonName;
public float? posX;
public float? posZ;
public float? rotation;
// Data helper functions
[XmlIgnore] public string AddonName
{
get { return addonName; }
set { addonName = value; }
}
[XmlIgnore] public float PosX
{
get { return posX.HasValue ? posX.Value : 0f; }
set { posX = value; EventsManager.OnAddonKeyframeChanged(this); }
}
[XmlIgnore] public float PosZ
{
get { return posZ.HasValue ? posZ.Value : 0f; }
set { posZ = value; EventsManager.OnAddonKeyframeChanged(this); }
}
[XmlIgnore] public float Rotation
{
get { return rotation.HasValue ? rotation.Value : 0f; }
set { rotation = value; EventsManager.OnAddonKeyframeChanged(this); }
}
// Constructors
public AddonKeyframe() { }
public AddonKeyframe(string addonName)
{
this.AddonName = addonName;
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 60509e7cd8e74e6419c5c93304440a17
guid: 339d47b209f50f545a84a8e8c7948ae1
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

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

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 20033872660014f4295d8ac40800a707
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,20 +0,0 @@
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
namespace RimWorldAnimationStudio
{
public abstract class AnimationClip
{
public string layer = "Pawn";
public List<string> tags;
public virtual int duration { get { return 0; } }
public abstract void BuildSimpleCurves();
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
public virtual void ValidateData() { }
}
}

View file

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

View file

@ -1,230 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
namespace RimWorldAnimationStudio
{
public class PawnAnimationClip : AnimationClip
{
[XmlArray("addons"), XmlArrayItem("li")] public List<ActorAddon> _addons = new List<ActorAddon>();
[XmlIgnore] public List<ActorAddon> addons = new List<ActorAddon>();
[XmlAttribute("Class")] public string className = "Rimworld_Animations.PawnAnimationClip";
[XmlArray("keyframes"), XmlArrayItem("li")] public List<PawnKeyframe> keyframes = new List<PawnKeyframe>();
[XmlIgnore] public Dictionary<int, bool> quiver = new Dictionary<int, bool>();
[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();
public override int duration { get { return keyframes.Max(x => x.atTick.Value); } }
public override void BuildSimpleCurves()
{
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();
}
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];
if (keyframe.atTick.HasValue)
{
if (keyframe.HasValidKeyframeID() == false)
{ keyframe.GenerateKeyframeID(Workspace.animationDef.animationStages[Workspace.stageID].animationClips.IndexOf(this)); }
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);
}
if (i + 1 < keyframes.Count)
{ keyframes[i].tickDuration = keyframes[i + 1].atTick.Value - keyframes[i].atTick.Value; }
}
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);
}
if (keyframe.tickDuration != 1 && keyframe.quiver.HasValue)
{
quiver.Add(keyframePosition, true);
quiver.Add(keyframePosition + keyframe.tickDuration - 1, false);
}
keyframe.atTick = keyframePosition + Constants.minTick;
keyframePosition += keyframe.tickDuration;
}
}
}
public void AddActorAddon(string addonName, float scale = 1f)
{
if (addons.Any(x => x.addonName == addonName) == false)
{
addons.Add(new ActorAddon(addonName, scale));
}
foreach (PawnKeyframe keyframe in keyframes)
{
if (keyframe.addonKeyframes.Any(x => x.addonName == addonName) == false)
{ keyframe.addonKeyframes.Add(new AddonKeyframe(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 override void ValidateData()
{
_addons.Clear();
foreach (ActorAddon addon in addons)
{
Debug.Log(addon.anchorName);
if (addon.render)
{
_addons.Add(addon);
}
}
}
public int GetOwningActorID()
{
if (Workspace.animationDef == null) return -1;
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;
}
public void OnPostLoad()
{
addons = _addons.Copy();
foreach (PawnKeyframe keyframe in keyframes)
{
keyframe.OnPostLoad();
}
AddActorAddon("left hand", 0.667f);
AddActorAddon("right hand", 0.667f);
AddActorAddon("dildo");
}
}
}

View file

@ -1,53 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
namespace RimWorldAnimationStudio
{
public class ThingAnimationClip : AnimationClip
{
[XmlAttribute("Class")] public string className = "Rimworld_Animations.ThingAnimationClip";
[XmlArray("keyframes"), XmlArrayItem("li")] public List<ThingKeyframe> keyframes = new List<ThingKeyframe>();
[XmlIgnore] public SimpleCurve PositionX = new SimpleCurve();
[XmlIgnore] public SimpleCurve PositionZ = new SimpleCurve();
[XmlIgnore] public SimpleCurve Rotation = new SimpleCurve();
public override int duration { get { return keyframes.Max(x => x.atTick.Value); } }
public override void BuildSimpleCurves()
{
int duration = 0;
//getting the length of the whole clip
foreach (ThingKeyframe frame in keyframes)
{
duration += frame.tickDuration;
}
//guarantees loops don't get cut off mid-anim
//this.duration = duration;
int keyframePosition = 0;
foreach (ThingKeyframe frame in keyframes)
{
if (frame.atTick.HasValue)
{
PositionX.Add((float)frame.atTick / (float)duration, frame.positionX, true);
PositionZ.Add((float)frame.atTick / (float)duration, frame.positionZ, true);
Rotation.Add((float)frame.atTick / (float)duration, frame.rotation, true);
}
else
{
PositionX.Add((float)keyframePosition / (float)duration, frame.positionX, true);
PositionZ.Add((float)keyframePosition / (float)duration, frame.positionZ, true);
Rotation.Add((float)keyframePosition / (float)duration, frame.rotation, true);
keyframePosition += frame.tickDuration;
}
}
}
}
}

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;
@ -8,33 +9,163 @@ namespace RimWorldAnimationStudio
{
public class AnimationDef
{
public string defName = "Undefined";
public string label = "Undefined";
// Data to/from animationDef
public string defName;
public string label;
public bool sounds = true;
[XmlArray("sexTypes"), XmlArrayItem("li")] public List<string> sexTypes;
[XmlArray("interactionDefTypes"), XmlArrayItem("li")] public List<string> interactionDefTypes;
[XmlArray("actors"), XmlArrayItem("li")] public List<Actor> actors;
[XmlArray("animationStages"), XmlArrayItem("li")] public List<AnimationStage> animationStages;
[XmlArray("sexTypes"), XmlArrayItem("li")] public List<string> sexTypes = new List<string>();
[XmlArray("interactionDefTypes"), XmlArrayItem("li")] public List<string> interactionDefTypes = new List<string>();
[XmlArray("actors"), XmlArrayItem("li")] public List<Actor> actors = new List<Actor>();
[XmlArray("animationStages"), XmlArrayItem("li")] public List<AnimationStage> animationStages = new List<AnimationStage>();
[XmlIgnore] public int animationTimeTicks { get { return animationStages.Sum(x => x.playTimeTicks); } }
[XmlIgnore] public int animationTimeTicksQuick { get { return animationStages.Sum(x => x.playTimeTicksQuick); } }
// Data serialization control
public bool ShouldSerializesexTypes() { return sexTypes.NotNullOrEmpty(); }
public bool ShouldSerializeinteractionDefTypes() { return interactionDefTypes.NotNullOrEmpty(); }
public bool ShouldSerializeactors() { return actors.NotNullOrEmpty(); }
public bool ShouldSerializeanimationStages() { return animationStages.NotNullOrEmpty(); }
// Data helper functions
[XmlIgnore] public string DefName
{
get { return defName != null && defName != "" ? defName : "newAnimation"; }
set { defName = value; EventsManager.OnAnimationDefChanged(); }
}
[XmlIgnore] public string Label
{
get { return label != null && label != "" ? label : "newAnimation"; }
set { label = value; EventsManager.OnAnimationDefChanged(); }
}
[XmlIgnore] public List<string> SexTypes
{
get { return sexTypes.NullOrEmpty() ? sexTypes = new List<string>() : sexTypes; }
set { sexTypes = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
}
[XmlIgnore] public List<string> InteractionDefTypes
{
get { return interactionDefTypes.NullOrEmpty() ? interactionDefTypes = new List<string>() : interactionDefTypes; }
set { interactionDefTypes = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
}
[XmlIgnore] public List<Actor> Actors
{
get { return actors.NullOrEmpty() ? actors = new List<Actor>() : actors; }
set { actors = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
}
[XmlIgnore] public List<AnimationStage> AnimationStages
{
get { if (animationStages.NullOrEmpty()){ animationStages = new List<AnimationStage>(); } return animationStages; }
set { animationStages = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationDefChanged(); }
}
// Local data
[XmlIgnore] public int animationTimeTicks { get { return AnimationStages.Sum(x => x.PlayTimeTicks); } }
[XmlIgnore] public int animationTimeTicksQuick { get { return AnimationStages.Sum(x => x.PlayTimeTicksQuick); } }
// Methods
public void Initialize()
{
foreach (AnimationStage stage in animationStages)
foreach (AnimationStage stage in AnimationStages)
{ stage.Initialize(); }
}
public void ValidateData()
public void AddActor()
{
sexTypes = sexTypes.Intersect(Tags.sexTypes.Concat(CustomTags.sexTypes))?.ToList();
interactionDefTypes = interactionDefTypes.Intersect(Tags.interactionDefTypes.Concat(CustomTags.interactionDefTypes))?.ToList();
if (Workspace.animationDef.Actors.Count >= 8)
{
Debug.LogWarning("Cannot add actor - the animation can only contain a maximum of eight actors.");
return;
}
Actor actor = new Actor();
Actors.Add(actor);
Workspace.ActorID = Workspace.animationDef.Actors.Count - 1;
foreach (AnimationStage stage in Workspace.animationDef.AnimationStages)
{ stage.AddAnimationClip(Workspace.ActorID); }
EventsManager.OnActorCountChanged();
Workspace.RecordEvent("Actor addition");
}
public void RemoveActor()
{
if (Workspace.animationDef.Actors.Count == 1)
{
Debug.LogWarning("Cannot delete actor - the animation must contain at least one actor.");
return;
}
foreach (AnimationStage stage in Workspace.animationDef.AnimationStages)
{ stage.AnimationClips.RemoveAt(Workspace.ActorID); }
Workspace.animationDef.Actors.RemoveAt(Workspace.ActorID);
Workspace.ActorID--;
EventsManager.OnActorCountChanged();
Workspace.RecordEvent("Actor deletion");
}
public void AddAnimationStage()
{
AnimationStage stage = new AnimationStage();
AnimationStages.Add(stage);
foreach (Actor actor in Workspace.animationDef.Actors)
{ stage.AddAnimationClip(actor.GetActorID()); }
Initialize();
Workspace.RecordEvent("Stage addition");
}
public void CloneAnimationStage()
{
AnimationStage stage = Workspace.GetCurrentAnimationStage().Copy();
stage.StageName += " (Clone)";
stage.Initialize();
Workspace.animationDef.AnimationStages.Insert(Workspace.StageID + 1, stage);
Workspace.RecordEvent("Stage clone");
}
public void MoveAnimationStage(int startIndex, int delta)
{
if (startIndex + delta < 0 || startIndex + delta >= AnimationStages.Count) return;
AnimationStage stage = AnimationStages[startIndex];
AnimationStages[startIndex] = Workspace.animationDef.AnimationStages[startIndex + delta];
AnimationStages[startIndex + delta] = stage;
Workspace.StageID = startIndex + delta;
Workspace.RecordEvent("Stage move");
}
public void RemoveAnimationStage()
{
if (Workspace.animationDef.AnimationStages.Count == 1)
{
Debug.LogWarning("Cannot delete animation stage - the animation must contain at least one animation stage.");
return;
}
AnimationStages.RemoveAt(Workspace.StageID);
Workspace.StageID--;
Workspace.RecordEvent("Stage deletion");
}
// Pre-save / post-load
public void OnPreSave()
{
SexTypes = SexTypes.Intersect(DefaultTags.sexTypes.Concat(CustomTags.sexTypes))?.ToList();
InteractionDefTypes = InteractionDefTypes.Intersect(DefaultTags.interactionDefTypes.Concat(CustomTags.interactionDefTypes))?.ToList();
}
public void OnPostLoad() { }
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9cca833a1987a2749aa6e4d640d32266
guid: 37ec1f5f150928e42bda942fe97046b9
MonoImporter:
externalObjects: {}
serializedVersion: 2

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;
@ -8,68 +9,145 @@ namespace RimWorldAnimationStudio
{
public class AnimationStage
{
public string stageName = "NewStage";
public int stageIndex = 0;
public int playTimeTicks = 0;
public int playTimeTicksQuick = 0;
public bool isLooping = false;
// Data to/from animationDef
[SerializeField] private string stageName;
[SerializeField] private int? playTimeTicks;
[SerializeField] private int? playTimeTicksQuick;
[SerializeField] private bool? isLooping;
[SerializeField, XmlArray("animationClips"), XmlArrayItem("li")] public List<PawnAnimationClip> animationClips;
[XmlArray("animationClips"), XmlArrayItem("li")] public List<PawnAnimationClip> animationClips = new List<PawnAnimationClip>();
// Data serialization control
public bool ShouldSerializeanimationClips() { return animationClips.NotNullOrEmpty(); }
// Data helper functions
[XmlIgnore] public string StageName
{
get { return stageName != null && stageName != "" ? stageName : "NewStage"; }
set { stageName = value; EventsManager.OnAnimationStageChanged(this); }
}
[XmlIgnore] public int PlayTimeTicks
{
get { return playTimeTicks.HasValue ? playTimeTicks.Value : 0; }
set { playTimeTicks = value; EventsManager.OnAnimationStageChanged(this); }
}
[XmlIgnore] public int PlayTimeTicksQuick
{
get { return playTimeTicksQuick.HasValue ? playTimeTicksQuick.Value : 0; }
set { playTimeTicksQuick = value; EventsManager.OnAnimationStageChanged(this); }
}
[XmlIgnore] public bool IsLooping
{
get { return isLooping == true; }
set { isLooping = value; EventsManager.OnAnimationStageChanged(this); }
}
[XmlIgnore] public List<PawnAnimationClip> AnimationClips
{
get { return animationClips.NullOrEmpty() ? animationClips = new List<PawnAnimationClip>() : animationClips; }
set { animationClips = value.NotNullOrEmpty() ? value : null; EventsManager.OnAnimationStageChanged(this); }
}
// Local data
[XmlIgnore] public int stageWindowSize = -1;
// Methods
public void Initialize()
{
foreach (PawnAnimationClip clip in animationClips)
foreach (PawnAnimationClip clip in AnimationClips)
{
clip.BuildSimpleCurves();
// Select playTimeTicks as longest playtime of all the animations
if (clip.duration > playTimeTicks)
{ playTimeTicks = clip.duration; }
if (clip.duration > PlayTimeTicks)
{ PlayTimeTicks = clip.duration; }
}
}
public void ValidateData()
{
// Sort keyframes by atTick
foreach (PawnAnimationClip clip in animationClips)
{ clip.keyframes = clip.keyframes.OrderBy(x => x.atTick).ToList(); }
PlayTimeTicksQuick = PlayTimeTicks;
}
public int GetStageID()
{
if (Workspace.animationDef == null) return -1;
return Workspace.animationDef.animationStages.IndexOf(this);
return Workspace.animationDef.AnimationStages.IndexOf(this);
}
public bool MakeNew()
public void StretchStageWindow(int newStageWindowSize)
{
if (Workspace.animationDef == null)
{ Debug.LogWarning("Cannot make new animation stage - there is no AnimationDef"); return false; }
float scale = (float)newStageWindowSize / Workspace.StageWindowSize;
Workspace.animationDef.animationStages.Add(this);
foreach (Actor actor in Workspace.animationDef.actors)
foreach (PawnAnimationClip clip in Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips)
{
PawnAnimationClip clip = new PawnAnimationClip();
foreach (PawnKeyframe keyframe in clip.Keyframes)
{
keyframe.TickDuration = Mathf.RoundToInt(keyframe.TickDuration * scale);
keyframe.atTick = null;
}
if (clip.MakeNew(actor.GetActorID()))
{ animationClips.Add(clip); }
clip.BuildSimpleCurves();
}
}
public void ResizeStageWindow(int newStageWindowSize)
{
Workspace.GetCurrentAnimationStage().stageWindowSize = newStageWindowSize;
Workspace.GetCurrentAnimationStage().PlayTimeTicks = newStageWindowSize * Workspace.stageLoopsNormal;
Workspace.GetCurrentAnimationStage().PlayTimeTicksQuick = newStageWindowSize * Workspace.stageLoopsQuick;
}
public void AddAnimationClip(int actorID = -1)
{
PawnAnimationClip clip = new PawnAnimationClip();
PawnKeyframe lastkeyframe = null;
if (actorID >= 0)
{ lastkeyframe = Workspace.GetPawnAnimationClip(actorID)?.Keyframes?.Last(); }
if (lastkeyframe != null)
{
PawnKeyframe keyframeA = lastkeyframe.Copy();
keyframeA.atTick = null;
keyframeA.TickDuration = Constants.defaultAnimationClipLength - 1;
keyframeA.GenerateKeyframeID(actorID);
clip.Keyframes.Add(keyframeA);
PawnKeyframe keyframeB = lastkeyframe.Copy();
keyframeB.atTick = null;
keyframeB.TickDuration = 1;
keyframeB.GenerateKeyframeID(actorID);
clip.Keyframes.Add(keyframeB);
}
Initialize();
playTimeTicksQuick = playTimeTicks;
else
{
PawnKeyframe keyframeA = new PawnKeyframe();
keyframeA.TickDuration = Constants.defaultAnimationClipLength - 1;
return true;
clip.Keyframes.Add(keyframeA);
PawnKeyframe keyframeB = new PawnKeyframe();
clip.Keyframes.Add(keyframeB);
}
clip.BuildSimpleCurves();
}
// Pre-save / post-load
public void OnPreSave()
{
foreach (PawnAnimationClip clip in AnimationClips)
{ clip.Keyframes = clip.Keyframes.OrderBy(x => x.atTick).ToList(); }
}
public void OnPostLoad()
{
foreach (PawnAnimationClip clip in animationClips)
{
clip.OnPostLoad();
}
foreach (PawnAnimationClip clip in AnimationClips)
{ clip.OnPostLoad(); }
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4d62c568c0ad7ea4ba7ddd3b9aa6d0e9
guid: 9270822a570a06f41afa00e169af500c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -1,6 +1,5 @@
using System.Reflection;
using UnityEngine;
//using Microsoft.Toolkit.Uwp.UI;
namespace RimWorldAnimationStudio
{
@ -12,6 +11,11 @@ namespace RimWorldAnimationStudio
public string Hulk;
public string Fat;
public bool AllOffsetsEmpty()
{
return string.IsNullOrEmpty(Male) && string.IsNullOrEmpty(Female) && string.IsNullOrEmpty(Thin) && string.IsNullOrEmpty(Hulk) && string.IsNullOrEmpty(Fat);
}
public void SetOffset(string bodyType, Vector2 bodyOffset)
{
FieldInfo bodyTypeOffsetInfo = typeof(BodyTypeOffset).GetField(bodyType);

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6db04cc11995126429fb12578d6620d7
guid: 1dfd90f8aa6d0e04086e2b4983d42ab6
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace RimWorldAnimationStudio
{
public class ButtonWithKeyCode : Button
{
public KeyCode keyCode;
public void Update()
{
if (Input.GetKeyDown(keyCode))
{ onClick.Invoke(); }
}
}
}

View file

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

View file

@ -1,24 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RimWorldAnimationStudio
{
public class Chaser : MonoBehaviour
{
public GameObject target;
public bool chaseAlongX = true;
public bool chaseAlongY = false;
void Update()
{
if (target == null)
{ return; }
float x = chaseAlongX ? target.transform.position.x : transform.position.x;
float y = chaseAlongY ? target.transform.position.y : transform.position.y;
transform.position = new Vector3(x, y, 0f);
}
}
}

View file

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

View file

@ -1,13 +0,0 @@
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
namespace RimWorldAnimationStudio
{
[XmlRoot("Defs", IsNullable = false)]
public class Defs
{
[XmlElement("Rimworld_Animations.AnimationDef")]
public List<AnimationDef> animationDefs = new List<AnimationDef>();
}
}

View file

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

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d3f1c8d8d1b51a147b17f5510eebb2cf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,21 +0,0 @@
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
namespace RimWorldAnimationStudio
{
public class AddonKeyframe
{
public string addonName;
public float posX;
public float posZ;
public float rotation;
public AddonKeyframe() { }
public AddonKeyframe(string addonName)
{
this.addonName = addonName;
}
}
}

View file

@ -1,19 +0,0 @@
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
namespace RimWorldAnimationStudio
{
public class Keyframe
{
[XmlIgnore] public int? atTick;
public int tickDuration = 1;
public string soundEffect;
public List<string> tags = new List<string>();
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
public virtual void ValidateData() { }
}
}

View file

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

View file

@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class PawnKeyframe : Keyframe
{
public float bodyAngle;
public float headAngle;
public float headBob;
public float bodyOffsetX;
public float bodyOffsetZ;
public float headFacing = 2;
public float bodyFacing = 2;
public float genitalAngle;
public bool? quiver;
[XmlArray("addonKeyframes"), XmlArrayItem("li")] public List<AddonKeyframe> _addonKeyframes = new List<AddonKeyframe>();
[XmlIgnore] public List<AddonKeyframe> addonKeyframes = new List<AddonKeyframe>();
[XmlIgnore] public int keyframeID;
[XmlIgnore] public int actorID = -1;
public bool ShouldSerializegenitalAngle() { return genitalAngle != 0; }
public bool ShouldSerializequiver() { return quiver != null; }
public override void ValidateData()
{
soundEffect = Tags.soundDefs.Concat(CustomTags.soundDefs).Contains(soundEffect) ? soundEffect : null;
_addonKeyframes.Clear();
foreach (AddonKeyframe addonKeyframe in addonKeyframes)
{
ActorAddon addon = Workspace.Instance.GetAnimationClipThatOwnsKeyframe(keyframeID, out int clipID).GetActorAddon(addonKeyframe.addonName);
if (addon.render)
{ _addonKeyframes.Add(addonKeyframe.Copy()); }
}
}
public void OnPostLoad()
{
addonKeyframes.Clear();
foreach (AddonKeyframe addonKeyframe in _addonKeyframes)
{
addonKeyframes.Add(addonKeyframe.Copy());
}
}
public void GenerateKeyframeID(int actorID)
{
this.actorID = actorID;
int _keyframeID = UnityEngine.Random.Range(100000, 1000000);
if (Workspace.animationDef.animationStages.Any(x => x.animationClips.Any(y => y.keyframes.Any(z => z.keyframeID == _keyframeID))))
{
GenerateKeyframeID(actorID);
return;
}
keyframeID = _keyframeID;
}
public bool HasValidKeyframeID()
{ return keyframeID >= 100000 && keyframeID < 1000000; }
public KeyframeSlider GetKeyframeSlider()
{
return Selectable.allSelectablesArray.FirstOrDefault(x => x.GetComponent<KeyframeSlider>()?.keyframeID == keyframeID)?.GetComponent< KeyframeSlider>();
}
public AddonKeyframe GetAddonKeyframe(string addonName)
{
return addonKeyframes.FirstOrDefault(x => x.addonName == addonName);
}
}
}

View file

@ -1,13 +0,0 @@
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
namespace RimWorldAnimationStudio
{
public class ThingKeyframe : Keyframe
{
public float positionX;
public float positionZ;
public float rotation;
}
}

View file

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

View file

@ -0,0 +1,390 @@
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; EventsManager.OnPawnAnimationClipChanged(this); }
}
[XmlIgnore] public List<PawnKeyframe> Keyframes
{
get { return keyframes.NullOrEmpty() ? keyframes = new List<PawnKeyframe>() : keyframes; }
set { keyframes = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnAnimationClipChanged(this); }
}
[XmlIgnore]
public List<string> Tags
{
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
set { tags = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnAnimationClipChanged(this); }
}
// 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()
{
// 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
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];
if (keyframe.atTick.HasValue)
{
if (keyframe.HasValidKeyframeID() == false)
{ keyframe.GenerateKeyframeID(Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips.IndexOf(this)); }
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);
}
if (i + 1 < Keyframes.Count)
{ Keyframes[i].TickDuration = Keyframes[i + 1].atTick.Value - Keyframes[i].atTick.Value; }
}
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(string addonName, float scale = 1f)
{
if (Addons.Any(x => x.AddonName == addonName) == false)
{
Addons.Add(new ActorAddon(addonName, scale));
}
foreach (PawnKeyframe keyframe in Keyframes)
{
if (keyframe.AddonKeyframes.Any(x => x.AddonName == addonName) == false)
{ keyframe.AddonKeyframes.Add(new AddonKeyframe(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()
{
if (Workspace.animationDef == null) return -1;
return Workspace.GetCurrentAnimationStage().AnimationClips.IndexOf(this);
}
public void AddPawnKeyframe()
{
PawnAnimationClip clip = Workspace.GetCurrentPawnAnimationClip();
List<PawnKeyframe> keyframes = clip?.Keyframes;
if (clip == null || 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 % 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 = (int)clip.HeadFacing.Evaluate(clipPercent);
keyframe.BodyFacing = (int)clip.BodyFacing.Evaluate(clipPercent);
keyframe.GenitalAngle = clip.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); }
clip.BuildSimpleCurves();
Workspace.RecordEvent("Keyframe addition");
}
public void ClonePawnKeyframe()
{
List<PawnKeyframe> keyframesToClone = Workspace.GetPawnKeyframesByID(Workspace.keyframeID);
foreach (PawnKeyframe keyframe in keyframesToClone)
{
PawnAnimationClip clip = Workspace.GetAnimationClipThatOwnsKeyframe(keyframe.keyframeID, out int clipID);
if (clip == null)
{ Debug.LogWarning("Cannot clone pawn keyframe - no clip owns this keyframe"); continue; }
if (clip.Keyframes.FirstOrDefault(x => x.atTick == Workspace.StageTick) != null)
{ Debug.LogWarning("Cannot clone pawn keyframe - a keyframe already exists at this tick"); return; }
PawnKeyframe cloneFrame = keyframe.Copy();
cloneFrame.GenerateKeyframeID(clipID);
cloneFrame.atTick = Workspace.StageTick;
PawnKeyframe nextKeyframe = clip.Keyframes.FirstOrDefault(x => x.atTick > Workspace.StageTick);
if (nextKeyframe != null)
{ clip.Keyframes.Insert(clip.Keyframes.IndexOf(nextKeyframe), cloneFrame); }
else
{ clip.Keyframes.Add(cloneFrame); }
clip.BuildSimpleCurves();
}
Workspace.RecordEvent("Keyframe clone");
}
public void CopyPawnKeyframes()
{
Workspace.copiedKeyframes.Clear();
List<PawnKeyframe> keyframesToClone = Workspace.GetPawnKeyframesByID(Workspace.keyframeID);
foreach (PawnKeyframe keyframe in keyframesToClone)
{ Workspace.copiedKeyframes.Add(keyframe.Copy()); }
}
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.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];
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();
}
if (originalWindowSize != Workspace.StageWindowSize)
{
Workspace.GetCurrentAnimationStage().StretchStageWindow(originalWindowSize);
Workspace.GetCurrentAnimationStage().ResizeStageWindow(originalWindowSize);
}
Workspace.RecordEvent("Keyframe pasted");
}
public void RemovePawnKeyframe()
{
foreach (int keyframeID in Workspace.keyframeID)
{
if (Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID, out int clipID) != null)
{ RemovePawnKeyframe(clipID, keyframeID); }
}
}
public void RemovePawnKeyframe(int actorID, int keyframeID, bool force = false)
{
PawnKeyframe keyframe = Workspace.GetPawnKeyframe(actorID, keyframeID);
PawnAnimationClip clip = Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips[actorID];
if (keyframe == null || clip == null) return;
if (keyframe.atTick == Constants.minTick && force == false)
{ Debug.LogWarning("Cannot delete key frame - the first key frame of an animation clip cannot be deleted"); return; }
if (clip.Keyframes.Count <= 2 && force == false)
{ Debug.LogWarning("Cannot delete key frame - an animation clip must have two or more keyframes"); return; }
clip.Keyframes.Remove(keyframe);
clip.BuildSimpleCurves();
Workspace.RecordEvent("Keyframe deletion");
}
public float GetStageTickPercentage()
{
return (float)(Workspace.StageTick % duration) / duration;
}
// Pre-save / post-load
public void OnPreSave()
{
foreach (ActorAddon addon in Addons)
{
if (addon.Render)
{ addons.Add(addon); }
}
}
public void OnPostLoad()
{
Addons = addons.Copy();
foreach (PawnKeyframe keyframe in Keyframes)
{
keyframe.OnPostLoad();
}
AddActorAddon("left hand", 0.667f);
AddActorAddon("right hand", 0.667f);
AddActorAddon("dildo");
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b7f2dc95148378445919ef3ed8705c5d
guid: bd5a477338567fb4cbb26b913a52ca65
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class PawnKeyframe
{
// Data to/from animationDef
public float? bodyAngle;
public float? headAngle;
public float? headBob;
public float? bodyOffsetX;
public float? bodyOffsetZ;
public int? headFacing;
public int? bodyFacing;
public float? genitalAngle;
public bool? quiver;
public int? tickDuration;
public string soundEffect;
public List<string> tags;
[XmlArray("addonKeyframes"), XmlArrayItem("li")] public List<AddonKeyframe> addonKeyframes;
// Data serialization control
public bool ShouldSerializebodyAngle() { return bodyAngle.HasValue && bodyAngle.Value != 0f; }
public bool ShouldSerializeheadAngle() { return headAngle.HasValue && headAngle.Value != 0f; }
public bool ShouldSerializeheadBob() { return headBob.HasValue && headBob.Value != 0f; }
public bool ShouldSerializebodyOffsetX() { return bodyOffsetX.HasValue && bodyOffsetX.Value != 0f; }
public bool ShouldSerializebodyOffsetZ() { return bodyOffsetZ.HasValue && bodyOffsetZ.Value != 0f; }
public bool ShouldSerializegenitalAngle() { return genitalAngle.HasValue && genitalAngle.Value != 0f; }
public bool ShouldSerializequiver() { return quiver == true; }
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
public bool ShouldSerializeaddonKeyframes() { return addonKeyframes.NotNullOrEmpty(); }
// Data helper functions
[XmlIgnore] public float BodyAngle
{
get { return bodyAngle.HasValue ? bodyAngle.Value : 0f; }
set { bodyAngle = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public float HeadAngle
{
get { return headAngle.HasValue ? headAngle.Value : (float)(headAngle = 0f); }
set { headAngle = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public float HeadBob
{
get { return headBob.HasValue ? headBob.Value : (float)(headBob = 0f); }
set { headBob = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public float BodyOffsetX
{
get { return bodyOffsetX.HasValue ? bodyOffsetX.Value : (float)(bodyOffsetX = 0f); }
set { bodyOffsetX = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public float BodyOffsetZ
{
get { return bodyOffsetZ.HasValue ? bodyOffsetZ.Value : (float)(bodyOffsetZ = 0f); }
set { bodyOffsetZ = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public int HeadFacing
{
get { return headFacing.HasValue ? headFacing.Value : (int)(headFacing = 2); }
set { headFacing = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public int BodyFacing
{
get { return bodyFacing.HasValue ? bodyFacing.Value : (int)(bodyFacing = 2); }
set { bodyFacing = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public float GenitalAngle
{
get { return genitalAngle.HasValue ? genitalAngle.Value : (float)(genitalAngle = 0f); }
set { genitalAngle = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public bool Quiver
{
get { return quiver == true; }
set { quiver = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public int TickDuration
{
get { return tickDuration.HasValue ? tickDuration.Value : (int)(tickDuration = 0); }
set { tickDuration = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public string SoundEffect
{
get { return soundEffect; }
set { soundEffect = value; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public List<string> Tags
{
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
set { tags = value.NotNullOrEmpty() ? value : null; EventsManager.OnPawnKeyframeChanged(this); }
}
[XmlIgnore] public List<AddonKeyframe> AddonKeyframes
{
get { return addonKeyframes.NullOrEmpty() ? addonKeyframes = new List<AddonKeyframe>() : addonKeyframes; }
set { addonKeyframes = value.NotNullOrEmpty()? value : null; EventsManager.OnPawnKeyframeChanged(this); }
}
// Local data
[XmlIgnore] public int keyframeID;
[XmlIgnore] public int actorID = -1;
[XmlIgnore] public int? atTick;
// Methods
public void GenerateKeyframeID(int actorID)
{
this.actorID = actorID;
int _keyframeID = UnityEngine.Random.Range(100000, 1000000);
if (Workspace.animationDef.AnimationStages.Any(x => x.AnimationClips.Any(y => y.Keyframes.Any(z => z.keyframeID == _keyframeID))))
{
GenerateKeyframeID(actorID);
return;
}
keyframeID = _keyframeID;
}
public bool HasValidKeyframeID()
{ return keyframeID >= 100000 && keyframeID < 1000000; }
public KeyframeSlider GetKeyframeSlider()
{
return Selectable.allSelectablesArray.FirstOrDefault(x => x.GetComponent<KeyframeSlider>()?.keyframeID == keyframeID)?.GetComponent< KeyframeSlider>();
}
public AddonKeyframe GetAddonKeyframe(string addonName)
{
return AddonKeyframes.FirstOrDefault(x => x.AddonName == addonName);
}
// Pre-save / post-load
public void OnPreSave()
{
SoundEffect = DefaultTags.soundDefs.Concat(CustomTags.soundDefs).Contains(SoundEffect) ? SoundEffect : null;
addonKeyframes.Clear();
foreach (AddonKeyframe addonKeyframe in AddonKeyframes)
{
ActorAddon addon = Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID, out int clipID).GetActorAddon(addonKeyframe.AddonName);
if (addon.Render)
{ addonKeyframes.Add(addonKeyframe.Copy()); }
}
}
public void OnPostLoad()
{
AddonKeyframes.Clear();
foreach (AddonKeyframe addonKeyframe in addonKeyframes)
{
AddonKeyframes.Add(addonKeyframe.Copy());
}
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: fe4a7d5f472a25945bac2d1892a4e2fa
guid: c8ced38490f6b174984453dc3336a543
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -9,8 +9,9 @@ using UnityEngine;
namespace RimWorldAnimationStudio
{
[Serializable]
public class AlienRaceDef
public class PawnRaceDef
{
// Local data
public string defName;
public bool isHumanoid = true;
public float scale = 1f;
@ -18,13 +19,15 @@ namespace RimWorldAnimationStudio
public List<MultiDirectionalGraphic> bodyTypeGraphics = new List<MultiDirectionalGraphic>();
public MultiDirectionalGraphic headGraphics = new MultiDirectionalGraphic();
public AlienRaceDef() { }
// Constructors
public PawnRaceDef() { }
public AlienRaceDef(string defName)
public PawnRaceDef(string defName)
{
this.defName = defName;
}
// Methods
public Sprite GetHeadGraphic(CardinalDirection facing)
{
if (HasValidHeadGraphicPath(facing) == false)

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: c4a44c0d3b9937c48b2ae8501126227e
guid: 187aef38ea296184b93265071536969c
MonoImporter:
externalObjects: {}
serializedVersion: 2

View file

@ -5,21 +5,25 @@ using UnityEngine;
namespace RimWorldAnimationStudio
{
[Serializable]
public class AlienRaceOffset
public class PawnRaceOffset
{
// Local data
public string defName = "Human";
public string offset = "(0, 0)";
// SHoulda serialize
public bool ShouldSerializedefName() { return OffsetIsZero() == false; }
public bool ShouldSerializeoffset() { return OffsetIsZero() == false; }
public AlienRaceOffset() { }
// Constructors
public PawnRaceOffset() { }
public AlienRaceOffset(string defName)
public PawnRaceOffset(string defName)
{
this.defName = defName;
}
// Methods
public void SetOffset(Vector2 raceOffset)
{
offset = "(" + raceOffset.x + ", " + raceOffset.y + ")";

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5b8a2db320a85494c882518c143b73f7
guid: 24eafaf092974414ca90bfd4a8d2e4ba
MonoImporter:
externalObjects: {}
serializedVersion: 2