JD2022-TU1/main/tools/framework/JD.Serialization/Timeline/Clips/MoveModel.cs

478 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using JD.Core;
using JD.Core.Collections;
using JD.Core.Helpers;
using JetBrains.Annotations;
namespace JD.Serialization
{
public class MoveModel : INotifyPropertyChanged, ICloneable
{
#region Constants
const float scoringCamera_defaultLowThreshold_PS4 = - 0.2f;
const float scoringCamera_defaultHighThreshold_PS4 = 0.6f;
#endregion
#region Variables
private string _moveName;
[NotNull]
private readonly Tape _tape;
private Path _classifierPath;
private Color _color;
private float _beatDuration;
private int _instancesCount;
private MotionClip.MoveType_Enum _moveType;
private MotionClip.MotionPlatformSpecific _camera_Xbox360 = new MotionClip.MotionPlatformSpecific();
private MotionClip.MotionPlatformSpecific _camera_XboxOne = new MotionClip.MotionPlatformSpecific();
private MotionClip.MotionPlatformSpecific _camera_PS4 = new MotionClip.MotionPlatformSpecific();
private static readonly List<Color> _palette = new List<Color>
{
Color.LightSteelBlue,
Color.Moccasin,
Color.LightGray,
Color.RosyBrown,
Color.SteelBlue,
Color.Olive,
Color.Beige,
Color.LightYellow,
Color.MediumPurple,
Color.PaleTurquoise
};
private static int _currentPaletteIndex;
private static readonly char[] _validModelNameChars = Enumerable.Range('a', 26)
.Union(Enumerable.Range('A', 26))
.Union(Enumerable.Range('0', 10))
.Union(new int[] { '_' })
.Select(x => (char)x).ToArray();
#endregion
#region Constructor
public MoveModel(Tape tape, Path classifierPath)
{
if (tape == null)
{
throw new ArgumentNullException(nameof(tape));
}
_tape = tape;
_classifierPath = classifierPath;
_moveName = GetMoveNameFromClassifierPath(_tape, ClassifierPath);
_moveType = GetMoveTypeFromClassifierPath(classifierPath);
if (Instances.Any())
{
MotionClip clip = Instances.First();
_color = clip.Color;
_beatDuration = clip.BeatDuration;
_camera_Xbox360.CopyFrom(clip.MotionPlatformSpecifics[Platform.X360]);
_camera_XboxOne.CopyFrom(clip.MotionPlatformSpecifics[Platform.DURANGO]);
_camera_PS4.CopyFrom(clip.MotionPlatformSpecifics[Platform.ORBIS]);
}
else
{
_color = _palette[_currentPaletteIndex];
_currentPaletteIndex = (_currentPaletteIndex + 1) % _palette.Count;
_beatDuration = 2;
_camera_PS4.LowThreshold = scoringCamera_defaultLowThreshold_PS4; // new move clip : for PS4 we override the UAF serialized default value
_camera_PS4.HighThreshold = scoringCamera_defaultHighThreshold_PS4; // new move clip : for PS4 we override the UAF serialized default value
}
Camera_Xbox360.PropertyChanged += delegate { OnPropertyChanged(nameof(Camera_Xbox360)); };
Camera_XboxOne.PropertyChanged += delegate { OnPropertyChanged(nameof(Camera_XboxOne)); };
Camera_PS4.PropertyChanged += delegate { OnPropertyChanged(nameof(Camera_PS4)); };
InstancesCount = Instances.Count();
}
#endregion
#region Properties
[Browsable(false)]
public IEnumerable<MotionClip> Instances
{
get
{
return _tape.Clips.OfType<MotionClip>().Where(clip => clip.ClassifierPath.Equals(ClassifierPath));
}
}
[Browsable(true)]
public string Name
{
get { return _moveName; }
set
{
if (_moveName != value)
{
string sanitizedValue = SanitizeMoveModelName(value);
string uniqueNewMoveName = sanitizedValue.MakeUniqueName(Tape.MoveModels().Where(x => x != this && x.MoveType == MoveType).Select(x => x.Name).ToList());
if (uniqueNewMoveName != sanitizedValue)
{
MoveNameMadeUnique?.Invoke(uniqueNewMoveName);
}
_moveName = uniqueNewMoveName != sanitizedValue ? uniqueNewMoveName : sanitizedValue;
string tmpMoveName = _moveName;
OnPropertyChanged();
ClassifierPath = new Path(GetClassifierPathFromMoveName(Tape, _moveName, _moveType));
_moveName = tmpMoveName; // when the ClassifierPath is modified, it modifies the casing of the _moveName (like a lowerCase())
}
}
}
private static string SanitizeMoveModelName(string modelName)
{
string sanitizedValue = modelName;
var invalidChars = modelName.ToCharArray().Distinct().Where(x => !_validModelNameChars.Contains(x)).ToList();
foreach (var invalidChar in invalidChars)
{
sanitizedValue = sanitizedValue.Replace(invalidChar.ToString(), string.Empty);
}
return sanitizedValue;
}
public static bool IsNameValid(string modelName)
{
return modelName.ToCharArray().All(@char => _validModelNameChars.Contains(@char));
}
[Browsable(true)]
[ReadOnly(true)]
public Path ClassifierPath
{
get { return _classifierPath; }
set
{
if (!_classifierPath.Equals(value))
{
_classifierPath = value;
_moveName = GetMoveNameFromClassifierPath(Tape, ClassifierPath);
_moveType = GetMoveTypeFromClassifierPath(ClassifierPath);
OnPropertyChanged();
}
}
}
[Browsable(true)]
public Color Color
{
get { return _color; }
set
{
if (_color != value)
{
_color = value;
OnPropertyChanged();
}
}
}
[Browsable(false)]
public MotionClip.MoveType_Enum MoveType
{
get { return _moveType; }
set
{
if (_moveType != value)
{
_moveType = value;
OnPropertyChanged();
ClassifierPath = new Path(GetClassifierPathFromMoveName(Tape, _moveName, _moveType));
}
}
}
[DisplayName("Duration (in beats)")]
[Description("Duration in beats")]
[Browsable(true)]
public float BeatDuration
{
get { return _beatDuration; }
set
{
if (!_beatDuration.AlmostEquals(value))
{
_beatDuration = value;
OnPropertyChanged();
}
}
}
[Browsable(true)]
[ReadOnly(true)]
public int InstancesCount
{
get { return _instancesCount; }
set
{
if (_instancesCount != value)
{
_instancesCount = value;
OnPropertyChanged();
}
}
}
[Browsable(true)]
public MotionClip.MotionPlatformSpecific Camera_Xbox360
{
get { return _camera_Xbox360; }
set { _camera_Xbox360 = value; }
}
[Browsable(true)]
public MotionClip.MotionPlatformSpecific Camera_XboxOne
{
get { return _camera_XboxOne; }
set { _camera_XboxOne = value; }
}
[Browsable(true)]
public MotionClip.MotionPlatformSpecific Camera_PS4
{
get { return _camera_PS4; }
set { _camera_PS4 = value; }
}
public Tape Tape
{
get { return _tape; }
}
#endregion
#region UIProperties
public string MoveTypeFriendlyName
{
get
{
switch (MoveType)
{
case MotionClip.MoveType_Enum.MoveType_HandOnly:
return "Move";
case MotionClip.MoveType_Enum.MoveType_FullBody:
return "Camera Move";
}
return "";
}
}
#endregion
#region Methods
public override string ToString()
{
return Name;
}
internal static string GetMoveNameFromClassifierPath(Tape tape, Path classifierPath)
{
string moveName = System.IO.Path.GetFileNameWithoutExtension(classifierPath.getAbsolute());
if (moveName != null)
{
moveName = Regex.Replace(moveName, tape.MapInfo?.Name + "_", "", RegexOptions.IgnoreCase);
}
return moveName;
}
internal static MotionClip.MoveType_Enum GetMoveTypeFromClassifierPath(Path classifierPath)
{
return classifierPath.ToString().EndsWith(".msm") ? MotionClip.MoveType_Enum.MoveType_HandOnly : MotionClip.MoveType_Enum.MoveType_FullBody;
}
internal static string GetClassifierPathFromMoveName(Tape tape, string moveName, MotionClip.MoveType_Enum moveType)
{
string classifierFileName = tape.MapInfo?.Name + "_" + moveName;
if (moveType == MotionClip.MoveType_Enum.MoveType_HandOnly)
classifierFileName += ".msm";
if (moveType == MotionClip.MoveType_Enum.MoveType_FullBody)
classifierFileName += ".gesture";
string classifierPath = System.IO.Path.Combine(tape.MapInfo?.MovesFolderRelative, classifierFileName);
classifierPath = classifierPath.Replace("\\", "/").ToLower();
return classifierPath;
}
public object Clone()
{
var cloneModel = (MoveModel)MemberwiseClone();
cloneModel.Camera_Xbox360 = Camera_Xbox360.Clone();
cloneModel.Camera_XboxOne = Camera_XboxOne.Clone();
cloneModel.Camera_PS4 = Camera_PS4.Clone();
return cloneModel;
}
#endregion
#region Events
public delegate void MoveNameMadeUniqueFunc(string newMoveName);
public static event MoveNameMadeUniqueFunc MoveNameMadeUnique;
public event PropertyChangedEventHandler PropertyChanged;
//[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
#region Manager
public class MoveModelCollection : UAFList<MoveModel>
{
}
public static class MoveModelManager
{
private static void CreateMoveModelCollection(this Tape tape)
{
tape.Extensions.Models = new MoveModelCollection();
tape.Clips.CollectionChanged += tape.ClipsOnCollectionChanged;
tape.Clips.ItemChanged += tape.ClipsOnItemChanged;
tape.PropertyChanged += tape.tape_PropertyChanged;
}
static void tape_PropertyChanged(this Tape tape, object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsLoading" && !tape.IsLoading)
{
RefreshInstancesCount(tape);
}
}
private static void ClipsOnItemChanged(this Tape tape, object sender, ItemChangedEventArgs<Clip> args)
{
if (args.PropertyName == "Model" && !tape.IsLoading)
{
RefreshInstancesCount(tape);
}
}
static void ClipsOnCollectionChanged(this Tape tape, object sender, NotifyCollectionChangedEventArgs e)
{
if (!tape.IsLoading)
{
if (e.NewItems != null)
{
foreach (var model in e.NewItems.OfType<MotionClip>().Select(x => x.Model).Distinct())
{
RefreshInstancesCount(tape, model);
}
}
if (e.OldItems != null)
{
foreach (var model in e.OldItems.OfType<MotionClip>().Select(x => x.Model).Distinct())
{
RefreshInstancesCount(tape, model);
}
}
}
}
private static void RefreshInstancesCount(this Tape tape)
{
foreach (var model in tape.MoveModels().ToList())
{
RefreshInstancesCount(tape, model);
}
}
private static void RefreshInstancesCount(this Tape tape, MoveModel model)
{
model.InstancesCount = tape.Clips.OfType<MotionClip>().Count(x => x.Model == model);
if (model.InstancesCount == 0)
{
tape.MoveModels().Remove(model);
}
}
public static MoveModel GetOrCreateMoveModel(this Tape tape, Path classifierPath, out bool created)
{
created = false;
var models = tape.MoveModels();
MoveModel model = models.FirstOrDefault(x => x.ClassifierPath.Equals(classifierPath));
if (model == null)
{
model = new MoveModel(tape, classifierPath);
models.Add(model);
created = true;
}
return model;
}
public static void UpdateTapeMovelModels(this Tape tape, MotionClip clip, Path classifierPath)
{
var models = tape.MoveModels();
MoveModel model = models.FirstOrDefault(x => x.ClassifierPath.Equals(clip.ClassifierPath));
if (model == null)
{
model = (MoveModel)clip.Model.Clone();
model.ClassifierPath = classifierPath;
models.Add(model);
}
}
public static MoveModel GetOrCreateMoveModel(this Tape tape, Path classifierPath)
{
bool created;
return GetOrCreateMoveModel(tape, classifierPath, out created);
}
public static MoveModel GetOrCreateMoveModel(this Tape tape, string moveModelName, MotionClip.MoveType_Enum moveType, out bool created)
{
var classifierPath = new Path(MoveModel.GetClassifierPathFromMoveName(tape, moveModelName, moveType));
var moveModel = GetOrCreateMoveModel(tape, classifierPath, out created);
return moveModel;
}
public static MoveModel GetOrCreateMoveModel(this Tape tape, string moveModelName, MotionClip.MoveType_Enum moveType)
{
bool created;
return GetOrCreateMoveModel(tape, moveModelName, moveType, out created);
}
public static MoveModelCollection MoveModels(this Tape tape)
{
if (tape.Extensions.Models == null)
{
tape.CreateMoveModelCollection();
}
return tape.Extensions.Models;
}
public static void ClearMoveModels(this Tape tape)
{
MoveModelCollection models = tape.Extensions.Models;
models?.Clear();
}
}
#endregion
}