diff --git a/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs b/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs
index 7714dca..4514fa5 100644
--- a/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs
+++ b/VPet.ModMaker/Models/ModModel/FoodAnimeModel.cs
@@ -23,19 +23,19 @@ public class FoodAnimeModel
///
/// 后图像列表
///
- public ObservableCollection BackImages { get; } = new();
- public ObservableValue BackImagesPath { get; } = new();
+ public ObservableCollection BackImages { get; set; } = new();
+ public ObservableValue BackImagesPath { get; } = new();
///
/// 前图像列表
///
- public ObservableCollection FrontImages { get; } = new();
- public ObservableValue FrontImagesPath { get; } = new();
+ public ObservableCollection FrontImages { get; set; } = new();
+ public ObservableValue FrontImagesPath { get; } = new();
///
/// 食物定位列表
///
- public ObservableCollection FoodLocations { get; } = new();
+ public ObservableCollection FoodLocations { get; } = new();
public FoodAnimeModel() { }
@@ -46,7 +46,7 @@ public class FoodAnimeModel
{
//var index = int.Parse(item.Name.Substring(1));
var infos = item.Info.Split(',');
- var foodLocationInfo = new FoodLocationInfoModel();
+ var foodLocationInfo = new FoodLocationModel();
foodLocationInfo.Duration.Value = int.Parse(infos[0]);
if (infos.Length > 1)
{
diff --git a/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs b/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs
index b9af468..1296a74 100644
--- a/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs
+++ b/VPet.ModMaker/Models/ModModel/FoodAnimeTypeModel.cs
@@ -4,6 +4,7 @@ using LinePutScript.Localization.WPF;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
@@ -24,6 +25,11 @@ public class FoodAnimeTypeModel
///
public ObservableValue Name { get; } = new();
+ ///
+ /// 动作类型
+ ///
+ public GraphInfo.GraphType GraphType => GraphInfo.GraphType.Common;
+
///
/// 开心动画
///
@@ -46,28 +52,73 @@ public class FoodAnimeTypeModel
public FoodAnimeTypeModel()
{
+ HappyAnimes.CollectionChanged += Animes_CollectionChanged;
//Name.ValueChanged += (_, _) =>
//{
// Id.Value = $"{GraphType.Value}_{Name.Value}";
//};
}
+ 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()
{
Name.Value = Path.GetFileName(path);
var infoFile = Path.Combine(path, ModMakerInfo.InfoFile);
- if (File.Exists(infoFile) is false)
+ if (
+ Directory.EnumerateFiles(path, ModMakerInfo.InfoFile, SearchOption.AllDirectories).Any()
+ is false
+ )
throw new Exception("信息文件\n{0}\n不存在".Translate(infoFile));
- var lps = new LPS(infoFile);
- var foodAnime = lps.FindAllLineInfo(nameof(FoodAnimation));
- if (foodAnime.Any() is false)
- throw new Exception("信息文件\n{0}\n未包含食物动画信息".Translate(infoFile));
- var anime = lps.FindAllLineInfo(nameof(PNGAnimation));
- foreach (var foodAnimation in foodAnime)
- {
- ParseFoodAnimeInfo(foodAnimation);
- }
+ if (File.Exists(infoFile))
+ ParseInfoFile(path, infoFile);
}
public FoodAnimeTypeModel(FoodAnimeTypeModel model)
@@ -104,21 +155,115 @@ public class FoodAnimeTypeModel
}
}
- public void ParseFoodAnimeInfo(ILine line)
+ public void ParseInfoFile(string path, string infoPath)
{
- var mode = (GameSave.ModeType)Enum.Parse(typeof(GameSave.ModeType), line.Find("mode").Info);
- if (mode is GameSave.ModeType.Happy)
- AddModeAnime(HappyAnimes, line);
- else if (mode is GameSave.ModeType.Nomal)
- AddModeAnime(NomalAnimes, line);
- else if (mode is GameSave.ModeType.PoorCondition)
- AddModeAnime(PoorConditionAnimes, line);
- else if (mode is GameSave.ModeType.Ill)
- AddModeAnime(IllAnimes, line);
+ var lps = new LPS(infoPath);
+ var foodAnimeInfos = lps.FindAllLineInfo(nameof(FoodAnimation));
+ if (foodAnimeInfos.Any() is false)
+ throw new Exception("信息文件\n{0}\n未包含食物动画信息".Translate(infoPath));
+ var pngAnimeInfos = lps.FindAllLineInfo(nameof(PNGAnimation))
+ .Select(
+ i =>
+ new PNGAnimeInfo(
+ i.Info,
+ i.Find("infoPath").Info,
+ (GameSave.ModeType)
+ Enum.Parse(typeof(GameSave.ModeType), i.Find("mode").Info, true)
+ )
+ )
+ .ToList();
+ foreach (var foodAnimation in foodAnimeInfos)
+ {
+ ParseFoodAnimeInfo(path, foodAnimation, pngAnimeInfos);
+ }
}
- public void AddModeAnime(ObservableCollection foodAnimes, ILine line)
+ public void ParseFoodAnimeInfo(string path, ILine line, List pngAnimeInfos)
{
- foodAnimes.Add(new(line));
+ var mode = (GameSave.ModeType)
+ Enum.Parse(typeof(GameSave.ModeType), line.Find("mode").Info, true);
+ if (mode is GameSave.ModeType.Happy)
+ AddModeAnime(path, GameSave.ModeType.Happy, HappyAnimes, line, pngAnimeInfos);
+ else if (mode is GameSave.ModeType.Nomal)
+ AddModeAnime(path, GameSave.ModeType.Nomal, NomalAnimes, line, pngAnimeInfos);
+ else if (mode is GameSave.ModeType.PoorCondition)
+ AddModeAnime(
+ path,
+ GameSave.ModeType.PoorCondition,
+ PoorConditionAnimes,
+ line,
+ pngAnimeInfos
+ );
+ else if (mode is GameSave.ModeType.Ill)
+ AddModeAnime(path, GameSave.ModeType.Ill, IllAnimes, line, pngAnimeInfos);
+ }
+
+ public void AddModeAnime(
+ string path,
+ GameSave.ModeType mode,
+ ObservableCollection foodAnimes,
+ ILine line,
+ List pngAnimeInfos
+ )
+ {
+ var anime = new FoodAnimeModel(line);
+ var frontLay = line.Find("front_lay").Info;
+ var backLay = line.Find("back_lay").Info;
+ var frontLayAnimes = pngAnimeInfos.Where(i => i.Name == frontLay).ToList();
+ var backLayAnimes = pngAnimeInfos.Where(i => i.Name == backLay).ToList();
+ // 尝试获取相同模式的动画
+ if (frontLayAnimes.FirstOrDefault(i => i.Mode == mode) is PNGAnimeInfo frontAnimeInfo)
+ {
+ anime.FrontImages = GetImages(path, frontAnimeInfo);
+ }
+ else
+ {
+ // 若没有则获取通用动画
+ anime.FrontImages = GetImages(
+ path,
+ frontLayAnimes.First(i => i.Mode == GameSave.ModeType.Nomal)
+ );
+ }
+ if (backLayAnimes.FirstOrDefault(i => i.Mode == mode) is PNGAnimeInfo backAnimeInfo)
+ {
+ anime.BackImages = GetImages(path, backAnimeInfo);
+ }
+ else
+ {
+ anime.BackImages = GetImages(
+ path,
+ backLayAnimes.First(i => i.Mode == GameSave.ModeType.Nomal)
+ );
+ }
+ foodAnimes.Add(anime);
+
+ static ObservableCollection GetImages(string path, PNGAnimeInfo pngAnimeInfo)
+ {
+ return new(
+ Directory
+ .EnumerateFiles(Path.Combine(path, pngAnimeInfo.Path))
+ .Select(
+ i =>
+ new ImageModel(
+ Utils.LoadImageToMemoryStream(i),
+ int.Parse(Path.GetFileNameWithoutExtension(i).Split('_')[1])
+ )
+ )
+ );
+ }
+ }
+}
+
+public class PNGAnimeInfo
+{
+ public string Name { get; }
+ public string Path { get; }
+ public GameSave.ModeType Mode { get; }
+
+ public PNGAnimeInfo(string name, string path, GameSave.ModeType mode)
+ {
+ Name = name;
+ Path = path;
+ Mode = mode;
}
}
diff --git a/VPet.ModMaker/Models/ModModel/FoodLocationInfoModel.cs b/VPet.ModMaker/Models/ModModel/FoodLocationModel.cs
similarity index 87%
rename from VPet.ModMaker/Models/ModModel/FoodLocationInfoModel.cs
rename to VPet.ModMaker/Models/ModModel/FoodLocationModel.cs
index 077a95d..c307681 100644
--- a/VPet.ModMaker/Models/ModModel/FoodLocationInfoModel.cs
+++ b/VPet.ModMaker/Models/ModModel/FoodLocationModel.cs
@@ -10,7 +10,7 @@ namespace VPet.ModMaker.Models.ModModel;
///
/// 食物图像模型
///
-public class FoodLocationInfoModel
+public class FoodLocationModel
{
///
/// 持续时间
@@ -32,7 +32,7 @@ public class FoodLocationInfoModel
///
public ObservableValue Opacity { get; } = new(100);
- public FoodLocationInfoModel()
+ public FoodLocationModel()
{
Rect.Width.ValueChanged += (o, n) =>
{
@@ -40,9 +40,9 @@ public class FoodLocationInfoModel
};
}
- public FoodLocationInfoModel Copy()
+ public FoodLocationModel Copy()
{
- var model = new FoodLocationInfoModel();
+ var model = new FoodLocationModel();
model.Duration.Value = Duration.Value;
model.Rect.SetValue(Rect.X.Value, Rect.Y.Value, Rect.Width.Value, Rect.Height.Value);
model.Rotate.Value = Rotate.Value;
diff --git a/VPet.ModMaker/Models/ModModel/PetModel.cs b/VPet.ModMaker/Models/ModModel/PetModel.cs
index 8c43a09..e3bec2d 100644
--- a/VPet.ModMaker/Models/ModModel/PetModel.cs
+++ b/VPet.ModMaker/Models/ModModel/PetModel.cs
@@ -70,6 +70,11 @@ public class PetModel : I18nModel
///
public ObservableCollection Animes { get; } = new();
+ ///
+ ///食物动画
+ ///
+ public ObservableCollection FoodAnimes { get; } = new();
+
public bool IsSimplePetModel { get; } = false;
public PetModel()
diff --git a/VPet.ModMaker/SimpleObservable/ObservableValue.cs b/VPet.ModMaker/SimpleObservable/ObservableValue.cs
index c1a152a..6fb88f7 100644
--- a/VPet.ModMaker/SimpleObservable/ObservableValue.cs
+++ b/VPet.ModMaker/SimpleObservable/ObservableValue.cs
@@ -34,6 +34,11 @@ public class ObservableValue : INotifyPropertyChanging, INotifyPropertyChange
}
}
+ ///
+ /// 含有值
+ ///
+ public bool HasValue => Value != null;
+
#region Ctor
///
public ObservableValue() { }
diff --git a/VPet.ModMaker/VPet.ModMaker.csproj b/VPet.ModMaker/VPet.ModMaker.csproj
index 31297fc..6ead5ed 100644
--- a/VPet.ModMaker/VPet.ModMaker.csproj
+++ b/VPet.ModMaker/VPet.ModMaker.csproj
@@ -106,7 +106,7 @@
-
+
diff --git a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs
index a8ffa1f..0905bd3 100644
--- a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs
+++ b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/AnimeEditWindowVM.cs
@@ -286,7 +286,6 @@ public class AnimeEditWindowVM
return;
}
} while (Loop.Value);
- Reset();
}
///
diff --git a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs
index 4ae76ae..9371f88 100644
--- a/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs
+++ b/VPet.ModMaker/ViewModels/ModEdit/AnimeEdit/FoodAnimeEditWindowVM.cs
@@ -7,8 +7,10 @@ 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;
using VPet.ModMaker.Models;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
@@ -22,25 +24,45 @@ public class FoodAnimeEditWindowVM
///
public PetModel CurrentPet { get; set; }
+ ///
+ /// 食物图片
+ ///
+ public ObservableValue FoodImage { get; } = new();
+
+ ///
+ /// 比例
+ ///
+ public ObservableValue LengthRatio { get; } = new(250.0 / 500.0);
+
///
/// 旧动画
///
- public AnimeTypeModel OldAnime { get; set; }
+ public FoodAnimeTypeModel OldAnime { get; set; }
///
/// 动画
///
- public ObservableValue Anime { get; } = new(new());
+ public ObservableValue Anime { get; } = new(new());
///
- /// 当前图像模型
+ /// 当前顶层图像模型
///
- public ObservableValue CurrentImageModel { get; } = new();
+ public ObservableValue CurrentFrontImageModel { get; } = new();
+
+ ///
+ /// 当前底层图像模型
+ ///
+ public ObservableValue CurrentBackImageModel { get; } = new();
+
+ ///
+ /// 当前食物定位模型
+ ///
+ public ObservableValue CurrentFoodLocationModel { get; } = new();
///
/// 当前动画模型
///
- public ObservableValue CurrentAnimeModel { get; } = new();
+ public ObservableValue CurrentAnimeModel { get; } = new();
///
/// 当前模式
@@ -100,13 +122,25 @@ public class FoodAnimeEditWindowVM
private bool _playing = false;
///
- /// 动画任务
+ /// 顶层动画任务
///
- private Task _playerTask;
+ private Task _frontPlayerTask;
+
+ ///
+ /// 底层动画任务
+ ///
+ private Task _backPlayerTask;
+
+ ///
+ /// 食物动画任务
+ ///
+ private Task _foodPlayerTask;
public FoodAnimeEditWindowVM()
{
- _playerTask = new(Play);
+ _frontPlayerTask = new(FrontPlay);
+ _backPlayerTask = new(BackPlay);
+ _foodPlayerTask = new(FoodPlay);
CurrentAnimeModel.ValueChanged += CurrentAnimeModel_ValueChanged;
@@ -121,18 +155,18 @@ public class FoodAnimeEditWindowVM
}
#region LoadAnime
- private void Anime_ValueChanged(AnimeTypeModel oldValue, AnimeTypeModel newValue)
+ private void Anime_ValueChanged(FoodAnimeTypeModel oldValue, FoodAnimeTypeModel newValue)
{
CheckGraphType(newValue);
}
- private void CheckGraphType(AnimeTypeModel model)
+ private void CheckGraphType(FoodAnimeTypeModel model)
{
- if (AnimeTypeModel.HasMultiTypeAnimes.Contains(model.GraphType.Value))
- HasMultiType.Value = true;
+ //if (FoodAnimeTypeModel.HasMultiTypeAnimes.Contains(model.GraphType.Value))
+ // HasMultiType.Value = true;
- if (AnimeTypeModel.HasNameAnimes.Contains(model.GraphType.Value))
- HasAnimeName.Value = true;
+ //if (FoodAnimeTypeModel.HasNameAnimes.Contains(model.GraphType.Value))
+ // HasAnimeName.Value = true;
}
#endregion
///
@@ -146,14 +180,14 @@ public class FoodAnimeEditWindowVM
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();
}
}
@@ -223,18 +257,26 @@ public class FoodAnimeEditWindowVM
/// 动画模型
private void RemoveImageCommand_ExecuteEvent(AnimeModel value)
{
- CurrentImageModel.Value.Close();
- value.Images.Remove(CurrentImageModel.Value);
+ CurrentFrontImageModel.Value.Close();
+ value.Images.Remove(CurrentFrontImageModel.Value);
}
- #region Player
- private void CurrentAnimeModel_ValueChanged(AnimeModel oldValue, AnimeModel newValue)
+ #region FrontPlayer
+ private void CurrentAnimeModel_ValueChanged(FoodAnimeModel oldValue, FoodAnimeModel newValue)
{
StopCommand_ExecuteEvent();
if (oldValue is not null)
- oldValue.Images.CollectionChanged -= Images_CollectionChanged;
+ {
+ oldValue.FrontImages.CollectionChanged -= Images_CollectionChanged;
+ oldValue.BackImages.CollectionChanged -= Images_CollectionChanged;
+ oldValue.FoodLocations.CollectionChanged -= Images_CollectionChanged;
+ }
if (newValue is not null)
- newValue.Images.CollectionChanged += Images_CollectionChanged;
+ {
+ newValue.FrontImages.CollectionChanged += Images_CollectionChanged;
+ newValue.BackImages.CollectionChanged += Images_CollectionChanged;
+ newValue.FoodLocations.CollectionChanged += Images_CollectionChanged;
+ }
}
private void Images_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
@@ -268,25 +310,60 @@ public class FoodAnimeEditWindowVM
return;
}
_playing = true;
- _playerTask.Start();
+ _frontPlayerTask.Start();
+ _backPlayerTask.Start();
+ _foodPlayerTask.Start();
}
///
- /// 播放
+ /// 顶层播放
///
- private void Play()
+ private void FrontPlay()
{
do
{
- foreach (var model in CurrentAnimeModel.Value.Images)
+ foreach (var model in CurrentAnimeModel.Value.FrontImages)
{
- CurrentImageModel.Value = model;
+ CurrentFrontImageModel.Value = model;
+ Task.Delay(model.Duration.Value).Wait();
+ if (_playing is false)
+ return;
+ }
+ } while (Loop.Value);
+ }
+
+ ///
+ /// 底层
+ ///
+ private void BackPlay()
+ {
+ do
+ {
+ foreach (var model in CurrentAnimeModel.Value.BackImages)
+ {
+ CurrentBackImageModel.Value = model;
+ Task.Delay(model.Duration.Value).Wait();
+ if (_playing is false)
+ return;
+ }
+ } while (Loop.Value);
+ }
+
+ ///
+ /// 食物
+ ///
+ private void FoodPlay()
+ {
+ do
+ {
+ foreach (var model in CurrentAnimeModel.Value.FoodLocations)
+ {
+ CurrentFoodLocationModel.Value = model;
Task.Delay(model.Duration.Value).Wait();
if (_playing is false)
return;
}
} while (Loop.Value);
- Reset();
}
///
@@ -295,7 +372,9 @@ public class FoodAnimeEditWindowVM
private void Reset()
{
_playing = false;
- _playerTask = new(Play);
+ _frontPlayerTask = new(FrontPlay);
+ _backPlayerTask = new(BackPlay);
+ _foodPlayerTask = new(FoodPlay);
}
#endregion
}
diff --git a/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml b/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml
index 1f7cd72..8d14c0a 100644
--- a/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml
+++ b/VPet.ModMaker/Views/ModEdit/AnimeEdit/AnimeEditWindow.xaml
@@ -46,18 +46,21 @@
+
-
-
-
+
+
+ Header="{ll:Str 添加图片}" />
+ Header="{ll:Str 清空图片}" />
+ Header="{ll:Str 删除此项}" />
@@ -50,14 +50,17 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -134,38 +216,85 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -194,7 +323,7 @@
-
+