From 4ca5d0e2c49c55150557af4059196db6443816c2 Mon Sep 17 00:00:00 2001 From: Ryan Kramer Date: Wed, 14 Jan 2026 19:35:25 -0600 Subject: [PATCH 1/6] working on snippets --- .../ResourceDeserializationContext.cs | 9 ++ .../Persistence/V1/ProjectV1.cs | 16 +++ src/Blocktavius.AppDQB2/ProjectControl.xaml | 6 + src/Blocktavius.AppDQB2/ProjectVM.cs | 29 +++++ .../ExtractedSnippetResourceEditor.xaml | 58 +++++++++ .../ExtractedSnippetResourceEditor.xaml.cs | 15 +++ .../Resources/ExtractedSnippetResourceVM.cs | 85 ++++++++++++++ src/Blocktavius.AppDQB2/ResourcesControl.xaml | 38 ++++++ .../ResourcesControl.xaml.cs | 15 +++ .../ScriptNodes/AreaDefinerVM.cs | 110 ++++++++++++++++++ .../ScriptNodes/PutGroundNodeVM.cs | 99 ++-------------- src/Blocktavius.DQB2/Snippet.cs | 79 +++++++++++++ 12 files changed, 467 insertions(+), 92 deletions(-) create mode 100644 src/Blocktavius.AppDQB2/Persistence/ResourceDeserializationContext.cs create mode 100644 src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml create mode 100644 src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml.cs create mode 100644 src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceVM.cs create mode 100644 src/Blocktavius.AppDQB2/ResourcesControl.xaml create mode 100644 src/Blocktavius.AppDQB2/ResourcesControl.xaml.cs create mode 100644 src/Blocktavius.AppDQB2/ScriptNodes/AreaDefinerVM.cs create mode 100644 src/Blocktavius.DQB2/Snippet.cs diff --git a/src/Blocktavius.AppDQB2/Persistence/ResourceDeserializationContext.cs b/src/Blocktavius.AppDQB2/Persistence/ResourceDeserializationContext.cs new file mode 100644 index 0000000..84327b9 --- /dev/null +++ b/src/Blocktavius.AppDQB2/Persistence/ResourceDeserializationContext.cs @@ -0,0 +1,9 @@ +using Blocktavius.AppDQB2.Resources; + +namespace Blocktavius.AppDQB2.Persistence; + +sealed class ResourceDeserializationContext +{ + public required IAreaManager AreaManager { get; init; } + public required IReadOnlyList Slots { get; init; } +} diff --git a/src/Blocktavius.AppDQB2/Persistence/V1/ProjectV1.cs b/src/Blocktavius.AppDQB2/Persistence/V1/ProjectV1.cs index 65fa0aa..74f4e01 100644 --- a/src/Blocktavius.AppDQB2/Persistence/V1/ProjectV1.cs +++ b/src/Blocktavius.AppDQB2/Persistence/V1/ProjectV1.cs @@ -49,6 +49,13 @@ public required IReadOnlyList? Scripts public required int? SelectedScriptIndex { get; init; } + private IContentEqualityList? _extractedSnippets = null; + public required IReadOnlyList? ExtractedSnippets + { + get => _extractedSnippets; + init => _extractedSnippets = value?.ToContentEqualityList(); + } + public ProjectV1 VerifyProfileHash(ProfileSettings profile) { if (profile.VerificationHash == ProfileVerificationHash) @@ -139,3 +146,12 @@ public Rect ToCoreRect() return new Rect(start, end); } } + +sealed record ExtractedSnippetV1 +{ + public required string? Name { get; init; } + public required SlotReferenceV1? SourceSlot { get; init; } + public required string? SourceStgdatFilename { get; init; } + public required string? AreaPersistentId { get; init; } + public required RectV1? CustomRectArea { get; init; } +} diff --git a/src/Blocktavius.AppDQB2/ProjectControl.xaml b/src/Blocktavius.AppDQB2/ProjectControl.xaml index 152b06f..22dd5e9 100644 --- a/src/Blocktavius.AppDQB2/ProjectControl.xaml +++ b/src/Blocktavius.AppDQB2/ProjectControl.xaml @@ -34,6 +34,12 @@ + + + Resources + + + diff --git a/src/Blocktavius.AppDQB2/ProjectVM.cs b/src/Blocktavius.AppDQB2/ProjectVM.cs index cc2c379..b3c787a 100644 --- a/src/Blocktavius.AppDQB2/ProjectVM.cs +++ b/src/Blocktavius.AppDQB2/ProjectVM.cs @@ -59,6 +59,7 @@ sealed partial class ProjectVM : ViewModelBaseWithCustomTypeDescriptor, IBlockLi // commands public I.Project.CommandEditChunkGrid CommandEditChunkGrid { get; } public ICommand CommandAddScript { get; } + public ICommand CommandAddSnippet { get; } // wrapper properties private ProfileSettings getProfile => xProfile.Value; @@ -113,10 +114,18 @@ public ProjectVM(IServices services, ProfileSettings profile) CommandExportChunkMask = new RelayCommand(_ => chunkGridLayer.ChunkGridImage != null, ExportChunkMask); CommandExportMinimap = new RelayCommand(_ => minimapLayer?.MinimapImage != null, ExportMinimap); CommandAddScript = new RelayCommand(_ => true, AddScript); + CommandAddSnippet = new RelayCommand(_ => true, AddSnippet); ForceUpdateProfile(profile); } + private void AddSnippet(object? _) + { + var newResource = new Resources.ExtractedSnippetResourceVM(); + newResource.Name = $"New Snippet {ExtractedSnippets.Count + 1}"; + ExtractedSnippets.Add(newResource); + } + private void ExportChunkMask(object? arg) => ExportImage(chunkGridLayer.ChunkGridImage, "exported-chunk-mask.png", 1.0); private void ExportMinimap(object? arg) => ExportImage(minimapLayer?.MinimapImage, "exported-minimap.png", 0.5); @@ -248,6 +257,11 @@ public ILayerVM? SelectedLayer public ObservableCollection Scripts { get; } = new(); + public ObservableCollection ExtractedSnippets { get; } = new(); + + public IReadOnlyList AvailableAreas + => Layers.Select(layer => layer.SelfAsAreaVM).WhereNotNull().ToList(); + private ScriptVM? _selectedScript; public ScriptVM? SelectedScript { @@ -393,6 +407,7 @@ public ProjectV1 ToPersistModel() ChunkGridVisible = chunkGridLayer.IsVisible, Scripts = this.Scripts.Select(s => s.ToPersistModelConcrete()).ToList(), SelectedScriptIndex = SelectedScript == null ? null : Scripts.IndexOf(SelectedScript), + ExtractedSnippets = this.ExtractedSnippets.Select(s => s.ToPersistModel()).ToList(), }; } @@ -469,6 +484,20 @@ private void Reload(ProjectV1 project, ExternalImageManager imageManager) } SelectedScript = Scripts.ElementAtOrDefault(project.SelectedScriptIndex.GetValueOrDefault(-1)); + var resourceContext = new Persistence.ResourceDeserializationContext + { + AreaManager = this, + Slots = getSourceSlots, + }; + + ExtractedSnippets.Clear(); + foreach (var snippet in project.ExtractedSnippets.EmptyIfNull()) + { + var snippetVM = Resources.ExtractedSnippetResourceVM.Load(snippet, resourceContext); + ExtractedSnippets.Add(snippetVM); + } + + changeset.Complete(); } diff --git a/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml b/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml new file mode 100644 index 0000000..ea797f9 --- /dev/null +++ b/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml.cs b/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml.cs new file mode 100644 index 0000000..efd4296 --- /dev/null +++ b/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceEditor.xaml.cs @@ -0,0 +1,15 @@ +using System.Windows.Controls; + +namespace Blocktavius.AppDQB2.Resources +{ + /// + /// Interaction logic for ExtractedSnippetResourceEditor.xaml + /// + public partial class ExtractedSnippetResourceEditor : UserControl + { + public ExtractedSnippetResourceEditor() + { + InitializeComponent(); + } + } +} diff --git a/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceVM.cs b/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceVM.cs new file mode 100644 index 0000000..1bbc405 --- /dev/null +++ b/src/Blocktavius.AppDQB2/Resources/ExtractedSnippetResourceVM.cs @@ -0,0 +1,85 @@ +using Blocktavius.AppDQB2.ScriptNodes; +using Blocktavius.AppDQB2.Persistence.V1; +using Blocktavius.AppDQB2.Persistence; +using System.Linq; +using Blocktavius.Core; + +namespace Blocktavius.AppDQB2.Resources; + +sealed class ExtractedSnippetResourceVM : ViewModelBase +{ + private string _name = "New Snippet"; + public string Name + { + get => _name; + set => ChangeProperty(ref _name, value); + } + + private SlotVM? _sourceSlot; + public SlotVM? SourceSlot + { + get => _sourceSlot; + set + { + if (ChangeProperty(ref _sourceSlot, value)) + { + SourceStage = null; + } + } + } + + private SlotStageVM? _sourceStage; + public SlotStageVM? SourceStage + { + get => _sourceStage; + set => ChangeProperty(ref _sourceStage, value); + } + + public AreaDefinerVM AreaDefiner { get; } = new(); + + public ExtractedSnippetV1 ToPersistModel() + { + return new ExtractedSnippetV1 + { + Name = this.Name, + SourceSlot = this.SourceSlot?.ToPersistModel(), + SourceStgdatFilename = this.SourceStage?.Filename, + AreaPersistentId = this.AreaDefiner.Area?.PersistentId, + CustomRectArea = this.AreaDefiner.RebuildCustomRect(), + }; + } + + public static ExtractedSnippetResourceVM Load(ExtractedSnippetV1 persistModel, ResourceDeserializationContext context) + { + var vm = new ExtractedSnippetResourceVM + { + Name = persistModel.Name ?? "Unnamed Snippet", + }; + + // Lookup Source Slot + if (persistModel.SourceSlot != null) + { + vm.SourceSlot = context.Slots.FirstOrDefault( + s => s.MatchesByNumber(persistModel.SourceSlot) || s.MatchesByName(persistModel.SourceSlot)); + } + + // Lookup Source Stage + if (persistModel.SourceStgdatFilename != null && vm.SourceSlot != null) + { + vm.SourceStage = vm.SourceSlot.Stages.EmptyIfNull().FirstOrDefault( + s => string.Equals(s.Filename, persistModel.SourceStgdatFilename, StringComparison.OrdinalIgnoreCase)); + } + + // AreaDefiner + if (persistModel.AreaPersistentId != null) + { + vm.AreaDefiner.Area = context.AreaManager.FindArea(persistModel.AreaPersistentId); + } + else if (persistModel.CustomRectArea != null) + { + vm.AreaDefiner.Load(persistModel.CustomRectArea); + } + + return vm; + } +} diff --git a/src/Blocktavius.AppDQB2/ResourcesControl.xaml b/src/Blocktavius.AppDQB2/ResourcesControl.xaml new file mode 100644 index 0000000..ac82aec --- /dev/null +++ b/src/Blocktavius.AppDQB2/ResourcesControl.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + +