AnimeEdit初始化

This commit is contained in:
Hakoyu 2023-09-18 21:39:28 +08:00
parent b34d44194f
commit 63df5c128d
17 changed files with 739 additions and 289 deletions

View File

@ -13,7 +13,7 @@ namespace VPet.ModMaker.Converters;
/// <para>示例:
/// <code><![CDATA[
/// <MultiBinding Converter="{StaticResource MarginConverter}">
/// <Binding Path="String" />
/// <Binding Path="StringFormat" />
/// <Binding Path="Value1" />
/// <Binding Path="Value2" />
/// </MultiBinding>

View File

@ -12,151 +12,117 @@ using static VPet_Simulator.Core.GraphInfo;
namespace VPet.ModMaker.Models.ModModel;
public class AnimeModel
public class AnimeTypeModel
{
public static ObservableCollection<GraphInfo.GraphType> GraphTypes { get; } =
new(Enum.GetValues(typeof(GraphInfo.GraphType)).Cast<GraphInfo.GraphType>());
public static ObservableCollection<GraphInfo.AnimatType> AnimatTypes { get; } =
new(Enum.GetValues(typeof(GraphInfo.AnimatType)).Cast<GraphInfo.AnimatType>());
public static ObservableCollection<GameSave.ModeType> ModeTypes { get; } =
new(Enum.GetValues(typeof(GameSave.ModeType)).Cast<GameSave.ModeType>());
public ObservableValue<string> Id { get; } = new();
public ObservableValue<GraphInfo.GraphType> GraphType { get; } = new();
public ObservableValue<GraphInfo.AnimatType> AnimeType { get; } = new();
public ObservableValue<GameSave.ModeType> ModeType { get; } = new();
public ObservableCollection<ImageModels> MultiHappyImageModels = new();
public ObservableCollection<ImageModels> MultiNomalImageModels = new();
public ObservableCollection<ImageModels> MultiPoorConditionImageModels = new();
public ObservableCollection<ImageModels> MultiIllImageModels = new();
public ObservableCollection<AnimeModel> HappyAnimes { get; } = new();
public ObservableCollection<AnimeModel> NomalAnimes { get; } = new();
public ObservableCollection<AnimeModel> PoorConditionAnimes { get; } = new();
public ObservableCollection<AnimeModel> IllAnimes { get; } = new();
public AnimeModel() { }
public AnimeTypeModel() { }
//public AnimeModel()
//{
//}
public static AnimeModel? Load(string path)
public AnimeTypeModel(AnimeTypeModel model)
{
var model = new AnimeModel();
var infoFile = Path.Combine(path, ModMakerInfo.InfoFile);
GraphType.Value = model.GraphType.Value;
foreach (var anime in model.HappyAnimes)
HappyAnimes.Add(anime.Copy());
foreach (var anime in model.NomalAnimes)
NomalAnimes.Add(anime.Copy());
foreach (var anime in model.PoorConditionAnimes)
PoorConditionAnimes.Add(anime.Copy());
foreach (var anime in model.IllAnimes)
IllAnimes.Add(anime.Copy());
}
if (
Enum.TryParse<GraphInfo.GraphType>(Path.GetFileName(path), true, out var graphType)
is false
)
return null;
public AnimeTypeModel(GraphInfo.GraphType graphType, string path)
{
GraphType.Value = graphType;
if (graphType is GraphInfo.GraphType.Default)
LoadDefault(path);
}
private void LoadDefault(string path)
{
foreach (var modeDir in Directory.EnumerateDirectories(path))
{
foreach (var dir in Directory.EnumerateDirectories(path))
var mode = Enum.Parse(typeof(GameSave.ModeType), Path.GetFileName(modeDir), true);
if (mode is GameSave.ModeType.Happy)
{
var dirName = Path.GetFileName(dir);
if (
dirName.Contains(
nameof(GameSave.ModeType.Happy),
StringComparison.OrdinalIgnoreCase
)
)
foreach (var imagesDir in Directory.EnumerateDirectories(modeDir))
{
if (Directory.GetFiles(dir).Length == 0)
{
foreach (var imageDir in Directory.EnumerateDirectories(dir))
{
model.MultiHappyImageModels.Add(new(imageDir));
}
}
else
{
model.MultiHappyImageModels.Add(new(dir));
}
HappyAnimes.Add(new(imagesDir));
}
else if (
dirName.Contains(
nameof(GameSave.ModeType.Nomal),
StringComparison.OrdinalIgnoreCase
)
)
}
else if (mode is GameSave.ModeType.Nomal)
{
foreach (var imagesDir in Directory.EnumerateDirectories(modeDir))
{
if (Directory.GetFiles(dir).Length == 0)
{
foreach (var imageDir in Directory.EnumerateDirectories(dir))
{
model.MultiNomalImageModels.Add(new(imageDir));
}
}
else
{
model.MultiNomalImageModels.Add(new(dir));
}
NomalAnimes.Add(new(imagesDir));
}
else if (
dirName.Contains(
nameof(GameSave.ModeType.PoorCondition),
StringComparison.OrdinalIgnoreCase
)
)
}
else if (mode is GameSave.ModeType.PoorCondition)
{
foreach (var imagesDir in Directory.EnumerateDirectories(modeDir))
{
if (Directory.GetFiles(dir).Length == 0)
{
foreach (var imageDir in Directory.EnumerateDirectories(dir))
{
model.MultiPoorConditionImageModels.Add(new(imageDir));
}
}
else
{
model.MultiPoorConditionImageModels.Add(new(dir));
}
PoorConditionAnimes.Add(new(imagesDir));
}
else if (
dirName.Contains(
nameof(GameSave.ModeType.Ill),
StringComparison.OrdinalIgnoreCase
)
)
}
else if (mode is GameSave.ModeType.Ill)
{
foreach (var imagesDir in Directory.EnumerateDirectories(modeDir))
{
if (Directory.GetFiles(dir).Length == 0)
{
foreach (var imageDir in Directory.EnumerateDirectories(dir))
{
model.MultiIllImageModels.Add(new(imageDir));
}
}
else
{
model.MultiIllImageModels.Add(new(dir));
}
IllAnimes.Add(new(imagesDir));
}
}
}
else
return null;
return model;
}
}
public class ImageModels : ObservableCollection<ImageModel>
public class AnimeModel
{
public ObservableValue<string> Id { get; } = new();
public ObservableValue<GraphInfo.AnimatType> AnimeType { get; } = new();
//public ObservableValue<GameSave.ModeType> ModeType { get; } = new();
public ObservableCollection<ImageModel> Images { get; } = new();
private static readonly char[] _splits = new char[] { '_' };
public ImageModels(string imagePath)
public AnimeModel() { }
public AnimeModel(string imagesPath)
{
foreach (var file in Directory.EnumerateFiles(imagePath))
foreach (var file in Directory.EnumerateFiles(imagesPath))
{
var info = Path.GetFileNameWithoutExtension(file)
.Split(_splits, StringSplitOptions.RemoveEmptyEntries);
var id = info[0];
Id.Value = info[0];
var duration = info.Last();
var imageModel = new ImageModel();
imageModel.Id.Value = id;
imageModel.Image.Value = Utils.LoadImageToMemoryStream(file);
imageModel.Duration.Value = int.Parse(duration);
Add(imageModel);
var imageModel = new ImageModel(
Utils.LoadImageToMemoryStream(file),
int.Parse(duration)
);
Images.Add(imageModel);
}
}
public AnimeModel Copy()
{
var model = new AnimeModel();
model.Id.Value = Id.Value;
model.AnimeType.Value = AnimeType.Value;
foreach (var image in Images)
model.Images.Add(image);
return model;
}
}

