mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
v 1.0.0
This commit is contained in:
parent
0828ecd037
commit
2998865184
9821 changed files with 90 additions and 90 deletions
8
Source/Assets/Scripts/AdvancedPolygonCollider.meta
Normal file
8
Source/Assets/Scripts/AdvancedPolygonCollider.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 177db18368bfbba47a6b714d621984dc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
Advanced Polygon Collider (c) 2015 Digital Ruby, LLC
|
||||
http://www.digitalruby.com
|
||||
|
||||
Source code may not be redistributed. Use in apps and games is fine.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
#endif
|
||||
|
||||
namespace DigitalRuby.AdvancedPolygonCollider
|
||||
{
|
||||
public struct PolygonParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture - must be readable and writeable.
|
||||
/// </summary>
|
||||
public Texture2D Texture;
|
||||
|
||||
/// <summary>
|
||||
/// Source rect from the texture containing the sprite.
|
||||
/// </summary>
|
||||
public Rect Rect;
|
||||
|
||||
/// <summary>
|
||||
/// Offset (pivot) for the sprite
|
||||
/// </summary>
|
||||
public Vector2 Offset;
|
||||
|
||||
/// <summary>
|
||||
/// X multiplier if pixels per unit are used, otherwise 1 (see UpdatePolygonCollider method).
|
||||
/// </summary>
|
||||
public float XMultiplier;
|
||||
|
||||
/// <summary>
|
||||
/// Y multiplier if pixels per unit are used, otherwise 1 (see UpdatePolygonCollider method).
|
||||
/// </summary>
|
||||
public float YMultiplier;
|
||||
|
||||
/// <summary>
|
||||
/// Alpha tolerance. Pixels with greater than this are considered solid.
|
||||
/// </summary>
|
||||
public byte AlphaTolerance;
|
||||
|
||||
/// <summary>
|
||||
/// Distance threshold to collapse vertices in pixels.
|
||||
/// </summary>
|
||||
public int DistanceThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// True to decompose into convex polygons, false otherwise.
|
||||
/// </summary>
|
||||
public bool Decompose;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the cache. Values will be cached accordingly.
|
||||
/// </summary>
|
||||
public bool UseCache;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int h = Texture.GetHashCode();
|
||||
if (h == 0)
|
||||
{
|
||||
h = 1;
|
||||
}
|
||||
return h * (int)(Rect.GetHashCode() * XMultiplier * YMultiplier * AlphaTolerance * Mathf.Max(DistanceThreshold, 1) * (Decompose ? 2 : 1));
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is PolygonParameters)
|
||||
{
|
||||
PolygonParameters p = (PolygonParameters)obj;
|
||||
return Texture == p.Texture && Rect == p.Rect &&
|
||||
XMultiplier == p.XMultiplier && YMultiplier == p.YMultiplier &&
|
||||
AlphaTolerance == p.AlphaTolerance && DistanceThreshold == p.DistanceThreshold &&
|
||||
Decompose == p.Decompose;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(PolygonCollider2D))]
|
||||
[RequireComponent(typeof(SpriteRenderer))]
|
||||
[ExecuteInEditMode]
|
||||
public class AdvancedPolygonCollider : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public struct ArrayWrapper
|
||||
{
|
||||
[SerializeField]
|
||||
public Vector2[] Array;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct ListWrapper
|
||||
{
|
||||
[SerializeField]
|
||||
public List<ArrayWrapper> List;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct CacheEntry
|
||||
{
|
||||
[SerializeField]
|
||||
public CacheKey Key;
|
||||
|
||||
[SerializeField]
|
||||
public ListWrapper Value;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct CacheKey
|
||||
{
|
||||
[SerializeField]
|
||||
public Texture2D Texture;
|
||||
|
||||
[SerializeField]
|
||||
public Rect Rect;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Texture.GetHashCode() * Rect.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is CacheKey)
|
||||
{
|
||||
CacheKey k = (CacheKey)obj;
|
||||
return (Texture == k.Texture && Rect == k.Rect);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Tooltip("Pixels with alpha greater than this count as solid.")]
|
||||
[Range(0, 254)]
|
||||
public byte AlphaTolerance = 20;
|
||||
|
||||
[Tooltip("Points further away than this number of pixels will be consolidated.")]
|
||||
[Range(0, 64)]
|
||||
public int DistanceThreshold = 8;
|
||||
|
||||
[Tooltip("Scale of the polygon.")]
|
||||
[Range(0.5f, 2.0f)]
|
||||
public float Scale = 1.0f;
|
||||
|
||||
[Tooltip("Whether to decompse vertices into convex only polygons.")]
|
||||
public bool Decompose = false;
|
||||
|
||||
[Tooltip("Whether to live update everything when in play mode. Typically for performance this can be false, " +
|
||||
"but if you plan on making changes to the sprite or parameters at runtime, you will want to set this to true.")]
|
||||
public bool RunInPlayMode = false;
|
||||
|
||||
[Tooltip("True to use the cache, false otherwise. The cache is populated in editor and play mode and uses the most recent geometry " +
|
||||
"for a texture and rect regardless of other parameters. When ignoring the cache, values will not be added to the cache either. Cache is " +
|
||||
"only useful if you will be changing your sprite at run-time (i.e. animation)")]
|
||||
public bool UseCache;
|
||||
|
||||
private SpriteRenderer spriteRenderer;
|
||||
private PolygonCollider2D polygonCollider;
|
||||
private bool dirty;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private byte lastAlphaTolerance;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private float lastScale;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private int lastDistanceThreshold;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private bool lastDecompose;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Sprite lastSprite;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Rect lastRect = new Rect();
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Vector2 lastOffset = new Vector2(-99999.0f, -99999.0f);
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private float lastPixelsPerUnit;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private bool lastFlipX;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private bool lastFlipY;
|
||||
|
||||
private static readonly Dictionary<CacheKey, List<Vector2[]>> cache = new Dictionary<CacheKey, List<Vector2[]>>();
|
||||
|
||||
[Tooltip("All the cached objects from the editor. Do not modify this data.")]
|
||||
[SerializeField]
|
||||
private List<CacheEntry> editorCache = new List<CacheEntry>();
|
||||
|
||||
// private readonly AdvancedPolygonColliderAutoGeometry geometryDetector = new AdvancedPolygonColliderAutoGeometry();
|
||||
private readonly TextureConverter geometryDetector = new TextureConverter();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private Texture2D blackBackground;
|
||||
|
||||
#endif
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
// move editor cache to regular cache
|
||||
foreach (var v in editorCache)
|
||||
{
|
||||
List<Vector2[]> list = new List<Vector2[]>();
|
||||
cache[v.Key] = list;
|
||||
foreach (ArrayWrapper w in v.Value.List)
|
||||
{
|
||||
list.Add(w.Array);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
blackBackground = new Texture2D(1, 1);
|
||||
blackBackground.SetPixel(0, 0, new Color(0.0f, 0.0f, 0.0f, 0.8f));
|
||||
blackBackground.Apply();
|
||||
|
||||
#endif
|
||||
|
||||
polygonCollider = GetComponent<PolygonCollider2D>();
|
||||
spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
}
|
||||
|
||||
private void UpdateDirtyState()
|
||||
{
|
||||
if (spriteRenderer.sprite != lastSprite)
|
||||
{
|
||||
lastSprite = spriteRenderer.sprite;
|
||||
dirty = true;
|
||||
}
|
||||
if (spriteRenderer.sprite != null)
|
||||
{
|
||||
if (lastOffset != spriteRenderer.sprite.pivot)
|
||||
{
|
||||
lastOffset = spriteRenderer.sprite.pivot;
|
||||
dirty = true;
|
||||
}
|
||||
if (lastRect != spriteRenderer.sprite.rect)
|
||||
{
|
||||
lastRect = spriteRenderer.sprite.rect;
|
||||
dirty = true;
|
||||
}
|
||||
if (lastPixelsPerUnit != spriteRenderer.sprite.pixelsPerUnit)
|
||||
{
|
||||
lastPixelsPerUnit = spriteRenderer.sprite.pixelsPerUnit;
|
||||
dirty = true;
|
||||
}
|
||||
if (lastFlipX != spriteRenderer.flipX)
|
||||
{
|
||||
lastFlipX = spriteRenderer.flipX;
|
||||
dirty = true;
|
||||
}
|
||||
if (lastFlipY != spriteRenderer.flipY)
|
||||
{
|
||||
lastFlipY = spriteRenderer.flipY;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
if (AlphaTolerance != lastAlphaTolerance)
|
||||
{
|
||||
lastAlphaTolerance = AlphaTolerance;
|
||||
dirty = true;
|
||||
}
|
||||
if (Scale != lastScale)
|
||||
{
|
||||
lastScale = Scale;
|
||||
dirty = true;
|
||||
}
|
||||
if (DistanceThreshold != lastDistanceThreshold)
|
||||
{
|
||||
lastDistanceThreshold = DistanceThreshold;
|
||||
dirty = true;
|
||||
}
|
||||
if (Decompose != lastDecompose)
|
||||
{
|
||||
lastDecompose = Decompose;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
if (!RunInPlayMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!UseCache)
|
||||
{
|
||||
editorCache.Clear();
|
||||
}
|
||||
|
||||
UpdateDirtyState();
|
||||
if (dirty)
|
||||
{
|
||||
RecalculatePolygon();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
UnityEditor.Handles.BeginGUI();
|
||||
GUI.color = Color.white;
|
||||
string text = " Vertices: " + VerticesCount + " ";
|
||||
var view = UnityEditor.SceneView.currentDrawingSceneView;
|
||||
Vector3 screenPos = view.camera.WorldToScreenPoint(gameObject.transform.position);
|
||||
Vector2 size = GUI.skin.label.CalcSize(new GUIContent(text));
|
||||
GUI.skin.box.normal.background = blackBackground;
|
||||
Rect rect = new Rect(screenPos.x - (size.x / 2), -screenPos.y + view.position.height + 4, size.x, size.y);
|
||||
GUI.Box(rect, GUIContent.none);
|
||||
GUI.Label(rect, text);
|
||||
UnityEditor.Handles.EndGUI();
|
||||
}
|
||||
|
||||
private void AddEditorCache(ref PolygonParameters p, List<Vector2[]> list)
|
||||
{
|
||||
CacheKey key = new CacheKey();
|
||||
key.Texture = p.Texture;
|
||||
key.Rect = p.Rect;
|
||||
|
||||
CacheEntry e = new CacheEntry();
|
||||
e.Key = key;
|
||||
e.Value = new ListWrapper();
|
||||
e.Value.List = new List<ArrayWrapper>();
|
||||
foreach (Vector2[] v in list)
|
||||
{
|
||||
ArrayWrapper w = new ArrayWrapper();
|
||||
w.Array = v;
|
||||
e.Value.List.Add(w);
|
||||
}
|
||||
|
||||
for (int i = 0; i < editorCache.Count; i++)
|
||||
{
|
||||
if (editorCache[i].Key.Equals(key))
|
||||
{
|
||||
editorCache.RemoveAt(i);
|
||||
editorCache.Insert(i, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
editorCache.Add(e);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public void RecalculatePolygon()
|
||||
{
|
||||
if (spriteRenderer.sprite != null)
|
||||
{
|
||||
PolygonParameters p = new PolygonParameters();
|
||||
p.AlphaTolerance = AlphaTolerance;
|
||||
p.Decompose = Decompose;
|
||||
p.DistanceThreshold = DistanceThreshold;
|
||||
p.Rect = spriteRenderer.sprite.rect;
|
||||
p.Offset = spriteRenderer.sprite.pivot;
|
||||
p.Texture = spriteRenderer.sprite.texture;
|
||||
p.XMultiplier = (spriteRenderer.sprite.rect.width * 0.5f) / spriteRenderer.sprite.pixelsPerUnit;
|
||||
p.YMultiplier = (spriteRenderer.sprite.rect.height * 0.5f) / spriteRenderer.sprite.pixelsPerUnit;
|
||||
p.UseCache = UseCache;
|
||||
UpdatePolygonCollider(ref p);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePolygonCollider(ref PolygonParameters p)
|
||||
{
|
||||
if (spriteRenderer.sprite == null || spriteRenderer.sprite.texture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
List<Vector2[]> cached;
|
||||
|
||||
if (Application.isPlaying && p.UseCache)
|
||||
{
|
||||
CacheKey key = new CacheKey();
|
||||
key.Texture = p.Texture;
|
||||
key.Rect = p.Rect;
|
||||
|
||||
if (cache.TryGetValue(key, out cached))
|
||||
{
|
||||
polygonCollider.pathCount = cached.Count;
|
||||
for (int i = 0; i < cached.Count; i++)
|
||||
{
|
||||
polygonCollider.SetPath(i, cached[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PopulateCollider(polygonCollider, ref p);
|
||||
}
|
||||
|
||||
public int VerticesCount
|
||||
{
|
||||
get { return (polygonCollider == null ? 0 : polygonCollider.GetTotalPointCount()); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the vertices of a collider
|
||||
/// </summary>
|
||||
/// <param name="collider">Collider to setup vertices in.</param>
|
||||
/// <param name="p">Polygon creation parameters</param>
|
||||
public void PopulateCollider(PolygonCollider2D collider, ref PolygonParameters p)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (p.Texture.format != TextureFormat.ARGB32 && p.Texture.format != TextureFormat.BGRA32 && p.Texture.format != TextureFormat.RGBA32 &&
|
||||
p.Texture.format != TextureFormat.RGB24 && p.Texture.format != TextureFormat.Alpha8 && p.Texture.format != TextureFormat.RGBAFloat &&
|
||||
p.Texture.format != TextureFormat.RGBAHalf && p.Texture.format != TextureFormat.RGB565)
|
||||
{
|
||||
Debug.LogWarning("Advanced Polygon Collider works best with a non-compressed texture in ARGB32, BGRA32, RGB24, RGBA4444, RGB565, RGBAFloat or RGBAHalf format");
|
||||
}
|
||||
int width = (int)p.Rect.width;
|
||||
int height = (int)p.Rect.height;
|
||||
int x = (int)p.Rect.x;
|
||||
int y = (int)p.Rect.y;
|
||||
UnityEngine.Color[] pixels = p.Texture.GetPixels(x, y, width, height, 0);
|
||||
List<Vertices> verts = geometryDetector.DetectVertices(pixels, width, p.AlphaTolerance);
|
||||
int pathIndex = 0;
|
||||
List<Vector2[]> list = new List<Vector2[]>();
|
||||
|
||||
for (int i = 0; i < verts.Count; i++)
|
||||
{
|
||||
ProcessVertices(collider, verts[i], list, ref p, ref pathIndex);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
|
||||
#endif
|
||||
|
||||
if (p.UseCache)
|
||||
{
|
||||
CacheKey key = new CacheKey();
|
||||
key.Texture = p.Texture;
|
||||
key.Rect = p.Rect;
|
||||
cache[key] = list;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
}
|
||||
else if (p.UseCache)
|
||||
{
|
||||
AddEditorCache(ref p, list);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//Debug.Log("Updated polygon.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError("Error creating collider: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector2[]> ProcessVertices(PolygonCollider2D collider, Vertices v, List<Vector2[]> list, ref PolygonParameters p, ref int pathIndex)
|
||||
{
|
||||
Vector2 offset = p.Offset;
|
||||
float flipXMultiplier = (spriteRenderer.flipX ? -1.0f : 1.0f);
|
||||
float flipYMultiplier = (spriteRenderer.flipY ? -1.0f : 1.0f);
|
||||
|
||||
if (p.DistanceThreshold > 1)
|
||||
{
|
||||
v = SimplifyTools.DouglasPeuckerSimplify (v, p.DistanceThreshold);
|
||||
}
|
||||
|
||||
if (p.Decompose)
|
||||
{
|
||||
List<List<Vector2>> points = BayazitDecomposer.ConvexPartition(v);
|
||||
for (int j = 0; j < points.Count; j++)
|
||||
{
|
||||
List<Vector2> v2 = points[j];
|
||||
for (int i = 0; i < v2.Count; i++)
|
||||
{
|
||||
float xValue = (2.0f * (((v2[i].x - offset.x) + 0.5f) / p.Rect.width));
|
||||
float yValue = (2.0f * (((v2[i].y - offset.y) + 0.5f) / p.Rect.height));
|
||||
v2[i] = new Vector2(xValue * p.XMultiplier * Scale * flipXMultiplier, yValue * p.YMultiplier * Scale * flipYMultiplier);
|
||||
}
|
||||
Vector2[] arr = v2.ToArray();
|
||||
collider.pathCount = pathIndex + 1;
|
||||
collider.SetPath(pathIndex++, arr);
|
||||
list.Add(arr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
collider.pathCount = pathIndex + 1;
|
||||
for (int i = 0; i < v.Count; i++)
|
||||
{
|
||||
float xValue = (2.0f * (((v[i].x - offset.x) + 0.5f) / p.Rect.width));
|
||||
float yValue = (2.0f * (((v[i].y - offset.y) + 0.5f) / p.Rect.height));
|
||||
v[i] = new Vector2(xValue * p.XMultiplier * Scale * flipXMultiplier, yValue * p.YMultiplier * Scale * flipYMultiplier);
|
||||
}
|
||||
Vector2[] arr = v.ToArray();
|
||||
collider.SetPath(pathIndex++, arr);
|
||||
list.Add(arr);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8c55c83d4c4dd1145ad5fbbab3c3df36
|
||||
timeCreated: 1450474475
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2d44a3587cd9f0f4ebdfbea6d8a9c789
|
||||
timeCreated: 1450472189
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/AnimationComponents.meta
Normal file
8
Source/Assets/Scripts/AnimationComponents.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e8cb35b5659798458ffebd1b3a5e993
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
186
Source/Assets/Scripts/AnimationComponents/Actor.cs
Normal file
186
Source/Assets/Scripts/AnimationComponents/Actor.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class Actor
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Data serialization control
|
||||
public bool ShouldSerializedefNames() { return defNames.NotNullOrEmpty(); }
|
||||
public bool ShouldSerializebodyDefTypes() { return bodyDefTypes.NotNullOrEmpty(); }
|
||||
public bool ShouldSerializerequiredGenitals() { return requiredGenitals.NotNullOrEmpty(); }
|
||||
public bool ShouldSerializeraceOffsets() { return raceOffsets.NotNullOrEmpty(); }
|
||||
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
|
||||
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; }
|
||||
|
||||
// Data helper functions
|
||||
[XmlIgnore] public List<string> DefNames
|
||||
{
|
||||
get { return defNames.NullOrEmpty() ? defNames = new List<string>() : defNames; }
|
||||
set { defNames = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<string> BodyDefTypes
|
||||
{
|
||||
get { return bodyDefTypes.NullOrEmpty() ? bodyDefTypes = new List<string>() : bodyDefTypes; }
|
||||
set { bodyDefTypes = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<string> RequiredGenitals
|
||||
{
|
||||
get { return requiredGenitals.NullOrEmpty() ? requiredGenitals = new List<string>() : requiredGenitals; }
|
||||
set { requiredGenitals = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<PawnRaceOffset> RaceOffsets {
|
||||
get { return raceOffsets.NullOrEmpty() ? raceOffsets = new List<PawnRaceOffset>() : raceOffsets; }
|
||||
set { raceOffsets = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<string> Tags
|
||||
{
|
||||
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
|
||||
set { tags = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public BodyTypeOffset BodyTypeOffset
|
||||
{
|
||||
get { return bodyTypeOffset == null ? bodyTypeOffset = new BodyTypeOffset() : bodyTypeOffset; }
|
||||
set { bodyTypeOffset = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool Initiator
|
||||
{
|
||||
get { return initiator == true; }
|
||||
set { if (value) { initiator = true; } else initiator = null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool ControlGenitalAngle
|
||||
{
|
||||
get { return controlGenitalAngle == true; }
|
||||
set { if (value) { controlGenitalAngle = true; } else controlGenitalAngle = null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool IsFucking
|
||||
{
|
||||
get { return isFucking == true; }
|
||||
set { if (value) { isFucking = true; } else isFucking = null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool IsFucked
|
||||
{
|
||||
get { return isFucked == true; }
|
||||
set { if (value) { isFucked = true; } else isFucked = null; }
|
||||
}
|
||||
|
||||
// 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 PawnRaceOffset(pawnRaceDef.defName);
|
||||
RaceOffsets.Add(raceOffset);
|
||||
}
|
||||
|
||||
return raceOffset.GetOffset();
|
||||
}
|
||||
|
||||
public void SetPawnRaceOffset(Vector2 offset)
|
||||
{
|
||||
if (pawnRaceDef == null)
|
||||
{ return; }
|
||||
|
||||
PawnRaceOffset raceOffset = RaceOffsets.FirstOrDefault(x => x.defName == pawnRaceDef.defName);
|
||||
|
||||
if (raceOffset == null)
|
||||
{
|
||||
raceOffset = new PawnRaceOffset(pawnRaceDef.defName);
|
||||
RaceOffsets.Add(raceOffset);
|
||||
|
||||
EventsManager.OnActorChanged(this);
|
||||
}
|
||||
|
||||
raceOffset.SetOffset(offset);
|
||||
}
|
||||
|
||||
public Vector3 GetFinalTransformOffset()
|
||||
{
|
||||
Vector3 offset = GetPawnRaceOffset() + (GetPawnRaceDef().isHumanoid ? BodyTypeOffset.GetOffset(bodyType) : new Vector3());
|
||||
|
||||
return new Vector3(offset.x, offset.z, offset.y);
|
||||
}
|
||||
|
||||
public int GetActorID()
|
||||
{
|
||||
if (Workspace.animationDef == null) return -1;
|
||||
return Workspace.animationDef.Actors.IndexOf(this);
|
||||
}
|
||||
|
||||
public ActorPosition GetCurrentPosition()
|
||||
{
|
||||
return GetPositionAtTick(Workspace.StageTick);
|
||||
}
|
||||
|
||||
public ActorPosition GetPositionAtTick(int atTick)
|
||||
{
|
||||
return new ActorPosition(GetActorID(), atTick);
|
||||
}
|
||||
|
||||
// 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() { }
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/AnimationComponents/Actor.cs.meta
Normal file
11
Source/Assets/Scripts/AnimationComponents/Actor.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 63a9fd7a0256e9849bc2bc07403528e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
78
Source/Assets/Scripts/AnimationComponents/ActorAddon.cs
Normal file
78
Source/Assets/Scripts/AnimationComponents/ActorAddon.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 string anchorName;
|
||||
public string layer = "Pawn";
|
||||
public GraphicData graphicData;
|
||||
public bool? render;
|
||||
|
||||
// Data serialization control
|
||||
public bool ShouldSerializeanchorName() { return string.IsNullOrEmpty(anchorName) == false && anchorName.ToLower() != "none"; }
|
||||
public bool ShouldSerializeanchoringActor() { return anchoringActor.HasValue; }
|
||||
public bool ShouldSerializerender() { return render == true; }
|
||||
|
||||
// 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; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public string AnchorName
|
||||
{
|
||||
get { return anchorName; }
|
||||
set { anchorName = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public string Layer
|
||||
{
|
||||
get { return layer; }
|
||||
set { layer = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public GraphicData GraphicData
|
||||
{
|
||||
get { return graphicData; }
|
||||
set { graphicData = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool Render
|
||||
{
|
||||
get { return render == true; }
|
||||
set { render = value; }
|
||||
}
|
||||
|
||||
// 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(ActorAddonDef actorAddonDef)
|
||||
{
|
||||
this.AddonName = actorAddonDef.addonName;
|
||||
this.GraphicData = actorAddonDef.graphicData.Copy();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/AnimationComponents/ActorAddon.cs.meta
Normal file
11
Source/Assets/Scripts/AnimationComponents/ActorAddon.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3759e796f4f62b044b9a652e746d79a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
Source/Assets/Scripts/AnimationComponents/ActorAddonDef.cs
Normal file
17
Source/Assets/Scripts/AnimationComponents/ActorAddonDef.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorAddonDef
|
||||
{
|
||||
public string addonName;
|
||||
public string label;
|
||||
public float scale = 1f;
|
||||
|
||||
public GraphicData graphicData;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d836663c2196924eab4a00f4f4ceb3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
55
Source/Assets/Scripts/AnimationComponents/AddonKeyFrame.cs
Normal file
55
Source/Assets/Scripts/AnimationComponents/AddonKeyFrame.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
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 serialization control
|
||||
public bool ShouldSerializeposX() { return posX.HasValue; }
|
||||
public bool ShouldSerializeposZ() { return posZ.HasValue; }
|
||||
public bool ShouldSerializerotation() { return rotation.HasValue; }
|
||||
|
||||
|
||||
// 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; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float PosZ
|
||||
{
|
||||
get { return posZ.HasValue ? posZ.Value : 0f; }
|
||||
set { posZ = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float Rotation
|
||||
{
|
||||
get { return rotation.HasValue ? rotation.Value : 0f; }
|
||||
set { rotation = value; }
|
||||
}
|
||||
|
||||
// Constructors
|
||||
public AddonKeyframe() { }
|
||||
|
||||
public AddonKeyframe(string addonName)
|
||||
{
|
||||
this.AddonName = addonName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 339d47b209f50f545a84a8e8c7948ae1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
177
Source/Assets/Scripts/AnimationComponents/AnimationDef.cs
Normal file
177
Source/Assets/Scripts/AnimationComponents/AnimationDef.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AnimationDef
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public string Label
|
||||
{
|
||||
get { return label != null && label != "" ? label : "newAnimation"; }
|
||||
set { label = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<string> SexTypes
|
||||
{
|
||||
get { return sexTypes.NullOrEmpty() ? sexTypes = new List<string>() : sexTypes; }
|
||||
set { sexTypes = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<string> InteractionDefTypes
|
||||
{
|
||||
get { return interactionDefTypes.NullOrEmpty() ? interactionDefTypes = new List<string>() : interactionDefTypes; }
|
||||
set { interactionDefTypes = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<Actor> Actors
|
||||
{
|
||||
get { return actors.NullOrEmpty() ? actors = new List<Actor>() : actors; }
|
||||
set { actors = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<AnimationStage> AnimationStages
|
||||
{
|
||||
get { if (animationStages.NullOrEmpty()){ animationStages = new List<AnimationStage>(); } return animationStages; }
|
||||
set { animationStages = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
// 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)
|
||||
{ stage.Initialize(); }
|
||||
}
|
||||
|
||||
public void AddActor()
|
||||
{
|
||||
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);
|
||||
|
||||
foreach (AnimationStage stage in Workspace.animationDef.AnimationStages)
|
||||
{ stage.AddAnimationClip(Workspace.animationDef.Actors.Count - 1); }
|
||||
|
||||
Initialize();
|
||||
Workspace.ActorID = Workspace.animationDef.Actors.Count - 1;
|
||||
|
||||
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.StageID = Workspace.animationDef.AnimationStages.Count - 1;
|
||||
|
||||
EventsManager.OnStageCountChanged();
|
||||
Workspace.RecordEvent("Stage addition");
|
||||
}
|
||||
|
||||
public void CloneAnimationStage()
|
||||
{
|
||||
AnimationStage stage = Workspace.GetCurrentAnimationStage().GetClone();
|
||||
stage.StageName += " (Clone)";
|
||||
|
||||
Workspace.animationDef.AnimationStages.Insert(Workspace.StageID + 1, stage);
|
||||
Initialize();
|
||||
|
||||
EventsManager.OnStageCountChanged();
|
||||
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--;
|
||||
|
||||
EventsManager.OnStageCountChanged();
|
||||
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() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37ec1f5f150928e42bda942fe97046b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
182
Source/Assets/Scripts/AnimationComponents/AnimationStage.cs
Normal file
182
Source/Assets/Scripts/AnimationComponents/AnimationStage.cs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AnimationStage
|
||||
{
|
||||
// Data to/from animationDef
|
||||
public string stageName;
|
||||
public int? playTimeTicks;
|
||||
public int? playTimeTicksQuick;
|
||||
public bool? isLooping;
|
||||
[XmlArray("animationClips"), XmlArrayItem("li")] public List<PawnAnimationClip> animationClips;
|
||||
|
||||
// Data serialization control
|
||||
public bool ShouldSerializeanimationClips() { return animationClips.NotNullOrEmpty(); }
|
||||
|
||||
// Data helper functions
|
||||
[XmlIgnore] public string StageName
|
||||
{
|
||||
get { return string.IsNullOrEmpty(stageName) ? stageName = "NewStage" : stageName; }
|
||||
set { stageName = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int PlayTimeTicks
|
||||
{
|
||||
get { return playTimeTicks.HasValue ? playTimeTicks.Value : 0; }
|
||||
set { playTimeTicks = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int PlayTimeTicksQuick
|
||||
{
|
||||
get { return playTimeTicksQuick.HasValue ? playTimeTicksQuick.Value : 0; }
|
||||
set { playTimeTicksQuick = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool IsLooping
|
||||
{
|
||||
get { return isLooping == true; }
|
||||
set { isLooping = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<PawnAnimationClip> AnimationClips
|
||||
{
|
||||
get { return animationClips.NullOrEmpty() ? animationClips = new List<PawnAnimationClip>() : animationClips; }
|
||||
set { animationClips = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int StageLoopsNormal
|
||||
{
|
||||
get { return Mathf.CeilToInt(PlayTimeTicks / Workspace.StageWindowSize); }
|
||||
set { value = Math.Max(1, value); PlayTimeTicks = value * Workspace.StageWindowSize; IsLooping = value > 1; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int StageLoopsQuick
|
||||
{
|
||||
get { return Mathf.CeilToInt(PlayTimeTicksQuick / Workspace.StageWindowSize); }
|
||||
set { value = Math.Max(0, Math.Min(value, StageLoopsNormal)); PlayTimeTicksQuick = value * Workspace.StageWindowSize; }
|
||||
}
|
||||
|
||||
// Local data
|
||||
[XmlIgnore] public int stageWindowSize = -1;
|
||||
|
||||
// Methods
|
||||
public void Initialize()
|
||||
{
|
||||
foreach (PawnAnimationClip clip in AnimationClips)
|
||||
{
|
||||
clip.BuildSimpleCurves();
|
||||
|
||||
if (clip.duration > PlayTimeTicks)
|
||||
{ PlayTimeTicks = clip.duration; }
|
||||
}
|
||||
}
|
||||
|
||||
public int GetStageID()
|
||||
{
|
||||
if (Workspace.animationDef == null) return -1;
|
||||
|
||||
return Workspace.animationDef.AnimationStages.IndexOf(this);
|
||||
}
|
||||
|
||||
public void StretchStageWindow(int newStageWindowSize)
|
||||
{
|
||||
ResizeStageWindow(newStageWindowSize);
|
||||
|
||||
float scale = (float)newStageWindowSize / Workspace.StageWindowSize;
|
||||
|
||||
foreach (PawnAnimationClip clip in AnimationClips)
|
||||
{
|
||||
foreach (PawnKeyframe keyframe in clip.Keyframes)
|
||||
{
|
||||
keyframe.atTick = keyframe.atTick == Constants.minTick ? Constants.minTick : Mathf.CeilToInt((float)keyframe.atTick.Value * scale);
|
||||
keyframe.TickDuration = 0;
|
||||
}
|
||||
|
||||
clip.BuildSimpleCurves();
|
||||
}
|
||||
|
||||
EventsManager.OnStageWindowSizeChanged(this);
|
||||
}
|
||||
|
||||
public void ResizeStageWindow(int newStageWindowSize)
|
||||
{
|
||||
Workspace.GetCurrentAnimationStage().PlayTimeTicks = newStageWindowSize * StageLoopsNormal;
|
||||
Workspace.GetCurrentAnimationStage().PlayTimeTicksQuick = newStageWindowSize * StageLoopsQuick;
|
||||
Workspace.GetCurrentAnimationStage().stageWindowSize = newStageWindowSize;
|
||||
|
||||
EventsManager.OnStageWindowSizeChanged(this);
|
||||
}
|
||||
|
||||
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.GetClone();
|
||||
keyframeA.GenerateKeyframeID(actorID);
|
||||
keyframeA.atTick = null;
|
||||
keyframeA.TickDuration = Constants.defaultAnimationClipLength - 1;
|
||||
|
||||
clip.Keyframes.Add(keyframeA);
|
||||
|
||||
PawnKeyframe keyframeB = lastkeyframe.GetClone();
|
||||
keyframeB.GenerateKeyframeID(actorID);
|
||||
keyframeB.atTick = null;
|
||||
keyframeB.TickDuration = 1;
|
||||
|
||||
clip.Keyframes.Add(keyframeB);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
PawnKeyframe keyframeA = new PawnKeyframe();
|
||||
keyframeA.TickDuration = Constants.defaultAnimationClipLength - 1;
|
||||
|
||||
clip.Keyframes.Add(keyframeA);
|
||||
|
||||
PawnKeyframe keyframeB = new PawnKeyframe();
|
||||
|
||||
clip.Keyframes.Add(keyframeB);
|
||||
}
|
||||
|
||||
animationClips.Add(clip);
|
||||
}
|
||||
|
||||
public AnimationStage GetClone()
|
||||
{
|
||||
AnimationStage clone = this.Copy();
|
||||
|
||||
foreach (PawnAnimationClip clip in clone.animationClips)
|
||||
{
|
||||
foreach (PawnKeyframe keyframe in clip.Keyframes)
|
||||
{ keyframe.GenerateKeyframeID(keyframe.actorID); }
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
// 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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9270822a570a06f41afa00e169af500c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
50
Source/Assets/Scripts/AnimationComponents/BodyTypeOffset.cs
Normal file
50
Source/Assets/Scripts/AnimationComponents/BodyTypeOffset.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class BodyTypeOffset
|
||||
{
|
||||
public string Male;
|
||||
public string Female;
|
||||
public string Thin;
|
||||
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);
|
||||
|
||||
if (bodyTypeOffsetInfo == null)
|
||||
{ return; }
|
||||
|
||||
string _bodyOffset = "(" + bodyOffset.x + ", " + bodyOffset.y + ")";
|
||||
bodyTypeOffsetInfo.SetValue(this, _bodyOffset);
|
||||
}
|
||||
|
||||
public Vector3 GetOffset(string bodyType)
|
||||
{
|
||||
FieldInfo bodyTypeOffsetInfo = typeof(BodyTypeOffset).GetField(bodyType);
|
||||
|
||||
if (bodyTypeOffsetInfo == null)
|
||||
{ return new Vector2(); }
|
||||
|
||||
string bodyTypeOffsetString = (string)bodyTypeOffsetInfo.GetValue(this);
|
||||
|
||||
if (bodyTypeOffsetString == null || bodyTypeOffsetString == "")
|
||||
{ return new Vector2(); }
|
||||
|
||||
bodyTypeOffsetString = bodyTypeOffsetString.Trim();
|
||||
bodyTypeOffsetString = bodyTypeOffsetString.Replace("(", "");
|
||||
bodyTypeOffsetString = bodyTypeOffsetString.Replace(")", "");
|
||||
var bodyTypeOffsetStrings = bodyTypeOffsetString.Split(',');
|
||||
|
||||
return new Vector3(float.Parse(bodyTypeOffsetStrings[0]), 0f, float.Parse(bodyTypeOffsetStrings[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1dfd90f8aa6d0e04086e2b4983d42ab6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
363
Source/Assets/Scripts/AnimationComponents/PawnAnimationClip.cs
Normal file
363
Source/Assets/Scripts/AnimationComponents/PawnAnimationClip.cs
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class PawnAnimationClip
|
||||
{
|
||||
// Data to/from animationDef
|
||||
public string layer = "Pawn";
|
||||
[XmlArray("addons"), XmlArrayItem("li")] public List<ActorAddon> addons;
|
||||
[XmlAttribute("Class")] public string className = "Rimworld_Animations.PawnAnimationClip";
|
||||
[XmlArray("keyframes"), XmlArrayItem("li")] public List<PawnKeyframe> keyframes;
|
||||
[XmlArray("tags"), XmlArrayItem("li")] public List<string> tags;
|
||||
|
||||
// Data serialization control
|
||||
public bool ShouldSerializeaddons() { return addons.Where(x => x.Render)?.Any() == true; }
|
||||
public bool ShouldSerializekeyframes() { return keyframes.NotNullOrEmpty(); }
|
||||
public bool ShouldSerializetags() { return tags.NotNullOrEmpty(); }
|
||||
|
||||
// Data helper functions
|
||||
[XmlIgnore] public string Layer
|
||||
{
|
||||
get { return layer; }
|
||||
set { layer = value; EventsManager.OnPawnAnimationClipChanged(this); }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<ActorAddon> Addons
|
||||
{
|
||||
get { return addons.NullOrEmpty() ? addons = new List<ActorAddon>() : addons; }
|
||||
set { addons = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<PawnKeyframe> Keyframes
|
||||
{
|
||||
get { return keyframes.NullOrEmpty() ? keyframes = new List<PawnKeyframe>() : keyframes; }
|
||||
set { keyframes = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public List<string> Tags
|
||||
{
|
||||
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
|
||||
set { tags = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
// Local data
|
||||
[XmlIgnore] public int duration { get { return Keyframes.Max(x => x.atTick.Value); } }
|
||||
|
||||
[XmlIgnore] public SimpleCurve GenitalAngle = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve BodyAngle = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve HeadAngle = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve HeadBob = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve BodyOffsetX = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve BodyOffsetZ = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve HeadFacing = new SimpleCurve();
|
||||
[XmlIgnore] public SimpleCurve BodyFacing = new SimpleCurve();
|
||||
|
||||
// Methods
|
||||
public void BuildSimpleCurves()
|
||||
{
|
||||
// Add addon data (if missing)
|
||||
foreach (ActorAddonDef actorAddonDef in ActorAddonDefs.allDefs)
|
||||
{ AddActorAddon(actorAddonDef); }
|
||||
|
||||
// Clear simple curve data
|
||||
BodyAngle.Clear();
|
||||
HeadAngle.Clear();
|
||||
BodyOffsetX.Clear();
|
||||
BodyOffsetZ.Clear();
|
||||
HeadFacing.Clear();
|
||||
BodyFacing.Clear();
|
||||
HeadBob.Clear();
|
||||
GenitalAngle.Clear();
|
||||
|
||||
foreach (ActorAddon addon in Addons)
|
||||
{
|
||||
addon.PosX.Clear();
|
||||
addon.PosZ.Clear();
|
||||
addon.Rotation.Clear();
|
||||
}
|
||||
|
||||
// Start building simple curves
|
||||
int keyframePosition = 0;
|
||||
int duration = 0;
|
||||
|
||||
Keyframes[Keyframes.Count - 1].TickDuration = 1;
|
||||
|
||||
foreach (PawnKeyframe frame in Keyframes)
|
||||
{ duration += frame.TickDuration; }
|
||||
|
||||
for (int i = 0; i < Keyframes.Count; i++)
|
||||
{
|
||||
PawnKeyframe keyframe = Keyframes[i];
|
||||
|
||||
if (keyframe.HasValidKeyframeID() == false)
|
||||
{ keyframe.GenerateKeyframeID(GetOwningActorID()); }
|
||||
|
||||
if (keyframe.atTick.HasValue)
|
||||
{
|
||||
if (i + 1 < Keyframes.Count)
|
||||
{ Keyframes[i].TickDuration = Keyframes[i + 1].atTick.Value - Keyframes[i].atTick.Value; }
|
||||
|
||||
// Safeguard - if two keys end up in having the same atTick, the duration of the first will be 0 and should be deleted
|
||||
if (Keyframes[i].TickDuration == 0)
|
||||
{
|
||||
Keyframes.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
BodyAngle.Add((float)keyframe.atTick / (float)duration, keyframe.BodyAngle, true);
|
||||
HeadAngle.Add((float)keyframe.atTick / (float)duration, keyframe.HeadAngle, true);
|
||||
BodyOffsetX.Add((float)keyframe.atTick / (float)duration, keyframe.BodyOffsetX, true);
|
||||
BodyOffsetZ.Add((float)keyframe.atTick / (float)duration, keyframe.BodyOffsetZ, true);
|
||||
HeadFacing.Add((float)keyframe.atTick / (float)duration, keyframe.HeadFacing, true);
|
||||
BodyFacing.Add((float)keyframe.atTick / (float)duration, keyframe.BodyFacing, true);
|
||||
HeadBob.Add((float)keyframe.atTick / (float)duration, keyframe.HeadBob, true);
|
||||
GenitalAngle.Add((float)keyframe.atTick / (float)duration, keyframe.GenitalAngle, true);
|
||||
|
||||
foreach (ActorAddon addon in Addons)
|
||||
{
|
||||
if (keyframe.AddonKeyframes.Any(x => x.AddonName == addon.AddonName) == false)
|
||||
{ keyframe.AddonKeyframes.Add(new AddonKeyframe(addon.AddonName)); }
|
||||
|
||||
addon.PosX.Add((float)keyframe.atTick / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosX, true);
|
||||
addon.PosZ.Add((float)keyframe.atTick / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosZ, true);
|
||||
addon.Rotation.Add((float)keyframe.atTick / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).Rotation, true);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
BodyAngle.Add((float)keyframePosition / (float)duration, keyframe.BodyAngle, true);
|
||||
HeadAngle.Add((float)keyframePosition / (float)duration, keyframe.HeadAngle, true);
|
||||
BodyOffsetX.Add((float)keyframePosition / (float)duration, keyframe.BodyOffsetX, true);
|
||||
BodyOffsetZ.Add((float)keyframePosition / (float)duration, keyframe.BodyOffsetZ, true);
|
||||
HeadFacing.Add((float)keyframePosition / (float)duration, keyframe.HeadFacing, true);
|
||||
BodyFacing.Add((float)keyframePosition / (float)duration, keyframe.BodyFacing, true);
|
||||
HeadBob.Add((float)keyframePosition / (float)duration, keyframe.HeadBob, true);
|
||||
GenitalAngle.Add((float)keyframePosition / (float)duration, keyframe.GenitalAngle, true);
|
||||
|
||||
foreach (ActorAddon addon in Addons)
|
||||
{
|
||||
if (keyframe.AddonKeyframes.Any(x => x.AddonName == addon.AddonName) == false)
|
||||
{ keyframe.AddonKeyframes.Add(new AddonKeyframe(addon.AddonName)); }
|
||||
|
||||
addon.PosX.Add((float)keyframePosition / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosX, true);
|
||||
addon.PosZ.Add((float)keyframePosition / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosZ, true);
|
||||
addon.Rotation.Add((float)keyframePosition / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).Rotation, true);
|
||||
}
|
||||
|
||||
keyframe.atTick = keyframePosition + Constants.minTick;
|
||||
keyframePosition += keyframe.TickDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddActorAddon(ActorAddonDef actorAddonDef)
|
||||
{
|
||||
if (Addons.Any(x => x.AddonName == actorAddonDef.addonName) == false)
|
||||
{ Addons.Add(new ActorAddon(actorAddonDef)); }
|
||||
|
||||
foreach (PawnKeyframe keyframe in Keyframes)
|
||||
{
|
||||
if (keyframe.AddonKeyframes.Any(x => x.AddonName == actorAddonDef.addonName) == false)
|
||||
{ keyframe.AddonKeyframes.Add(new AddonKeyframe(actorAddonDef.addonName)); }
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowOrHideActorAddon(string addonName, bool flag)
|
||||
{
|
||||
ActorAddon addon = GetActorAddon(addonName);
|
||||
|
||||
if (addon != null)
|
||||
{ addon.Render = flag; }
|
||||
}
|
||||
|
||||
public bool IsActorAddonVisible(string addonName)
|
||||
{
|
||||
ActorAddon addon = GetActorAddon(addonName);
|
||||
|
||||
if (addon != null)
|
||||
{ return addon.Render; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public ActorAddon GetActorAddon(string addonName)
|
||||
{
|
||||
return Addons.FirstOrDefault(x => x.AddonName == addonName);
|
||||
}
|
||||
|
||||
public int GetOwningActorID()
|
||||
{
|
||||
if (Workspace.animationDef == null) return -1;
|
||||
|
||||
return Workspace.GetCurrentAnimationStage().AnimationClips.IndexOf(this);
|
||||
}
|
||||
|
||||
public void AddPawnKeyframe()
|
||||
{
|
||||
if (Keyframes == null)
|
||||
{ Debug.LogWarning("Cannot add pawn keyframe - the AnimationDef is invalid"); return; }
|
||||
|
||||
if (Keyframes.FirstOrDefault(x => x.atTick == Workspace.StageTick) != null)
|
||||
{ Debug.LogWarning("Cannot add pawn keyframe - a keyframe already exists at this tick"); return; }
|
||||
|
||||
float clipPercent = (float)(Workspace.StageTick % duration) / duration;
|
||||
|
||||
PawnKeyframe keyframe = new PawnKeyframe();
|
||||
keyframe.BodyAngle = BodyAngle.Evaluate(clipPercent);
|
||||
keyframe.HeadAngle = HeadAngle.Evaluate(clipPercent);
|
||||
keyframe.HeadBob = HeadBob.Evaluate(clipPercent);
|
||||
keyframe.BodyOffsetX = BodyOffsetX.Evaluate(clipPercent);
|
||||
keyframe.BodyOffsetZ = BodyOffsetZ.Evaluate(clipPercent);
|
||||
keyframe.HeadFacing = (int)HeadFacing.Evaluate(clipPercent);
|
||||
keyframe.BodyFacing = (int)BodyFacing.Evaluate(clipPercent);
|
||||
keyframe.GenitalAngle = GenitalAngle.Evaluate(clipPercent);
|
||||
|
||||
keyframe.atTick = Workspace.StageTick;
|
||||
|
||||
PawnKeyframe nextKeyframe = Keyframes.FirstOrDefault(x => x.atTick > Workspace.StageTick);
|
||||
|
||||
if (nextKeyframe != null)
|
||||
{ keyframes.Insert(keyframes.IndexOf(nextKeyframe), keyframe); }
|
||||
|
||||
else
|
||||
{ keyframes.Add(keyframe); }
|
||||
|
||||
BuildSimpleCurves();
|
||||
|
||||
EventsManager.OnKeyframeCountChanged(this);
|
||||
Workspace.RecordEvent("Keyframe addition");
|
||||
}
|
||||
|
||||
public void CopyPawnKeyframes()
|
||||
{
|
||||
Workspace.copiedKeyframes.Clear();
|
||||
|
||||
List<PawnKeyframe> keyframesToClone = Workspace.GetPawnKeyframesByID(Workspace.keyframeID);
|
||||
|
||||
foreach (PawnKeyframe keyframe in keyframesToClone)
|
||||
{ Workspace.copiedKeyframes.Add(keyframe.GetClone()); }
|
||||
}
|
||||
|
||||
public void PastePawnKeyframes()
|
||||
{
|
||||
int originalWindowSize = Workspace.StageWindowSize;
|
||||
|
||||
List<int> actorsInvolved = Workspace.copiedKeyframes.Select(x => x.actorID)?.ToList();
|
||||
actorsInvolved = actorsInvolved?.Distinct()?.ToList();
|
||||
|
||||
if (actorsInvolved.NullOrEmpty()) { Debug.Log("Cannot paste keyframes - there were no copied keyframes to paste"); return; }
|
||||
if (actorsInvolved.Count > 1 && actorsInvolved.Contains(Workspace.ActorID) == false) { Debug.Log("Cannot paste keyframes - keyframes copied across multiple timelines can only be pasted back into these source timelines"); return; }
|
||||
|
||||
int earliestTick = actorsInvolved.Count == 1 ? Workspace.GetEarliestAtTickInCopiedKeyframes(actorsInvolved[0]) : Workspace.GetEarliestAtTickInCopiedKeyframes(Workspace.ActorID);
|
||||
if (earliestTick < 1) { Debug.Log("Unknown error occured during keyframe paste operation"); return; }
|
||||
|
||||
foreach (PawnKeyframe copiedKeyframe in Workspace.copiedKeyframes)
|
||||
{
|
||||
int tickToPasteAt = Workspace.StageTick + (copiedKeyframe.atTick.Value - earliestTick);
|
||||
|
||||
if (tickToPasteAt < 1) continue;
|
||||
if (tickToPasteAt > Workspace.StageWindowSize)
|
||||
{
|
||||
if (Workspace.stretchKeyframes)
|
||||
{ Workspace.GetCurrentAnimationStage().ResizeStageWindow(tickToPasteAt); }
|
||||
|
||||
else continue;
|
||||
}
|
||||
|
||||
int targetActorID = actorsInvolved.Count == 1 ? Workspace.ActorID : copiedKeyframe.actorID;
|
||||
|
||||
if (Workspace.DoesPawnKeyframeExistAtTick(Workspace.StageID, targetActorID, tickToPasteAt))
|
||||
{
|
||||
PawnKeyframe oldKeyframe = Workspace.GetPawnAnimationClip(targetActorID).Keyframes.First(x => x.atTick == tickToPasteAt);
|
||||
Workspace.GetAnimationClipThatOwnsKeyframe(oldKeyframe.keyframeID).RemovePawnKeyframe(oldKeyframe.keyframeID, true);
|
||||
}
|
||||
|
||||
PawnKeyframe clonedKeyframe = copiedKeyframe.GetClone();
|
||||
clonedKeyframe.GenerateKeyframeID(targetActorID);
|
||||
clonedKeyframe.atTick = tickToPasteAt;
|
||||
|
||||
PawnAnimationClip clip = Workspace.animationDef.AnimationStages[Workspace.StageID].AnimationClips[targetActorID];
|
||||
PawnKeyframe nextKeyframe = clip.Keyframes.FirstOrDefault(x => x.atTick > tickToPasteAt);
|
||||
|
||||
if (nextKeyframe != null)
|
||||
{ clip.Keyframes.Insert(clip.Keyframes.IndexOf(nextKeyframe), clonedKeyframe); }
|
||||
|
||||
else
|
||||
{ clip.Keyframes.Add(clonedKeyframe); }
|
||||
|
||||
clip.BuildSimpleCurves();
|
||||
|
||||
EventsManager.OnKeyframeCountChanged(clip);
|
||||
}
|
||||
|
||||
if (originalWindowSize != Workspace.StageWindowSize)
|
||||
{
|
||||
Workspace.GetCurrentAnimationStage().StretchStageWindow(originalWindowSize);
|
||||
Workspace.GetCurrentAnimationStage().ResizeStageWindow(originalWindowSize);
|
||||
}
|
||||
|
||||
Workspace.RecordEvent("Keyframe pasted");
|
||||
}
|
||||
|
||||
public void RemovePawnKeyframe(int keyframeID, bool force = false)
|
||||
{
|
||||
PawnKeyframe keyframe = Workspace.GetPawnKeyframe(keyframeID);
|
||||
if (keyframe == null || IsOwnerOfKeyframe(keyframeID) == false) return;
|
||||
|
||||
Keyframes.Remove(keyframe);
|
||||
|
||||
if (Workspace.GetAllPawnKeyframesAtTick(GetOwningActorID(), Constants.minTick).NullOrEmpty())
|
||||
{
|
||||
PawnKeyframe newKeyframe = new PawnKeyframe();
|
||||
newKeyframe.GenerateKeyframeID(GetOwningActorID());
|
||||
newKeyframe.atTick = Constants.minTick;
|
||||
Keyframes.Insert(0, newKeyframe);
|
||||
}
|
||||
|
||||
// Add missing second keyframe (if needed)
|
||||
if (Keyframes.Count == 1)
|
||||
{
|
||||
PawnKeyframe newKeyframe = Workspace.GetAllPawnKeyframesAtTick(GetOwningActorID(), Constants.minTick).First().GetClone();
|
||||
newKeyframe.atTick = 10;
|
||||
Keyframes.Add(newKeyframe);
|
||||
}
|
||||
|
||||
BuildSimpleCurves();
|
||||
|
||||
EventsManager.OnKeyframeCountChanged(this);
|
||||
Workspace.RecordEvent("Keyframe deletion");
|
||||
}
|
||||
|
||||
public bool IsOwnerOfKeyframe(int keyframeID)
|
||||
{
|
||||
return Keyframes.Any(x => x.keyframeID == keyframeID);
|
||||
}
|
||||
|
||||
// Pre-save / post-load
|
||||
public void OnPreSave()
|
||||
{
|
||||
var temp = Addons.Copy();
|
||||
Addons.Clear();
|
||||
|
||||
foreach (ActorAddon addon in temp)
|
||||
{
|
||||
if (addon.Render)
|
||||
{ addons.Add(addon); }
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPostLoad()
|
||||
{
|
||||
foreach (PawnKeyframe keyframe in Keyframes)
|
||||
{ keyframe.OnPostLoad(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bd5a477338567fb4cbb26b913a52ca65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
241
Source/Assets/Scripts/AnimationComponents/PawnKeyframe.cs
Normal file
241
Source/Assets/Scripts/AnimationComponents/PawnKeyframe.cs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
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 float? headFacing;
|
||||
public float? 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 : (float)(bodyAngle = 0f); }
|
||||
set { bodyAngle = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float HeadAngle
|
||||
{
|
||||
get { return headAngle.HasValue ? headAngle.Value : (float)(headAngle = 0f); }
|
||||
set { headAngle = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float HeadBob
|
||||
{
|
||||
get { return headBob.HasValue ? headBob.Value : (float)(headBob = 0f); }
|
||||
set { headBob = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float BodyOffsetX
|
||||
{
|
||||
get { return bodyOffsetX.HasValue ? bodyOffsetX.Value : (float)(bodyOffsetX = 0f); }
|
||||
set { bodyOffsetX = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float BodyOffsetZ
|
||||
{
|
||||
get { return bodyOffsetZ.HasValue ? bodyOffsetZ.Value : (float)(bodyOffsetZ = 0f); }
|
||||
set { bodyOffsetZ = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int HeadFacing
|
||||
{
|
||||
get { return headFacing.HasValue ? (int)headFacing.Value : (int)(headFacing = 2); }
|
||||
set { headFacing = (int)value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int BodyFacing
|
||||
{
|
||||
get { return bodyFacing.HasValue ? (int)bodyFacing.Value : (int)(bodyFacing = 2); }
|
||||
set { bodyFacing = (int)value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public float GenitalAngle
|
||||
{
|
||||
get { return genitalAngle.HasValue ? genitalAngle.Value : (float)(genitalAngle = 0f); }
|
||||
set { genitalAngle = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public bool Quiver
|
||||
{
|
||||
get { return quiver == true; }
|
||||
set { quiver = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public int TickDuration
|
||||
{
|
||||
get { return tickDuration.HasValue ? tickDuration.Value : (int)(tickDuration = 0); }
|
||||
set { tickDuration = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public string SoundEffect
|
||||
{
|
||||
get { return soundEffect; }
|
||||
set { soundEffect = value; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<string> Tags
|
||||
{
|
||||
get { return tags.NullOrEmpty() ? tags = new List<string>() : tags; }
|
||||
set { tags = value.NotNullOrEmpty() ? value : null; }
|
||||
}
|
||||
|
||||
[XmlIgnore] public List<AddonKeyframe> AddonKeyframes
|
||||
{
|
||||
get { return addonKeyframes.NullOrEmpty() ? addonKeyframes = new List<AddonKeyframe>() : addonKeyframes; }
|
||||
set { addonKeyframes = value.NotNullOrEmpty()? value : null; }
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
public void AdjustActor(Vector2 deltaOffset)
|
||||
{
|
||||
float deltaAngle = -deltaOffset.x * 33.3333f + deltaOffset.y * 33.3333f;
|
||||
int facing = deltaOffset.x < 0 ? 3 : deltaOffset.y < 0 ? 2 : deltaOffset.x > 0 ? 1 : 0;
|
||||
|
||||
switch (Workspace.actorManipulationMode)
|
||||
{
|
||||
case ActorManipulationMode.Pan: MoveActor(deltaOffset); break;
|
||||
case ActorManipulationMode.Rotate: RotateActor(deltaAngle); break;
|
||||
case ActorManipulationMode.Face: FaceActor(facing); break;
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveActor(Vector2 deltaOffset)
|
||||
{
|
||||
if (Workspace.selectedBodyPart == null)
|
||||
{
|
||||
BodyOffsetX += deltaOffset.x;
|
||||
BodyOffsetZ += deltaOffset.y;
|
||||
}
|
||||
|
||||
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
|
||||
{ HeadBob += deltaOffset.y; }
|
||||
|
||||
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
||||
Workspace.RecordEvent("Actor position / orientation");
|
||||
}
|
||||
|
||||
public void RotateActor(float deltaAngle)
|
||||
{
|
||||
if (Workspace.selectedBodyPart == null)
|
||||
{ BodyAngle += deltaAngle; }
|
||||
|
||||
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
|
||||
{ HeadAngle += deltaAngle; }
|
||||
|
||||
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "appendage")
|
||||
{ GenitalAngle -= deltaAngle; }
|
||||
|
||||
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
||||
Workspace.RecordEvent("Actor position / orientation");
|
||||
}
|
||||
|
||||
public void FaceActor(int facing)
|
||||
{
|
||||
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
|
||||
|
||||
if (Workspace.selectedBodyPart == null)
|
||||
{ keyframe.BodyFacing = facing; }
|
||||
|
||||
else if (Workspace.selectedBodyPart.bodyPart.ToLower() == "head")
|
||||
{ keyframe.HeadFacing = facing; }
|
||||
|
||||
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
||||
Workspace.RecordEvent("Actor position / orientation");
|
||||
}
|
||||
|
||||
public PawnKeyframe GetClone()
|
||||
{
|
||||
PawnKeyframe clone = this.Copy();
|
||||
clone.GenerateKeyframeID(actorID);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
// Pre-save / post-load
|
||||
public void OnPreSave()
|
||||
{
|
||||
SoundEffect = DefaultTags.soundDefs.Concat(CustomTags.soundDefs).Contains(SoundEffect) ? SoundEffect : null;
|
||||
|
||||
if (addonKeyframes.NotNullOrEmpty())
|
||||
{
|
||||
var temp = AddonKeyframes.Copy();
|
||||
addonKeyframes.Clear();
|
||||
|
||||
foreach (AddonKeyframe addonKeyframe in temp)
|
||||
{
|
||||
ActorAddon addon = Workspace.GetAnimationClipThatOwnsKeyframe(keyframeID).GetActorAddon(addonKeyframe.AddonName);
|
||||
|
||||
if (addon.Render)
|
||||
{ addonKeyframes.Add(addonKeyframe.Copy()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPostLoad()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c8ced38490f6b174984453dc3336a543
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
151
Source/Assets/Scripts/AnimationComponents/PawnRaceDef.cs
Normal file
151
Source/Assets/Scripts/AnimationComponents/PawnRaceDef.cs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
[Serializable]
|
||||
public class PawnRaceDef
|
||||
{
|
||||
// Local data
|
||||
public string defName;
|
||||
public bool isHumanoid = true;
|
||||
public float scale = 1f;
|
||||
|
||||
public List<MultiDirectionalGraphic> bodyTypeGraphics = new List<MultiDirectionalGraphic>();
|
||||
public MultiDirectionalGraphic headGraphics = new MultiDirectionalGraphic();
|
||||
|
||||
// Constructors
|
||||
public PawnRaceDef() { }
|
||||
|
||||
public PawnRaceDef(string defName)
|
||||
{
|
||||
this.defName = defName;
|
||||
}
|
||||
|
||||
// Methods
|
||||
public Sprite GetHeadGraphic(CardinalDirection facing)
|
||||
{
|
||||
if (HasValidHeadGraphicPath(facing) == false)
|
||||
{ return null; }
|
||||
|
||||
switch (facing)
|
||||
{
|
||||
case CardinalDirection.North: return headGraphics.northGraphic.sprite;
|
||||
case CardinalDirection.East: return headGraphics.eastGraphic.sprite;
|
||||
case CardinalDirection.South: return headGraphics.southGraphic.sprite;
|
||||
default: return headGraphics.eastGraphic.sprite;
|
||||
}
|
||||
}
|
||||
|
||||
public Sprite GetBodyTypeGraphic(CardinalDirection facing, string bodyType = "None")
|
||||
{
|
||||
if (HasValidBodyTypeGraphicPath(facing, bodyType) == false)
|
||||
{ return null; }
|
||||
|
||||
MultiDirectionalGraphic bodyTypeGraphic = bodyTypeGraphics.FirstOrDefault(x => x.bodyType == bodyType);
|
||||
|
||||
if (bodyTypeGraphic == null)
|
||||
{
|
||||
bodyTypeGraphic = new MultiDirectionalGraphic(bodyType);
|
||||
bodyTypeGraphics.Add(bodyTypeGraphic);
|
||||
}
|
||||
|
||||
switch (facing)
|
||||
{
|
||||
case CardinalDirection.North: return bodyTypeGraphic.northGraphic.sprite;
|
||||
case CardinalDirection.East: return bodyTypeGraphic.eastGraphic.sprite;
|
||||
case CardinalDirection.South: return bodyTypeGraphic.southGraphic.sprite;
|
||||
default: return bodyTypeGraphic.eastGraphic.sprite;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetHeadGraphicPath(CardinalDirection facing)
|
||||
{
|
||||
if (HasValidHeadGraphicPath(facing) == false)
|
||||
{ return "Invalid file path"; }
|
||||
|
||||
switch (facing)
|
||||
{
|
||||
case CardinalDirection.North: return headGraphics.northGraphic.path;
|
||||
case CardinalDirection.East: return headGraphics.eastGraphic.path;
|
||||
case CardinalDirection.South: return headGraphics.southGraphic.path;
|
||||
default: return headGraphics.eastGraphic.path;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetBodyTypeGraphicPath(CardinalDirection facing, string bodyType = "None")
|
||||
{
|
||||
if (HasValidBodyTypeGraphicPath(facing, bodyType) == false)
|
||||
{ return "Invalid file path"; }
|
||||
|
||||
MultiDirectionalGraphic bodyTypeGraphic = bodyTypeGraphics.FirstOrDefault(x => x.bodyType == bodyType);
|
||||
|
||||
if (bodyTypeGraphic == null)
|
||||
{
|
||||
bodyTypeGraphic = new MultiDirectionalGraphic(bodyType);
|
||||
bodyTypeGraphics.Add(bodyTypeGraphic);
|
||||
}
|
||||
|
||||
switch (facing)
|
||||
{
|
||||
case CardinalDirection.North: return bodyTypeGraphic.northGraphic.path;
|
||||
case CardinalDirection.East: return bodyTypeGraphic.eastGraphic.path;
|
||||
case CardinalDirection.South: return bodyTypeGraphic.southGraphic.path;
|
||||
default: return bodyTypeGraphic.eastGraphic.path;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHeadGraphicPath(string path, CardinalDirection facing)
|
||||
{
|
||||
switch (facing)
|
||||
{
|
||||
case CardinalDirection.North: headGraphics.northGraphic.SetPath(path); break;
|
||||
case CardinalDirection.East: headGraphics.eastGraphic.SetPath(path); break;
|
||||
case CardinalDirection.South: headGraphics.southGraphic.SetPath(path); break;
|
||||
default: headGraphics.eastGraphic.SetPath(path); break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBodyTypeGraphicPath(string path, CardinalDirection facing, string bodyType = "None")
|
||||
{
|
||||
MultiDirectionalGraphic bodyTypeGraphic = bodyTypeGraphics.FirstOrDefault(x => x.bodyType == bodyType);
|
||||
|
||||
if (bodyTypeGraphic == null)
|
||||
{
|
||||
bodyTypeGraphic = new MultiDirectionalGraphic(bodyType);
|
||||
bodyTypeGraphics.Add(bodyTypeGraphic);
|
||||
}
|
||||
|
||||
switch (facing)
|
||||
{
|
||||
case CardinalDirection.North: bodyTypeGraphic.northGraphic.SetPath(path); break;
|
||||
case CardinalDirection.East: bodyTypeGraphic.eastGraphic.SetPath(path); break;
|
||||
case CardinalDirection.South:bodyTypeGraphic.southGraphic.SetPath(path); break;
|
||||
default: bodyTypeGraphic.eastGraphic.SetPath(path); break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasValidHeadGraphicPath(CardinalDirection facing)
|
||||
{
|
||||
return headGraphics.HasValidPathForDirection(facing);
|
||||
}
|
||||
|
||||
public bool HasValidBodyTypeGraphicPath(CardinalDirection facing, string bodyType = "None")
|
||||
{
|
||||
MultiDirectionalGraphic bodyTypeGraphic = bodyTypeGraphics.FirstOrDefault(x => x.bodyType == bodyType);
|
||||
|
||||
if (bodyTypeGraphic == null)
|
||||
{
|
||||
bodyTypeGraphic = new MultiDirectionalGraphic(bodyType);
|
||||
bodyTypeGraphics.Add(bodyTypeGraphic);
|
||||
}
|
||||
|
||||
return bodyTypeGraphic.HasValidPathForDirection(facing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 187aef38ea296184b93265071536969c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
49
Source/Assets/Scripts/AnimationComponents/PawnRaceOffset.cs
Normal file
49
Source/Assets/Scripts/AnimationComponents/PawnRaceOffset.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
[Serializable]
|
||||
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; }
|
||||
|
||||
// Constructors
|
||||
public PawnRaceOffset() { }
|
||||
|
||||
public PawnRaceOffset(string defName)
|
||||
{
|
||||
this.defName = defName;
|
||||
}
|
||||
|
||||
// Methods
|
||||
public void SetOffset(Vector2 raceOffset)
|
||||
{
|
||||
offset = "(" + raceOffset.x + ", " + raceOffset.y + ")";
|
||||
}
|
||||
|
||||
public Vector3 GetOffset()
|
||||
{
|
||||
string raceOffset = offset;
|
||||
raceOffset = raceOffset.Trim();
|
||||
raceOffset = raceOffset.Replace("(", "");
|
||||
raceOffset = raceOffset.Replace(")", "");
|
||||
var raceOffsets = raceOffset.Split(',');
|
||||
|
||||
return new Vector3(float.Parse(raceOffsets[0]), 0f, float.Parse(raceOffsets[1]));
|
||||
}
|
||||
|
||||
public bool OffsetIsZero()
|
||||
{
|
||||
Vector3 vec = GetOffset();
|
||||
return Mathf.Approximately(vec.x, 0f) && Mathf.Approximately(vec.y, 0f) && Mathf.Approximately(vec.x, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 24eafaf092974414ca90bfd4a8d2e4ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
55
Source/Assets/Scripts/AudioController.cs
Normal file
55
Source/Assets/Scripts/AudioController.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AudioController : MonoBehaviour
|
||||
{
|
||||
private AudioSource audioSource;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
}
|
||||
|
||||
public void PlaySound(string soundDefName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(soundDefName) || soundDefName.ToLower() == "none")
|
||||
{ return; }
|
||||
|
||||
SoundDef soundDef = SoundDefs.GetNamed(soundDefName);
|
||||
|
||||
if (soundDef == null)
|
||||
{
|
||||
Debug.LogWarning("Could not play audio clip - SoundDef '" + soundDefName + "' was not found");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (SubSoundDef subSoundDef in soundDef.subSounds)
|
||||
{
|
||||
AudioGrain audioGrain = subSoundDef.grains[UnityEngine.Random.Range(0, subSoundDef.grains.Count - 1)];
|
||||
|
||||
string fullPath = Path.GetFullPath(Path.Combine(Application.streamingAssetsPath, "Sounds", audioGrain.clipPath)) + ".wav";
|
||||
AudioClip audioClip = SoundDefs.GetAudioClip(fullPath);
|
||||
|
||||
if (audioClip == null) return;
|
||||
|
||||
// Set up audio source to play
|
||||
audioSource.clip = audioClip;
|
||||
audioSource.volume = UnityEngine.Random.Range(subSoundDef.volumeRange.min, subSoundDef.volumeRange.max);
|
||||
audioSource.minDistance = subSoundDef.distRange.min;
|
||||
audioSource.maxDistance = subSoundDef.distRange.max;
|
||||
audioSource.pitch = UnityEngine.Random.Range(subSoundDef.pitchRange.min, subSoundDef.pitchRange.max);
|
||||
|
||||
audioSource.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/AudioController.cs.meta
Normal file
11
Source/Assets/Scripts/AudioController.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e5ead9ecb50b3174c8e2716f7fe1ac3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/Data.meta
Normal file
8
Source/Assets/Scripts/Data.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0288c6c177cf10045b08f5d2b9ad52c4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Source/Assets/Scripts/Data/ActorPosition.cs
Normal file
43
Source/Assets/Scripts/Data/ActorPosition.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorPosition
|
||||
{
|
||||
public float bodyOffsetX;
|
||||
public float bodyOffsetZ;
|
||||
public float bodyAngle;
|
||||
public float headBob;
|
||||
public float headAngle;
|
||||
public float genitalAngle;
|
||||
|
||||
public int bodyFacing;
|
||||
public int headFacing;
|
||||
|
||||
public ActorPosition(int actorID, int atTick)
|
||||
{
|
||||
PawnAnimationClip clip = Workspace.GetPawnAnimationClip(actorID);
|
||||
|
||||
float clipPercent = (float)(atTick % clip.duration) / clip.duration;
|
||||
if (atTick > Constants.minTick && atTick == clip.duration) clipPercent = 1f;
|
||||
|
||||
if (Workspace.GetCurrentAnimationStage().IsLooping == false)
|
||||
{ clipPercent = (float)atTick / clip.duration; }
|
||||
|
||||
bodyOffsetX = clip.BodyOffsetX.Evaluate(clipPercent);
|
||||
bodyOffsetZ = clip.BodyOffsetZ.Evaluate(clipPercent);
|
||||
bodyAngle = clip.BodyAngle.Evaluate(clipPercent);
|
||||
headBob = clip.HeadBob.Evaluate(clipPercent);
|
||||
headAngle = clip.HeadAngle.Evaluate(clipPercent);
|
||||
genitalAngle = clip.GenitalAngle.Evaluate(clipPercent);
|
||||
|
||||
bodyFacing = (int)clip.BodyFacing.Evaluate(clipPercent);
|
||||
headFacing = (int)clip.HeadFacing.Evaluate(clipPercent);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/Data/ActorPosition.cs.meta
Normal file
11
Source/Assets/Scripts/Data/ActorPosition.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e2a9b0bb71978ea49909b3e1814303a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
53
Source/Assets/Scripts/Data/Constants.cs
Normal file
53
Source/Assets/Scripts/Data/Constants.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
// Project data
|
||||
public static string currentVersion = "1.0.0";
|
||||
public static string projectHome = "https://gitgud.io/AbstractConcept/rimworld-animation-studio";
|
||||
public static string projectWiki = "https://gitgud.io/AbstractConcept/rimworld-animation-studio/-/wikis/home";
|
||||
|
||||
// Actions
|
||||
public static float actionRepeatSpeed = 0.250f;
|
||||
|
||||
// Animation defaults
|
||||
public static int defaultAnimationClipLength = 600;
|
||||
public static int minTick = 1;
|
||||
public static int minAnimationClipLength = 5;
|
||||
public static int maxAnimationClipLength = 9999;
|
||||
|
||||
// Lists
|
||||
public static Dictionary<string, string> bodyPartAnchorNames = new Dictionary<string, string>()
|
||||
{
|
||||
{ "none", "None" },
|
||||
{ "torso", "Torso"},
|
||||
{ "head", "Head"},
|
||||
{ "groin", "Groin"},
|
||||
{ "left breast", "Breast (left)"},
|
||||
{ "right breast", "Breast (right)"},
|
||||
};
|
||||
|
||||
// Colors used
|
||||
public static Color ColorWhite = new Color(1f, 1f, 1f);
|
||||
public static Color ColorGreen = new Color(0f, 1f, 0f);
|
||||
public static Color ColorLightGreen = new Color(0.75f, 1f, 0.75f);
|
||||
public static Color ColorGoldYellow = new Color(1f, 0.85f, 0f);
|
||||
public static Color ColorDarkGold = new Color(0.75f, 0.64f, 0f);
|
||||
public static Color ColorLightGrey = new Color(0.9f, 0.9f, 0.9f);
|
||||
public static Color ColorMidGrey = new Color(0.75f, 0.75f, 0.75f);
|
||||
public static Color ColorGrey = new Color(0.5f, 0.5f, 0.5f);
|
||||
public static Color ColorDarkGrey = new Color(0.2f, 0.2f, 0.2f);
|
||||
public static Color ColorPink = new Color(1.0f, 0.5f, 0.5f);
|
||||
public static Color ColorOrange = new Color(1.0f, 0.7f, 0.0f);
|
||||
public static Color ColorRichOrange = new Color(1.0f, 0.4f, 0.1f);
|
||||
public static Color ColorCyan = new Color(0.0f, 1.0f, 1.0f);
|
||||
public static Color ColorPurple = new Color(0.85f, 0.0f, 1.0f);
|
||||
public static Color ColorGhost = new Color(0.5f, 0f, 0f, 0.5f);
|
||||
public static Color ColorRed = new Color(0.9f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/Data/Constants.cs.meta
Normal file
11
Source/Assets/Scripts/Data/Constants.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5736ef91f32211942abe6c2f765da6c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
44
Source/Assets/Scripts/Data/DefaultTags.cs
Normal file
44
Source/Assets/Scripts/Data/DefaultTags.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public static class DefaultTags
|
||||
{
|
||||
public static List<string> defNames = new List<string>() { "Human", "Wolf_Timber", "Horse" };
|
||||
public static List<string> bodyParts = new List<string>() { "Penis", "Vagina", "Anus", "Breasts", "Mouth" };
|
||||
public static List<string> bodyDefTypes = new List<string>() { "Human", "Bird", "BeetleLike", "BeetleLikeWithClaw", "MechanicalCentipede", "MechanicalTermite", "Lancer", "Pikeman", "Monkey", "QuadrupedAnimalWithClawsTailAndJowl", "QuadrupedAnimalWithHooves", "QuadrupedAnimalWithHoovesAndHorn", "QuadrupedAnimalWithHoovesAndHump", "QuadrupedAnimalWithHoovesAndTusks", "QuadrupedAnimalWithHoovesTusksAndTrunk", "QuadrupedAnimalWithPaws", "QuadrupedAnimalWithPawsAndTail", "Scyther", "Snake", "TurtleLike" };
|
||||
public static List<string> sexTypes = new List<string>() { "None", "Vaginal", "Anal", "Oral", "Masturbation", "DoublePenetration", "Boobjob", "Handjob", "Footjob", "Fingering", "Scissoring", "MutualMasturbation", "Fisting", "MechImplant", "Rimming", "Fellatio", "Cunnilingus", "Sixtynine" };
|
||||
public static List<string> interactionDefTypes = new List<string>() { "Bestiality_Anal", "Bestiality_Double_Penetration_M", "Bestiality_Oral", "Bestiality_Vaginal", "Breeding_Lick", "Breeding_Fingering", "Breeding_OralS", "Bestiality_Reverse_Anal", "Bestiality_Reverse_Double_Penetration_M", "Bestiality_Reverse_Handjob", "Bestiality_Reverse_Oral", "Bestiality_Reverse_Oral_Service", "Bestiality_Reverse_Vaginal", "AnimalSexChat", "Masturbation_AutoBreastjob", "Masturbation_AutoFellatio", "Masturbation_Breastjob", "Masturbation_HandjobA", "Masturbation_HandjobP", "Masturbation_HandjobV", "rjw_interaction_template", "Necro_Anal", "Necro_DoublePenetration", "Necro_DoublePenetrationM", "Necro_Vaginal", "Necro_Reverse_Anal", "Necro_Reverse_DoublePenetration", "Necro_Reverse_DoublePenetrationM", "Necro_Reverse_Vaginal", "Rape_MechImplant", "Rape_Anal", "Rape_Beakjob", "Rape_Breastjob", "Rape_Cunnilingus", "Rape_DoublePenetration", "Rape_DoublePenetrationM", "Rape_Fellatio", "Rape_Fingering", "Rape_Fisting", "Rape_Footjob", "Rape_Handjob", "Rape_Oral", "Rape_Rimming", "Rape_Scissoring", "Rape_Vaginal", "Rape_Reverse_Anal", "Rape_Reverse_Beakjob", "Rape_Reverse_Breastjob", "Rape_Reverse_Cunnilingus", "Rape_Reverse_DoublePenetration", "Rape_Reverse_DoublePenetrationM", "Rape_Reverse_Fellatio", "Rape_Reverse_Fingering", "Rape_Reverse_Fisting", "Rape_Reverse_Footjob", "Rape_Reverse_Handjob", "Rape_Reverse_Rimming", "Rape_Reverse_Scissoring", "Rape_Reverse_Vaginal", "Sex_Reverse_Anal", "Sex_Reverse_Beakjob", "Sex_Reverse_Breastjob", "Sex_Reverse_Cunnilingus", "Sex_Reverse_DoublePenetration", "Sex_Reverse_DoublePenetrationM", "Sex_Reverse_Fellatio", "Sex_Reverse_Fingering", "Sex_Reverse_Fisting", "Sex_Reverse_Footjob", "Sex_Reverse_Handjob", "Sex_Reverse_Rimming", "Sex_Reverse_Vaginal", "Sex_Anal", "Sex_Beakjob", "Sex_Breastjob", "Sex_Cunnilingus", "Sex_DoublePenetration", "Sex_DoublePenetrationM", "Sex_Fellatio", "Sex_Fingering", "Sex_Fisting", "Sex_Footjob", "Sex_Handjob", "Sex_MutualMasturbation", "Sex_Rimming", "Sex_Scissoring", "Sex_Sixtynine", "Sex_Vaginal" };
|
||||
public static List<string> soundDefs = new List<string>() { "None", "Sex", "Fuck", "Slimy", "Suck", "Cum" };
|
||||
public static List<string> actorLayers = new List<string>() { "LayingPawn", "PawnRope", "Projectile", "Pawn", "PawnUnused" };
|
||||
public static List<string> bodyTypes = new List<string>() { "Male", "Female", "Fat", "Hulk", "Thin" };
|
||||
}
|
||||
|
||||
public static class CustomTags
|
||||
{
|
||||
public static List<string> defNames = new List<string>();
|
||||
public static List<string> bodyParts = new List<string>();
|
||||
public static List<string> bodyDefTypes = new List<string>();
|
||||
public static List<string> sexTypes = new List<string>();
|
||||
public static List<string> interactionDefTypes = new List<string>();
|
||||
public static List<string> soundDefs = new List<string>();
|
||||
public static List<string> bodyTypes = new List<string>();
|
||||
}
|
||||
|
||||
[XmlRoot("CustomTagsHelper", IsNullable = false)]
|
||||
public class CustomTagsHelper
|
||||
{
|
||||
[XmlArray("defNames"), XmlArrayItem("li")] public List<string> defNames = new List<string>();
|
||||
[XmlArray("bodyParts"), XmlArrayItem("li")] public List<string> bodyParts = new List<string>();
|
||||
[XmlArray("bodyDefTypes"), XmlArrayItem("li")] public List<string> bodyDefTypes = new List<string>();
|
||||
[XmlArray("sexTypes"), XmlArrayItem("li")] public List<string> sexTypes = new List<string>();
|
||||
[XmlArray("interactionDefTypes"), XmlArrayItem("li")] public List<string> interactionDefTypes = new List<string>();
|
||||
[XmlArray("soundDefs"), XmlArrayItem("li")] public List<string> soundDefs = new List<string>();
|
||||
[XmlArray("bodyTypes"), XmlArrayItem("li")] public static List<string> bodyTypes = new List<string>();
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/Data/DefaultTags.cs.meta
Normal file
11
Source/Assets/Scripts/Data/DefaultTags.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d2a4f1a7ea83f0544a350664fba7fc49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Source/Assets/Scripts/Data/Enums.cs
Normal file
21
Source/Assets/Scripts/Data/Enums.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
public enum ActorManipulationMode
|
||||
{
|
||||
Pan,
|
||||
Rotate,
|
||||
Face,
|
||||
}
|
||||
|
||||
public enum ActorGender
|
||||
{
|
||||
Female,
|
||||
None,
|
||||
Male,
|
||||
}
|
||||
|
||||
public enum CardinalDirection
|
||||
{
|
||||
North,
|
||||
East,
|
||||
South,
|
||||
West,
|
||||
}
|
||||
11
Source/Assets/Scripts/Data/Enums.cs.meta
Normal file
11
Source/Assets/Scripts/Data/Enums.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ee120e06e7d8c1f45ad58fc6baab0d47
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
41
Source/Assets/Scripts/Data/SoundDef.cs
Normal file
41
Source/Assets/Scripts/Data/SoundDef.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
[Serializable]
|
||||
public class SoundDef
|
||||
{
|
||||
public string defName;
|
||||
public int maxSimultaneous;
|
||||
public int maxVoices;
|
||||
[XmlArray("subSounds"), XmlArrayItem("li")] public List<SubSoundDef> subSounds;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SubSoundDef
|
||||
{
|
||||
[XmlArray("grains"), XmlArrayItem("li")] public List<AudioGrain> grains;
|
||||
public FloatRange volumeRange;
|
||||
public FloatRange pitchRange;
|
||||
public FloatRange distRange;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AudioGrain
|
||||
{
|
||||
public string clipPath;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class FloatRange
|
||||
{
|
||||
public float min;
|
||||
public float max;
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/Data/SoundDef.cs.meta
Normal file
11
Source/Assets/Scripts/Data/SoundDef.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 809f70f9c2ecfe64e80d88134af30c81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/DefParents.meta
Normal file
8
Source/Assets/Scripts/DefParents.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c9a9e093aedeac24687d421f33a98e94
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Source/Assets/Scripts/DefParents/ActorAddonDefs.cs
Normal file
30
Source/Assets/Scripts/DefParents/ActorAddonDefs.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public static class ActorAddonDefs
|
||||
{
|
||||
public static List<ActorAddonDef> allDefs = new List<ActorAddonDef>();
|
||||
|
||||
public static ActorAddonDef GetNamed(string addonName)
|
||||
{
|
||||
return allDefs.FirstOrDefault(x => x.addonName == addonName);
|
||||
}
|
||||
|
||||
public static void AddDef(ActorAddonDef actorAddonDef)
|
||||
{
|
||||
if (allDefs.Any(x => x.addonName == actorAddonDef.addonName)) return;
|
||||
|
||||
allDefs.Add(actorAddonDef);
|
||||
}
|
||||
|
||||
public static void OnLoad()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/DefParents/ActorAddonDefs.cs.meta
Normal file
11
Source/Assets/Scripts/DefParents/ActorAddonDefs.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fc7248e80b340a42a688378a9cc2296
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
16
Source/Assets/Scripts/DefParents/AnimationDefs.cs
Normal file
16
Source/Assets/Scripts/DefParents/AnimationDefs.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
[XmlRoot("Defs", IsNullable = false)]
|
||||
public class AnimationDefs
|
||||
{
|
||||
[XmlElement("Rimworld_Animations.AnimationDef")]
|
||||
public List<AnimationDef> animationDefs = new List<AnimationDef>();
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/DefParents/AnimationDefs.cs.meta
Normal file
11
Source/Assets/Scripts/DefParents/AnimationDefs.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e65e28553800cf489ca2b0bc7e37408
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
53
Source/Assets/Scripts/DefParents/PawnRaceDefs.cs
Normal file
53
Source/Assets/Scripts/DefParents/PawnRaceDefs.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public static class PawnRaceDefs
|
||||
{
|
||||
public static List<PawnRaceDef> allDefs = new List<PawnRaceDef>();
|
||||
|
||||
public static PawnRaceDef GetNamed(string pawnRaceDef)
|
||||
{
|
||||
return allDefs.FirstOrDefault(x => x.defName == pawnRaceDef);
|
||||
}
|
||||
|
||||
public static void AddDef(PawnRaceDef pawnRaceDef)
|
||||
{
|
||||
if (allDefs.Any(x => x.defName == pawnRaceDef.defName)) return;
|
||||
|
||||
allDefs.Add(pawnRaceDef);
|
||||
}
|
||||
|
||||
public static void OnLoad()
|
||||
{
|
||||
List<string> allTags = DefaultTags.bodyTypes.Concat(CustomTags.bodyTypes).ToList();
|
||||
allTags.Add("None");
|
||||
|
||||
List<CardinalDirection> facings = new List<CardinalDirection>() { CardinalDirection.North, CardinalDirection.East, CardinalDirection.South };
|
||||
string path;
|
||||
|
||||
foreach (PawnRaceDef pawnRaceDef in allDefs)
|
||||
{
|
||||
foreach (CardinalDirection facing in facings)
|
||||
{
|
||||
foreach (string bodyType in allTags)
|
||||
{
|
||||
path = pawnRaceDef.GetBodyTypeGraphicPath(facing, bodyType);
|
||||
|
||||
if (string.IsNullOrEmpty(path) == false)
|
||||
{ pawnRaceDef.SetBodyTypeGraphicPath(path, facing, bodyType); }
|
||||
}
|
||||
|
||||
path = pawnRaceDef.GetHeadGraphicPath(facing);
|
||||
|
||||
if (string.IsNullOrEmpty(path) == false)
|
||||
{ pawnRaceDef.SetHeadGraphicPath(path, facing); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/DefParents/PawnRaceDefs.cs.meta
Normal file
11
Source/Assets/Scripts/DefParents/PawnRaceDefs.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53e7f97b4bb5a3441884e4795d7e9391
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
54
Source/Assets/Scripts/DefParents/SoundDefs.cs
Normal file
54
Source/Assets/Scripts/DefParents/SoundDefs.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class SoundDefs
|
||||
{
|
||||
public static List<SoundDef> allDefs = new List<SoundDef>();
|
||||
public static Dictionary<string, AudioClip> audioClips = new Dictionary<string, AudioClip>();
|
||||
|
||||
public static SoundDef GetNamed(string defName)
|
||||
{
|
||||
return allDefs.FirstOrDefault(x => x.defName == defName);
|
||||
}
|
||||
|
||||
public static void AddDef(SoundDef soundDef)
|
||||
{
|
||||
if (allDefs.Any(x => x.defName == soundDef.defName)) return;
|
||||
|
||||
allDefs.Add(soundDef);
|
||||
}
|
||||
public static void AddAudioClip(string path, AudioClip audioClip)
|
||||
{
|
||||
if (audioClips.TryGetValue(path, out AudioClip _audioClip) == false)
|
||||
{ audioClips.Add(path, audioClip); }
|
||||
|
||||
else
|
||||
{ audioClips[path] = audioClip; }
|
||||
}
|
||||
|
||||
public static AudioClip GetAudioClip(string path)
|
||||
{
|
||||
if (audioClips.TryGetValue(path, out AudioClip audioClip) == false)
|
||||
{
|
||||
Debug.LogWarning("Could not find audio clip '" + path + "'");
|
||||
return null;
|
||||
}
|
||||
|
||||
return audioClip;
|
||||
}
|
||||
|
||||
public static void OnLoad()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/DefParents/SoundDefs.cs.meta
Normal file
11
Source/Assets/Scripts/DefParents/SoundDefs.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2a2030426d02b0346b4ee939407ca0f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/Extensions.meta
Normal file
8
Source/Assets/Scripts/Extensions.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b51dcdd346a0cc469b21a6ffe1592fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Source/Assets/Scripts/Extensions/IListExtensions.cs
Normal file
36
Source/Assets/Scripts/Extensions/IListExtensions.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public static class IListExtensions
|
||||
{
|
||||
public static bool NullOrEmpty<T>(this IList<T> list)
|
||||
{
|
||||
return list == null || list.Any() == false;
|
||||
}
|
||||
|
||||
public static bool NotNullOrEmpty<T>(this IList<T> list)
|
||||
{
|
||||
return NullOrEmpty<T>(list) == false;
|
||||
}
|
||||
|
||||
public static void AddDistinct<T>(this IList<T> list, T item)
|
||||
{
|
||||
if (item == null || list.Contains(item))
|
||||
{ return; }
|
||||
|
||||
list.Add(item);
|
||||
}
|
||||
|
||||
public static void AddRangeDistinct<T>(this IList<T> list, IEnumerable<T> collection)
|
||||
{
|
||||
if (collection == null)
|
||||
{ return; }
|
||||
|
||||
foreach(T item in collection)
|
||||
{ AddDistinct(list, item); }
|
||||
}
|
||||
}
|
||||
|
||||
11
Source/Assets/Scripts/Extensions/IListExtensions.cs.meta
Normal file
11
Source/Assets/Scripts/Extensions/IListExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a030b2589ac872442a0d1aea7f5b5e70
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
131
Source/Assets/Scripts/Extensions/ObjectExtensions.cs
Normal file
131
Source/Assets/Scripts/Extensions/ObjectExtensions.cs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.ArrayExtensions;
|
||||
|
||||
namespace System
|
||||
{
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
public static bool IsPrimitive(this Type type)
|
||||
{
|
||||
if (type == typeof(String)) return true;
|
||||
return (type.IsValueType & type.IsPrimitive);
|
||||
}
|
||||
|
||||
public static Object Copy(this Object originalObject)
|
||||
{
|
||||
return InternalCopy(originalObject, new Dictionary<Object, Object>(new ReferenceEqualityComparer()));
|
||||
}
|
||||
|
||||
private static Object InternalCopy(Object originalObject, IDictionary<Object, Object> visited)
|
||||
{
|
||||
if (originalObject == null) return null;
|
||||
var typeToReflect = originalObject.GetType();
|
||||
if (IsPrimitive(typeToReflect)) return originalObject;
|
||||
if (visited.ContainsKey(originalObject)) return visited[originalObject];
|
||||
if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
|
||||
var cloneObject = CloneMethod.Invoke(originalObject, null);
|
||||
if (typeToReflect.IsArray)
|
||||
{
|
||||
var arrayType = typeToReflect.GetElementType();
|
||||
if (IsPrimitive(arrayType) == false)
|
||||
{
|
||||
Array clonedArray = (Array)cloneObject;
|
||||
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
|
||||
}
|
||||
|
||||
}
|
||||
visited.Add(originalObject, cloneObject);
|
||||
CopyFields(originalObject, visited, cloneObject, typeToReflect);
|
||||
RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
|
||||
return cloneObject;
|
||||
}
|
||||
|
||||
private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
|
||||
{
|
||||
if (typeToReflect.BaseType != null)
|
||||
{
|
||||
RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
|
||||
CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
|
||||
{
|
||||
foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags))
|
||||
{
|
||||
if (filter != null && filter(fieldInfo) == false) continue;
|
||||
if (IsPrimitive(fieldInfo.FieldType)) continue;
|
||||
var originalFieldValue = fieldInfo.GetValue(originalObject);
|
||||
var clonedFieldValue = InternalCopy(originalFieldValue, visited);
|
||||
fieldInfo.SetValue(cloneObject, clonedFieldValue);
|
||||
}
|
||||
}
|
||||
public static T Copy<T>(this T original)
|
||||
{
|
||||
return (T)Copy((Object)original);
|
||||
}
|
||||
}
|
||||
|
||||
public class ReferenceEqualityComparer : EqualityComparer<Object>
|
||||
{
|
||||
public override bool Equals(object x, object y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
public override int GetHashCode(object obj)
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
return obj.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
namespace ArrayExtensions
|
||||
{
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
public static void ForEach(this Array array, Action<Array, int[]> action)
|
||||
{
|
||||
if (array.LongLength == 0) return;
|
||||
ArrayTraverse walker = new ArrayTraverse(array);
|
||||
do action(array, walker.Position);
|
||||
while (walker.Step());
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayTraverse
|
||||
{
|
||||
public int[] Position;
|
||||
private int[] maxLengths;
|
||||
|
||||
public ArrayTraverse(Array array)
|
||||
{
|
||||
maxLengths = new int[array.Rank];
|
||||
for (int i = 0; i < array.Rank; ++i)
|
||||
{
|
||||
maxLengths[i] = array.GetLength(i) - 1;
|
||||
}
|
||||
Position = new int[array.Rank];
|
||||
}
|
||||
|
||||
public bool Step()
|
||||
{
|
||||
for (int i = 0; i < Position.Length; ++i)
|
||||
{
|
||||
if (Position[i] < maxLengths[i])
|
||||
{
|
||||
Position[i]++;
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
Position[j] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/Extensions/ObjectExtensions.cs.meta
Normal file
11
Source/Assets/Scripts/Extensions/ObjectExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f7f234a811195334b8390740155d41dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Source/Assets/Scripts/Extensions/TransformExtensions.cs
Normal file
24
Source/Assets/Scripts/Extensions/TransformExtensions.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static Transform FindDeepChild(this Transform parent, string childName)
|
||||
{
|
||||
Queue<Transform> queue = new Queue<Transform>();
|
||||
queue.Enqueue(parent);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var c = queue.Dequeue();
|
||||
if (c.name == childName)
|
||||
return c;
|
||||
foreach (Transform t in c)
|
||||
queue.Enqueue(t);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
11
Source/Assets/Scripts/Extensions/TransformExtensions.cs.meta
Normal file
11
Source/Assets/Scripts/Extensions/TransformExtensions.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 859d0be8594b5bc46aa3fb70f78149ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
Source/Assets/Scripts/Extensions/Vector3Extension.cs
Normal file
17
Source/Assets/Scripts/Extensions/Vector3Extension.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public static class Vector3Extension
|
||||
{
|
||||
public static Vector3 FlipAxes(this Vector3 v)
|
||||
{
|
||||
return new Vector3(v.x, v.z, v.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/Extensions/Vector3Extension.cs.meta
Normal file
11
Source/Assets/Scripts/Extensions/Vector3Extension.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b49e51e0077dee41a199e4e81f26e32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/GUI.meta
Normal file
8
Source/Assets/Scripts/GUI.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e344d7c1fea27134ca49b05d9cac249c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Source/Assets/Scripts/GUI/ActorManipulator.cs
Normal file
28
Source/Assets/Scripts/GUI/ActorManipulator.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorManipulator : MonoBehaviour
|
||||
{
|
||||
public ActorManipulationMode actorManipulationMode;
|
||||
|
||||
private Image button;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
button = GetComponent<Image>();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Workspace.actorManipulationMode == actorManipulationMode)
|
||||
{ button.color = Constants.ColorGoldYellow; }
|
||||
|
||||
else
|
||||
{ button.color = Constants.ColorWhite; }
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/ActorManipulator.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/ActorManipulator.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f627be313335bf748b33eea98541581d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/GUI/Actors.meta
Normal file
8
Source/Assets/Scripts/GUI/Actors.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3c96477ef8cc42468ea6a39764a2e81
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
126
Source/Assets/Scripts/GUI/Actors/ActorBody.cs
Normal file
126
Source/Assets/Scripts/GUI/Actors/ActorBody.cs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorBody : MonoBehaviour, IPointerClickHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
public int actorID;
|
||||
public SpriteRenderer bodyRenderer;
|
||||
public ActorBodyPart actorBodyPartPrefab;
|
||||
|
||||
private Vector3 dragDelta = new Vector3();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
EventsManager.onActorBodyPartSelected.AddListener(delegate(ActorBodyPart bodyPart) { OnActorBodyPartSelected(bodyPart); });
|
||||
EventsManager.onActorBodySelected.AddListener(delegate(ActorBody actorBody) { OnActorBodySelected(actorBody); });
|
||||
EventsManager.onActorIDChanged.AddListener(delegate {
|
||||
if (Workspace.ActorID == actorID)
|
||||
{ EventsManager.OnActorBodySelected(this); }
|
||||
});
|
||||
|
||||
if (Workspace.ActorID == actorID)
|
||||
{ Activate(); }
|
||||
|
||||
foreach (ActorAddonDef actorAddonDef in ActorAddonDefs.allDefs)
|
||||
{
|
||||
ActorBodyPart actorBodyPart = Instantiate(actorBodyPartPrefab, transform);
|
||||
actorBodyPart.Initialize(this, actorAddonDef);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActorBodySelected(ActorBody actorBody)
|
||||
{
|
||||
if (actorBody == this)
|
||||
{ bodyRenderer.color = Constants.ColorGreen; }
|
||||
|
||||
else
|
||||
{ bodyRenderer.color = Constants.ColorWhite; }
|
||||
}
|
||||
|
||||
public void OnActorBodyPartSelected(ActorBodyPart bodyPart)
|
||||
{
|
||||
if (bodyPart.parent == this)
|
||||
{ bodyRenderer.color = Constants.ColorLightGreen; }
|
||||
|
||||
else
|
||||
{ bodyRenderer.color = Constants.ColorWhite; }
|
||||
}
|
||||
|
||||
public void Initialize(int actorID)
|
||||
{
|
||||
this.actorID = actorID;
|
||||
|
||||
if (actorID == Workspace.ActorID)
|
||||
{ Activate(); }
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.pointerCurrentRaycast.gameObject.GetComponent<ActorBodyPart>())
|
||||
{ return; }
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
Activate();
|
||||
|
||||
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
|
||||
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
|
||||
if (dragDelta == Vector3.zero)
|
||||
{ dragDelta = mousePosition - transform.position; }
|
||||
|
||||
if (Workspace.actorManipulationMode == ActorManipulationMode.Pan)
|
||||
{
|
||||
keyframe.BodyOffsetX = mousePosition.x - dragDelta.x - Workspace.GetActor(actorID).GetFinalTransformOffset().x;
|
||||
keyframe.BodyOffsetZ = mousePosition.y - dragDelta.y - Workspace.GetActor(actorID).GetFinalTransformOffset().y;
|
||||
}
|
||||
|
||||
else if (Workspace.actorManipulationMode == ActorManipulationMode.Rotate)
|
||||
{
|
||||
float angle = -Vector2.SignedAngle(Vector2.down, (Vector2)mousePosition - (Vector2)transform.position);
|
||||
keyframe.BodyAngle = angle;
|
||||
}
|
||||
|
||||
else if (Workspace.actorManipulationMode == ActorManipulationMode.Face)
|
||||
{
|
||||
float angle = Vector2.SignedAngle(Vector2.up, (Vector2)mousePosition - (Vector2)transform.position);
|
||||
int facing = -Mathf.RoundToInt(angle / 90f );
|
||||
facing = facing < 0 ? facing + 4 : facing;
|
||||
|
||||
keyframe.BodyFacing = facing;
|
||||
}
|
||||
|
||||
PawnAnimationClip clip = Workspace.GetPawnAnimationClip(actorID);
|
||||
clip.BuildSimpleCurves();
|
||||
|
||||
EventsManager.OnPawnKeyframeChanged(keyframe);
|
||||
}
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
Workspace.RecordEvent("Actor position / orientation");
|
||||
dragDelta = Vector3.zero;
|
||||
}
|
||||
|
||||
public ActorBodyPart GetActorBodyPart(string bodyPart)
|
||||
{
|
||||
return GetComponentsInChildren<ActorBodyPart>(true)?.FirstOrDefault(x => x.bodyPart.ToLower() == bodyPart);
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
Workspace.ActorID = actorID;
|
||||
Workspace.selectedBodyPart = null;
|
||||
|
||||
EventsManager.OnActorBodySelected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Actors/ActorBody.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Actors/ActorBody.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 511a9ed9093e7fc458dec8d3c657f9a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
165
Source/Assets/Scripts/GUI/Actors/ActorBodyPart.cs
Normal file
165
Source/Assets/Scripts/GUI/Actors/ActorBodyPart.cs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorBodyPart : MonoBehaviour, IPointerClickHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
public SpriteRenderer bodyPartRenderer;
|
||||
public ActorBody parent;
|
||||
public string bodyPart;
|
||||
|
||||
private Vector3 dragDelta = new Vector3();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (parent == null) return;
|
||||
|
||||
EventsManager.onActorBodyPartSelected.AddListener(delegate (ActorBodyPart bodyPart) { OnActorBodyPartSelected(bodyPart); });
|
||||
EventsManager.onActorBodySelected.AddListener(delegate (ActorBody actorBody) { OnActorBodySelected(actorBody); });
|
||||
|
||||
if (Workspace.ActorID == parent.actorID)
|
||||
{ parent.Activate(); }
|
||||
}
|
||||
|
||||
public void Initialize(ActorBody parent, ActorAddonDef actorAddonDef)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.bodyPart = actorAddonDef.addonName;
|
||||
|
||||
bodyPartRenderer.sprite = actorAddonDef.graphicData.GetSprite();
|
||||
bodyPartRenderer.transform.localScale = (Vector3)actorAddonDef.graphicData.GetDrawSize();
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
public void OnActorAddonChange(ActorAddon actorAddon)
|
||||
{
|
||||
if (actorAddon.AddonName == bodyPart)
|
||||
{ gameObject?.SetActive(actorAddon.Render); }
|
||||
}
|
||||
|
||||
public void OnActorBodySelected(ActorBody actorBody)
|
||||
{
|
||||
if (actorBody == parent)
|
||||
{ bodyPartRenderer.color = Constants.ColorLightGreen; }
|
||||
|
||||
else
|
||||
{ bodyPartRenderer.color = Constants.ColorWhite; }
|
||||
}
|
||||
|
||||
public void OnActorBodyPartSelected(ActorBodyPart bodyPart)
|
||||
{
|
||||
if (bodyPart == this)
|
||||
{ bodyPartRenderer.color = Constants.ColorGreen; }
|
||||
|
||||
else if (bodyPart.parent == parent)
|
||||
{ bodyPartRenderer.color = Constants.ColorLightGreen; }
|
||||
|
||||
else
|
||||
{ bodyPartRenderer.color = Constants.ColorWhite; }
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.pointerCurrentRaycast.gameObject.GetComponent<ActorBodyPart>() == null)
|
||||
{ return; }
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
Activate();
|
||||
|
||||
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
|
||||
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
mousePosition = new Vector3(mousePosition.x, mousePosition.y, 0f);
|
||||
|
||||
if (dragDelta == Vector3.zero)
|
||||
{ dragDelta = mousePosition - transform.position; }
|
||||
|
||||
if (bodyPart.ToLower() == "head")
|
||||
{
|
||||
if (Workspace.actorManipulationMode == ActorManipulationMode.Pan)
|
||||
{
|
||||
// It's stupid, but it works
|
||||
Vector3 localPosA = transform.localPosition;
|
||||
transform.position = mousePosition - dragDelta;
|
||||
Vector3 localPosB = transform.localPosition;
|
||||
transform.localPosition = localPosA;
|
||||
|
||||
keyframe.HeadBob += localPosB.y - localPosA.y;
|
||||
}
|
||||
|
||||
else if (Workspace.actorManipulationMode == ActorManipulationMode.Rotate)
|
||||
{
|
||||
float angle = -Vector2.SignedAngle(Vector2.down, (Vector2)mousePosition - (Vector2)transform.position);
|
||||
keyframe.HeadAngle = angle;
|
||||
}
|
||||
|
||||
else if (Workspace.actorManipulationMode == ActorManipulationMode.Face)
|
||||
{
|
||||
float angle = Vector2.SignedAngle(Vector2.up, (Vector2)mousePosition - (Vector2)transform.position);
|
||||
int facing = -Mathf.RoundToInt(angle / 90f);
|
||||
facing = facing < 0 ? facing + 4 : facing;
|
||||
|
||||
keyframe.HeadFacing = facing;
|
||||
}
|
||||
}
|
||||
|
||||
else if (bodyPart.ToLower() == "appendage")
|
||||
{
|
||||
if (Workspace.actorManipulationMode == ActorManipulationMode.Rotate)
|
||||
{
|
||||
float angle = -Vector2.SignedAngle(Vector2.up, (Vector2)mousePosition - (Vector2)transform.position);
|
||||
keyframe.GenitalAngle = angle;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
AddonKeyframe addonKeyframe = keyframe.GetAddonKeyframe(bodyPart);
|
||||
ActorAddon addon = Workspace.GetCurrentPawnAnimationClip().GetActorAddon(bodyPart);
|
||||
|
||||
if (Workspace.actorManipulationMode == ActorManipulationMode.Pan)
|
||||
{
|
||||
ActorBody anchoringActorBody = AnimationController.Instance.actorBodies.GetComponentsInChildren<ActorBody>()?.FirstOrDefault(x => x.actorID == addon.AnchoringActor);
|
||||
Vector3 anchor = PawnUtility.GetBodyPartAnchor(anchoringActorBody, addon.anchorName);
|
||||
transform.position = mousePosition - dragDelta;
|
||||
|
||||
addonKeyframe.PosX = transform.position.x - anchor.x;
|
||||
addonKeyframe.PosZ = transform.position.y - anchor.y;
|
||||
}
|
||||
|
||||
else if (Workspace.actorManipulationMode == ActorManipulationMode.Rotate)
|
||||
{
|
||||
float angle = -Vector2.SignedAngle(Vector2.down, (Vector2)mousePosition - (Vector2)transform.position);
|
||||
addonKeyframe.Rotation = angle;
|
||||
}
|
||||
}
|
||||
|
||||
PawnAnimationClip clip = Workspace.GetPawnAnimationClip(parent.actorID);
|
||||
clip.BuildSimpleCurves();
|
||||
|
||||
EventsManager.OnPawnKeyframeChanged(keyframe);
|
||||
}
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
Workspace.RecordEvent("Actor position / orientation");
|
||||
dragDelta = Vector3.zero;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
Workspace.ActorID = parent.actorID;
|
||||
Workspace.selectedBodyPart = this;
|
||||
|
||||
EventsManager.OnActorBodyPartSelected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Actors/ActorBodyPart.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Actors/ActorBodyPart.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b23e33f312d52c642b86f5f2138f4030
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Source/Assets/Scripts/GUI/AddSoundDefButton.cs
Normal file
28
Source/Assets/Scripts/GUI/AddSoundDefButton.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AddSoundDefButton : MonoBehaviour
|
||||
{
|
||||
private Text text;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
text = GetComponentInChildren<Text>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
PawnKeyframe keyframe = Workspace.GetCurrentOrPreviousKeyframe(Workspace.ActorID);
|
||||
|
||||
if (keyframe != null)
|
||||
{ text.text = string.IsNullOrEmpty(keyframe.SoundEffect) ? "None" : keyframe.SoundEffect; }
|
||||
|
||||
else
|
||||
{ text.text = "None"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/AddSoundDefButton.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/AddSoundDefButton.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7e9565ba13ac2e469767bcfad7da5d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Source/Assets/Scripts/GUI/AddonAnchorDropdown.cs
Normal file
19
Source/Assets/Scripts/GUI/AddonAnchorDropdown.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AddonAnchorDropdown : MonoBehaviour
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
Dropdown dropdown = GetComponent<Dropdown>();
|
||||
|
||||
dropdown.ClearOptions();
|
||||
dropdown.AddOptions(Constants.bodyPartAnchorNames.Values.ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/AddonAnchorDropdown.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/AddonAnchorDropdown.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6a0ca236433ad584998f9c7a96dc3af8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Source/Assets/Scripts/GUI/AnimationLengthsCard.cs
Normal file
39
Source/Assets/Scripts/GUI/AnimationLengthsCard.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AnimationLengthsCard : MonoBehaviour
|
||||
{
|
||||
public Text stageLengthNormalText;
|
||||
public Text stageLengthQuickText;
|
||||
public Text animationLengthNormalText;
|
||||
public Text animationLengthQuickText;
|
||||
public float spacing = 10f;
|
||||
|
||||
private RectTransform rect;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
rect = GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.parent.GetComponent<RectTransform>());
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(transform.parent.GetComponent<RectTransform>());
|
||||
rect.localPosition = new Vector3(rect.localPosition.x, spacing + transform.parent.GetComponent<RectTransform>().sizeDelta.y, rect.localPosition.z);
|
||||
|
||||
stageLengthNormalText.text = Workspace.GetCurrentAnimationStage().PlayTimeTicks + " (" + string.Format("{0:0.00}", Workspace.GetCurrentAnimationStage().PlayTimeTicks / 60f) + " s)";
|
||||
animationLengthNormalText.text = Workspace.animationDef.animationTimeTicks + " (" + string.Format("{0:0.00}", Workspace.animationDef.animationTimeTicks / 60f) + " s)";
|
||||
|
||||
stageLengthQuickText.text = Workspace.GetCurrentAnimationStage().PlayTimeTicksQuick + " (" + string.Format("{0:0.00}", Workspace.GetCurrentAnimationStage().PlayTimeTicksQuick / 60f) + " s)";
|
||||
animationLengthQuickText.text = Workspace.animationDef.animationTimeTicksQuick + " (" + string.Format("{0:0.00}", Workspace.animationDef.animationTimeTicksQuick / 60f) + " s)";
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/AnimationLengthsCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/AnimationLengthsCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 060e49b40b1097e46b662059e4e29cdf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
119
Source/Assets/Scripts/GUI/AnimationTimeline.cs
Normal file
119
Source/Assets/Scripts/GUI/AnimationTimeline.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
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 AnimationTimeline : MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
public int actorID = -1;
|
||||
public KeyframeSlider keyframeSliderPrefab;
|
||||
|
||||
private Transform anchorTransform;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
EventsManager.onAnimationTimelinesChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onKeyframeCountChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onActorIDChanged.AddListener(delegate { UpdateTimelineSelection(); });
|
||||
EventsManager.onStageWindowSizeChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onAnimationStageChanged.AddListener(delegate { UpdateGUI(); });
|
||||
|
||||
UpdateTimelineSelection();
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void Initialize(int actorID)
|
||||
{
|
||||
anchorTransform = transform.parent;
|
||||
this.actorID = actorID;
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void ClearKeyframeSliders()
|
||||
{
|
||||
foreach (KeyframeSlider slider in GetComponentsInChildren<KeyframeSlider>())
|
||||
{ Destroy(slider.gameObject); }
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
if (actorID < 0) return;
|
||||
|
||||
PawnAnimationClip clip = Workspace.GetPawnAnimationClip(actorID);
|
||||
if (clip == null) return;
|
||||
|
||||
clip.BuildSimpleCurves();
|
||||
|
||||
ClearKeyframeSliders();
|
||||
|
||||
foreach (PawnKeyframe keyframe in clip.Keyframes)
|
||||
{ AddPawnKeyFrame(keyframe.keyframeID); }
|
||||
|
||||
InitiateUpdateOfGhostFrames();
|
||||
}
|
||||
|
||||
public void UpdateTimelineSelection()
|
||||
{
|
||||
GetComponent<Image>().color = (Workspace.ActorID == actorID ? Constants.ColorGoldYellow : Constants.ColorMidGrey);
|
||||
}
|
||||
|
||||
public void AddPawnKeyFrame(int keyframeID)
|
||||
{
|
||||
KeyframeSlider keyframeSlider = Instantiate(keyframeSliderPrefab, transform);
|
||||
keyframeSlider.Initialize(this, actorID, keyframeID);
|
||||
}
|
||||
|
||||
public void RemovePawnKeyFrame(int keyframeID)
|
||||
{
|
||||
KeyframeSlider keyframeSlider = GetComponentsInChildren<KeyframeSlider>().FirstOrDefault(x => x.keyframeID == keyframeID);
|
||||
Destroy(keyframeSlider?.gameObject);
|
||||
}
|
||||
|
||||
public void InitiateUpdateOfGhostFrames()
|
||||
{
|
||||
BroadcastMessage("UpdateGhostFrames");
|
||||
}
|
||||
|
||||
public void OnMoveTimeline(int delta)
|
||||
{
|
||||
int? siblingIndex = anchorTransform.parent.GetComponentsInChildren<AnimationTimeline>()?.ToList()?.IndexOf(this);
|
||||
int? siblingCount = anchorTransform.parent.GetComponentsInChildren<AnimationTimeline>()?.ToList()?.Count();
|
||||
|
||||
if (siblingIndex != null && siblingCount != null && MoveAnimationTimeline(siblingIndex.Value, delta))
|
||||
{ AnimationController.Instance.Initialize(); }
|
||||
}
|
||||
|
||||
public bool MoveAnimationTimeline(int startIndex, int delta)
|
||||
{
|
||||
if (startIndex + delta < 0 || startIndex + delta >= Workspace.GetCurrentAnimationStage().AnimationClips.Count)
|
||||
{ Debug.Log("Cannot move animation timeline - movement would exceed bounds"); return false; }
|
||||
|
||||
Actor actor = Workspace.animationDef.Actors[startIndex];
|
||||
Workspace.animationDef.Actors[startIndex] = Workspace.animationDef.Actors[startIndex + delta];
|
||||
Workspace.animationDef.Actors[startIndex + delta] = actor;
|
||||
|
||||
PawnAnimationClip clip = Workspace.GetPawnAnimationClip(startIndex);
|
||||
Workspace.GetCurrentAnimationStage().AnimationClips[startIndex] = Workspace.GetCurrentAnimationStage().AnimationClips[startIndex + delta];
|
||||
Workspace.GetCurrentAnimationStage().AnimationClips[startIndex + delta] = clip;
|
||||
|
||||
Workspace.ActorID = startIndex + delta;
|
||||
|
||||
Workspace.RecordEvent("Timeline move");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
Workspace.ActorID = actorID;
|
||||
Workspace.keyframeID.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/AnimationTimeline.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/AnimationTimeline.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b3dd6b099b5c67744b84a5ec7283277b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Source/Assets/Scripts/GUI/ButtonWithKeyCode.cs
Normal file
22
Source/Assets/Scripts/GUI/ButtonWithKeyCode.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
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(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/ButtonWithKeyCode.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/ButtonWithKeyCode.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1bfc022fc38c6474db2a742159e458f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/GUI/Cards.meta
Normal file
8
Source/Assets/Scripts/GUI/Cards.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c026d569e32726d4eb8821db713d0aac
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
102
Source/Assets/Scripts/GUI/Cards/ActorAddonCard.cs
Normal file
102
Source/Assets/Scripts/GUI/Cards/ActorAddonCard.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorAddonCard : MonoBehaviour
|
||||
{
|
||||
public string addonName;
|
||||
public Text label;
|
||||
public Toggle toggle;
|
||||
public Dropdown anchorDropdown;
|
||||
public InputField anchoringPawnField;
|
||||
public Dropdown layerDropdown;
|
||||
public ActorAddonKeyframeCard actorAddonKeyframeCard;
|
||||
|
||||
private ActorAddonDef actorAddonDef;
|
||||
|
||||
private PawnAnimationClip clip { get { return Workspace.GetCurrentPawnAnimationClip(); } }
|
||||
|
||||
private void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Initialize(ActorAddonDef actorAddonDef, ActorAddonKeyframeCard actorAddonKeyframeCard)
|
||||
{
|
||||
this.actorAddonDef = actorAddonDef;
|
||||
this.actorAddonKeyframeCard = actorAddonKeyframeCard;
|
||||
|
||||
addonName = actorAddonDef.addonName;
|
||||
label.text = actorAddonDef.label;
|
||||
|
||||
EventsManager.onAnimationChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onActorIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
if (Workspace.animationDef == null || string.IsNullOrEmpty(addonName)) return;
|
||||
|
||||
if (clip?.GetActorAddon(addonName) != null)
|
||||
{
|
||||
int i = Constants.bodyPartAnchorNames.Keys.ToList().IndexOf(clip.GetActorAddon(addonName).AnchorName);
|
||||
anchorDropdown.SetValueWithoutNotify(i);
|
||||
layerDropdown.SetValueWithoutNotify(layerDropdown.options.IndexOf(layerDropdown.options.First(x => x.text == clip.GetActorAddon(addonName).Layer)));
|
||||
anchoringPawnField.SetTextWithoutNotify(clip.GetActorAddon(addonName).AnchoringActor.ToString());
|
||||
toggle.SetIsOnWithoutNotify(clip.IsActorAddonVisible(addonName));
|
||||
|
||||
anchoringPawnField.interactable = anchorDropdown.value != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnToggleChanged()
|
||||
{
|
||||
if (clip?.GetActorAddon(addonName) != null)
|
||||
{ clip.GetActorAddon(addonName).render = toggle.isOn; }
|
||||
|
||||
EventsManager.OnPawnKeyframeChanged(null);
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnAnchorChanged()
|
||||
{
|
||||
if (clip?.GetActorAddon(addonName) != null)
|
||||
{ clip.GetActorAddon(addonName).AnchorName = Constants.bodyPartAnchorNames.Keys.ElementAt(anchorDropdown.value); }
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnLayerChanged()
|
||||
{
|
||||
if (clip?.GetActorAddon(addonName) != null)
|
||||
{ clip.GetActorAddon(addonName).Layer = layerDropdown.options[layerDropdown.value].text; }
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnAnchoringPawnChanged()
|
||||
{
|
||||
if (clip?.GetActorAddon(addonName) != null)
|
||||
{
|
||||
int i = int.Parse(anchoringPawnField.text);
|
||||
|
||||
if (i < 0) { i = clip.GetOwningActorID(); }
|
||||
i = Mathf.Clamp(i, 0, Workspace.animationDef.Actors.Count - 1);
|
||||
|
||||
clip.GetActorAddon(addonName).AnchoringActor = i;
|
||||
anchoringPawnField.SetTextWithoutNotify(i.ToString());
|
||||
}
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/ActorAddonCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/ActorAddonCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 919aed3c4b9671c4e8dc109c7d608683
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
71
Source/Assets/Scripts/GUI/Cards/ActorAddonKeyframeCard.cs
Normal file
71
Source/Assets/Scripts/GUI/Cards/ActorAddonKeyframeCard.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorAddonKeyframeCard : MonoBehaviour
|
||||
{
|
||||
public string addonName;
|
||||
public Text label;
|
||||
public InputField xOffsetField;
|
||||
public InputField zOffsetField;
|
||||
public InputField rotationField;
|
||||
|
||||
private ActorAddonDef actorAddonDef;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Initialize(ActorAddonDef actorAddonDef)
|
||||
{
|
||||
this.actorAddonDef = actorAddonDef;
|
||||
this.addonName = actorAddonDef.addonName;
|
||||
|
||||
label.text = actorAddonDef.label + ":";
|
||||
|
||||
EventsManager.onAnimationChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onActorIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageTickChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onPawnKeyframeChanged.AddListener(delegate { UpdateGUI(); });
|
||||
|
||||
xOffsetField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
zOffsetField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
rotationField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnValueChanged()
|
||||
{
|
||||
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
|
||||
|
||||
keyframe.GetAddonKeyframe(addonName).PosX = float.Parse(xOffsetField.text);
|
||||
keyframe.GetAddonKeyframe(addonName).PosZ = float.Parse(zOffsetField.text);
|
||||
keyframe.GetAddonKeyframe(addonName).Rotation = float.Parse(rotationField.text);
|
||||
|
||||
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
||||
Workspace.RecordEvent("Actor addon position / orientation");
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
PawnAnimationClip clip = Workspace.GetCurrentPawnAnimationClip();
|
||||
|
||||
xOffsetField.SetTextWithoutNotify(string.Format("{0:0.000}", clip.GetActorAddon(addonName).PosX.Evaluate((float)Workspace.StageTick / Workspace.StageWindowSize)));
|
||||
zOffsetField.SetTextWithoutNotify(string.Format("{0:0.000}", clip.GetActorAddon(addonName).PosZ.Evaluate((float)Workspace.StageTick / Workspace.StageWindowSize)));
|
||||
rotationField.SetTextWithoutNotify(string.Format("{0:0.000}", clip.GetActorAddon(addonName).Rotation.Evaluate((float)Workspace.StageTick / Workspace.StageWindowSize)));
|
||||
|
||||
gameObject.SetActive(clip.GetActorAddon(addonName).render == true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a63bb35c73985c46b65383f08d4afd9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
129
Source/Assets/Scripts/GUI/Cards/ActorCard.cs
Normal file
129
Source/Assets/Scripts/GUI/Cards/ActorCard.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorCard : MonoBehaviour
|
||||
{
|
||||
public Toggle initiatorToggle;
|
||||
public Dropdown selectActorLayerDropdown;
|
||||
public Dropdown bodyTypeDropdown;
|
||||
public InputField bodyOffsetXField;
|
||||
public InputField bodyOffsetZField;
|
||||
public Dropdown raceDropdown;
|
||||
public InputField raceOffsetXField;
|
||||
public InputField raceOffsetZField;
|
||||
|
||||
private Actor actor { get { return Workspace.GetCurrentActor(); } }
|
||||
private PawnAnimationClip clip { get { return Workspace.GetCurrentPawnAnimationClip(); } }
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
UpdateRaceDropdown();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// General events
|
||||
EventsManager.onAnimationChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onActorIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onDefNamesChanged.AddListener(delegate { UpdateRaceDropdown(); });
|
||||
|
||||
// Local events
|
||||
initiatorToggle.onValueChanged.AddListener(delegate {
|
||||
actor.initiator = initiatorToggle.isOn;
|
||||
Workspace.RecordEvent("Change in actor sex initiator status ");
|
||||
});
|
||||
|
||||
selectActorLayerDropdown.onValueChanged.AddListener(delegate {
|
||||
clip.Layer = selectActorLayerDropdown.options[selectActorLayerDropdown.value].text;
|
||||
Workspace.RecordEvent("Change in actor render layer");
|
||||
});
|
||||
|
||||
bodyTypeDropdown.onValueChanged.AddListener(delegate { OnDropdownChanged(); });
|
||||
bodyOffsetXField.onEndEdit.AddListener(delegate { OnInputFieldChanged(); });
|
||||
bodyOffsetZField.onEndEdit.AddListener(delegate { OnInputFieldChanged(); });
|
||||
|
||||
raceDropdown.onValueChanged.AddListener(delegate { OnDropdownChanged(); });
|
||||
raceOffsetXField.onEndEdit.AddListener(delegate { OnInputFieldChanged(); });
|
||||
raceOffsetZField.onEndEdit.AddListener(delegate { OnInputFieldChanged(); });
|
||||
|
||||
// Initialize
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnInputFieldChanged()
|
||||
{
|
||||
string bodyType = bodyTypeDropdown.options[bodyTypeDropdown.value].text;
|
||||
bodyType = string.IsNullOrEmpty(bodyType) ? "Male" : bodyType;
|
||||
|
||||
float.TryParse(bodyOffsetXField.text, out float x);
|
||||
float.TryParse(bodyOffsetZField.text, out float z);
|
||||
actor.BodyTypeOffset.SetOffset(bodyType, new Vector2(x, z));
|
||||
|
||||
float.TryParse(raceOffsetXField.text, out x);
|
||||
float.TryParse(raceOffsetZField.text, out z);
|
||||
actor.SetPawnRaceOffset(new Vector2(x, z));
|
||||
|
||||
Workspace.RecordEvent("Actor offset");
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnDropdownChanged()
|
||||
{
|
||||
actor.bodyType = bodyTypeDropdown.options[bodyTypeDropdown.value].text;
|
||||
|
||||
if (raceDropdown.options[raceDropdown.value].text != actor.GetPawnRaceDef().defName)
|
||||
{ Workspace.selectedBodyPart = null; }
|
||||
|
||||
actor.SetPawnRaceDef(raceDropdown.options[raceDropdown.value].text);
|
||||
|
||||
Workspace.RecordEvent("Actor body type/race change");
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void UpdateRaceDropdown()
|
||||
{
|
||||
int index = raceDropdown.value;
|
||||
raceDropdown.ClearOptions();
|
||||
|
||||
IEnumerable<string> optionsList = DefaultTags.defNames.Concat(CustomTags.defNames);
|
||||
foreach (string defName in optionsList)
|
||||
{ raceDropdown.options.Add(new Dropdown.OptionData(defName)); }
|
||||
|
||||
raceDropdown.value = Mathf.Clamp(index, 0, raceDropdown.options.Count - 1);
|
||||
raceDropdown.captionText.text = raceDropdown.options[raceDropdown.value].text;
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
initiatorToggle.isOn = actor.Initiator;
|
||||
|
||||
string layer = clip.Layer;
|
||||
selectActorLayerDropdown.SetValueWithoutNotify(selectActorLayerDropdown.options.FindIndex(x => x.text == layer));
|
||||
|
||||
string bodyType = actor.bodyType;
|
||||
bodyTypeDropdown.SetValueWithoutNotify(bodyTypeDropdown.options.FindIndex(x => x.text == bodyType));
|
||||
|
||||
bodyOffsetXField.SetTextWithoutNotify(string.Format("{0:0.000}", actor.BodyTypeOffset.GetOffset(bodyType).x));
|
||||
bodyOffsetZField.SetTextWithoutNotify(string.Format("{0:0.000}", actor.BodyTypeOffset.GetOffset(bodyType).z));
|
||||
|
||||
bodyTypeDropdown.interactable = actor.GetPawnRaceDef().isHumanoid;
|
||||
bodyOffsetXField.interactable = actor.GetPawnRaceDef().isHumanoid;
|
||||
bodyOffsetZField.interactable = actor.GetPawnRaceDef().isHumanoid;
|
||||
|
||||
string race = actor.GetPawnRaceDef().defName;
|
||||
raceDropdown.SetValueWithoutNotify(raceDropdown.options.FindIndex(x => x.text == race));
|
||||
|
||||
raceOffsetXField.SetTextWithoutNotify(string.Format("{0:0.000}", actor.GetPawnRaceOffset().x));
|
||||
raceOffsetZField.SetTextWithoutNotify(string.Format("{0:0.000}", actor.GetPawnRaceOffset().z));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/ActorCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/ActorCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9731614c7527b624492dd33f9b006fcb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
87
Source/Assets/Scripts/GUI/Cards/ActorKeyframeCard.cs
Normal file
87
Source/Assets/Scripts/GUI/Cards/ActorKeyframeCard.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ActorKeyframeCard : Singleton<ActorKeyframeCard>
|
||||
{
|
||||
public InputField positionXField;
|
||||
public InputField positionZField;
|
||||
public InputField rotationField;
|
||||
public InputField headBobField;
|
||||
public InputField headRotationField;
|
||||
public InputField appendageRotationField;
|
||||
|
||||
public ActorAddonCard actorAddonCardPrefab;
|
||||
public ActorAddonKeyframeCard actorAddonKeyframeCardPrefab;
|
||||
|
||||
public Transform actorAddonCards;
|
||||
public Transform actorKeyframeCards;
|
||||
|
||||
public SelectActorAddonsDialog selectActorAddonsDialog;
|
||||
|
||||
private Actor actor { get { return Workspace.GetCurrentActor(); } }
|
||||
|
||||
private void Start()
|
||||
{
|
||||
EventsManager.onAnimationChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onActorIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageTickChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onKeyframeCountChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onPawnKeyframeChanged.AddListener(delegate { UpdateGUI(); });
|
||||
|
||||
positionXField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
positionZField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
rotationField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
headBobField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
headRotationField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
appendageRotationField.onEndEdit.AddListener(delegate { OnValueChanged(); });
|
||||
|
||||
foreach (ActorAddonDef actorAddonDef in ActorAddonDefs.allDefs)
|
||||
{
|
||||
ActorAddonKeyframeCard actorAddonKeyframeCard = Instantiate(actorAddonKeyframeCardPrefab, actorKeyframeCards);
|
||||
actorAddonKeyframeCard.Initialize(actorAddonDef);
|
||||
|
||||
ActorAddonCard actorAddonCard = Instantiate(actorAddonCardPrefab, actorAddonCards);
|
||||
actorAddonCard.Initialize(actorAddonDef, actorAddonKeyframeCard);
|
||||
|
||||
selectActorAddonsDialog.AddActorAddonCard(actorAddonCard);
|
||||
}
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnValueChanged()
|
||||
{
|
||||
PawnKeyframe keyframe = Workspace.GetCurrentPawnKeyframe(true);
|
||||
|
||||
keyframe.BodyOffsetX = float.Parse(positionXField.text);
|
||||
keyframe.BodyOffsetZ = float.Parse(positionZField.text);
|
||||
keyframe.BodyAngle = float.Parse(rotationField.text);
|
||||
keyframe.HeadBob = float.Parse(headBobField.text);
|
||||
keyframe.HeadAngle = float.Parse(headRotationField.text);
|
||||
keyframe.GenitalAngle = float.Parse(appendageRotationField.text);
|
||||
|
||||
Workspace.GetCurrentPawnAnimationClip().BuildSimpleCurves();
|
||||
Workspace.RecordEvent("Actor position / orientation");
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
ActorPosition actorPosition = actor.GetCurrentPosition();
|
||||
|
||||
positionXField.SetTextWithoutNotify(string.Format("{0:0.000}", actorPosition.bodyOffsetX));
|
||||
positionZField.SetTextWithoutNotify(string.Format("{0:0.000}", actorPosition.bodyOffsetZ));
|
||||
rotationField.SetTextWithoutNotify(string.Format("{0:0.000}", actorPosition.bodyAngle));
|
||||
headBobField.SetTextWithoutNotify(string.Format("{0:0.000}", actorPosition.headBob));
|
||||
headRotationField.SetTextWithoutNotify(string.Format("{0:0.000}", actorPosition.headAngle));
|
||||
appendageRotationField.SetTextWithoutNotify(string.Format("{0:0.000}", actorPosition.genitalAngle));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/ActorKeyframeCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/ActorKeyframeCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58ad3d066d9103541806d07bc98823d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
94
Source/Assets/Scripts/GUI/Cards/AnimationControlCard.cs
Normal file
94
Source/Assets/Scripts/GUI/Cards/AnimationControlCard.cs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AnimationControlCard : MonoBehaviour
|
||||
{
|
||||
public InputField currentTimeField;
|
||||
public InputField stageWindowLengthField;
|
||||
public InputField playBackSpeedField;
|
||||
public Button playToggleButton;
|
||||
public Slider stageTimelineSlider;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
EventsManager.onAnimationChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageIDChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageTickChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onAnimationToggled.AddListener(delegate { playToggleButton.image.color = Workspace.IsAnimating ? Constants.ColorGoldYellow : Constants.ColorWhite; });
|
||||
|
||||
stageTimelineSlider.onValueChanged.AddListener(delegate { OnStageTimelineSliderChange(); });
|
||||
currentTimeField.onEndEdit.AddListener(delegate { OnCurrentTimeFieldChange(); });
|
||||
stageWindowLengthField.onEndEdit.AddListener(delegate { OnStageWindowLengthFieldChange(); });
|
||||
playBackSpeedField.onEndEdit.AddListener(delegate { OnPlayBackSpeedChange(); });
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnStageTimelineSliderChange()
|
||||
{
|
||||
Workspace.StageTick = (int)stageTimelineSlider.value;
|
||||
}
|
||||
|
||||
public void OnPlayBackSpeedChange()
|
||||
{
|
||||
Workspace.PlayBackSpeed = float.Parse(playBackSpeedField.text);
|
||||
}
|
||||
|
||||
public void OnCurrentTimeFieldChange()
|
||||
{
|
||||
Workspace.StageTick = Mathf.Clamp(int.Parse(currentTimeField.text), Constants.minTick, Workspace.StageWindowSize);
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnStageWindowLengthFieldChange()
|
||||
{
|
||||
int.TryParse(stageWindowLengthField.text, out int newStageWindowSize);
|
||||
newStageWindowSize = Mathf.Clamp(newStageWindowSize, Constants.minAnimationClipLength, Constants.maxAnimationClipLength);
|
||||
|
||||
Debug.Log("Resizing animation clip length to " + newStageWindowSize.ToString() + " ticks.");
|
||||
|
||||
if (Workspace.stretchKeyframes)
|
||||
{ Workspace.GetCurrentAnimationStage().StretchStageWindow(newStageWindowSize); }
|
||||
|
||||
else
|
||||
{
|
||||
Workspace.GetCurrentAnimationStage().ResizeStageWindow(newStageWindowSize);
|
||||
|
||||
foreach (PawnAnimationClip clip in Workspace.GetCurrentAnimationStage().AnimationClips)
|
||||
{
|
||||
List<PawnKeyframe> keyframes = clip.Keyframes.Where(x => x.atTick > newStageWindowSize)?.ToList();
|
||||
|
||||
if (keyframes.NullOrEmpty())
|
||||
{ continue; }
|
||||
|
||||
foreach (PawnKeyframe keyframe in keyframes)
|
||||
{
|
||||
if (clip.Keyframes.Count <= 2)
|
||||
{ break; }
|
||||
|
||||
clip.RemovePawnKeyframe(keyframe.keyframeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Workspace.RecordEvent("Stage length");
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
stageTimelineSlider.maxValue = Workspace.StageWindowSize;
|
||||
stageTimelineSlider.SetValueWithoutNotify(Workspace.StageTick);
|
||||
currentTimeField.SetTextWithoutNotify(Workspace.StageTick.ToString());
|
||||
stageWindowLengthField.SetTextWithoutNotify(Workspace.StageWindowSize.ToString());
|
||||
playBackSpeedField.SetTextWithoutNotify(Workspace.PlayBackSpeed.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/AnimationControlCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/AnimationControlCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b414452bfd6c9b4bb99542a51d77468
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Source/Assets/Scripts/GUI/Cards/AnimationDefCard.cs
Normal file
39
Source/Assets/Scripts/GUI/Cards/AnimationDefCard.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AnimationDefCard : MonoBehaviour
|
||||
{
|
||||
public InputField defNameField;
|
||||
public InputField labelField;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
EventsManager.onAnimationChanged.AddListener(delegate { UpdateInputFields(); });
|
||||
|
||||
defNameField.onEndEdit.AddListener(delegate {
|
||||
Workspace.animationDef.DefName = defNameField.text;
|
||||
Workspace.MakeHistoricRecord("AnimationDef update");
|
||||
});
|
||||
|
||||
labelField.onEndEdit.AddListener(delegate {
|
||||
Workspace.animationDef.Label = labelField.text;
|
||||
Workspace.MakeHistoricRecord("AnimationDef update");
|
||||
});
|
||||
|
||||
UpdateInputFields();
|
||||
}
|
||||
|
||||
public void UpdateInputFields()
|
||||
{
|
||||
defNameField.SetTextWithoutNotify(Workspace.animationDef.DefName);
|
||||
labelField.SetTextWithoutNotify(Workspace.animationDef.Label);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/AnimationDefCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/AnimationDefCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e1d8f33927e6b4d44914a445362b802d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
67
Source/Assets/Scripts/GUI/Cards/StageCard.cs
Normal file
67
Source/Assets/Scripts/GUI/Cards/StageCard.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class StageCard : MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
public Text stageName;
|
||||
public InputField stageNameField;
|
||||
public Image banner;
|
||||
|
||||
private int stageID { get { return transform.GetSiblingIndex(); } }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
EventsManager.onStageIDChanged.AddListener(delegate { Initialize(stageName.text); });
|
||||
stageNameField.onEndEdit.AddListener(delegate { OnNameChange(); });
|
||||
}
|
||||
|
||||
public void Initialize(string stageName)
|
||||
{
|
||||
this.stageName.text = stageName;
|
||||
|
||||
if (Workspace.StageID == transform.GetSiblingIndex())
|
||||
{
|
||||
banner.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
banner.gameObject.SetActive(false);
|
||||
stageNameField.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNameChange()
|
||||
{
|
||||
stageName.text = stageNameField.text;
|
||||
stageNameField.gameObject.SetActive(false);
|
||||
|
||||
Workspace.GetCurrentAnimationStage().StageName = stageName.text;
|
||||
Workspace.RecordEvent("Stage renamed");
|
||||
}
|
||||
|
||||
public void OnMoveStage(int delta)
|
||||
{
|
||||
Workspace.animationDef.MoveAnimationStage(stageID, delta);
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.clickCount >= 2)
|
||||
{
|
||||
stageNameField.text = stageName.text;
|
||||
stageNameField.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
if (Workspace.StageID != transform.GetSiblingIndex())
|
||||
{ Workspace.RecordEvent("Stage selected"); }
|
||||
|
||||
Workspace.StageID = transform.GetSiblingIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/StageCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/StageCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20b2be62d5fdc4b4992cede005ec2aee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
53
Source/Assets/Scripts/GUI/Cards/StageLoopsCard.cs
Normal file
53
Source/Assets/Scripts/GUI/Cards/StageLoopsCard.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class StageLoopsCard : MonoBehaviour
|
||||
{
|
||||
public InputField stageLoopsNormalField;
|
||||
public InputField stageLoopsQuickField;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
EventsManager.onAnimationTimelinesChanged.AddListener(delegate { UpdateGUI(); });
|
||||
EventsManager.onStageWindowSizeChanged.AddListener(delegate { UpdateGUI(); });
|
||||
|
||||
stageLoopsNormalField.onEndEdit.AddListener(delegate { OnStageLoopsNormalFieldChange(); });
|
||||
stageLoopsQuickField.onEndEdit.AddListener(delegate { OnStageLoopsFastFieldChange(); });
|
||||
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
public void OnStageLoopsNormalFieldChange()
|
||||
{
|
||||
if (Workspace.animationDef == null) return;
|
||||
|
||||
Workspace.GetCurrentAnimationStage().StageLoopsNormal = int.Parse(stageLoopsNormalField.text);
|
||||
|
||||
EventsManager.OnAnimationStageChanged(Workspace.GetCurrentAnimationStage());
|
||||
Workspace.RecordEvent("Cycle count (normal)");
|
||||
}
|
||||
|
||||
public void OnStageLoopsFastFieldChange()
|
||||
{
|
||||
if (Workspace.animationDef == null) return;
|
||||
|
||||
Workspace.GetCurrentAnimationStage().StageLoopsQuick = int.Parse(stageLoopsQuickField.text);
|
||||
|
||||
EventsManager.OnAnimationStageChanged(Workspace.GetCurrentAnimationStage());
|
||||
Workspace.RecordEvent("Cycle count (fast)");
|
||||
}
|
||||
|
||||
public void UpdateGUI()
|
||||
{
|
||||
stageLoopsNormalField.SetTextWithoutNotify(Workspace.GetCurrentAnimationStage().StageLoopsNormal.ToString());
|
||||
stageLoopsQuickField.SetTextWithoutNotify(Workspace.GetCurrentAnimationStage().StageLoopsQuick.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Cards/StageLoopsCard.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Cards/StageLoopsCard.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 38691dc973d99734f8f0f2a240df73fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Source/Assets/Scripts/GUI/Chaser.cs
Normal file
24
Source/Assets/Scripts/GUI/Chaser.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Source/Assets/Scripts/GUI/Chaser.cs.meta
Normal file
11
Source/Assets/Scripts/GUI/Chaser.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9584d659e560b88409843604ae04229b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Source/Assets/Scripts/GUI/DialogBoxes.meta
Normal file
8
Source/Assets/Scripts/GUI/DialogBoxes.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eabe165e5b1a70744a8073c1b5b541a7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class ConsoleMessagesDialog : MonoBehaviour
|
||||
{
|
||||
public bool isExpanded = false;
|
||||
public GameObject consoleWindow;
|
||||
public Transform logContent;
|
||||
public Text stackTrace;
|
||||
public Text currentMessage;
|
||||
public Text logMessagePrefab;
|
||||
public int maxMessages = 500;
|
||||
|
||||
private int currentMessages = 0;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Application.logMessageReceived += LogMessage;
|
||||
}
|
||||
|
||||
public void ToggleExpand()
|
||||
{
|
||||
isExpanded = !isExpanded;
|
||||
|
||||
consoleWindow.SetActive(isExpanded);
|
||||
}
|
||||
|
||||
public void LogMessage(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
if (currentMessages > maxMessages) return;
|
||||
currentMessages++;
|
||||
|
||||
currentMessage.text = logString;
|
||||
|
||||
Text logMessage = Instantiate(logMessagePrefab, logContent);
|
||||
logMessage.text = logString;
|
||||
|
||||
logMessage.GetComponent<Button>().onClick.AddListener(delegate { OpenStackTrace(stackTrace); });
|
||||
|
||||
if (type == LogType.Warning)
|
||||
{
|
||||
currentMessage.color = Constants.ColorRichOrange;
|
||||
logMessage.color = Constants.ColorRichOrange;
|
||||
}
|
||||
|
||||
else if (type == LogType.Exception || type == LogType.Error)
|
||||
{
|
||||
currentMessage.color = Constants.ColorRed;
|
||||
logMessage.color = Constants.ColorRed;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
currentMessage.color = Constants.ColorDarkGrey;
|
||||
logMessage.color = Constants.ColorDarkGrey;
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenStackTrace(string stackTrace)
|
||||
{
|
||||
this.stackTrace.text = stackTrace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c62f9942e9d933848913ee2b28f65791
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
78
Source/Assets/Scripts/GUI/DialogBoxes/DialogBox.cs
Normal file
78
Source/Assets/Scripts/GUI/DialogBoxes/DialogBox.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class DialogBox : MonoBehaviour
|
||||
{
|
||||
public List<GameObject> cloneObjects;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Pop()
|
||||
{
|
||||
Initialize();
|
||||
gameObject.SetActive(gameObject.activeSelf == false);
|
||||
}
|
||||
|
||||
public void RemoveCloneObjectsFromParent(Transform parent)
|
||||
{
|
||||
for (int i = 0; i < parent.childCount; i++)
|
||||
{ Destroy(parent.transform.GetChild(i).gameObject); }
|
||||
}
|
||||
|
||||
public GameObject AddCloneObjectToParent(Transform parent, int i = 0)
|
||||
{
|
||||
GameObject cloneObject = null;
|
||||
|
||||
if (cloneObjects != null && cloneObjects.Count > i)
|
||||
{ cloneObject = Instantiate(cloneObjects[i], parent); }
|
||||
|
||||
return cloneObject;
|
||||
}
|
||||
|
||||
public void AddCustomTag(InputField field, ref List<string> tags, ref List<string> customTags)
|
||||
{
|
||||
if (field?.text == null || field.text == "")
|
||||
{ return; }
|
||||
|
||||
if (tags.Contains(field.text) || customTags.Contains(field.text))
|
||||
{ field.text = ""; return; }
|
||||
|
||||
customTags.Add(field.text);
|
||||
|
||||
ApplicationManager.Instance.SaveCustomArrays();
|
||||
Initialize(true);
|
||||
}
|
||||
|
||||
public void RemoveCustomTag(ref List<string> customTags, string tag)
|
||||
{
|
||||
customTags.Remove(tag);
|
||||
|
||||
ApplicationManager.Instance.SaveCustomArrays();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void AddCustomRace(InputField field)
|
||||
{
|
||||
if (field?.text == null || field.text == "")
|
||||
{ return; }
|
||||
|
||||
PawnRaceDefs.AddDef(new PawnRaceDef(field.text));
|
||||
|
||||
ApplicationManager.Instance.SavePawnRaceDefs();
|
||||
Initialize(true);
|
||||
}
|
||||
|
||||
public virtual void Initialize(bool addedNewTag = false) { }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue