This commit is contained in:
AbstractConcept 2022-12-09 11:40:08 -06:00
parent 0828ecd037
commit 2998865184
9821 changed files with 90 additions and 90 deletions

View file

@ -0,0 +1,250 @@
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class AnimationController : Singleton<AnimationController>
{
[Header("Object references")]
public Transform animationTimelines;
public Transform actorBodies;
public Dropdown stageLoopDropdown;
[Header("Prefabs")]
public ActorBody actorBodyPrefab;
public GameObject animationTimelinePrefab;
// Private timing variables
private float timeSinceLastUpdate = 0;
private int loopCount = 0;
private float playBackSpeed = 1f;
public void Start()
{
EventsManager.onAnimationChanged.AddListener(delegate{ Initialize(); });
EventsManager.onStageIDChanged.AddListener(delegate { Initialize(); });
EventsManager.onActorCountChanged.AddListener(delegate { Initialize(); });
}
public void Update()
{
if (Workspace.animationDef == null) return;
// Update stage tick / loop count if animating
if (Workspace.IsAnimating)
{
timeSinceLastUpdate += Time.deltaTime;
if (timeSinceLastUpdate < 1 / (playBackSpeed * 60f))
{ return; }
timeSinceLastUpdate -= 1 / (playBackSpeed * 60f);
Workspace.StageTick += 1;
if (Workspace.StageTick >= Workspace.StageWindowSize)
{
switch (stageLoopDropdown.value)
{
case 1: Workspace.StageTick = Constants.minTick; break;
case 2: UpdateLoopCount(Workspace.GetCurrentAnimationStage().StageLoopsNormal); break;
case 3: UpdateLoopCount(Workspace.GetCurrentAnimationStage().StageLoopsQuick); break;
default: Workspace.IsAnimating = false; break;
}
}
}
// Update animation preview
UpdateAnimationPreview();
}
public void UpdateLoopCount(int stageLoops)
{
loopCount++;
Workspace.StageTick = Constants.minTick;
if (loopCount >= stageLoops)
{
if (Workspace.StageID >= Workspace.animationDef.AnimationStages.Count - 1)
{ Workspace.IsAnimating = false; }
else
{
Workspace.StageID++;
loopCount = 0;
}
}
}
public void UpdateAnimationPreview()
{
if (Workspace.animationDef == null || Workspace.StageID >= Workspace.animationDef?.AnimationStages.Count) return;
List<ActorBody> actorBodiesList = actorBodies.GetComponentsInChildren<ActorBody>().ToList();
for (int actorID = 0; actorID < Workspace.animationDef.actors.Count; actorID++)
{
// Get the current actor
Actor actor = Workspace.GetActor(actorID);
// Get their animation clip and rebuild it to ensure it is up to date
PawnAnimationClip clip = Workspace.GetPawnAnimationClip(actorID);
clip.BuildSimpleCurves();
// Get flags
bool quiver = Workspace.IsAnimating && Workspace.GetCurrentOrPreviousKeyframe(actorID).Quiver == true;
bool requiresGenitals = actor.RequiredGenitals.Any(x => x == "Penis") || actor.IsFucking;
// Get clip percentage
float clipPercent = (float)(Workspace.StageTick % clip.duration) / clip.duration;
if (Workspace.StageTick > Constants.minTick && Workspace.StageTick == clip.duration) clipPercent = 1f;
if (Workspace.GetCurrentAnimationStage().IsLooping == false)
{ clipPercent = (float)Workspace.StageTick / clip.duration; }
// Get the actors race and body type
PawnRaceDef pawnRaceDef = actor.GetPawnRaceDef();
string bodyType = pawnRaceDef.isHumanoid ? actor.bodyType : "None";
// Evalute the actor's basic offsets and facing
ActorPosition actorPosition = actor.GetCurrentPosition();
float bodyPosX = actorPosition.bodyOffsetX;
float bodyPosZ = actorPosition.bodyOffsetZ;
float bodyAngle = actorPosition.bodyAngle + (quiver ? UnityEngine.Random.value * 2f - 1f : 0f);
float headBob = actorPosition.headBob;
float headAngle = actorPosition.headAngle;
float genitalAngle = actorPosition.genitalAngle;
int bodyFacing = actorPosition.bodyFacing;
int headFacing = actorPosition.headFacing;
// Convert values to world coordinates
Vector3 bodyPos = new Vector3(bodyPosX, bodyPosZ, 0f);
Vector3 headPos = new Vector3(0f, headBob, 0f) + PawnUtility.BaseHeadOffsetAt(bodyType, bodyFacing);
Vector3 appendagePos = PawnUtility.GroinOffsetAt(bodyType, bodyFacing);
// Update actorbody
ActorBody actorBody = actorBodiesList[actorID];
ActorBodyPart actorBodypart;
actorBody.transform.localScale = new Vector3(pawnRaceDef.scale, pawnRaceDef.scale, pawnRaceDef.scale);
// Body
actorBody.transform.position = bodyPos + actor.GetFinalTransformOffset();
actorBody.transform.eulerAngles = new Vector3(0, 0, -bodyAngle);
actorBody.bodyRenderer.sortingLayerName = clip.Layer;
actorBody.bodyRenderer.sprite = pawnRaceDef.GetBodyTypeGraphic((CardinalDirection)bodyFacing, bodyType);
actorBody.bodyRenderer.flipX = bodyFacing == 3;
actorBody.bodyRenderer.gameObject.SetActive(actorBody.bodyRenderer.sprite != null);
// Head
actorBodypart = actorBody.GetActorBodyPart("head");
actorBodypart.transform.localPosition = headPos;
actorBodypart.transform.eulerAngles = new Vector3(0, 0, -headAngle);
actorBodypart.bodyPartRenderer.sortingLayerName = clip.Layer;
actorBodypart.bodyPartRenderer.sortingOrder = bodyFacing == 0 ? -1 : 1;
actorBodypart.bodyPartRenderer.sprite = pawnRaceDef.isHumanoid ? pawnRaceDef.GetHeadGraphic((CardinalDirection)headFacing) : null;
actorBodypart.bodyPartRenderer.flipX = headFacing == 3;
actorBodypart.gameObject.SetActive(actorBodypart.bodyPartRenderer.sprite != null);
// Appendage
actorBodypart = actorBody.GetActorBodyPart("appendage");
actorBodypart.transform.localPosition = new Vector3(appendagePos.x, appendagePos.z, 0f);
actorBodypart.transform.eulerAngles = new Vector3(0, 0, -genitalAngle);
actorBodypart.bodyPartRenderer.sortingLayerName = clip.Layer;
actorBodypart.bodyPartRenderer.sprite = requiresGenitals && pawnRaceDef.isHumanoid ? Resources.Load<Sprite>("Textures/Humanlike/Appendages/Appendage" + bodyFacing) : null;
//actorBody.appendageRenderer.flipX = bodyFacing == 3;
actorBodypart.gameObject.SetActive(actorBodypart.bodyPartRenderer.sprite != null);
// Add-ons
foreach (ActorAddon addon in clip.Addons)
{
actorBodypart = actorBody.GetActorBodyPart(addon.AddonName);
if (actorBodypart == null) continue;
ActorBody anchoringActorBody = actorBodies.GetComponentsInChildren<ActorBody>()?.FirstOrDefault(x => x.actorID == addon.AnchoringActor);
Vector3 anchor = PawnUtility.GetBodyPartAnchor(anchoringActorBody, addon.anchorName);
actorBodypart.transform.position = anchor + new Vector3(addon.PosX.Evaluate(clipPercent), addon.PosZ.Evaluate(clipPercent), 0);
actorBodypart.transform.eulerAngles = new Vector3(0, 0, -addon.Rotation.Evaluate(clipPercent));
actorBodypart.bodyPartRenderer.sortingLayerName = addon.Layer;
//actorBodypart.bodyPartRenderer.sprite
actorBodypart.gameObject.SetActive(addon.Render);
}
// Play sounds
if (Workspace.IsAnimating)
{
PawnKeyframe keyframe = clip.keyframes.FirstOrDefault(x => x.atTick == (int)(clip.duration * clipPercent));
if (keyframe != null && string.IsNullOrEmpty(keyframe.soundEffect) == false)
{ actorBody.GetComponent<AudioController>().PlaySound(keyframe.soundEffect); }
}
}
}
public void Initialize()
{
Debug.Log("Initializing animation preview");
int actorCount = Workspace.animationDef.Actors.Count;
int childCount = animationTimelines.GetComponentsInChildren<AnimationTimeline>().Count();
for (int actorID = 0; actorID < Mathf.Max(actorCount, childCount); actorID++)
{
// 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];
// 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);
}
}
EventsManager.OnAnimationTimelinesChanged();
}
public void Reset()
{
Workspace.IsAnimating = false;
timeSinceLastUpdate = 0;
loopCount = 0;
foreach (Transform child in transform)
{ child.gameObject.SetActive(true); }
}
}
}