View File

@ -54,7 +54,7 @@ public class FoodModel : I18nModel<I18nFoodModel>
Likability.Value = model.Likability.Value;
Price.Value = model.Price.Value;
Exp.Value = model.Exp.Value;
Image.Value = Utils.LoadImageToStream(model.Image.Value);
Image.Value = Utils.LoadImageToStream(model.Image.Value.GetSourceFile());
foreach (var item in model.I18nDatas)
I18nDatas[item.Key] = item.Value.Copy();
CurrentI18nData.Value = I18nDatas[I18nHelper.Current.CultureName.Value];

View File

@ -10,8 +10,21 @@ namespace VPet.ModMaker.Models.ModModel;
public class ImageModel
{
public ObservableValue<string> Id { get; } = new();
public ObservableValue<BitmapImage> Image { get; } = new();
public ObservableValue<int> Duration { get; } = new(100);
public ImageModel(BitmapImage image, int duration = 100)
{
Image.Value = image;
Duration.Value = duration;
}
public ImageModel Copy()
{
var model = new ImageModel(
Utils.LoadImageToStream(Image.Value.GetSourceFile()),
Duration.Value
);
return model;
}
}

View File

@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
using VPet_Simulator.Windows.Interface;
namespace VPet.ModMaker.Models;
@ -74,17 +75,19 @@ public class ModInfoModel : I18nModel<I18nModInfoModel>
var petModel = new PetModel(pet);
Pets.Add(petModel);
// TODO: 动画加载
//foreach (var p in pet.path)
//{
// foreach (var dir in Directory.EnumerateDirectories(p))
// {
// var animeModel = AnimeModel.Load(dir);
// if (animeModel != null)
// {
// petModel.Animes.TryAdd(animeModel.GraphType.Value, animeModel);
// }
// }
//}
foreach (var p in pet.path)
{
foreach (var dir in Directory.EnumerateDirectories(p))
{
Enum.TryParse<GraphInfo.GraphType>(
Path.GetFileName(dir),
true,
out var animeType
);
if (animeType is GraphInfo.GraphType.Default)
petModel.Animes.Add(new(animeType, dir));
}
}
}
foreach (var lang in loader.I18nDatas)

