mirror of
https://github.com/LorisYounger/VPet.ModMaker.git
synced 2024-08-30 18:22:21 +00:00
实装 食物动画保存
This commit is contained in:
parent
8dc2109f65
commit
23ddacdeb1
@ -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
|
||||
|
@ -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>
|
||||
/// 食物定位列表
|
||||
|
@ -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
|
||||
|
@ -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}";
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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}]";
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
/// 值改变后事件
|
||||
|
Loading…
Reference in New Issue
Block a user