From 58fe49abc66f8a256247321742d2b5d43d370489 Mon Sep 17 00:00:00 2001 From: Hakoyu Date: Wed, 18 Oct 2023 23:54:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Converters/CalculatorConverter.cs | 3 + .../Converters/RatioMarginConverter.cs | 2 + .../Models/ModModel/AnimeTypeModel.cs | 20 ++ .../Models/ModModel/FoodAnimeModel.cs | 4 +- .../Models/ModModel/FoodAnimeTypeModel.cs | 54 +++- VPet.ModMaker/Models/ModModel/ImageModel.cs | 2 +- VPet.ModMaker/Models/ModModel/ModInfoModel.cs | 5 + VPet.ModMaker/Models/Utils.cs | 3 +- .../SimpleObservable/ObservableCommand.cs | 25 +- .../SimpleObservable/ObservableCommandT.cs | 29 +- .../SimpleObservable/ObservableValue.cs | 33 +- .../ModEdit/AnimeEdit/AnimeEditWindowVM.cs | 157 +++++++--- .../ModEdit/AnimeEdit/AnimePageVM.cs | 108 +++++-- .../AnimeEdit/FoodAnimeEditWindowVM.cs | 292 +++++++++++++----- .../ModEdit/AnimeEdit/AnimeEditWindow.xaml | 30 +- .../ModEdit/AnimeEdit/AnimeEditWindow.xaml.cs | 14 +- .../Views/ModEdit/AnimeEdit/AnimePage.xaml.cs | 4 +- .../AnimeEdit/FoodAnimeEditWindow.xaml | 55 ++-- .../AnimeEdit/FoodAnimeEditWindow.xaml.cs | 108 +++---- 19 files changed, 647 insertions(+), 301 deletions(-) diff --git a/VPet.ModMaker/Converters/CalculatorConverter.cs b/VPet.ModMaker/Converters/CalculatorConverter.cs index c87c747..188accd 100644 --- a/VPet.ModMaker/Converters/CalculatorConverter.cs +++ b/VPet.ModMaker/Converters/CalculatorConverter.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; using System.Windows.Data; namespace VPet.ModMaker.Converters; @@ -38,6 +39,8 @@ public class CalculatorConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { + if (values.Any(i => i == DependencyProperty.UnsetValue)) + return 0; if (values.Length == 1) return values[0]; double result = System.Convert.ToDouble(values[0]); diff --git a/VPet.ModMaker/Converters/RatioMarginConverter.cs b/VPet.ModMaker/Converters/RatioMarginConverter.cs index db26979..2380d12 100644 --- a/VPet.ModMaker/Converters/RatioMarginConverter.cs +++ b/VPet.ModMaker/Converters/RatioMarginConverter.cs @@ -29,6 +29,8 @@ public class RatioMarginConverter : IMultiValueConverter System.Globalization.CultureInfo culture ) { + if (values.Any(i => i == DependencyProperty.UnsetValue)) + return new Thickness(); if (values.Length == 0) { return new Thickness(); diff --git a/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs b/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs index 3ccbc2e..025d4e9 100644 --- a/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs +++ b/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs @@ -142,6 +142,26 @@ public class AnimeTypeModel } } + public void Close() + { + foreach (var anime in HappyAnimes) + anime.Close(); + foreach (var anime in NomalAnimes) + anime.Close(); + foreach (var anime in PoorConditionAnimes) + anime.Close(); + foreach (var anime in IllAnimes) + anime.Close(); + } + + public void Clear() + { + HappyAnimes.Clear(); + NomalAnimes.Clear(); + PoorConditionAnimes.Clear(); + IllAnimes.Clear(); + } + public AnimeTypeModel(GraphInfo.GraphType graphType, string path) { Name.Value = Path.GetFileName(path); diff --git a/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs b/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs index 4514fa5..6d6d2b8 100644 --- a/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs +++ b/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs @@ -57,9 +57,9 @@ public class FoodAnimeModel double.Parse(infos[3]) ); } - if (infos.Length > 3) - foodLocationInfo.Rotate.Value = double.Parse(infos[4]); if (infos.Length > 4) + foodLocationInfo.Rotate.Value = double.Parse(infos[4]); + if (infos.Length > 5) foodLocationInfo.Opacity.Value = double.Parse(infos[5]); FoodLocations.Add(foodLocationInfo); } diff --git a/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs b/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs index 1296a74..c06f2f4 100644 --- a/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs +++ b/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs @@ -15,6 +15,17 @@ namespace VPet.ModMaker.Models.ModModel; public class FoodAnimeTypeModel { + /// + /// 动作类型 + /// + public static GraphInfo.GraphType GraphType => GraphInfo.GraphType.Common; + + /// + /// 动画名称 + /// + public static HashSet FoodAnimeNames = + new(StringComparer.InvariantCultureIgnoreCase) { "Eat", "Drink", "Gift", }; + /// /// Id /// @@ -25,11 +36,6 @@ public class FoodAnimeTypeModel /// public ObservableValue Name { get; } = new(); - /// - /// 动作类型 - /// - public GraphInfo.GraphType GraphType => GraphInfo.GraphType.Common; - /// /// 开心动画 /// @@ -53,10 +59,30 @@ public class FoodAnimeTypeModel public FoodAnimeTypeModel() { HappyAnimes.CollectionChanged += Animes_CollectionChanged; - //Name.ValueChanged += (_, _) => - //{ - // Id.Value = $"{GraphType.Value}_{Name.Value}"; - //}; + Name.ValueChanged += (_, _) => + { + Id.Value = $"{GraphType}_{Name.Value}"; + }; + } + + public void Close() + { + foreach (var anime in HappyAnimes) + anime.Close(); + foreach (var anime in NomalAnimes) + anime.Close(); + foreach (var anime in PoorConditionAnimes) + anime.Close(); + foreach (var anime in IllAnimes) + anime.Close(); + } + + public void Clear() + { + HappyAnimes.Clear(); + NomalAnimes.Clear(); + PoorConditionAnimes.Clear(); + IllAnimes.Clear(); } private void Animes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -157,16 +183,16 @@ public class FoodAnimeTypeModel public void ParseInfoFile(string path, string infoPath) { - var lps = new LPS(infoPath); - var foodAnimeInfos = lps.FindAllLineInfo(nameof(FoodAnimation)); + var lps = new LPS(File.ReadAllText(infoPath)); + var foodAnimeInfos = lps.FindAllLine(nameof(FoodAnimation)); if (foodAnimeInfos.Any() is false) throw new Exception("信息文件\n{0}\n未包含食物动画信息".Translate(infoPath)); - var pngAnimeInfos = lps.FindAllLineInfo(nameof(PNGAnimation)) + var pngAnimeInfos = lps.FindAllLine(nameof(PNGAnimation)) .Select( i => new PNGAnimeInfo( i.Info, - i.Find("infoPath").Info, + i.Find("path").Info, (GameSave.ModeType) Enum.Parse(typeof(GameSave.ModeType), i.Find("mode").Info, true) ) @@ -246,7 +272,7 @@ public class FoodAnimeTypeModel i => new ImageModel( Utils.LoadImageToMemoryStream(i), - int.Parse(Path.GetFileNameWithoutExtension(i).Split('_')[1]) + int.Parse(Path.GetFileNameWithoutExtension(i).Split('_')[2]) ) ) ); diff --git a/VPet.ModMaker/Models/ModModel/ImageModel.cs b/VPet.ModMaker/Models/ModModel/ImageModel.cs index 1dd8d64..d44a9de 100644 --- a/VPet.ModMaker/Models/ModModel/ImageModel.cs +++ b/VPet.ModMaker/Models/ModModel/ImageModel.cs @@ -31,7 +31,7 @@ public class ImageModel public ImageModel Copy() { - var model = new ImageModel(Image.Value, Duration.Value); + var model = new ImageModel(Image.Value.Copy(), Duration.Value); return model; } diff --git a/VPet.ModMaker/Models/ModModel/ModInfoModel.cs b/VPet.ModMaker/Models/ModModel/ModInfoModel.cs index 937b491..79f5644 100644 --- a/VPet.ModMaker/Models/ModModel/ModInfoModel.cs +++ b/VPet.ModMaker/Models/ModModel/ModInfoModel.cs @@ -236,6 +236,11 @@ public class ModInfoModel : I18nModel ) petModel.Animes.Add(model1); } + else if (FoodAnimeTypeModel.FoodAnimeNames.Contains(dirName)) + { + //if (FoodAnimeTypeModel.Create(animeDir) is FoodAnimeTypeModel model1) + // petModel.FoodAnimes.Add(model1); + } } } diff --git a/VPet.ModMaker/Models/Utils.cs b/VPet.ModMaker/Models/Utils.cs index b84cea2..9f671de 100644 --- a/VPet.ModMaker/Models/Utils.cs +++ b/VPet.ModMaker/Models/Utils.cs @@ -50,7 +50,6 @@ public static class Utils { BitmapImage bitmapImage = new(); bitmapImage.BeginInit(); - bitmapImage.DecodePixelWidth = DecodePixelWidth; try { var ms = new MemoryStream(); @@ -62,6 +61,8 @@ public static class Utils { bitmapImage.EndInit(); } + if (bitmapImage.PixelWidth > DecodePixelWidth) + bitmapImage.DecodePixelWidth = DecodePixelWidth; return bitmapImage; } } diff --git a/VPet.ModMaker/SimpleObservable/ObservableCommand.cs b/VPet.ModMaker/SimpleObservable/ObservableCommand.cs index 672de43..0239400 100644 --- a/VPet.ModMaker/SimpleObservable/ObservableCommand.cs +++ b/VPet.ModMaker/SimpleObservable/ObservableCommand.cs @@ -13,7 +13,7 @@ namespace HKW.HKWViewModels.SimpleObservable; /// 可观察命令 /// [DebuggerDisplay( - "CanExecute = {CanExecuteProperty.EnumValue}, EventCount = {ExecuteEvent.GetInvocationList().Length}, AsyncEventCount = {AsyncExecuteEvent.GetInvocationList().Length}" + "CanExecute = {CanExecuteProperty.Value}, EventCount = {ExecuteEvent.GetInvocationList().Length}, AsyncEventCount = {AsyncExecuteEvent.GetInvocationList().Length}" )] public class ObservableCommand : ICommand { @@ -23,16 +23,23 @@ public class ObservableCommand : ICommand public ObservableValue CanExecuteProperty { get; } = new(true); /// - /// 等待异步执行完成 + /// 当前可执行状态 /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly ObservableValue r_waiting = new(false); + public ObservableValue CurrentCanExecute { get; } = new(true); /// public ObservableCommand() { CanExecuteProperty.PropertyChanged += InvokeCanExecuteChanged; - r_waiting.PropertyChanged += InvokeCanExecuteChanged; + CurrentCanExecute.PropertyChanged += InvokeCanExecuteChanged; + CurrentCanExecute.ValueChanging += CurrentCanExecute_ValueChanging; + } + + private bool CurrentCanExecute_ValueChanging(bool oldValue, bool newValue) + { + if (newValue is true && CanExecuteProperty.Value is false) + return true; + return false; } private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e) @@ -48,9 +55,7 @@ public class ObservableCommand : ICommand /// 能被执行为 否则为 public bool CanExecute(object? parameter) { - if (r_waiting.Value is true) - return false; - return CanExecuteProperty.Value; + return CurrentCanExecute.Value && CanExecuteProperty.Value; } /// @@ -71,12 +76,12 @@ public class ObservableCommand : ICommand { if (AsyncExecuteEvent is null) return; - r_waiting.Value = true; + CurrentCanExecute.Value = false; foreach ( var asyncEvent in AsyncExecuteEvent.GetInvocationList().Cast() ) await asyncEvent.Invoke(); - r_waiting.Value = false; + CurrentCanExecute.Value = true; } #endregion diff --git a/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs b/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs index a9e2706..99837b9 100644 --- a/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs +++ b/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs @@ -14,7 +14,7 @@ namespace HKW.HKWViewModels.SimpleObservable; /// /// 参数类型 [DebuggerDisplay( - "CanExecute = {CanExecuteProperty.EnumValue}, EventCount = {ExecuteEvent.GetInvocationList().Length}, AsyncEventCount = {AsyncExecuteEvent.GetInvocationList().Length}" + "CanExecute = {CanExecuteProperty.Value}, EventCount = {ExecuteEvent.GetInvocationList().Length}, AsyncEventCount = {AsyncExecuteEvent.GetInvocationList().Length}" )] public class ObservableCommand : ICommand where T : notnull @@ -22,15 +22,24 @@ public class ObservableCommand : ICommand /// public ObservableValue CanExecuteProperty { get; } = new(true); - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly ObservableValue r_waiting = new(false); + /// + /// 当前可执行状态 + /// + public ObservableValue CurrentCanExecute { get; } = new(true); - /// + /// public ObservableCommand() { CanExecuteProperty.PropertyChanged += InvokeCanExecuteChanged; - r_waiting.PropertyChanged += InvokeCanExecuteChanged; + CurrentCanExecute.PropertyChanged += InvokeCanExecuteChanged; + CurrentCanExecute.ValueChanging += CurrentCanExecute_ValueChanging; + } + + private bool CurrentCanExecute_ValueChanging(bool oldValue, bool newValue) + { + if (newValue is true && CanExecuteProperty.Value is false) + return true; + return false; } private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e) @@ -42,9 +51,7 @@ public class ObservableCommand : ICommand /// public bool CanExecute(object? parameter) { - if (r_waiting.Value is true) - return false; - return CanExecuteProperty.Value; + return CurrentCanExecute.Value && CanExecuteProperty.Value; } /// @@ -60,12 +67,12 @@ public class ObservableCommand : ICommand { if (AsyncExecuteEvent is null) return; - r_waiting.Value = true; + CurrentCanExecute.Value = false; foreach ( var asyncEvent in AsyncExecuteEvent.GetInvocationList().Cast() ) await asyncEvent.Invoke(parameter); - r_waiting.Value = false; + CurrentCanExecute.Value = true; } #endregion diff --git a/VPet.ModMaker/SimpleObservable/ObservableValue.cs b/VPet.ModMaker/SimpleObservable/ObservableValue.cs index 6fb88f7..21f6807 100644 --- a/VPet.ModMaker/SimpleObservable/ObservableValue.cs +++ b/VPet.ModMaker/SimpleObservable/ObservableValue.cs @@ -28,9 +28,11 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange { if (_value?.Equals(value) is true) return; - NotifyPropertyChanging(_value, value); + var oldValue = _value; + if (NotifyPropertyChanging(oldValue, value)) + return; _value = value; - NotifyPropertyChanged(_value, value); + NotifyPropertyChanged(oldValue, value); } } @@ -57,10 +59,15 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange /// /// 旧值 /// 新值 - private void NotifyPropertyChanging(T oldValue, T newValue) + private bool NotifyPropertyChanging(T oldValue, T newValue) { PropertyChanging?.Invoke(this, new(nameof(Value))); - ValueChanging?.Invoke(oldValue, newValue); + // 若全部事件取消改变 则取消改变 + return ValueChanging + ?.GetInvocationList() + .Cast() + .All(e => e.Invoke(oldValue, newValue) is true) + is true; } /// @@ -98,8 +105,8 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange /// { /// v = "B"; // trigger this /// }; - /// value1.EnumValue = "A"; // execute this - /// // result: value1.EnumValue == "A" , value2.EnumValue == "B" + /// value1.Value = "A"; // execute this + /// // result: value1.Value == "A" , value2.Value == "B" /// ]]> /// /// @@ -142,12 +149,12 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange /// /// 值改变前事件 /// - public event ValueChangeEventHandler? ValueChanging; + public event ValueChangingEventHandler? ValueChanging; /// /// 值改变后事件 /// - public event ValueChangeEventHandler? ValueChanged; + public event ValueChangedEventHandler? ValueChanged; /// /// 通知接收器事件 @@ -161,7 +168,15 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange /// /// 旧值 /// 新值 - public delegate void ValueChangeEventHandler(T oldValue, T newValue); + /// 取消改变 + public delegate bool ValueChangingEventHandler(T oldValue, T newValue); + + /// + /// 值改变后事件 + /// + /// 旧值 + /// 新值 + public delegate void ValueChangedEventHandler(T oldValue, T newValue); /// /// 通知接收器 diff --git a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs index 0905bd3..52491b5 100644 --- a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs +++ b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs @@ -3,12 +3,14 @@ using LinePutScript.Localization.WPF; using Microsoft.Win32; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; +using System.Windows.Media.Imaging; using VPet.ModMaker.Models; using VPet.ModMaker.Models.ModModel; using VPet_Simulator.Core; @@ -74,24 +76,34 @@ public class AnimeEditWindowVM public ObservableCommand StopCommand { get; } = new(); /// - /// 添加图片命令 + /// 添加动画命令 /// - public ObservableCommand AddImageCommand { get; } = new(); - - /// - /// 清除图片命令 - /// - public ObservableCommand ClearImageCommand { get; } = new(); + public ObservableCommand AddAnimeCommand { get; } = new(); /// /// 删除动画命令 /// public ObservableCommand RemoveAnimeCommand { get; } = new(); + /// + /// 添加图片命令 + /// + public ObservableCommand AddImageCommand { get; } = new(); + /// /// 删除图片命令 /// public ObservableCommand RemoveImageCommand { get; } = new(); + + /// + /// 替换图片命令 + /// + public ObservableCommand ChangeImageCommand { get; } = new(); + + /// + /// 清除图片命令 + /// + public ObservableCommand ClearImageCommand { get; } = new(); #endregion /// @@ -110,12 +122,14 @@ public class AnimeEditWindowVM CurrentAnimeModel.ValueChanged += CurrentAnimeModel_ValueChanged; - PlayCommand.ExecuteEvent += PlayCommand_ExecuteEvent; + PlayCommand.AsyncExecuteEvent += PlayCommand_AsyncExecuteEvent; StopCommand.ExecuteEvent += StopCommand_ExecuteEvent; - AddImageCommand.ExecuteEvent += AddImageCommand_ExecuteEvent; - ClearImageCommand.ExecuteEvent += ClearImageCommand_ExecuteEvent; + AddAnimeCommand.ExecuteEvent += AddAnimeCommand_ExecuteEvent; RemoveAnimeCommand.ExecuteEvent += RemoveAnimeCommand_ExecuteEvent; + AddImageCommand.ExecuteEvent += AddImageCommand_ExecuteEvent; RemoveImageCommand.ExecuteEvent += RemoveImageCommand_ExecuteEvent; + ChangeImageCommand.ExecuteEvent += ChangeImageCommand_ExecuteEvent; + ClearImageCommand.ExecuteEvent += ClearImageCommand_ExecuteEvent; Anime.ValueChanged += Anime_ValueChanged; } @@ -135,6 +149,24 @@ public class AnimeEditWindowVM HasAnimeName.Value = true; } #endregion + + #region AnimeCommand + /// + /// 添加动画 + /// + /// 动画模型 + private void AddAnimeCommand_ExecuteEvent() + { + if (CurrentMode is GameSave.ModeType.Happy) + Anime.Value.HappyAnimes.Add(new()); + else if (CurrentMode is GameSave.ModeType.Nomal) + Anime.Value.NomalAnimes.Add(new()); + else if (CurrentMode is GameSave.ModeType.PoorCondition) + Anime.Value.PoorConditionAnimes.Add(new()); + else if (CurrentMode is GameSave.ModeType.Ill) + Anime.Value.IllAnimes.Add(new()); + } + /// /// 删除动画 /// @@ -157,6 +189,64 @@ public class AnimeEditWindowVM value.Close(); } } + #endregion + + #region ImageCommand + + /// + /// 添加图片 + /// + /// 动画模型 + private void AddImageCommand_ExecuteEvent(AnimeModel value) + { + OpenFileDialog openFileDialog = + new() + { + Title = "选择图片".Translate(), + Filter = $"图片|*.png".Translate(), + Multiselect = true + }; + if (openFileDialog.ShowDialog() is not true) + return; + AddImages(value.Images, openFileDialog.FileNames); + } + + /// + /// 删除图片 + /// + /// 动画模型 + private void RemoveImageCommand_ExecuteEvent(AnimeModel value) + { + value.Images.Remove(CurrentImageModel.Value); + CurrentImageModel.Value.Close(); + } + + /// + /// 替换图片 + /// + /// + /// + private void ChangeImageCommand_ExecuteEvent(AnimeModel value) + { + OpenFileDialog openFileDialog = + new() { Title = "选择图片".Translate(), Filter = $"图片|*.png".Translate() }; + if (openFileDialog.ShowDialog() is not true) + return; + BitmapImage newImage; + try + { + newImage = Utils.LoadImageToMemoryStream(openFileDialog.FileName); + } + catch (Exception ex) + { + MessageBox.Show("替换失败失败 \n{0}".Translate(ex)); + return; + } + if (newImage is null) + return; + CurrentImageModel.Value.Close(); + CurrentImageModel.Value.Image.Value = newImage; + } /// /// 清空图片 @@ -176,57 +266,36 @@ public class AnimeEditWindowVM /// /// 添加图片 /// - /// 动画模型 - private void AddImageCommand_ExecuteEvent(AnimeModel value) - { - OpenFileDialog openFileDialog = - new() { Title = "选择图片".Translate(), Filter = $"图片|*.png".Translate() }; - if (openFileDialog.ShowDialog() is true) - { - value.Images.Add(new(Utils.LoadImageToMemoryStream(openFileDialog.FileName))); - } - } - - /// - /// 添加图片 - /// - /// 动画模型 + /// 动画 /// 路径 - public void AddImages(AnimeModel model, IEnumerable paths) + public void AddImages(ObservableCollection images, IEnumerable paths) { try { + var newImages = new List(); foreach (string path in paths) { if (File.Exists(path)) { - model.Images.Add(new(Utils.LoadImageToMemoryStream(path))); + newImages.Add(new(Utils.LoadImageToMemoryStream(path))); } else if (Directory.Exists(path)) { foreach (var file in Directory.EnumerateFiles(path, "*.png")) { - model.Images.Add(new(Utils.LoadImageToMemoryStream(path))); + newImages.Add(new(Utils.LoadImageToMemoryStream(path))); } } } + foreach (var image in newImages) + images.Add(image); } catch (Exception ex) { MessageBox.Show("添加失败 \n{0}".Translate(ex)); } } - - /// - /// 删除图片 - /// - /// 动画模型 - private void RemoveImageCommand_ExecuteEvent(AnimeModel value) - { - CurrentImageModel.Value.Close(); - value.Images.Remove(CurrentImageModel.Value); - } - + #endregion #region Player private void CurrentAnimeModel_ValueChanged(AnimeModel oldValue, AnimeModel newValue) { @@ -249,19 +318,13 @@ public class AnimeEditWindowVM { if (_playing is false) return; - Reset(); } /// /// 开始播放 /// - private void PlayCommand_ExecuteEvent() + private async Task PlayCommand_AsyncExecuteEvent() { - if (_playing) - { - MessageBox.Show("正在播放".Translate()); - return; - } if (CurrentAnimeModel.Value is null) { MessageBox.Show("未选中动画".Translate()); @@ -269,6 +332,8 @@ public class AnimeEditWindowVM } _playing = true; _playerTask.Start(); + await Task.WhenAll(_playerTask); + Reset(); } /// diff --git a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimePageVM.cs b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimePageVM.cs index ecfe136..86cf870 100644 --- a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimePageVM.cs +++ b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimePageVM.cs @@ -61,12 +61,12 @@ public class AnimePageVM /// /// 编辑命令 /// - public ObservableCommand EditCommand { get; } = new(); + public ObservableCommand EditCommand { get; } = new(); /// /// 删除命令 /// - public ObservableCommand RemoveCommand { get; } = new(); + public ObservableCommand RemoveCommand { get; } = new(); #endregion public AnimePageVM() { @@ -143,43 +143,83 @@ public class AnimePageVM selectGraphTypeWindow.CurrentPet.Value = CurrentPet.Value; selectGraphTypeWindow.ShowDialog(); var graphType = selectGraphTypeWindow.GraphType.Value; + var animeName = selectGraphTypeWindow.AnimeName.Value; if (selectGraphTypeWindow.IsCancel) return; - // TODO: FoodAnime - var window = new AnimeEditWindow(); - var vm = window.ViewModel; - vm.CurrentPet = CurrentPet.Value; - vm.Anime.Value.GraphType.Value = graphType; - vm.Anime.Value.Name.Value = selectGraphTypeWindow.AnimeName.Value; - window.ShowDialog(); - if (window.IsCancel) - return; - Animes.Add(vm.Anime.Value); + if ( + graphType is VPet_Simulator.Core.GraphInfo.GraphType.Common + && FoodAnimeTypeModel.FoodAnimeNames.Contains(animeName) + ) + { + var window = new FoodAnimeEditWindow(); + var vm = window.ViewModel; + vm.CurrentPet = CurrentPet.Value; + vm.Anime.Value.Name.Value = animeName; + window.ShowDialog(); + if (window.IsCancel) + return; + FoodAnimes.Add(vm.Anime.Value); + } + else + { + var window = new AnimeEditWindow(); + var vm = window.ViewModel; + vm.CurrentPet = CurrentPet.Value; + vm.Anime.Value.GraphType.Value = graphType; + vm.Anime.Value.Name.Value = animeName; + window.ShowDialog(); + if (window.IsCancel) + return; + Animes.Add(vm.Anime.Value); + } } /// /// 编辑动画 /// /// 动画类型模型 - public void Edit(AnimeTypeModel model) + public void Edit(object model) { - // TODO: FoodAnime - var window = new AnimeEditWindow(); - var vm = window.ViewModel; - vm.CurrentPet = CurrentPet.Value; - vm.OldAnime = model; - var newAnime = vm.Anime.Value = new(model); - window.ShowDialog(); - if (window.IsCancel) - return; - if (ShowAnimes.Value.Count == Animes.Count) + if (model is AnimeTypeModel animeTypeModel) { - Animes[Animes.IndexOf(model)] = newAnime; + // TODO: FoodAnime + var window = new AnimeEditWindow(); + var vm = window.ViewModel; + vm.CurrentPet = CurrentPet.Value; + vm.OldAnime = animeTypeModel; + var newAnime = vm.Anime.Value = new(animeTypeModel); + window.ShowDialog(); + if (window.IsCancel) + return; + if (ShowAnimes.Value.Count == Animes.Count) + { + Animes[Animes.IndexOf(animeTypeModel)] = newAnime; + } + else + { + Animes[Animes.IndexOf(animeTypeModel)] = newAnime; + ShowAnimes.Value[ShowAnimes.Value.IndexOf(animeTypeModel)] = newAnime; + } } - else + else if (model is FoodAnimeTypeModel foodAnimeTypeModel) { - Animes[Animes.IndexOf(model)] = newAnime; - ShowAnimes.Value[ShowAnimes.Value.IndexOf(model)] = newAnime; + var window = new FoodAnimeEditWindow(); + var vm = window.ViewModel; + vm.CurrentPet = CurrentPet.Value; + vm.OldAnime = foodAnimeTypeModel; + var newAnime = vm.Anime.Value = new(foodAnimeTypeModel); + window.ShowDialog(); + if (window.IsCancel) + return; + if (ShowAnimes.Value.Count == FoodAnimes.Count) + { + FoodAnimes[FoodAnimes.IndexOf(foodAnimeTypeModel)] = newAnime; + } + else + { + FoodAnimes[FoodAnimes.IndexOf(foodAnimeTypeModel)] = newAnime; + ShowAnimes.Value[ShowAnimes.Value.IndexOf(foodAnimeTypeModel)] = newAnime; + } } } @@ -187,18 +227,20 @@ public class AnimePageVM /// 删除动画 /// /// 动画类型模型 - private void Remove(AnimeTypeModel model) + private void Remove(object model) { if (MessageBox.Show("确定删除吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.No) return; - if (ShowAnimes.Value.Count == AllAnimes.Count) + ShowAnimes.Value.Remove(model); + if (model is AnimeTypeModel animeTypeModel) { - AllAnimes.Remove(model); + Animes.Remove(animeTypeModel); + animeTypeModel.Close(); } - else + else if (model is FoodAnimeTypeModel foodAnimeTypeModel) { - ShowAnimes.Value.Remove(model); - AllAnimes.Remove(model); + FoodAnimes.Remove(foodAnimeTypeModel); + foodAnimeTypeModel.Close(); } } } diff --git a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs index 0631693..21b12ad 100644 --- a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs +++ b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs @@ -3,11 +3,9 @@ using LinePutScript.Localization.WPF; using Microsoft.Win32; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; -using System.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Imaging; @@ -27,12 +25,15 @@ public class FoodAnimeEditWindowVM /// /// 默认食物图片 /// - public static BitmapImage DefaultFoodImage { get; } = Utils.LoadImageToMemoryStream(""); + public static BitmapImage DefaultFoodImage { get; } = + Utils.LoadImageToMemoryStream( + "C:\\Users\\HKW\\Desktop\\TestPicture\\0000_core\\image\\food.png" + ); /// /// 食物图片 /// - public ObservableValue FoodImage { get; } = new(); + public ObservableValue FoodImage { get; } = new(DefaultFoodImage); /// /// 比例 @@ -101,25 +102,64 @@ public class FoodAnimeEditWindowVM public ObservableCommand StopCommand { get; } = new(); /// - /// 添加图片命令 + /// 添加动画命令 /// - public ObservableCommand AddImageCommand { get; } = new(); - - /// - /// 清除图片命令 - /// - public ObservableCommand ClearImageCommand { get; } = new(); + public ObservableCommand AddAnimeCommand { get; } = new(); /// /// 删除动画命令 /// - public ObservableCommand RemoveAnimeCommand { get; } = new(); + public ObservableCommand RemoveAnimeCommand { get; } = new(); + + #region FrontImage + /// + /// 添加顶层图片命令 + /// + public ObservableCommand AddFrontImageCommand { get; } = new(); /// - /// 删除图片命令 + /// 删除顶层图片命令 /// - public ObservableCommand RemoveImageCommand { get; } = new(); + public ObservableCommand RemoveFrontImageCommand { get; } = new(); + /// + /// 清除顶层图片命令 + /// + public ObservableCommand ClearFrontImageCommand { get; } = new(); + #endregion + + #region BackImage + /// + /// 添加底层图片命令 + /// + public ObservableCommand AddBackImageCommand { get; } = new(); + + /// + /// 删除底层图片命令 + /// + public ObservableCommand RemoveBackImageCommand { get; } = new(); + + /// + /// 清除底层图片命令 + /// + public ObservableCommand ClearBackImageCommand { get; } = new(); + #endregion + #region FoodLocation + /// + /// 添加食物定位命令 + /// + public ObservableCommand AddeFoodLocationCommand { get; } = new(); + + /// + /// 删除食物定位命令 + /// + public ObservableCommand RemoveFoodLocationCommand { get; } = new(); + + /// + /// 清除食物定位命令 + /// + public ObservableCommand ClearFoodLocationCommand { get; } = new(); + #endregion /// /// 改变食物图片 /// @@ -159,16 +199,25 @@ public class FoodAnimeEditWindowVM CurrentAnimeModel.ValueChanged += CurrentAnimeModel_ValueChanged; - PlayCommand.ExecuteEvent += PlayCommand_ExecuteEvent; + PlayCommand.AsyncExecuteEvent += PlayCommand_AsyncExecuteEvent; StopCommand.ExecuteEvent += StopCommand_ExecuteEvent; - AddImageCommand.ExecuteEvent += AddImageCommand_ExecuteEvent; - ClearImageCommand.ExecuteEvent += ClearImageCommand_ExecuteEvent; - RemoveAnimeCommand.ExecuteEvent += RemoveAnimeCommand_ExecuteEvent; - RemoveImageCommand.ExecuteEvent += RemoveImageCommand_ExecuteEvent; - ReplaceFoodImageCommand.ExecuteEvent += ReplaceFoodImageCommand_ExecuteEvent; + ReplaceFoodImageCommand.ExecuteEvent += ChangeFoodImageCommand_ExecuteEvent; ResetFoodImageCommand.ExecuteEvent += ResetFoodImageCommand_ExecuteEvent; - Anime.ValueChanged += Anime_ValueChanged; + AddAnimeCommand.ExecuteEvent += AddAnimeCommand_ExecuteEvent; + RemoveAnimeCommand.ExecuteEvent += RemoveAnimeCommand_ExecuteEvent; + + AddFrontImageCommand.ExecuteEvent += AddFrontImageCommand_ExecuteEvent; + RemoveFrontImageCommand.ExecuteEvent += RemoveFrontImageCommand_ExecuteEvent; + ClearFrontImageCommand.ExecuteEvent += ClearFrontImageCommand_ExecuteEvent; + + AddBackImageCommand.ExecuteEvent += AddBackImageCommand_ExecuteEvent; + RemoveBackImageCommand.ExecuteEvent += RemoveBackImageCommand_ExecuteEvent; + ClearBackImageCommand.ExecuteEvent += ClearBackImageCommand_ExecuteEvent; + + AddeFoodLocationCommand.ExecuteEvent += AddeFoodLocationCommand_ExecuteEvent; + RemoveFoodLocationCommand.ExecuteEvent += RemoveFoodLocationCommand_ExecuteEvent; + ClearFoodLocationCommand.ExecuteEvent += ClearFoodLocationCommand_ExecuteEvent; } private void ResetFoodImageCommand_ExecuteEvent() @@ -178,7 +227,7 @@ public class FoodAnimeEditWindowVM FoodImage.Value = DefaultFoodImage; } - private void ReplaceFoodImageCommand_ExecuteEvent() + private void ChangeFoodImageCommand_ExecuteEvent() { OpenFileDialog openFileDialog = new() { Title = "选择食物图片".Translate(), Filter = $"图片|*.png".Translate() }; @@ -190,113 +239,218 @@ public class FoodAnimeEditWindowVM } } - #region LoadAnime - private void Anime_ValueChanged(FoodAnimeTypeModel oldValue, FoodAnimeTypeModel newValue) + //#region LoadAnime + //private void Anime_ValueChanged(FoodAnimeTypeModel oldValue, FoodAnimeTypeModel newValue) + //{ + // CheckGraphType(newValue); + //} + + //private void CheckGraphType(FoodAnimeTypeModel model) + //{ + // //if (FoodAnimeTypeModel.HasMultiTypeAnimes.Contains(model.GraphType.Value)) + // // HasMultiType.Value = true; + + // //if (FoodAnimeTypeModel.HasNameAnimes.Contains(model.GraphType.Value)) + // // HasAnimeName.Value = true; + //} + //#endregion + #region AnimeCommand + private void AddAnimeCommand_ExecuteEvent() { - CheckGraphType(newValue); + if (CurrentMode is GameSave.ModeType.Happy) + Anime.Value.HappyAnimes.Add(new()); + else if (CurrentMode is GameSave.ModeType.Nomal) + Anime.Value.NomalAnimes.Add(new()); + else if (CurrentMode is GameSave.ModeType.PoorCondition) + Anime.Value.PoorConditionAnimes.Add(new()); + else if (CurrentMode is GameSave.ModeType.Ill) + Anime.Value.IllAnimes.Add(new()); } - private void CheckGraphType(FoodAnimeTypeModel model) - { - //if (FoodAnimeTypeModel.HasMultiTypeAnimes.Contains(model.GraphType.Value)) - // HasMultiType.Value = true; - - //if (FoodAnimeTypeModel.HasNameAnimes.Contains(model.GraphType.Value)) - // HasAnimeName.Value = true; - } - #endregion /// /// 删除动画 /// /// 动画模型 - private void RemoveAnimeCommand_ExecuteEvent(AnimeModel value) + private void RemoveAnimeCommand_ExecuteEvent(FoodAnimeModel value) { if ( MessageBox.Show("确定删除吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.Yes ) { - //if (CurrentMode is GameSave.ModeType.Happy) - // Anime.Value.HappyAnimes.Remove(value); - //else if (CurrentMode is GameSave.ModeType.Nomal) - // Anime.Value.NomalAnimes.Remove(value); - //else if (CurrentMode is GameSave.ModeType.PoorCondition) - // Anime.Value.PoorConditionAnimes.Remove(value); - //else if (CurrentMode is GameSave.ModeType.Ill) - // Anime.Value.IllAnimes.Remove(value); + if (CurrentMode is GameSave.ModeType.Happy) + Anime.Value.HappyAnimes.Remove(value); + else if (CurrentMode is GameSave.ModeType.Nomal) + Anime.Value.NomalAnimes.Remove(value); + else if (CurrentMode is GameSave.ModeType.PoorCondition) + Anime.Value.PoorConditionAnimes.Remove(value); + else if (CurrentMode is GameSave.ModeType.Ill) + Anime.Value.IllAnimes.Remove(value); value.Close(); } } + #endregion + #region ImageCommand + + #region FrontImageCommand + /// + /// 添加顶层图片 + /// + /// 动画模型 + private void AddFrontImageCommand_ExecuteEvent(FoodAnimeModel value) + { + OpenFileDialog openFileDialog = + new() + { + Title = "选择图片".Translate(), + Filter = $"图片|*.png".Translate(), + Multiselect = true + }; + if (openFileDialog.ShowDialog() is true) + { + AddImages(value.FrontImages, openFileDialog.FileNames); + } + } /// - /// 清空图片 + /// 删除顶层图片 /// /// 动画模型 - private void ClearImageCommand_ExecuteEvent(AnimeModel value) + private void RemoveFrontImageCommand_ExecuteEvent(FoodAnimeModel value) + { + value.FrontImages.Remove(CurrentFrontImageModel.Value); + CurrentFrontImageModel.Value.Close(); + } + + /// + /// 清空顶层图片 + /// + /// 动画模型 + private void ClearFrontImageCommand_ExecuteEvent(FoodAnimeModel value) { if ( MessageBox.Show("确定清空吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.Yes ) { - value.Close(); - value.Images.Clear(); + foreach (var image in value.FrontImages) + image.Close(); + value.FrontImages.Clear(); } } + private void ShowFrontImagesPathInfo(FoodImagesPath imagesPath) + { + MessageBox.Show( + "此顶层动画源位于其它位置\n请去源位置修改此动画\n源位置 模式:{0} 索引:{1}".Translate( + imagesPath.Mode, + imagesPath.Index + ) + ); + } + #endregion + + #region BackImageCommand /// - /// 添加图片 + /// 添加顶层图片 /// /// 动画模型 - private void AddImageCommand_ExecuteEvent(AnimeModel value) + private void AddBackImageCommand_ExecuteEvent(FoodAnimeModel value) { OpenFileDialog openFileDialog = - new() { Title = "选择图片".Translate(), Filter = $"图片|*.png".Translate() }; + new() + { + Title = "选择图片".Translate(), + Filter = $"图片|*.png".Translate(), + Multiselect = true + }; if (openFileDialog.ShowDialog() is true) { - value.Images.Add(new(Utils.LoadImageToMemoryStream(openFileDialog.FileName))); + AddImages(value.BackImages, openFileDialog.FileNames); } } + /// + /// 删除顶层图片 + /// + /// 动画模型 + private void RemoveBackImageCommand_ExecuteEvent(FoodAnimeModel value) + { + value.BackImages.Remove(CurrentBackImageModel.Value); + CurrentBackImageModel.Value.Close(); + } + + /// + /// 清空顶层图片 + /// + /// 动画模型 + private void ClearBackImageCommand_ExecuteEvent(FoodAnimeModel value) + { + if ( + MessageBox.Show("确定清空吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.Yes + ) + { + foreach (var image in value.BackImages) + image.Close(); + value.BackImages.Clear(); + } + } + #endregion + /// /// 添加图片 /// - /// 动画模型 + /// 动画 /// 路径 - public void AddImages(AnimeModel model, IEnumerable paths) + public void AddImages(ObservableCollection images, IEnumerable paths) { try { + var newImages = new List(); foreach (string path in paths) { if (File.Exists(path)) { - model.Images.Add(new(Utils.LoadImageToMemoryStream(path))); + newImages.Add(new(Utils.LoadImageToMemoryStream(path))); } else if (Directory.Exists(path)) { foreach (var file in Directory.EnumerateFiles(path, "*.png")) { - model.Images.Add(new(Utils.LoadImageToMemoryStream(path))); + newImages.Add(new(Utils.LoadImageToMemoryStream(path))); } } } + foreach (var image in newImages) + images.Add(image); } catch (Exception ex) { MessageBox.Show("添加失败 \n{0}".Translate(ex)); } } - - /// - /// 删除图片 - /// - /// 动画模型 - private void RemoveImageCommand_ExecuteEvent(AnimeModel value) + #endregion + #region FoodLocationCommand + private void AddeFoodLocationCommand_ExecuteEvent(FoodAnimeModel value) { - CurrentFrontImageModel.Value.Close(); - value.Images.Remove(CurrentFrontImageModel.Value); + value.FoodLocations.Add(new()); } + private void RemoveFoodLocationCommand_ExecuteEvent(FoodAnimeModel value) + { + value.FoodLocations.Remove(CurrentFoodLocationModel.Value); + CurrentFoodLocationModel.Value = null; + } + + private void ClearFoodLocationCommand_ExecuteEvent(FoodAnimeModel value) + { + if ( + MessageBox.Show("确定清空吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.Yes + ) + { + value.FoodLocations.Clear(); + } + } + #endregion #region FrontPlayer private void CurrentAnimeModel_ValueChanged(FoodAnimeModel oldValue, FoodAnimeModel newValue) { @@ -327,19 +481,13 @@ public class FoodAnimeEditWindowVM { if (_playing is false) return; - Reset(); } /// /// 开始播放 /// - private void PlayCommand_ExecuteEvent() + private async Task PlayCommand_AsyncExecuteEvent() { - if (_playing) - { - MessageBox.Show("正在播放".Translate()); - return; - } if (CurrentAnimeModel.Value is null) { MessageBox.Show("未选中动画".Translate()); @@ -349,6 +497,8 @@ public class FoodAnimeEditWindowVM _frontPlayerTask.Start(); _backPlayerTask.Start(); _foodPlayerTask.Start(); + await Task.WhenAll(_frontPlayerTask, _backPlayerTask, _foodPlayerTask); + Reset(); } /// diff --git a/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml b/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml index 110c3fa..7b2c6c5 100644 --- a/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml +++ b/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml @@ -12,6 +12,7 @@ Width="1000" Height="600" d:DataContext="{d:DesignInstance Type=vm:AnimeEditWindowVM}" + WindowStartupLocation="CenterScreen" mc:Ignorable="d">