View File

@ -25,8 +25,7 @@ public class PetModel : I18nModel<I18nPetInfoModel>
public ObservableCollection<MoveModel> Moves { get; } = new();
public ObservableValue<AnimeModel> CurrentAnime { get; } = new();
public Dictionary<GraphInfo.GraphType, AnimeModel> Animes { get; } = new();
public ObservableCollection<AnimeTypeModel> Animes { get; } = new();
public PetModel()
{

View File

@ -11,10 +11,14 @@ namespace VPet.ModMaker.Models;
public static class Utils
{
public const int DecodePixelWidth = 250;
public const int DecodePixelHeight = 250;
public static BitmapImage LoadImageToStream(string imagePath)
{
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
bitmapImage.DecodePixelWidth = DecodePixelWidth;
try
{
bitmapImage.StreamSource = new StreamReader(imagePath).BaseStream;
@ -26,15 +30,11 @@ public static class Utils
return bitmapImage;
}
public static BitmapImage LoadImageToStream(this BitmapImage image)
{
return LoadImageToStream(image.GetSourceFile());
}
public static BitmapImage LoadImageToMemoryStream(string imagePath)
{
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
bitmapImage.DecodePixelWidth = DecodePixelWidth;
try
{
var ms = new MemoryStream();
@ -48,9 +48,4 @@ public static class Utils
}
return bitmapImage;
}
public static BitmapImage LoadImageToMemoryStream(this BitmapImage image)
{
return LoadImageToMemoryStream(image.GetSourceFile());
}
}

View File

@ -136,6 +136,9 @@
<Compile Include="Views\ModEdit\AnimeEdit\AnimeEditWindow.xaml.cs">
<DependentUpon>AnimeEditWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ModEdit\AnimeEdit\AnimePage.xaml.cs">
<DependentUpon>AnimePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ModEdit\ClickTextEdit\ClickTextPage.xaml.cs">
<DependentUpon>ClickTextPage.xaml</DependentUpon>
</Compile>
@ -238,6 +241,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModEdit\AnimeEdit\AnimePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModEdit\ClickTextEdit\ClickTextPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -1,24 +1,34 @@
using HKW.HKWViewModels.SimpleObservable;
using LinePutScript.Localization.WPF;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using VPet.ModMaker.Models;
using VPet.ModMaker.Models.ModModel;
using VPet_Simulator.Core;
namespace VPet.ModMaker.ViewModels.ModEdit.AnimeEdit;
public class AnimeEditWindowVM
{
public AnimeModel OldAnime { get; set; }
public ObservableValue<AnimeModel> Anime { get; } = new(new());
public PetModel CurrentPet { get; set; }
public AnimeTypeModel OldAnime { get; set; }
public ObservableValue<AnimeTypeModel> Anime { get; } = new(new());
public ObservableValue<ImageModel> CurrentImageModel { get; } = new();
public GameSave.ModeType CurrentMode { get; set; }
#region Command
public ObservableCommand PlayCommand { get; } = new();
public ObservableCommand PauseCommand { get; } = new();
public ObservableCommand<AnimeModel> AddImageCommand { get; } = new();
public ObservableCommand<AnimeModel> ClearImageCommand { get; } = new();
public ObservableCommand<AnimeModel> RemoveAnimeCommand { get; } = new();
public ObservableCommand<AnimeModel> RemoveImageCommand { get; } = new();
#endregion
public bool _pause = false;
@ -26,27 +36,67 @@ public class AnimeEditWindowVM
public AnimeEditWindowVM()
{
//foreach (
// var file in Directory.EnumerateFiles(
// @"C:\Users\HKW\Desktop\TestPicture\0000_core\pet\vup\Default\Happy\1"
// )
//)
//{
// Anime.Value.MultiImageModels.Add(new(Utils.LoadImageToMemoryStream(file)));
//}
_playerTask = new(Play);
PlayCommand.ExecuteEvent += PlayCommand_ExecuteEvent;
PauseCommand.ExecuteEvent += PauseCommand_ExecuteEvent;
//_playerTask = new(Play);
//PlayCommand.ExecuteEvent += PlayCommand_ExecuteEvent;
//PauseCommand.ExecuteEvent += PauseCommand_ExecuteEvent;
AddImageCommand.ExecuteEvent += AddImageCommand_ExecuteEvent;
ClearImageCommand.ExecuteEvent += ClearImageCommand_ExecuteEvent;
RemoveAnimeCommand.ExecuteEvent += RemoveAnimeCommand_ExecuteEvent;
RemoveImageCommand.ExecuteEvent += RemoveImageCommand_ExecuteEvent;
}
private void RemoveImageCommand_ExecuteEvent(AnimeModel value)
{
value.Images.Remove(CurrentImageModel.Value);
}
private void RemoveAnimeCommand_ExecuteEvent(AnimeModel value)
{
if (
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);
}
}
private void ClearImageCommand_ExecuteEvent(AnimeModel value)
{
if (
MessageBox.Show("确定清空吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.Yes
)
value.Images.Clear();
}
private void AddImageCommand_ExecuteEvent(AnimeModel value)
{
OpenFileDialog openFileDialog =
new()
{
Title = "选择图片".Translate(),
Filter = $"图片|*.jpg;*.jpeg;*.png;*.bmp".Translate()
};
if (openFileDialog.ShowDialog() is true)
{
value.Images.Add(new(Utils.LoadImageToStream(openFileDialog.FileName)));
}
}
private void PauseCommand_ExecuteEvent()
{
_pause = true;
//_pause = true;
}
private void PlayCommand_ExecuteEvent()
{
_playerTask.Start();
//_playerTask.Start();
}
private void Play()

View File

@ -15,95 +15,95 @@ namespace VPet.ModMaker.ViewModels.ModEdit.AnimeEdit;
public class AnimePageVM
{
//#region Value
//public ObservableValue<ObservableCollection<AnimeModel>> ShowAnimes { get; } = new();
//public ObservableCollection<AnimeModel> Works => CurrentPet.Value.Works;
#region Value
public ObservableValue<ObservableCollection<AnimeTypeModel>> ShowAnimes { get; } = new();
public ObservableCollection<AnimeTypeModel> Animes => CurrentPet.Value.Animes;
//public ObservableCollection<PetModel> Pets => ModInfoModel.Current.Pets;
//public ObservableValue<PetModel> CurrentPet { get; } = new(new());
public ObservableCollection<PetModel> Pets => ModInfoModel.Current.Pets;
public ObservableValue<PetModel> CurrentPet { get; } = new(new());
//public ObservableValue<string> Filter { get; } = new();
//#endregion
//#region Command
//public ObservableCommand AddCommand { get; } = new();
//public ObservableCommand<AnimeModel> EditCommand { get; } = new();
//public ObservableCommand<AnimeModel> RemoveCommand { get; } = new();
//#endregion
//public AnimePageVM()
//{
// ShowAnimes.Value = Works;
// CurrentPet.ValueChanged += CurrentPet_ValueChanged;
// Filter.ValueChanged += Filter_ValueChanged;
#endregion
#region Command
public ObservableCommand AddCommand { get; } = new();
public ObservableCommand<AnimeTypeModel> EditCommand { get; } = new();
public ObservableCommand<AnimeTypeModel> RemoveCommand { get; } = new();
#endregion
public AnimePageVM()
{
ShowAnimes.Value = Animes;
CurrentPet.ValueChanged += CurrentPet_ValueChanged;
//Filter.ValueChanged += Filter_ValueChanged;
// AddCommand.ExecuteEvent += Add;
// EditCommand.ExecuteEvent += Edit;
// RemoveCommand.ExecuteEvent += Remove;
//}
AddCommand.ExecuteEvent += Add;
EditCommand.ExecuteEvent += Edit;
RemoveCommand.ExecuteEvent += Remove;
}
//private void CurrentPet_ValueChanged(PetModel oldValue, PetModel newValue)
//{
// //ShowAnimes.Value = newValue.Animes;
//}
private void CurrentPet_ValueChanged(PetModel oldValue, PetModel newValue)
{
ShowAnimes.Value = newValue.Animes;
}
//private void Filter_ValueChanged(string oldValue, string newValue)
//{
// if (string.IsNullOrWhiteSpace(newValue))
// {
// ShowAnimes.Value = Works;
// }
// else
// {
// ShowAnimes.Value = new(
// Works.Where(m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase))
// );
// }
//}
private void Filter_ValueChanged(string oldValue, string newValue)
{
//if (string.IsNullOrWhiteSpace(newValue))
//{
// ShowAnimes.Value = Animes;
//}
//else
//{
// ShowAnimes.Value = new(
// Animes.Where(m => m.Id.Value.Contains(newValue, StringComparison.OrdinalIgnoreCase))
// );
//}
}
//public void Close() { }
public void Close() { }
//private void Add()
//{
// var window = new AnimeEditWindow();
// var vm = window.ViewModel;
// vm.CurrentPet = CurrentPet.Value;
// window.ShowDialog();
// if (window.IsCancel)
// return;
// Works.Add(vm.Work.Value);
//}
private void Add()
{
var window = new AnimeEditWindow();
var vm = window.ViewModel;
vm.CurrentPet = CurrentPet.Value;
window.ShowDialog();
if (window.IsCancel)
return;
Animes.Add(vm.Anime.Value);
}
//public void Edit(AnimeModel model)
//{
// var window = new AnimeEditWindow();
// var vm = window.ViewModel;
// vm.CurrentPet = CurrentPet.Value;
// vm.OldWork = model;
// var newWork = vm.Work.Value = new(model);
// window.ShowDialog();
// if (window.IsCancel)
// return;
// if (ShowAnimes.Value.Count == Works.Count)
// {
// Works[Works.IndexOf(model)] = newWork;
// }
// else
// {
// Works[Works.IndexOf(model)] = newWork;
// ShowAnimes.Value[ShowAnimes.Value.IndexOf(model)] = newWork;
// }
//}
public void Edit(AnimeTypeModel model)
{
var window = new AnimeEditWindow();
var vm = window.ViewModel;
vm.CurrentPet = CurrentPet.Value;
vm.OldAnime = model;
var newAnime = vm.Anime.Value = new(model);
window.ShowDialog();
if (window.IsCancel)
return;
if (ShowAnimes.Value.Count == Animes.Count)
{
Animes[Animes.IndexOf(model)] = newAnime;
}
else
{
Animes[Animes.IndexOf(model)] = newAnime;
ShowAnimes.Value[ShowAnimes.Value.IndexOf(model)] = newAnime;
}
}
//private void Remove(AnimeModel food)
//{
// if (MessageBox.Show("确定删除吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.No)
// return;
// if (ShowAnimes.Value.Count == Works.Count)
// {
// Works.Remove(food);
// }
// else
// {
// ShowAnimes.Value.Remove(food);
// Works.Remove(food);
// }
//}
private void Remove(AnimeTypeModel food)
{
if (MessageBox.Show("确定删除吗".Translate(), "", MessageBoxButton.YesNo) is MessageBoxResult.No)
return;
if (ShowAnimes.Value.Count == Animes.Count)
{
Animes.Remove(food);
}
else
{
ShowAnimes.Value.Remove(food);
Animes.Remove(food);
}
}
}

View File

@ -9,12 +9,114 @@
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:vm="clr-namespace:VPet.ModMaker.ViewModels.ModEdit.AnimeEdit"
Title="AnimeEditWindow"
Width="800"
Height="450"
Width="1000"
Height="600"
mc:Ignorable="d">
<d:Window.DataContext>
<vm:AnimeEditWindowVM />
</d:Window.DataContext>
<Window.Resources>
<Style
x:Key="ListBoxItem_Style"
BasedOn="{StaticResource {x:Type ListBoxItem}}"
TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="Expander_AnimeItem" DataType="ListBoxItem">
<Expander
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DataContext=""
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<Expander.ContextMenu>
<ContextMenu d:DataContext="">
<MenuItem
Command="{Binding PlacementTarget.Tag.AddImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="添加图片" />
<MenuItem
Command="{Binding PlacementTarget.Tag.ClearImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="清空图片" />
<MenuItem
Command="{Binding PlacementTarget.Tag.RemoveAnimeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="删除此项" />
</ContextMenu>
</Expander.ContextMenu>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox pu:TextBoxHelper.Watermark="动画Id" Text="{Binding Id.Value}" />
<TextBlock Grid.Column="1" Margin="10,0,0,0">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}({0})">
<Binding Path="Images.Count" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Expander.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
AllowDrop="True"
Drop="ListBox_Drop"
ItemsSource="{Binding Images, IsAsync=True}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseWheel="ListBox_PreviewMouseWheel"
SelectedItem="{Binding DataContext.CurrentImageModel.Value, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Expander, Mode=FindAncestor}}" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding PlacementTarget.Tag.RemoveImageCommand, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"
Header="删除图片" />
</ContextMenu>
</Grid.ContextMenu>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
Width="150"
Height="150"
d:DataContext=""
DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}"
Source="{Binding Image.Value, IsAsync=True}">
<Image.ToolTip>
<Image
Width="250"
Height="250"
Source="{Binding Image.Value, IsAsync=True}" />
</Image.ToolTip>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="持续时间(ms)" />
<pu:NumberInput Grid.Column="1" Value="{Binding DataContext.Duration.Value, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}" />
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -26,10 +128,11 @@
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
Width="250"
Height="250"
Source="{Binding Anime.Value.CurrentImageModel.Value.Image.Value}" />
<Image Width="250" Height="250">
<Image.ToolTip>
<Image Width="500" Height="500" />
</Image.ToolTip>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@ -52,10 +155,14 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="{ll:Str 动画Id}" />
<TextBox Grid.Column="1" />
<!--<Label Content="{ll:Str 动画Id}" />
<TextBox Grid.Column="1" />-->
<Label Grid.Row="1" Content="{ll:Str 动画类型}" />
<ComboBox Grid.Row="1" Grid.Column="1" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
IsEnabled="True"
Text="{Binding Anime.Value.GraphType.Value}" />
</Grid>
</Grid>
<Grid Grid.Column="1">
@ -63,62 +170,84 @@
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox
x:Name="ListBox_Images"
d:ItemsSource="{d:SampleData ItemCount=5}"
d:SelectedIndex="0"
ItemsSource="{Binding Anime.Value.ImageModels}"
SelectedItem="{Binding Anime.Value.CurrentImageModel.Value}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="150" Height="200">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image
Width="150"
Height="150"
Source="{Binding Image.Value}">
<Image.ToolTip>
<Image
Width="250"
Height="250"
Source="{Binding Image.Value}" />
</Image.ToolTip>
</Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Content="持续时间" />
<pu:NumberInput Grid.Column="1" Value="{Binding Duration.Value}" />
</Grid>
<TextBox Grid.Row="2" Text="ImageName" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TabControl SelectionChanged="TabControl_SelectionChanged">
<TabItem Tag="{Binding Anime.Value.ModeTypes[0]}">
<TabItem.Header>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}{0} ({1})">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="Anime.Value.HappyAnimes.Count" />
</MultiBinding>
</TabItem.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
d:SelectedIndex="0"
ItemContainerStyle="{StaticResource ListBoxItem_Style}"
ItemTemplate="{StaticResource Expander_AnimeItem}"
ItemsSource="{Binding Anime.Value.HappyAnimes, IsAsync=True}" />
</TabItem>
<TabItem d:Header="Nomal (0)" Tag="Nomal">
<TabItem.Header>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}{0} ({1})">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="Anime.Value.NomalAnimes.Count" />
</MultiBinding>
</TabItem.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
d:SelectedIndex="0"
ItemContainerStyle="{StaticResource ListBoxItem_Style}"
ItemTemplate="{StaticResource Expander_AnimeItem}"
ItemsSource="{Binding Anime.Value.NomalAnimes, IsAsync=True}" />
</TabItem>
<TabItem d:Header="PoorCondition (0)" Tag="PoorCondition">
<TabItem.Header>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}{0} ({1})">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="Anime.Value.PoorConditionAnimes.Count" />
</MultiBinding>
</TabItem.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
d:SelectedIndex="0"
ItemContainerStyle="{StaticResource ListBoxItem_Style}"
ItemTemplate="{StaticResource Expander_AnimeItem}"
ItemsSource="{Binding Anime.Value.PoorConditionAnimes, IsAsync=True}" />
</TabItem>
<TabItem d:Header="Ill (0)" Tag="Ill">
<TabItem.Header>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}{0} ({1})">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="Anime.Value.IllAnimes.Count" />
</MultiBinding>
</TabItem.Header>
<ListBox
d:ItemsSource="{d:SampleData ItemCount=5}"
d:SelectedIndex="0"
ItemContainerStyle="{StaticResource ListBoxItem_Style}"
ItemTemplate="{StaticResource Expander_AnimeItem}"
ItemsSource="{Binding Anime.Value.IllAnimes, IsAsync=True}" />
</TabItem>
</TabControl>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
x:Name="Button_AddAnime"
Margin="10"
Click="Button_AddAnime_Click"
Content="{ll:Str 添加动画}" />
<Button
x:Name="Button_Cancel"
Grid.Column="1"
Margin="10"
Click="Button_Cancel_Click"
Content="{ll:Str 取消}" />
<Button
x:Name="Button_Yes"
Grid.Column="1"
Grid.Column="2"
Margin="10"
Click="Button_Yes_Click"
Content="{ll:Str 确定}" />