View file

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

View file

@ -0,0 +1,344 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using SFB;
using System.Collections;
namespace RimWorldAnimationStudio
{
public class ApplicationManager : Singleton<ApplicationManager>
{
public DialogBox exitDialog;
public DialogBox newAnimationDialog;
public SelectAnimationDialog selectAnimationDialog;
public void Awake()
{
LoadPawnRaceDefs();
LoadActorAddonDefs();
LoadSoundDefs();
LoadAudioClips();
LoadCustomArrays();
}
public void TryToCloseApplication()
{
exitDialog.Pop();
}
public void CloseApplication()
{
Debug.Log("Exiting application");
Application.Quit();
}
public void TryToLoadAnimation()
{
var paths = StandaloneFileBrowser.OpenFilePanel("Open AnimationDef File", "", "xml", false);
if (paths == null || paths.Any() == false)
{ Debug.LogWarning("Selected file was null or invalid"); return; }
AnimationDefs defs = XmlUtility.ReadXML<AnimationDefs>(paths[0]);
try
{ defs = XmlUtility.ReadXML<AnimationDefs>(paths[0]); }
catch
{ Debug.LogError("Could not read .xml file '" + paths[0] + "' - this file either uses a schema which is outdated, incorrect, or contains empty fields"); return; }
if (defs?.animationDefs == null)
{ Debug.LogError("Selected file contains no animation data"); return; }
if (defs.animationDefs.Count == 1)
{ LoadAnimation(defs.animationDefs[0]); return; }
Workspace.animationSavePath = paths[0];
selectAnimationDialog.Initialize(defs);
selectAnimationDialog.Pop();
}
public void LoadAnimation(AnimationDef animationDef)
{
Workspace.Reset();
Workspace.animationDef = animationDef;
UpdateCustomArrays(animationDef);
RunPostLoadOperations(animationDef);
animationDef.Initialize();
Debug.Log("Loaded AnimationDef: " + animationDef.DefName);
AnimationController.Instance.Reset();
AnimationController.Instance.Initialize();
EventsManager.OnAnimationChanged();
Workspace.RecordEvent("AnimationDef loaded");
Workspace.ActorID = 0;
EventsManager.OnActorIDChanged();
}
public void RunPostLoadOperations(AnimationDef animationDef)
{
if (animationDef.animationTimeTicksQuick <= 0)
{
if (animationDef.AnimationStages.Count > 1)
{
for (int i = 0; i < animationDef.AnimationStages.Count; i++)
{
if (i == 0) continue;
animationDef.AnimationStages[i].PlayTimeTicksQuick = animationDef.AnimationStages[i].PlayTimeTicks;
}
}
else if (animationDef.AnimationStages.Count == 1)
{ animationDef.AnimationStages[0].PlayTimeTicksQuick = animationDef.AnimationStages[0].PlayTimeTicks; }
}
foreach (AnimationStage stage in animationDef.AnimationStages)
{ stage.OnPostLoad(); }
}
public void TryToSaveAnimation()
{
if (Workspace.animationDef == null)
{ return; }
string path = Workspace.animationSavePath;
if (path != null && path != "" && File.Exists(path))
{ SaveAnimation(path); }
else
{ TryToSaveAnimationAs(); }
}
public void TryToSaveAnimationAs()
{
if (Workspace.animationDef == null)
{ return; }
string defName = Workspace.animationDef.DefName != null && Workspace.animationDef.DefName != "" ? Workspace.animationDef.DefName : "newAnimationDef";
string path = StandaloneFileBrowser.SaveFilePanel("Save AnimationDef File", "", defName, "xml");
if (path != null && path != "")
{ SaveAnimation(path); }
}
public void SaveAnimation(string path)
{
AnimationDef animationDef = Workspace.animationDef.Copy();
RunPreSaveOperations(animationDef);
Debug.Log("Saving AnimationDef: " + Workspace.animationDef.DefName);
AnimationDefs defs = new AnimationDefs();
defs.animationDefs.Add(animationDef);
XmlUtility.WriteXML(defs, path);
Workspace.animationSavePath = path;
}
public void RunPreSaveOperations(AnimationDef animationDef)
{
animationDef.OnPreSave();
foreach (Actor actor in animationDef.Actors)
{ actor.OnPreSave(); }
foreach (AnimationStage stage in animationDef.AnimationStages)
{
stage.OnPreSave();
foreach (PawnAnimationClip clip in stage.AnimationClips)
{
clip.OnPreSave();
foreach (PawnKeyframe keyframe in clip.Keyframes)
{ keyframe.OnPreSave(); }
}
}
}
public void TryToMakeNewAnimation()
{
if (Workspace.animationDef == null)
{ NewAnimation(); return; }
newAnimationDialog.Pop();
}
public void NewAnimation()
{
var path = Path.Combine(Application.streamingAssetsPath, "AnimationDefs/newAnimationDef.xml");
AnimationDefs defs = XmlUtility.ReadXML<AnimationDefs>(path);
if (defs?.animationDefs == null)
{ Debug.LogError("Default animation def file contains no animation data"); return; }
LoadAnimation(defs.animationDefs[0]);
Workspace.animationSavePath = null;
}
public void SaveCustomArrays()
{
var path = Path.Combine(Application.persistentDataPath, "customTags.xml");
CustomTagsHelper helper = new CustomTagsHelper();
helper.defNames = CustomTags.defNames;
helper.bodyParts = CustomTags.bodyParts;
helper.bodyDefTypes = CustomTags.bodyDefTypes;
helper.sexTypes = CustomTags.sexTypes;
helper.interactionDefTypes = CustomTags.interactionDefTypes;
helper.soundDefs = CustomTags.soundDefs;
XmlUtility.WriteXML(helper, path);
}
public void LoadCustomArrays()
{
string path;
if (File.Exists(Path.Combine(Application.persistentDataPath, "customTags.xml")))
{ path = Path.Combine(Application.persistentDataPath, "customTags.xml"); }
else
{ path = Path.Combine(Application.streamingAssetsPath, "customTags.xml"); }
if (File.Exists(path) == false)
{ SaveCustomArrays(); return; }
CustomTagsHelper helper = XmlUtility.ReadXML<CustomTagsHelper>(path);
CustomTags.defNames = helper.defNames;
CustomTags.bodyParts = helper.bodyParts;
CustomTags.bodyDefTypes = helper.bodyDefTypes;
CustomTags.sexTypes = helper.sexTypes;
CustomTags.interactionDefTypes = helper.interactionDefTypes;
CustomTags.soundDefs = helper.soundDefs;
}
public void UpdateCustomArrays(AnimationDef animationDef)
{
CustomTags.bodyParts.AddRangeDistinct(animationDef.Actors.SelectMany(x => x.RequiredGenitals).Except(DefaultTags.bodyParts));
CustomTags.bodyDefTypes.AddRangeDistinct(animationDef.Actors.SelectMany(x => x.BodyDefTypes).Except(DefaultTags.bodyDefTypes));
CustomTags.sexTypes.AddRangeDistinct(animationDef.SexTypes.Except(DefaultTags.sexTypes));
CustomTags.interactionDefTypes.AddRangeDistinct(animationDef.InteractionDefTypes.Except(DefaultTags.interactionDefTypes));
CustomTags.soundDefs.AddRangeDistinct(animationDef.AnimationStages.SelectMany(x => x.AnimationClips.SelectMany(y => y.Keyframes.Select(z => z.SoundEffect))).Except(DefaultTags.soundDefs));
SaveCustomArrays();
}
public void LoadPawnRaceDefs()
{
string path;
if (File.Exists(Path.Combine(Application.persistentDataPath, "pawnRaceDefs.xml")))
{ path = Path.Combine(Application.persistentDataPath, "pawnRaceDefs.xml"); }
else
{ path = Path.Combine(Application.streamingAssetsPath, "pawnRaceDefs.xml"); }
PawnRaceDefs.allDefs = XmlUtility.ReadXML<List<PawnRaceDef>>(path);
SavePawnRaceDefs();
PawnRaceDefs.OnLoad();
}
public void SavePawnRaceDefs()
{
var path = Path.Combine(Application.persistentDataPath, "pawnRaceDefs.xml");
XmlUtility.WriteXML(PawnRaceDefs.allDefs, path);
}
public void LoadActorAddonDefs()
{
string path;
if (File.Exists(Path.Combine(Application.persistentDataPath, "actorAddonDefs.xml")))
{ path = Path.Combine(Application.persistentDataPath, "actorAddonDefs.xml"); }
else
{ path = Path.Combine(Application.streamingAssetsPath, "actorAddonDefs.xml"); }
ActorAddonDefs.allDefs = XmlUtility.ReadXML<List<ActorAddonDef>>(path);
SaveActorAddonDefs();
ActorAddonDefs.OnLoad();
}
public void SaveActorAddonDefs()
{
}
public void LoadSoundDefs()
{
string path;
if (File.Exists(Path.Combine(Application.persistentDataPath, "soundDefs.xml")))
{ path = Path.Combine(Application.persistentDataPath, "soundDefs.xml"); }
else
{ path = Path.Combine(Application.streamingAssetsPath, "soundDefs.xml"); }
SoundDefs.allDefs = XmlUtility.ReadXML<List<SoundDef>>(path);
SaveSoundDefs();
SoundDefs.OnLoad();
}
public void SaveSoundDefs()
{
}
public void LoadAudioClips()
{
StartCoroutine(LoadAudioClipsCoroutine());
}
private IEnumerator LoadAudioClipsCoroutine()
{
foreach (SoundDef soundDef in SoundDefs.allDefs)
{
foreach (SubSoundDef subSoundDef in soundDef.subSounds)
{
foreach (AudioGrain audioGrain in subSoundDef.grains)
{
string fullPath = Path.GetFullPath(Path.Combine(Application.streamingAssetsPath, "Sounds", audioGrain.clipPath)) + ".wav";
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.WAV))
{
yield return www.SendWebRequest();
if (string.IsNullOrEmpty(www.error) == false)
{
Debug.Log("Could not load audio clip '" + fullPath + "' - .WAV audio clip was not found");
continue;
}
AudioClip audioClip = DownloadHandlerAudioClip.GetContent(www);
SoundDefs.AddAudioClip(fullPath, audioClip);
}
}
}
}
}
}
}

