Files
VPet.ModMaker/VPet.ModMaker/Models/ModModel/PetModel.cs
Hakoyu bf1f989a57 #修复
- 在文化添加界面输入错误文化会出错的问题
2024-04-26 22:52:26 +08:00

856 lines
24 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using HKW.HKWUtils;
using HKW.HKWUtils.Observable;
using LinePutScript;
using LinePutScript.Converter;
using LinePutScript.Localization.WPF;
using Mapster;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
namespace VPet.ModMaker.Models;
/// <summary>
/// 宠物模型
/// </summary>
public class PetModel : ObservableObjectX
{
public PetModel()
{
PropertyChanged += PetModel_PropertyChanged;
Animes.PropertyChanged += Animes_PropertyChanged;
FoodAnimes.PropertyChanged += FoodAnimes_PropertyChanged;
}
private void PetModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ID))
{
PetNameID = $"{ID}_{nameof(PetNameID)}";
DescriptionID = $"{ID}_{nameof(DescriptionID)}";
}
}
public PetModel(PetModel model)
: this()
{
ID = model.ID;
PetNameID = model.PetNameID;
DescriptionID = model.DescriptionID;
Tags = model.Tags;
TouchHeadRectangleLocation = model.TouchHeadRectangleLocation.Clone();
TouchBodyRectangleLocation = model.TouchBodyRectangleLocation.Clone();
TouchRaisedRectangleLocation = model.TouchRaisedRectangleLocation.Clone();
RaisePoint = model.RaisePoint.Clone();
foreach (var work in model.Works)
Works.Add(work);
}
public PetModel(PetLoader loader, bool fromMain = false)
: this()
{
ID = loader.Name;
PetNameID = loader.PetName;
DescriptionID = loader.Intor;
Tags = loader.Config.Data["tag"].Info;
TouchHeadRectangleLocation = new(
loader.Config.TouchHeadLocate.X,
loader.Config.TouchHeadLocate.Y,
loader.Config.TouchHeadSize.Width,
loader.Config.TouchHeadSize.Height
);
TouchBodyRectangleLocation = new(
loader.Config.TouchBodyLocate.X,
loader.Config.TouchBodyLocate.Y,
loader.Config.TouchBodySize.Width,
loader.Config.TouchBodySize.Height
);
TouchRaisedRectangleLocation.Happy = new(
loader.Config.TouchRaisedLocate[0].X,
loader.Config.TouchRaisedLocate[0].Y,
loader.Config.TouchRaisedSize[0].Width,
loader.Config.TouchRaisedSize[0].Height
);
TouchRaisedRectangleLocation.Nomal = new(
loader.Config.TouchRaisedLocate[1].X,
loader.Config.TouchRaisedLocate[1].Y,
loader.Config.TouchRaisedSize[1].Width,
loader.Config.TouchRaisedSize[1].Height
);
TouchRaisedRectangleLocation.PoorCondition = new(
loader.Config.TouchRaisedLocate[2].X,
loader.Config.TouchRaisedLocate[2].Y,
loader.Config.TouchRaisedSize[2].Width,
loader.Config.TouchRaisedSize[2].Height
);
TouchRaisedRectangleLocation.Ill = new(
loader.Config.TouchRaisedLocate[3].X,
loader.Config.TouchRaisedLocate[3].Y,
loader.Config.TouchRaisedSize[3].Width,
loader.Config.TouchRaisedSize[3].Height
);
RaisePoint.Happy = new(loader.Config.RaisePoint[0].X, loader.Config.RaisePoint[0].Y);
RaisePoint.Nomal = new(loader.Config.RaisePoint[1].X, loader.Config.RaisePoint[1].Y);
RaisePoint.PoorCondition = new(
loader.Config.RaisePoint[2].X,
loader.Config.RaisePoint[2].Y
);
RaisePoint.Ill = new(loader.Config.RaisePoint[3].X, loader.Config.RaisePoint[3].Y);
// 如果这个宠物数据来自本体, 则不载入 Work 和 Move
if (FromMain = fromMain)
return;
foreach (var work in loader.Config.Works)
Works.Add(new(work) { I18nResource = I18nResource! });
foreach (var move in loader.Config.Moves)
Moves.Add(new(move));
}
public static PetModel Default { get; } = new() { I18nResource = new() };
#region FromMain
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private bool _fromMain;
/// <summary>
/// 来自本体
/// </summary>
public bool FromMain
{
get => _fromMain;
set => SetProperty(ref _fromMain, value);
}
#endregion
#region ID
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _id = string.Empty;
/// <summary>
/// Id
/// </summary>
public string ID
{
get => _id;
set
{
SetProperty(ref _id, value);
RefreshID();
}
}
#endregion
#region PetNameID
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _petNameID = string.Empty;
/// <summary>
/// 名称ID
/// </summary>
public string PetNameID
{
get => _petNameID;
set => SetProperty(ref _petNameID, value);
}
#endregion
#region DescriptionID
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _descriptionID = string.Empty;
/// <summary>
/// 描述ID
/// </summary>
public string DescriptionID
{
get => _descriptionID;
set => SetProperty(ref _descriptionID, value);
}
#endregion
#region I18nData
[AdaptIgnore]
private I18nResource<string, string> _i18nResource = null!;
[AdaptIgnore]
public required I18nResource<string, string> I18nResource
{
get => _i18nResource;
set
{
if (_i18nResource is not null)
I18nResource.I18nObjectInfos.Remove(this);
_i18nResource = value;
InitializeI18nResource();
}
}
public void InitializeI18nResource()
{
I18nResource.I18nObjectInfos.Add(
this,
new I18nObjectInfo<string, string>(this, OnPropertyChanged).AddPropertyInfo(
[
(nameof(ID), ID, nameof(Name)),
(nameof(PetNameID), PetNameID, nameof(PetName)),
(nameof(DescriptionID), DescriptionID, nameof(Description))
],
true
)
);
foreach (var work in Works)
work.I18nResource = I18nResource;
if (FromMain)
{
foreach (var cultureName in LocalizeCore.AvailableCultures)
{
if (CultureUtils.TryGetCultureInfo(cultureName, out var culture) is false)
continue;
if (LocalizeCore.Localizations.TryGetValue(cultureName, out var data) is false)
continue;
foreach (var line in data)
{
if (line?.Name == ID)
{
I18nResource.AddCultureData(culture, ID, line.Info);
}
else if (line?.Name == PetNameID)
{
I18nResource.AddCultureData(culture, PetNameID, line.Info);
}
else if (line?.Name == DescriptionID)
{
I18nResource.AddCultureData(culture, DescriptionID, line.Info);
}
}
I18nResource.AddCultureData(culture, ID, ID);
I18nResource.AddCultureData(culture, PetNameID, PetNameID);
I18nResource.AddCultureData(culture, DescriptionID, DescriptionID);
}
}
}
[AdaptIgnore]
public string Name
{
get => I18nResource.GetCurrentCultureDataOrDefault(ID);
set => I18nResource.SetCurrentCultureData(ID, value);
}
[AdaptIgnore]
public string PetName
{
get => I18nResource.GetCurrentCultureDataOrDefault(PetNameID);
set => I18nResource.SetCurrentCultureData(PetNameID, value);
}
[AdaptIgnore]
public string Description
{
get => I18nResource.GetCurrentCultureDataOrDefault(DescriptionID);
set => I18nResource.SetCurrentCultureData(DescriptionID, value);
}
#endregion
#region Tags
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _tags = string.Empty;
/// <summary>
/// 标签
/// </summary>
public string Tags
{
get => _tags;
set => SetProperty(ref _tags, value);
}
#endregion
#region TouchHeadRect
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableRectangle<double> _touchHeadRectangleLocation = new(159, 16, 189, 178);
/// <summary>
/// 头部点击区域
/// </summary>
public ObservableRectangle<double> TouchHeadRectangleLocation
{
get => _touchHeadRectangleLocation;
set => SetProperty(ref _touchHeadRectangleLocation, value);
}
#endregion
#region TouchBodyRect
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableRectangle<double> _touchBodyRectangleLocation = new(166, 206, 163, 136);
/// <summary>
/// 身体区域
/// </summary>
public ObservableRectangle<double> TouchBodyRectangleLocation
{
get => _touchBodyRectangleLocation;
set => SetProperty(ref _touchBodyRectangleLocation, value);
}
#endregion
/// <summary>
/// 提起区域
/// </summary>
#region TouchRaisedRect
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableMultiStateRectangleLocation _touchRaisedRectangleLocation =
new(
new(0, 50, 500, 200),
new(0, 50, 500, 200),
new(0, 50, 500, 200),
new(0, 200, 500, 300)
);
public ObservableMultiStateRectangleLocation TouchRaisedRectangleLocation
{
get => _touchRaisedRectangleLocation;
set => SetProperty(ref _touchRaisedRectangleLocation, value);
}
#endregion
#region RaisePoint
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableMultiStatePoint _raisePoint =
new(new(290, 128), new(290, 128), new(290, 128), new(225, 115));
/// <summary>
/// 提起定位
/// </summary>
public ObservableMultiStatePoint RaisePoint
{
get => _raisePoint;
set => SetProperty(ref _raisePoint, value);
}
#endregion
/// <summary>
/// 工作
/// </summary>
public ObservableList<WorkModel> Works { get; } = new();
/// <summary>
/// 移动
/// </summary>
public ObservableList<MoveModel> Moves { get; } = new();
/// <summary>
/// 动画
/// </summary>
public ObservableList<AnimeTypeModel> Animes { get; } = new();
/// <summary>
///食物动画
/// </summary>
public ObservableList<FoodAnimeTypeModel> FoodAnimes { get; } = new();
#region AnimeCount
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private int _animeCount;
public int AnimeCount
{
get => _animeCount;
set => SetProperty(ref _animeCount, value);
}
#endregion
private void FoodAnimes_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
AnimeCount = Animes.Count + FoodAnimes.Count;
}
private void Animes_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
AnimeCount = Animes.Count + FoodAnimes.Count;
}
public void RefreshID()
{
PetNameID = $"{ID}_{nameof(PetNameID)}";
DescriptionID = $"{ID}_{nameof(DescriptionID)}";
}
public void Close()
{
foreach (var anime in Animes)
anime.Close();
foreach (var anime in FoodAnimes)
anime.Close();
}
public void CloseI18nResource()
{
I18nResource.I18nObjectInfos.Remove(this);
}
#region Save
/// <summary>
/// 能被保存
/// </summary>
/// <returns></returns>
public bool CanSave()
{
if (
FromMain
&& Works.Count == 0
&& Moves.Count == 0
&& Animes.Count == 0
&& FoodAnimes.Count == 0
)
return false;
return true;
}
/// <summary>
/// 保存宠物
/// </summary>
/// <param name="path">路径</param>
public void Save(string path)
{
var petFile = Path.Combine(path, $"{ID}.lps");
if (File.Exists(petFile) is false)
File.Create(petFile).Close();
var lps = new LPS();
// 如果本体中存在相同的宠物, 则只保存差异信息
if (ModMakerInfo.MainPets.TryGetValue(ID, out var mainPet))
SaveDifferentPetInfo(lps, mainPet);
else
SavePetInfo(lps);
SaveWorksInfo(lps);
SaveMoveInfo(lps);
File.WriteAllText(petFile, lps.ToString());
// 保存图片
SaveAnime(path);
}
private void SaveAnime(string path)
{
var petAnimePath = Path.Combine(path, ID);
foreach (var anime in Animes)
anime.Save(petAnimePath);
foreach (var anime in FoodAnimes)
anime.Save(petAnimePath);
}
/// <summary>
/// 保存移动信息
/// </summary>
/// <param name="lps"></param>
void SaveMoveInfo(LPS lps)
{
foreach (var move in Moves)
{
lps.Add(LPSConvert.SerializeObjectToLine<Line>(move.ToMove(), "move"));
}
}
/// <summary>
/// 保存工作信息
/// </summary>
/// <param name="lps"></param>
void SaveWorksInfo(LPS lps)
{
foreach (var work in Works)
{
lps.Add(LPSConvert.SerializeObjectToLine<Line>(work.ToWork(), "work"));
}
}
#region SavePetInfo
/// <summary>
/// 保存宠物信息
/// </summary>
/// <param name="lps"></param>
private void SavePetInfo(LPS lps)
{
SavePetBasicInfo(lps);
SavePetTouchHeadInfo(lps);
SavePetTouchBodyInfo(lps);
SavePetTouchRaisedInfo(lps);
SavePetRaisePointInfo(lps);
}
/// <summary>
/// 保存差异宠物信息
/// <para>
/// 用于本体存在同名宠物的情况下
/// </para>
/// </summary>
/// <param name="lps"></param>
/// <param name="mainPet">本体宠物</param>
private void SaveDifferentPetInfo(LPS lps, PetModel mainPet)
{
SavePetBasicInfo(lps);
// 如果值不为默认并且不与本体值相同, 则保存
if (
TouchHeadRectangleLocation != Default.TouchHeadRectangleLocation
&& TouchHeadRectangleLocation != mainPet.TouchHeadRectangleLocation
)
SavePetTouchHeadInfo(lps);
if (
TouchBodyRectangleLocation != Default.TouchBodyRectangleLocation
&& TouchBodyRectangleLocation != mainPet.TouchBodyRectangleLocation
)
SavePetTouchBodyInfo(lps);
if (
TouchRaisedRectangleLocation != Default.TouchRaisedRectangleLocation
&& TouchRaisedRectangleLocation != mainPet.TouchRaisedRectangleLocation
)
SavePetTouchRaisedInfo(lps);
if (RaisePoint != Default.RaisePoint && RaisePoint != mainPet.RaisePoint)
SavePetRaisePointInfo(lps);
}
private void SavePetBasicInfo(LPS lps)
{
lps.Add(
new Line("pet", ID)
{
new Sub("intor", DescriptionID),
new Sub("path", ID),
new Sub("petname", PetNameID)
}
);
if (string.IsNullOrWhiteSpace(Tags) is false)
lps.Add(new Line("tag", Tags));
}
private void SavePetTouchHeadInfo(LPS lps)
{
lps.Add(
new Line("touchhead")
{
new Sub("px", TouchHeadRectangleLocation.X),
new Sub("py", TouchHeadRectangleLocation.Y),
new Sub("sw", TouchHeadRectangleLocation.Width),
new Sub("sh", TouchHeadRectangleLocation.Height),
}
);
}
private void SavePetTouchBodyInfo(LPS lps)
{
lps.Add(
new Line("touchbody")
{
new Sub("px", TouchBodyRectangleLocation.X),
new Sub("py", TouchBodyRectangleLocation.Y),
new Sub("sw", TouchBodyRectangleLocation.Width),
new Sub("sh", TouchBodyRectangleLocation.Height),
}
);
}
private void SavePetTouchRaisedInfo(LPS lps)
{
lps.Add(
new Line("touchraised")
{
new Sub("happy_px", TouchRaisedRectangleLocation.Happy.X),
new Sub("happy_py", TouchRaisedRectangleLocation.Happy.Y),
new Sub("happy_sw", TouchRaisedRectangleLocation.Happy.Width),
new Sub("happy_sh", TouchRaisedRectangleLocation.Happy.Height),
//
new Sub("nomal_px", TouchRaisedRectangleLocation.Nomal.X),
new Sub("nomal_py", TouchRaisedRectangleLocation.Nomal.Y),
new Sub("nomal_sw", TouchRaisedRectangleLocation.Nomal.Width),
new Sub("nomal_sh", TouchRaisedRectangleLocation.Nomal.Height),
//
new Sub("poorcondition_px", TouchRaisedRectangleLocation.PoorCondition.X),
new Sub("poorcondition_py", TouchRaisedRectangleLocation.PoorCondition.Y),
new Sub("poorcondition_sw", TouchRaisedRectangleLocation.PoorCondition.Width),
new Sub("poorcondition_sh", TouchRaisedRectangleLocation.PoorCondition.Height),
//
new Sub("ill_px", TouchRaisedRectangleLocation.Ill.X),
new Sub("ill_py", TouchRaisedRectangleLocation.Ill.Y),
new Sub("ill_sw", TouchRaisedRectangleLocation.Ill.Width),
new Sub("ill_sh", TouchRaisedRectangleLocation.Ill.Height),
}
);
}
private void SavePetRaisePointInfo(LPS lps)
{
lps.Add(
new Line("raisepoint")
{
new Sub("happy_x", RaisePoint.Happy.X),
new Sub("happy_y", RaisePoint.Happy.Y),
//
new Sub("nomal_x", RaisePoint.Nomal.X),
new Sub("nomal_y", RaisePoint.Nomal.Y),
//
new Sub("poorcondition_x", RaisePoint.PoorCondition.X),
new Sub("poorcondition_y", RaisePoint.PoorCondition.Y),
//
new Sub("ill_x", RaisePoint.Ill.X),
new Sub("ill_y", RaisePoint.Ill.Y),
}
);
}
#endregion
#endregion
}
public class ObservableMultiStateRectangleLocation
: ObservableObjectX,
IEquatable<ObservableMultiStateRectangleLocation>,
ICloneable<ObservableMultiStateRectangleLocation>
{
#region Happy
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableRectangle<double> _happy = null!;
public ObservableRectangle<double> Happy
{
get => _happy;
set => SetProperty(ref _happy, value);
}
#endregion
#region Nomal
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableRectangle<double> _nomal = null!;
public ObservableRectangle<double> Nomal
{
get => _nomal;
set => SetProperty(ref _nomal, value);
}
#endregion
#region PoorCondition
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableRectangle<double> _poorCondition = null!;
public ObservableRectangle<double> PoorCondition
{
get => _poorCondition;
set => SetProperty(ref _poorCondition, value);
}
#endregion
#region Ill
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservableRectangle<double> _ill = null!;
public ObservableRectangle<double> Ill
{
get => _ill;
set => SetProperty(ref _ill, value);
}
#endregion
public ObservableMultiStateRectangleLocation()
{
Happy = new();
Nomal = new();
PoorCondition = new();
Ill = new();
}
public ObservableMultiStateRectangleLocation(
ObservableRectangle<double> happy,
ObservableRectangle<double> nomal,
ObservableRectangle<double> poorCondition,
ObservableRectangle<double> ill
)
{
Happy = happy;
Nomal = nomal;
PoorCondition = poorCondition;
Ill = ill;
}
public ObservableMultiStateRectangleLocation Clone()
{
return new()
{
Happy = Happy.Clone(),
Nomal = Nomal.Clone(),
PoorCondition = PoorCondition.Clone(),
Ill = Ill.Clone(),
};
}
object ICloneable.Clone() => Clone();
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(Happy, Nomal, PoorCondition, Ill);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as ObservableMultiStateRectangleLocation);
}
/// <inheritdoc/>
public bool Equals(ObservableMultiStateRectangleLocation? other)
{
return Happy.Equals(other?.Happy)
&& Nomal.Equals(other?.Nomal)
&& PoorCondition.Equals(other?.PoorCondition)
&& Ill.Equals(other?.Ill);
}
/// <inheritdoc/>
public static bool operator ==(
ObservableMultiStateRectangleLocation a,
ObservableMultiStateRectangleLocation b
)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(
ObservableMultiStateRectangleLocation a,
ObservableMultiStateRectangleLocation b
)
{
return Equals(a, b) is not true;
}
#endregion
}
public class ObservableMultiStatePoint
: ObservableObjectX,
IEquatable<ObservableMultiStatePoint>,
ICloneable<ObservableMultiStatePoint>
{
#region Happy
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservablePoint<double> _happy = null!;
public ObservablePoint<double> Happy
{
get => _happy;
set => SetProperty(ref _happy, value);
}
#endregion
#region Nomal
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservablePoint<double> _nomal = null!;
public ObservablePoint<double> Nomal
{
get => _nomal;
set => SetProperty(ref _nomal, value);
}
#endregion
#region PoorCondition
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservablePoint<double> _poorCondition = null!;
public ObservablePoint<double> PoorCondition
{
get => _poorCondition;
set => SetProperty(ref _poorCondition, value);
}
#endregion
#region Ill
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private ObservablePoint<double> _ill = null!;
public ObservablePoint<double> Ill
{
get => _ill;
set => SetProperty(ref _ill, value);
}
#endregion
public ObservableMultiStatePoint()
{
Happy = new();
Nomal = new();
PoorCondition = new();
Ill = new();
}
public ObservableMultiStatePoint(
ObservablePoint<double> happy,
ObservablePoint<double> nomal,
ObservablePoint<double> poorCondition,
ObservablePoint<double> ill
)
{
Happy = happy;
Nomal = nomal;
PoorCondition = poorCondition;
Ill = ill;
}
public ObservableMultiStatePoint Clone()
{
return new()
{
Happy = Happy.Clone(),
Nomal = Nomal.Clone(),
PoorCondition = PoorCondition.Clone(),
Ill = Ill.Clone(),
};
}
object ICloneable.Clone() => Clone();
#region Other
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(Happy, Nomal, PoorCondition, Ill);
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as ObservableMultiStatePoint);
}
/// <inheritdoc/>
public bool Equals(ObservableMultiStatePoint? other)
{
return Happy.Equals(other?.Happy)
&& Nomal.Equals(other?.Nomal)
&& PoorCondition.Equals(other?.PoorCondition)
&& Ill.Equals(other?.Ill);
}
/// <inheritdoc/>
public static bool operator ==(ObservableMultiStatePoint a, ObservableMultiStatePoint b)
{
return Equals(a, b);
}
/// <inheritdoc/>
public static bool operator !=(ObservableMultiStatePoint a, ObservableMultiStatePoint b)
{
return Equals(a, b) is not true;
}
#endregion
}