View File

@ -11,7 +11,9 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using VPet.ModMaker.Models.ModModel;
using VPet.ModMaker.ViewModels.ModEdit.AnimeEdit;
using VPet_Simulator.Core;
namespace VPet.ModMaker.Views.ModEdit.AnimeEdit;
@ -22,8 +24,8 @@ public partial class AnimeEditWindow : Window
{
public AnimeEditWindow()
{
InitializeComponent();
DataContext = new AnimeEditWindowVM();
InitializeComponent();
Closed += (s, e) =>
{
try
@ -71,4 +73,126 @@ public partial class AnimeEditWindow : Window
IsCancel = false;
Close();
}
//private void ListBox_Drop(object sender, DragEventArgs e)
//{
// var fileName = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
//}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (
sender is not TabControl tabControl
|| tabControl.SelectedItem is not TabItem item
|| item.Tag is not string str
)
return;
if (Enum.TryParse<GameSave.ModeType>(str, true, out var mode))
ViewModel.CurrentMode = mode;
}
private void ListBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{
RoutedEvent = MouseWheelEvent,
Source = sender
};
var parent = ((Control)sender).Parent as UIElement;
parent.RaiseEvent(eventArg);
e.Handled = true;
}
private object _dropSender;
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (sender is not ListBox listBox)
return;
if (e.LeftButton != MouseButtonState.Pressed)
return;
var pos = e.GetPosition(listBox);
HitTestResult result = VisualTreeHelper.HitTest(listBox, pos);
if (result is null)
return;
var listBoxItem = FindVisualParent<ListBoxItem>(result.VisualHit);
if (listBoxItem == null || listBoxItem.Content != listBox.SelectedItem)
return;
var dataObj = new DataObject(listBoxItem.Content);
DragDrop.DoDragDrop(listBox, dataObj, DragDropEffects.Move);
_dropSender = sender;
}
private void ListBox_Drop(object sender, DragEventArgs e)
{
if (sender.Equals(_dropSender) is false)
{
MessageBox.Show("无法移动不同动画的图片");
return;
}
if (sender is not ListBox listBox)
return;
var pos = e.GetPosition(listBox);
var result = VisualTreeHelper.HitTest(listBox, pos);
if (result == null)
return;
//查找元数据
if (e.Data.GetData(typeof(ImageModel)) is not ImageModel sourcePerson)
return;
//查找目标数据
var listBoxItem = FindVisualParent<ListBoxItem>(result.VisualHit);
if (listBoxItem == null)
return;
var targetPerson = listBoxItem.Content as ImageModel;
if (ReferenceEquals(targetPerson, sourcePerson))
return;
if (listBox.ItemsSource is not IList<ImageModel> list)
return;
var sourceIndex = list.IndexOf(sourcePerson);
var targetIndex = list.IndexOf(targetPerson);
var temp = list[sourceIndex];
list[sourceIndex] = list[targetIndex];
list[targetIndex] = temp;
}
public static T? FindVisualChild<T>(DependencyObject obj)
where T : DependencyObject
{
if (obj is null)
return null;
var count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is T t)
return t;
if (FindVisualChild<T>(child) is T childItem)
return childItem;
}
return null;
}
public static T FindVisualParent<T>(DependencyObject obj)
where T : class
{
while (obj != null)
{
if (obj is T)
return obj as T;
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
private void Button_AddAnime_Click(object sender, RoutedEventArgs e)
{
if (ViewModel.CurrentMode is GameSave.ModeType.Happy)
ViewModel.Anime.Value.HappyAnimes.Add(new());
else if (ViewModel.CurrentMode is GameSave.ModeType.Nomal)
ViewModel.Anime.Value.NomalAnimes.Add(new());
else if (ViewModel.CurrentMode is GameSave.ModeType.PoorCondition)
ViewModel.Anime.Value.PoorConditionAnimes.Add(new());
else if (ViewModel.CurrentMode is GameSave.ModeType.Ill)
ViewModel.Anime.Value.IllAnimes.Add(new());
}
}

View File

@ -0,0 +1,114 @@
<Page
x:Class="VPet.ModMaker.Views.ModEdit.AnimeEdit.AnimePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ll="clr-namespace:LinePutScript.Localization.WPF;assembly=LinePutScript.Localization.WPF"
xmlns:local="clr-namespace:VPet.ModMaker.Views.ModEdit.AnimeEdit"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:vm="clr-namespace:VPet.ModMaker.ViewModels.ModEdit.AnimeEdit"
Title="AnimePage"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<d:Page.DataContext>
<vm:AnimePageVM />
</d:Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!--<TextBox pu:TextBoxHelper.Watermark="{ll:Str 搜索Id}" Text="{Binding Filter.Value, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style BasedOn="{StaticResource {x:Type TextBox}}" TargetType="TextBox">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=ComboBox_Pet}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>-->
<ComboBox
x:Name="ComboBox_Pet"
Grid.Column="1"
pu:ComboBoxHelper.Watermark="{ll:Str 选择宠物}"
DisplayMemberPath="Id.Value"
ItemsSource="{Binding Pets}"
SelectedItem="{Binding CurrentPet.Value}">
<ComboBox.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type ComboBoxItem}}" TargetType="ComboBoxItem">
<Setter Property="ToolTip" Value="{Binding CurrentI18nData.Value.Name.Value}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
<Grid Grid.Row="1">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=ComboBox_Pet}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<DataGrid
d:ItemsSource="{d:SampleData ItemCount=5}"
pu:DataGridHelper.ColumnHeaderHorizontalContentAlignment="Center"
AutoGenerateColumns="False"
CanUserAddRows="False"
GridLinesVisibility="Horizontal"
ItemsSource="{Binding ShowAnimes.Value}"
MouseDoubleClick="DataGrid_MouseDoubleClick"
RowDetailsVisibilityMode="Visible"
RowHeight="64"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<DataGrid.RowStyle>
<Style BasedOn="{StaticResource {x:Type DataGridRow}}" TargetType="DataGridRow">
<Setter Property="Height" Value="64" />
<Setter Property="Tag" Value="{Binding}" />
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu_DataGridRow}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding GraphType.Value}"
CanUserSort="True"
IsReadOnly="True"
SortMemberPath="GraphType.Value">
<DataGridTextColumn.Header>
<TextBlock Text="{ll:Str 动画类型}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<!--<DataGridTextColumn
Binding="{Binding LocateType.EnumValue.Value}"
CanUserSort="True"
IsReadOnly="True"
SortMemberPath="LocateType.EnumValue.Value">
<DataGridTextColumn.Header>
<TextBlock Text="{ll:Str 动画支持的状态}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>-->
</DataGrid.Columns>
</DataGrid>
<Button
Grid.Row="1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Command="{Binding AddCommand}"
Content=""
Style="{StaticResource AddButton}" />
</Grid>
</Grid>
</Page>

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using VPet.ModMaker.Models.ModModel;
using VPet.ModMaker.ViewModels.ModEdit.AnimeEdit;
namespace VPet.ModMaker.Views.ModEdit.AnimeEdit;
/// <summary>
/// AnimePage.xaml 的交互逻辑
/// </summary>
public partial class AnimePage : Page
{
public AnimePage()
{
InitializeComponent();
DataContext = new AnimePageVM();
}
public AnimePageVM ViewModel => (AnimePageVM)DataContext;
private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender is not DataGrid dataGrid || dataGrid.SelectedItem is not AnimeTypeModel model)
return;
ViewModel.Edit(model);
}
}