View file

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

View file

@ -0,0 +1,79 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RimWorldAnimationStudio
{
public class CameraController : MonoBehaviour
{
private Camera cam;
[Header("Scroll controls")]
public float scrollSpeed = 100f;
[Header("Zoom controls")]
public float zoom = -5f;
public float minZoom = -3f;
public float maxZoom = -15f;
[Header("Max bounds")]
public float maxBoundsXAxis = 30;
public float maxBoundsYAxis = 30;
private float x;
private float y;
private float curZoom;
private bool mouseDragActive = false;
private Vector3 mouseDragOrigin;
private void Start()
{
x = transform.position.x;
y = transform.position.y;
curZoom = zoom;
transform.position = new Vector3(transform.position.x, transform.position.y, -10);
cam = this.GetComponent<Camera>();
}
private void Update()
{
if (Input.GetMouseButton(2))
{ StartMouseDrag(); }
else if (Input.GetMouseButtonUp(2))
{ ResetMouseDrag(); }
curZoom += Input.GetAxis("Mouse ScrollWheel") * scrollSpeed * 0.1f;
curZoom = Mathf.Clamp(curZoom, maxZoom, minZoom);
cam.orthographicSize = Mathf.Abs(curZoom);
}
public void StartMouseDrag()
{
Vector3 delta = cam.ScreenToWorldPoint(Input.mousePosition) - cam.transform.position;
if (mouseDragActive == false)
{
mouseDragActive = true;
mouseDragOrigin = cam.ScreenToWorldPoint(Input.mousePosition);
}
cam.transform.position = mouseDragOrigin - delta;
}
public void ResetMouseDrag()
{
mouseDragActive = false;
}
public void ResetCamera()
{
cam.transform.position = new Vector3(0, 0, -10);
curZoom = zoom;
mouseDragActive = false;
}
}
}

