实装 食物动画保存

This commit is contained in:
Hakoyu 2023-10-23 19:39:27 +08:00
parent 8dc2109f65
commit 23ddacdeb1
9 changed files with 256 additions and 78 deletions

View File

@ -614,12 +614,8 @@ public class AnimeTypeModel
static void SaveAnimes(string animePath, ObservableCollection<AnimeModel> 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

View File

@ -24,13 +24,11 @@ public class FoodAnimeModel
/// 后图像列表
/// </summary>
public ObservableCollection<ImageModel> BackImages { get; set; } = new();
public ObservableValue<FoodImagesPath?> BackImagesPath { get; } = new();
/// <summary>
/// 前图像列表
/// </summary>
public ObservableCollection<ImageModel> FrontImages { get; set; } = new();
public ObservableValue<FoodImagesPath?> FrontImagesPath { get; } = new();
/// <summary>
/// 食物定位列表

View File

@ -26,6 +26,16 @@ public class FoodAnimeTypeModel
public static HashSet<string> FoodAnimeNames =
new(StringComparer.InvariantCultureIgnoreCase) { "Eat", "Drink", "Gift", };
/// <summary>
/// 顶层名称
/// </summary>
public const string FrontLayName = "front_lay";
/// <summary>
/// 底层名称
/// </summary>
public const string BackLayName = "back_lay";
/// <summary>
/// Id
/// </summary>
@ -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<FoodAnimeModel>())
{
SetImagesPathValueChanged(model);
}
}
if (e.OldItems is not null)
{
foreach (var model in e.OldItems.Cast<FoodAnimeModel>())
{
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
}
}
/// <summary>
/// 解析信息文件
/// </summary>
/// <param name="path">路径</param>
/// <param name="infoPath">信息文件路径</param>
/// <exception cref="Exception"></exception>
public void ParseInfoFile(string path, string infoPath)
{
var lps = new LPS(File.ReadAllText(infoPath));
@ -215,6 +182,12 @@ public class FoodAnimeTypeModel
}
}
/// <summary>
/// 解析食物动画信息
/// </summary>
/// <param name="path">路径</param>
/// <param name="line">食物动画信息</param>
/// <param name="pngAnimeInfos">PNG动画信息</param>
public void ParseFoodAnimeInfo(string path, ILine line, List<PNGAnimeInfo> pngAnimeInfos)
{
var mode = (GameSave.ModeType)
@ -235,6 +208,14 @@ public class FoodAnimeTypeModel
AddModeAnime(path, GameSave.ModeType.Ill, IllAnimes, line, pngAnimeInfos);
}
/// <summary>
/// 添加模式动画
/// </summary>
/// <param name="path">路径</param>
/// <param name="mode">模式</param>
/// <param name="foodAnimes">食物动画</param>
/// <param name="line">食物动画信息</param>
/// <param name="pngAnimeInfos">PNG动画信息</param>
public void AddModeAnime(
string path,
GameSave.ModeType mode,
@ -290,7 +271,157 @@ public class FoodAnimeTypeModel
}
}
public void Save(string path) { }
/// <summary>
/// 保存
/// </summary>
/// <param name="path">路径</param>
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);
}
/// <summary>
/// 保存动画信息
/// </summary>
/// <param name="animePath">路径</param>
/// <param name="animes">动画</param>
/// <param name="mode">模式</param>
private void SaveAnimeInfo(
string animePath,
ObservableCollection<FoodAnimeModel> 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);
}
}
/// <summary>
/// 保存图片
/// </summary>
/// <param name="anime">动画</param>
/// <param name="indexPath">索引路径</param>
/// <param name="frontLayName">顶层名称</param>
/// <param name="backLayName">底层名称</param>
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());
}
/// <summary>
/// 保存图片
/// </summary>
/// <param name="imagesPath"></param>
/// <param name="model"></param>
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

View File

@ -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}";
}
}

View File

@ -204,8 +204,10 @@ public class PetModel : I18nModel<I18nPetInfoModel>
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)

View File

@ -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;
}
/// <summary>
/// 枚举出带有索引值的枚举值
/// </summary>
/// <typeparam name="T">值类型</typeparam>
/// <param name="collection">集合</param>
/// <returns>带有索引的枚举值</returns>
public static IEnumerable<ItemInfo<T>> Enumerate<T>(this IEnumerable<T> collection)
{
var index = 0;
foreach (var item in collection)
yield return new(index++, item);
}
}
/// <summary>
/// 项信息
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("[{Index}, {Value}]")]
public readonly struct ItemInfo<T>
{
/// <summary>
/// 索引值
/// </summary>
public int Index { get; }
/// <summary>
/// 值
/// </summary>
public T Value { get; }
/// <inheritdoc/>
/// <param name="value">值</param>
/// <param name="index">索引值</param>
public ItemInfo(int index, T value)
{
Index = index;
Value = value;
}
/// <inheritdoc/>
public override string ToString()
{
return $"[{Index}, {Value}]";
}
}

View File

@ -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)

View File

@ -35,11 +35,12 @@ public class ObservableCommand<T> : 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)

View File

@ -59,15 +59,14 @@ public class ObservableValue<T> : INotifyPropertyChanging, INotifyPropertyChange
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <returns>取消改变</returns>
private bool NotifyPropertyChanging(T oldValue, T newValue)
{
PropertyChanging?.Invoke(this, new(nameof(Value)));
var cancel = false;
// 若全部事件取消改变 则取消改变
return ValueChanging
?.GetInvocationList()
.Cast<ValueChangingEventHandler>()
.All(e => e.Invoke(oldValue, newValue) is true)
is true;
ValueChanging?.Invoke(oldValue, newValue, ref cancel);
return cancel;
}
/// <summary>
@ -168,8 +167,8 @@ public class ObservableValue<T> : INotifyPropertyChanging, INotifyPropertyChange
/// </summary>
/// <param name="oldValue">旧值</param>
/// <param name="newValue">新值</param>
/// <returns>取消改变</returns>
public delegate bool ValueChangingEventHandler(T oldValue, T newValue);
/// <param name="cancel">取消</param>
public delegate void ValueChangingEventHandler(T oldValue, T newValue, ref bool cancel);
/// <summary>
/// 值改变后事件