View File

@ -62,7 +62,7 @@
Height="64"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Image.Value}"
Source="{Binding Image.Value, IsAsync=True}"
Stretch="Uniform">
<Image.ToolTip>
<Image
@ -70,7 +70,7 @@
Height="256"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Image.Value}"
Source="{Binding Image.Value, IsAsync=True}"
Stretch="Uniform" />
</Image.ToolTip>
</Image>

View File

@ -212,6 +212,15 @@
</TabItem.Header>
<Frame Content="{Binding ModEditWindow.MovePage}" />
</TabItem>
<TabItem Tag="{ll:Str 动画}">
<TabItem.Header>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter="{}{0} ({1})">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="ModEditWindow.MovePage.ViewModel.CurrentPet.Value.Animes.Count" />
</MultiBinding>
</TabItem.Header>
<Frame Content="{Binding ModEditWindow.AnimePage}" />
</TabItem>
<!--<TabItem Header="物品 (0)" Tag="{ll:Str 物品}">
<Grid>
<Grid.RowDefinitions>

View File

@ -17,6 +17,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using VPet.ModMaker.ViewModels.ModEdit;
using VPet.ModMaker.Views.ModEdit.AnimeEdit;
using VPet.ModMaker.Views.ModEdit.ClickTextEdit;
using VPet.ModMaker.Views.ModEdit.FoodEdit;
using VPet.ModMaker.Views.ModEdit.LowTextEdit;
@ -40,8 +41,8 @@ public partial class ModEditWindow : Window
public SelectTextPage SelectTextPage { get; } = new();
public PetPage PetPage { get; } = new();
public WorkPage WorkPage { get; } = new();
public MovePage MovePage { get; } = new();
public AnimePage AnimePage { get; } = new();
public ModEditWindow()
{
@ -63,6 +64,7 @@ public partial class ModEditWindow : Window
PetPage.DataContext = null;
WorkPage.DataContext = null;
MovePage.DataContext = null;
AnimePage.DataContext = null;
}
catch { }
}