View file

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

View file

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
namespace RimWorldAnimationStudio
{
public static class EventsManager
{
// Event classes
public class WorkspaceIntEvent : UnityEvent<int> { }
public class ActorEvent : UnityEvent<Actor> { }
public class AnimationStageEvent : UnityEvent<AnimationStage> { }
public class PawnAnimationClipEvent : UnityEvent<PawnAnimationClip> { }
public class PawnKeyframeEvent : UnityEvent<PawnKeyframe> { }
public class ActorAddonEvent : UnityEvent<ActorAddon> { }
public class AddonKeyframeEvent : UnityEvent<AddonKeyframe> { }
public class ActorBodyEvent : UnityEvent<ActorBody> { }
public class ActorBodyPartEvent : UnityEvent<ActorBodyPart> { }
// Event list
public static UnityEvent onAnimationTimelinesChanged = new UnityEvent();
public static UnityEvent onAnimationToggled = new UnityEvent();
public static UnityEvent onAnimationChanged = new UnityEvent();
public static UnityEvent onAnimationDefChanged = new UnityEvent();
public static WorkspaceIntEvent onActorIDChanged = new WorkspaceIntEvent();
public static WorkspaceIntEvent onStageIDChanged = new WorkspaceIntEvent();
public static WorkspaceIntEvent onStageTickChanged = new WorkspaceIntEvent();
public static WorkspaceIntEvent onStageCountChanged = new WorkspaceIntEvent();
public static WorkspaceIntEvent onActorCountChanged = new WorkspaceIntEvent();
public static WorkspaceIntEvent onKeyframeCountChanged = new WorkspaceIntEvent();
public static ActorEvent onActorChanged = new ActorEvent();
public static AnimationStageEvent onAnimationStageChanged = new AnimationStageEvent();
public static AnimationStageEvent onStageWindowSizeChanged = new AnimationStageEvent();
public static PawnAnimationClipEvent onPawnAnimationClipChanged = new PawnAnimationClipEvent();
public static PawnKeyframeEvent onPawnKeyframeChanged = new PawnKeyframeEvent();
public static ActorAddonEvent onActorAddonChanged = new ActorAddonEvent();
public static AddonKeyframeEvent onAddonKeyframeChanged = new AddonKeyframeEvent();
public static UnityEvent onDefNamesChanged = new UnityEvent();
public static ActorBodyEvent onActorBodySelected = new ActorBodyEvent();
public static ActorBodyPartEvent onActorBodyPartSelected = new ActorBodyPartEvent();
// Event invoking
public static void OnAnimationTimelinesChanged() { onAnimationTimelinesChanged.Invoke(); }
public static void OnAnimationToggled() { onAnimationToggled.Invoke(); }
public static void OnAnimationChanged() { onAnimationChanged.Invoke(); }
public static void OnAnimationDefChanged() { onAnimationDefChanged.Invoke(); }
public static void OnActorIDChanged() { onActorIDChanged.Invoke(Workspace.ActorID); }
public static void OnStageIDChanged() { onStageIDChanged.Invoke(Workspace.ActorID); }
public static void OnStageTickChanged() { onStageTickChanged.Invoke(Workspace.StageTick); }
public static void OnStageCountChanged() { onStageCountChanged.Invoke(Workspace.animationDef.AnimationStages.Count); }
public static void OnActorCountChanged() { onActorCountChanged.Invoke(Workspace.animationDef.Actors.Count); }
public static void OnKeyframeCountChanged(PawnAnimationClip clip) { onKeyframeCountChanged.Invoke(clip.Keyframes.Count); }
public static void OnActorChanged(Actor actor) { onActorChanged.Invoke(actor); }
public static void OnAnimationStageChanged(AnimationStage stage) { onAnimationStageChanged.Invoke(stage); }
public static void OnStageWindowSizeChanged(AnimationStage stage) { onStageWindowSizeChanged.Invoke(stage); }
public static void OnPawnAnimationClipChanged(PawnAnimationClip clip) { onPawnAnimationClipChanged.Invoke(clip); }
public static void OnPawnKeyframeChanged(PawnKeyframe keyframe) { onPawnKeyframeChanged.Invoke(keyframe); }
public static void OnActorAddonChanged(ActorAddon actorAddon) { onActorAddonChanged.Invoke(actorAddon); }
public static void OnAddonKeyframeChanged(AddonKeyframe addonKeyframe) { onAddonKeyframeChanged.Invoke(addonKeyframe); }
public static void OnDefNamesChanged() { onDefNamesChanged.Invoke(); }
public static void OnActorBodySelected(ActorBody actorBody) { onActorBodySelected.Invoke(actorBody); }
public static void OnActorBodyPartSelected(ActorBodyPart bodyPart) { onActorBodyPartSelected.Invoke(bodyPart); }
}
}

