From 23ddacdeb12fb08e12800374c8f57c26d61d8593 Mon Sep 17 00:00:00 2001 From: Hakoyu Date: Mon, 23 Oct 2023 19:39:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E8=A3=85=20=E9=A3=9F=E7=89=A9?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Models/ModModel/AnimeTypeModel.cs | 16 +- .../Models/ModModel/FoodAnimeModel.cs | 2 - .../Models/ModModel/FoodAnimeTypeModel.cs | 231 ++++++++++++++---- .../Models/ModModel/FoodLocationModel.cs | 5 + VPet.ModMaker/Models/ModModel/PetModel.cs | 6 +- VPet.ModMaker/Models/Utils.cs | 47 ++++ .../SimpleObservable/ObservableCommand.cs | 7 +- .../SimpleObservable/ObservableCommandT.cs | 7 +- .../SimpleObservable/ObservableValue.cs | 13 +- 9 files changed, 256 insertions(+), 78 deletions(-) diff --git a/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs b/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs index 025d4e9..04ac45f 100644 --- a/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs +++ b/VPet.ModMaker/Models/ModModel/AnimeTypeModel.cs @@ -614,12 +614,8 @@ public class AnimeTypeModel static void SaveAnimes(string animePath, ObservableCollection animes) { Directory.CreateDirectory(animePath); - var count = 0; - foreach (var anime in animes) - { - SaveImages(Path.Combine(animePath, count.ToString()), anime); - count++; - } + foreach (var anime in animes.Enumerate()) + SaveImages(Path.Combine(animePath, anime.Index.ToString()), anime.Value); } } @@ -631,16 +627,14 @@ public class AnimeTypeModel static void SaveImages(string imagesPath, AnimeModel model) { Directory.CreateDirectory(imagesPath); - var imageIndex = 0; - foreach (var image in model.Images) + foreach (var image in model.Images.Enumerate()) { - image.Image.Value.SaveToPng( + image.Value.Image.Value.SaveToPng( Path.Combine( imagesPath, - $"{model.Id.Value}_{imageIndex:000}_{image.Duration.Value}.png" + $"{model.Id.Value}_{image.Index:000}_{image.Value.Duration.Value}.png" ) ); - imageIndex++; } } #endregion diff --git a/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs b/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs index 6d6d2b8..6436977 100644 --- a/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs +++ b/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs @@ -24,13 +24,11 @@ public class FoodAnimeModel /// 后图像列表 /// public ObservableCollection BackImages { get; set; } = new(); - public ObservableValue BackImagesPath { get; } = new(); /// /// 前图像列表 /// public ObservableCollection FrontImages { get; set; } = new(); - public ObservableValue FrontImagesPath { get; } = new(); /// /// 食物定位列表 diff --git a/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs b/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs index 1014c3d..6efc93f 100644 --- a/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs +++ b/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs @@ -26,6 +26,16 @@ public class FoodAnimeTypeModel public static HashSet FoodAnimeNames = new(StringComparer.InvariantCultureIgnoreCase) { "Eat", "Drink", "Gift", }; + /// + /// 顶层名称 + /// + public const string FrontLayName = "front_lay"; + + /// + /// 底层名称 + /// + public const string BackLayName = "back_lay"; + /// /// Id /// @@ -58,7 +68,6 @@ public class FoodAnimeTypeModel public FoodAnimeTypeModel() { - HappyAnimes.CollectionChanged += Animes_CollectionChanged; Name.ValueChanged += (_, _) => { Id.Value = $"{GraphType}_{Name.Value}"; @@ -85,54 +94,6 @@ public class FoodAnimeTypeModel IllAnimes.Clear(); } - private void Animes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (e.NewItems is not null) - { - foreach (var model in e.NewItems.Cast()) - { - SetImagesPathValueChanged(model); - } - } - if (e.OldItems is not null) - { - foreach (var model in e.OldItems.Cast()) - { - SetImagesPathValueChanged(model); - } - } - } - - private void SetImagesPathValueChanged(FoodAnimeModel model) - { - model.FrontImagesPath.ValueChanged += (o, n) => - { - if (n is null) - return; - if (n.Mode.Value is GameSave.ModeType.Happy) - model.FrontImages = HappyAnimes[n.Index.Value].FrontImages; - else if (n.Mode.Value is GameSave.ModeType.Nomal) - model.FrontImages = NomalAnimes[n.Index.Value].FrontImages; - else if (n.Mode.Value is GameSave.ModeType.PoorCondition) - model.FrontImages = PoorConditionAnimes[n.Index.Value].FrontImages; - else if (n.Mode.Value is GameSave.ModeType.Ill) - model.FrontImages = IllAnimes[n.Index.Value].FrontImages; - }; - model.BackImagesPath.ValueChanged += (o, n) => - { - if (n is null) - return; - if (n.Mode.Value is GameSave.ModeType.Happy) - model.BackImages = HappyAnimes[n.Index.Value].BackImages; - else if (n.Mode.Value is GameSave.ModeType.Nomal) - model.BackImages = NomalAnimes[n.Index.Value].BackImages; - else if (n.Mode.Value is GameSave.ModeType.PoorCondition) - model.BackImages = PoorConditionAnimes[n.Index.Value].BackImages; - else if (n.Mode.Value is GameSave.ModeType.Ill) - model.BackImages = IllAnimes[n.Index.Value].BackImages; - }; - } - public FoodAnimeTypeModel(string path) : this() { @@ -192,6 +153,12 @@ public class FoodAnimeTypeModel } } + /// + /// 解析信息文件 + /// + /// 路径 + /// 信息文件路径 + /// public void ParseInfoFile(string path, string infoPath) { var lps = new LPS(File.ReadAllText(infoPath)); @@ -215,6 +182,12 @@ public class FoodAnimeTypeModel } } + /// + /// 解析食物动画信息 + /// + /// 路径 + /// 食物动画信息 + /// PNG动画信息 public void ParseFoodAnimeInfo(string path, ILine line, List pngAnimeInfos) { var mode = (GameSave.ModeType) @@ -235,6 +208,14 @@ public class FoodAnimeTypeModel AddModeAnime(path, GameSave.ModeType.Ill, IllAnimes, line, pngAnimeInfos); } + /// + /// 添加模式动画 + /// + /// 路径 + /// 模式 + /// 食物动画 + /// 食物动画信息 + /// PNG动画信息 public void AddModeAnime( string path, GameSave.ModeType mode, @@ -290,7 +271,157 @@ public class FoodAnimeTypeModel } } - public void Save(string path) { } + /// + /// 保存 + /// + /// 路径 + public void Save(string path) + { + var animePath = Path.Combine(path, Name.Value); + if ( + Directory.Exists(animePath) + && HappyAnimes.Count == 0 + && NomalAnimes.Count == 0 + && PoorConditionAnimes.Count == 0 + && IllAnimes.Count == 0 + ) + { + Directory.Delete(animePath, true); + return; + } + if (HappyAnimes.Count > 0) + SaveAnimeInfo(animePath, HappyAnimes, GameSave.ModeType.Happy); + if (NomalAnimes.Count > 0) + SaveAnimeInfo(animePath, NomalAnimes, GameSave.ModeType.Nomal); + if (PoorConditionAnimes.Count > 0) + SaveAnimeInfo(animePath, PoorConditionAnimes, GameSave.ModeType.PoorCondition); + if (IllAnimes.Count > 0) + SaveAnimeInfo(animePath, IllAnimes, GameSave.ModeType.Ill); + } + + /// + /// 保存动画信息 + /// + /// 路径 + /// 动画 + /// 模式 + private void SaveAnimeInfo( + string animePath, + ObservableCollection animes, + GameSave.ModeType mode + ) + { + var modeAnimePath = Path.Combine(animePath, mode.ToString()); + foreach (var anime in animes.Enumerate()) + { + var indexPath = Path.Combine(modeAnimePath, anime.Index.ToString()); + Directory.CreateDirectory(indexPath); + var infoFile = Path.Combine(indexPath, ModMakerInfo.InfoFile); + var frontLayName = $"{Name.Value.ToLower()}_{FrontLayName}_{anime.Index}"; + var backLayName = $"{Name.Value.ToLower()}_{BackLayName}_{anime.Index}"; + SaveInfoFile(infoFile, frontLayName, backLayName, anime.Value, mode); + SaveImages(anime.Value, indexPath, frontLayName, backLayName); + } + } + + /// + /// 保存图片 + /// + /// 动画 + /// 索引路径 + /// 顶层名称 + /// 底层名称 + private static void SaveImages( + FoodAnimeModel anime, + string indexPath, + string frontLayName, + string backLayName + ) + { + var frontLayPath = Path.Combine(indexPath, frontLayName); + var backLayPath = Path.Combine(indexPath, backLayName); + Directory.CreateDirectory(frontLayPath); + Directory.CreateDirectory(backLayPath); + foreach (var frontImage in anime.FrontImages.Enumerate()) + { + frontImage.Value.Image.Value.SaveToPng( + Path.Combine( + frontLayPath, + $"{anime.Id.Value}_{frontImage.Index:000}_{frontImage.Value.Duration.Value}.png" + ) + ); + } + foreach (var backImage in anime.BackImages.Enumerate()) + { + backImage.Value.Image.Value.SaveToPng( + Path.Combine( + backLayPath, + $"{anime.Id.Value}_{backImage.Index:000}_{backImage.Value.Duration.Value}.png" + ) + ); + } + } + + private void SaveInfoFile( + string infoFile, + string frontLayName, + string backLayName, + FoodAnimeModel anime, + GameSave.ModeType mode + ) + { + var lps = new LPS() + { + new Line(nameof(PNGAnimation), frontLayName) + { + new Sub("path", FrontLayName), + new Sub("mode", mode.ToString()), + new Sub("graph", nameof(GraphInfo.GraphType.Common)) + }, + new Line(nameof(PNGAnimation), backLayName) + { + new Sub("path", BackLayName), + new Sub("mode", mode.ToString()), + new Sub("graph", nameof(GraphInfo.GraphType.Common)) + }, + }; + var line = new Line(nameof(FoodAnimation), Name.Value.ToLower()) + { + new Sub("mode", mode.ToString()), + new Sub("graph", Name.Value) + }; + foreach (var foodLocation in anime.FoodLocations.Enumerate()) + { + var sub = new Sub($"a{foodLocation.Index}"); + sub.info = foodLocation.Value.ToString(); + line.Add(sub); + } + line.Add(new Sub(FrontLayName, frontLayName)); + line.Add(new Sub(BackLayName, backLayName)); + lps.Add(line); + File.WriteAllText(infoFile, lps.ToString()); + } + + /// + /// 保存图片 + /// + /// + /// + static void SaveImages(string imagesPath, AnimeModel model) + { + Directory.CreateDirectory(imagesPath); + var imageIndex = 0; + foreach (var image in model.Images) + { + image.Image.Value.SaveToPng( + Path.Combine( + imagesPath, + $"{model.Id.Value}_{imageIndex:000}_{image.Duration.Value}.png" + ) + ); + imageIndex++; + } + } } public class PNGAnimeInfo diff --git a/VPet.ModMaker/Models/ModModel/FoodLocationModel.cs b/VPet.ModMaker/Models/ModModel/FoodLocationModel.cs index e491f4e..bc0abae 100644 --- a/VPet.ModMaker/Models/ModModel/FoodLocationModel.cs +++ b/VPet.ModMaker/Models/ModModel/FoodLocationModel.cs @@ -49,4 +49,9 @@ public class FoodLocationModel model.Opacity.Value = Opacity.Value; return model; } + + public override string ToString() + { + return $"{Duration.Value},{Rect.X.Value},{Rect.Y.Value},{Rect.Width.Value},{Rotate.Value},{Opacity.Value}"; + } } diff --git a/VPet.ModMaker/Models/ModModel/PetModel.cs b/VPet.ModMaker/Models/ModModel/PetModel.cs index e3bec2d..5fbe69e 100644 --- a/VPet.ModMaker/Models/ModModel/PetModel.cs +++ b/VPet.ModMaker/Models/ModModel/PetModel.cs @@ -204,8 +204,10 @@ public class PetModel : I18nModel File.WriteAllText(petFile, lps.ToString()); var petAnimePath = Path.Combine(path, Id.Value); - foreach (var animeType in Animes) - animeType.Save(petAnimePath); + foreach (var anime in Animes) + anime.Save(petAnimePath); + foreach (var anime in FoodAnimes) + anime.Save(petAnimePath); } private void SaveSimplePetInfo(string path) diff --git a/VPet.ModMaker/Models/Utils.cs b/VPet.ModMaker/Models/Utils.cs index 681392d..732b294 100644 --- a/VPet.ModMaker/Models/Utils.cs +++ b/VPet.ModMaker/Models/Utils.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -64,4 +65,50 @@ public static class Utils } return bitmapImage; } + + /// + /// 枚举出带有索引值的枚举值 + /// + /// 值类型 + /// 集合 + /// 带有索引的枚举值 + public static IEnumerable> Enumerate(this IEnumerable collection) + { + var index = 0; + foreach (var item in collection) + yield return new(index++, item); + } +} + +/// +/// 项信息 +/// +/// +[DebuggerDisplay("[{Index}, {Value}]")] +public readonly struct ItemInfo +{ + /// + /// 索引值 + /// + public int Index { get; } + + /// + /// 值 + /// + public T Value { get; } + + /// + /// 值 + /// 索引值 + public ItemInfo(int index, T value) + { + Index = index; + Value = value; + } + + /// + public override string ToString() + { + return $"[{Index}, {Value}]"; + } } diff --git a/VPet.ModMaker/SimpleObservable/ObservableCommand.cs b/VPet.ModMaker/SimpleObservable/ObservableCommand.cs index 0239400..60771ed 100644 --- a/VPet.ModMaker/SimpleObservable/ObservableCommand.cs +++ b/VPet.ModMaker/SimpleObservable/ObservableCommand.cs @@ -35,11 +35,12 @@ public class ObservableCommand : ICommand CurrentCanExecute.ValueChanging += CurrentCanExecute_ValueChanging; } - private bool CurrentCanExecute_ValueChanging(bool oldValue, bool newValue) + private void CurrentCanExecute_ValueChanging(bool oldValue, bool newValue, ref bool cancel) { if (newValue is true && CanExecuteProperty.Value is false) - return true; - return false; + cancel = true; + else + cancel = false; } private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e) diff --git a/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs b/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs index 99837b9..8edc5c8 100644 --- a/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs +++ b/VPet.ModMaker/SimpleObservable/ObservableCommandT.cs @@ -35,11 +35,12 @@ public class ObservableCommand : ICommand CurrentCanExecute.ValueChanging += CurrentCanExecute_ValueChanging; } - private bool CurrentCanExecute_ValueChanging(bool oldValue, bool newValue) + private void CurrentCanExecute_ValueChanging(bool oldValue, bool newValue, ref bool cancel) { if (newValue is true && CanExecuteProperty.Value is false) - return true; - return false; + cancel = true; + else + cancel = false; } private void InvokeCanExecuteChanged(object? sender, PropertyChangedEventArgs e) diff --git a/VPet.ModMaker/SimpleObservable/ObservableValue.cs b/VPet.ModMaker/SimpleObservable/ObservableValue.cs index 21f6807..5ae1f0c 100644 --- a/VPet.ModMaker/SimpleObservable/ObservableValue.cs +++ b/VPet.ModMaker/SimpleObservable/ObservableValue.cs @@ -59,15 +59,14 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange /// /// 旧值 /// 新值 + /// 取消改变 private bool NotifyPropertyChanging(T oldValue, T newValue) { PropertyChanging?.Invoke(this, new(nameof(Value))); + var cancel = false; // 若全部事件取消改变 则取消改变 - return ValueChanging - ?.GetInvocationList() - .Cast() - .All(e => e.Invoke(oldValue, newValue) is true) - is true; + ValueChanging?.Invoke(oldValue, newValue, ref cancel); + return cancel; } /// @@ -168,8 +167,8 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange /// /// 旧值 /// 新值 - /// 取消改变 - public delegate bool ValueChangingEventHandler(T oldValue, T newValue); + /// 取消 + public delegate void ValueChangingEventHandler(T oldValue, T newValue, ref bool cancel); /// /// 值改变后事件