View file

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

View file

@ -0,0 +1,478 @@
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 InputManager : Singleton<InputManager>
{
public Transform dialogBoxes;
public GameObject keyframeSelector;
private float lastUpdate = -1f;
private float largeStep = 0.1f;
private float smallStep = 0.03f;
private bool isDragging;
private Vector2 v0;
public bool CanRepeatThisUpdate()
{
if (Time.unscaledTime > lastUpdate + Constants.actionRepeatSpeed)
{
lastUpdate = Time.unscaledTime;
return true;
}
return false;
}
public bool IsModifierKeyHeld()
{
if (Input.GetKey(KeyCode.LeftShift)) return true;
if (Input.GetKey(KeyCode.LeftControl)) return true;
if (Input.GetKey(KeyCode.LeftAlt)) return true;
if (Input.GetKey(KeyCode.LeftCommand)) return true;
if (Input.GetKey(KeyCode.RightShift)) return true;
if (Input.GetKey(KeyCode.RightControl)) return true;
if (Input.GetKey(KeyCode.RightAlt)) return true;
if (Input.GetKey(KeyCode.RightCommand)) return true;
return false;
}
public bool IsDialogBoxIsActive()
{
foreach (Transform child in dialogBoxes)
{ if (child.gameObject.activeSelf) return true; }
return false;
}
public void Update()
{
if (IsDialogBoxIsActive()) return;
if (EventSystem.current.currentSelectedGameObject != null && EventSystem.current.currentSelectedGameObject.GetComponent<InputField>()?.isFocused == true) return;
bool canRepeatThisUpdate = CanRepeatThisUpdate();
// Check keybinds
foreach (Keybind keybind in KeybindConfig.GetAllKeybinds())
{
if (IsModifierKeyHeld() && keybind.keyModifiers.NullOrEmpty()) goto nextKeybind;
foreach (KeyCode modKeyCode in keybind.keyModifiers)
{ if (Input.GetKey(modKeyCode) == false) goto nextKeybind; }
if (keybind.repeatable && canRepeatThisUpdate)
{ if (Input.GetKey(keybind.keyCode) == false) goto nextKeybind; }
else
{ if (Input.GetKeyDown(keybind.keyCode) == false) goto nextKeybind; }
//Debug.Log(keybind.command);
Invoke(keybind.command, 0f); return;
nextKeybind:;
}
// Drag selection
if (Input.GetMouseButtonDown(0))
{ StartKeyframeSelectionDrag(); }
else if (Input.GetMouseButtonUp(0))
{ EndKeyframeSelectionDrag(); }
else if (isDragging)
{ UpdateKeyframeSelector(); }
else
{ HideKeyframeSelector(); }
}
public void StartKeyframeSelectionDrag()
{
PointerEventData pointer = new PointerEventData(EventSystem.current);
pointer.position = Input.mousePosition;
List<RaycastResult> raycastResults = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointer, raycastResults);
if (raycastResults.Count > 0)
{
foreach (var go in raycastResults)
{
//Debug.Log(go.gameObject);
if (go.gameObject?.GetComponent<Button>() != null) return;
if (go.gameObject?.GetComponent<IDragHandler>() != null) return;
if (go.gameObject?.GetComponentInParent<IDragHandler>() != null) return;
}
}
v0 = (Vector2)Input.mousePosition;
isDragging = true;
}
public void EndKeyframeSelectionDrag()
{
if (isDragging == false) return;
isDragging = false;
Vector2 v1 = (Vector2)Input.mousePosition;
List<int> keyframeIDs = new List<int>();
IEnumerable<KeyframeSlider> sliders = Selectable.allSelectablesArray.Select(x => x.GetComponent<KeyframeSlider>());
foreach (KeyframeSlider slider in sliders)
{
Transform handle = slider?.transform?.FindDeepChild("Handle");
if (handle == null) continue;
Vector2 vi = (Vector2)handle.GetComponent<RectTransform>().position;
if (vi.x < Mathf.Min(v0.x, v1.x)) continue;
if (vi.x > Mathf.Max(v0.x, v1.x)) continue;
if (vi.y < Mathf.Min(v0.y, v1.y)) continue;
if (vi.y > Mathf.Max(v0.y, v1.y)) continue;
keyframeIDs.Add(slider.keyframeID);
}
if (keyframeIDs.NotNullOrEmpty())
{ Workspace.keyframeID = keyframeIDs; }
}
public void UpdateKeyframeSelector()
{
Vector2 v1 = (Vector2)Input.mousePosition;
Vector2 pos = new Vector2(Mathf.Min(v0.x, v1.x), Mathf.Min(v0.y, v1.y));
Vector2 dim = new Vector2(Mathf.Abs(v1.x - v0.x), Mathf.Abs(v1.y - v0.y));
keyframeSelector.GetComponent<RectTransform>().anchoredPosition = pos;
keyframeSelector.GetComponent<RectTransform>().sizeDelta = dim;
if (keyframeSelector.activeSelf == false)
{ keyframeSelector.SetActive(true); }
}
public void HideKeyframeSelector()
{
if (keyframeSelector.activeSelf)
{ keyframeSelector.SetActive(false); }
}
public void QuitApplication()
{
ApplicationManager.Instance.TryToCloseApplication();
}
public void NewAnimation()
{
ApplicationManager.Instance.TryToMakeNewAnimation();
}
public void SaveAnimation()
{
ApplicationManager.Instance.TryToSaveAnimation();
}
public void SaveAnimationAs()
{
ApplicationManager.Instance.TryToSaveAnimationAs();
}
public void LoadAnimation()
{
ApplicationManager.Instance.TryToLoadAnimation();
}
public void UndoAction()
{
if (Workspace.animationDef == null) return;
Workspace.Undo();
}
public void RedoAction()
{
if (Workspace.animationDef == null) return;
Workspace.Redo();
}
public void ToggleAnimationPreview()
{
if (Workspace.animationDef == null) return;
Workspace.IsAnimating = !Workspace.IsAnimating;
}
public void AddKeyframe()
{
if (Workspace.animationDef == null) return;
Workspace.GetCurrentPawnAnimationClip().AddPawnKeyframe();
}
public void CopyKeyframes()
{
if (Workspace.animationDef == null) return;
Workspace.GetCurrentPawnAnimationClip().CopyPawnKeyframes();
}
public void PasteKeyframes()
{
if (Workspace.animationDef == null) return;
Workspace.GetCurrentPawnAnimationClip().PastePawnKeyframes();
}
public void DeleteKeyframes()
{
if (Workspace.animationDef == null) return;
foreach (int keyframeID in Workspace.keyframeID)
{ Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID).RemovePawnKeyframe(keyframeID); }
}
public void ActorMovementMode()
{
Workspace.actorManipulationMode = (ActorManipulationMode)0;
}
public void ActorRotateMode()
{
Workspace.actorManipulationMode = (ActorManipulationMode)1;
}
public void ActorFacingMode()
{
Workspace.actorManipulationMode = (ActorManipulationMode)2;
}
public void AdjustActorUpward()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.up * largeStep);
}
public void AdjustActorDownward()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.down * largeStep);
}
public void AdjustActorLeftward()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.left * largeStep);
}
public void AdjustActorRightward()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.right * largeStep);
}
public void AdjustActorUpwardSmall()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.up * smallStep);
}
public void AdjustActorDownwardSmall()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.down * smallStep);
}
public void AdjustActorLeftwardSmall()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.left * smallStep);
}
public void AdjustActorRightwardSmall()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
keyframe.AdjustActor(Vector2.right * smallStep);
}
public void CycleActorBodyPartSelecion()
{
if (Workspace.animationDef == null) return;
ActorBody actorBody = AnimationController.Instance.actorBodies.GetComponentsInChildren<ActorBody>().FirstOrDefault(x => x.actorID == Workspace.ActorID);
List<ActorBodyPart> actorBodyParts = actorBody?.GetComponentsInChildren<ActorBodyPart>()?.Where(x => x.gameObject.activeInHierarchy)?.ToList();
if (actorBodyParts.NullOrEmpty()) return;
if (Workspace.selectedBodyPart == null)
{ actorBodyParts[0].Activate(); return; }
else
{
for (int i = 0; i < actorBodyParts.Count; i++)
{
ActorBodyPart part = actorBodyParts[i];
if (part == Workspace.selectedBodyPart)
{
if (i < actorBodyParts.Count - 1)
{ actorBodyParts[i + 1].Activate(); return; }
else
{ actorBody.Activate(); return; }
}
}
}
}
public void ToPreviousActor()
{
if (Workspace.animationDef == null) return;
Workspace.selectedBodyPart = null;
Workspace.ActorID = Mathf.Clamp(Workspace.ActorID - 1, 0, Workspace.animationDef.Actors.Count - 1);
}
public void ToNextActor()
{
if (Workspace.animationDef == null) return;
Workspace.selectedBodyPart = null;
Workspace.ActorID = Mathf.Clamp(Workspace.ActorID + 1, 0, Workspace.animationDef.Actors.Count - 1);
}
public void ToPreviousTick()
{
if (Workspace.animationDef == null) return;
Workspace.StageTick = Mathf.Clamp(Workspace.StageTick - 1, Constants.minTick, Workspace.StageWindowSize);
}
public void ToNextTick()
{
if (Workspace.animationDef == null) return;
Workspace.StageTick = Mathf.Clamp(Workspace.StageTick + 1, Constants.minTick, Workspace.StageWindowSize);
}
public void ToFirstTick()
{
if (Workspace.animationDef == null) return;
Workspace.StageTick = Constants.minTick;
}
public void ToLastTick()
{
if (Workspace.animationDef == null) return;
Workspace.StageTick = Workspace.StageWindowSize;
}
public void ToPreviousKey()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetPreviousKeyframe(Workspace.ActorID);
if (keyframe != null) Workspace.StageTick = keyframe.atTick.Value;
}
public void ToNextKey()
{
if (Workspace.animationDef == null) return;
PawnKeyframe keyframe = Workspace.GetNextKeyframe(Workspace.ActorID);
if (keyframe != null) Workspace.StageTick = keyframe.atTick.Value;
}
public void ToPreviousStage()
{
if (Workspace.animationDef == null) return;
int prevStageID = Workspace.StageID;
Workspace.StageID = Mathf.Clamp(Workspace.StageID - 1, 0, Workspace.animationDef.AnimationStages.Count - 1);
if (Workspace.StageID != prevStageID)
{ Workspace.RecordEvent("Stage selected"); }
}
public void ToNextStage()
{
if (Workspace.animationDef == null) return;
int prevStageID = Workspace.StageID;
Workspace.StageID = Mathf.Clamp(Workspace.StageID + 1, 0, Workspace.animationDef.AnimationStages.Count - 1);
if (Workspace.StageID != prevStageID)
{ Workspace.RecordEvent("Stage selected"); }
}
public void CenterView()
{
Camera.main.GetComponent<CameraController>().ResetCamera();
}
public void StretchKeyframesToggle()
{
Workspace.stretchKeyframes = !Workspace.stretchKeyframes;
}
public void OpenProjectHome()
{
if (Uri.IsWellFormedUriString(Constants.projectHome, UriKind.RelativeOrAbsolute))
{ Application.OpenURL(Constants.projectHome); }
}
public void OpenProjectWiki()
{
if (Uri.IsWellFormedUriString(Constants.projectWiki, UriKind.RelativeOrAbsolute))
{ Application.OpenURL(Constants.projectWiki); }
}
public void AddActor()
{
if (Workspace.animationDef == null) return;
Workspace.animationDef.AddActor();
}
public void RemoveActor()
{
if (Workspace.animationDef == null) return;
Workspace.animationDef.RemoveActor();
}
public void AddStage()
{
if (Workspace.animationDef == null) return;
Workspace.animationDef.AddAnimationStage();
}
public void CloneStage()
{
if (Workspace.animationDef == null) return;
Workspace.animationDef.CloneAnimationStage();
}
public void RemoveStage()
{
if (Workspace.animationDef == null) return;
Workspace.animationDef.RemoveAnimationStage();
}
}
}

View file

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

View file

@ -0,0 +1,47 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class StageCardManager : Singleton<StageCardManager>
{
public StageCard stageCardPrefab;
public void Start()
{
EventsManager.onAnimationChanged.AddListener(delegate { Initialize(); });
EventsManager.onStageIDChanged.AddListener(delegate { Initialize(); });
EventsManager.onStageCountChanged.AddListener(delegate { Initialize(); });
Initialize();
}
public void Initialize()
{
int stageCount = Workspace.animationDef.AnimationStages.Count;
int childCount = GetComponentsInChildren<StageCard>().Count();
for (int i = 0; i < Mathf.Max(stageCount, childCount); i++)
{
// Add new stage cards as required
if (i >= childCount)
{ Instantiate(stageCardPrefab, transform); }
// Get objects to update
StageCard stageCard = GetComponentsInChildren<StageCard>()[i];
// Update values
if (i < stageCount)
{ stageCard.Initialize(Workspace.animationDef.AnimationStages[i].StageName); }
// Remove excess objects as required
else
{ Destroy(stageCard.gameObject); }
}
}
}
}

View file

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