diff --git a/Ultima/AnimationEdit.cs b/Ultima/AnimationEdit.cs
index 86f0125c..3fe862cd 100644
--- a/Ultima/AnimationEdit.cs
+++ b/Ultima/AnimationEdit.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
@@ -313,6 +314,107 @@ public static void ExportToVD(int fileType, int body, string file)
}
}
+ ///
+ /// .vd header type of a file that holds one action (5 directions). Body files use 0, 1 and 2.
+ ///
+ public const short ActionVdType = 3;
+
+ ///
+ /// True when of has idx records in the given anim file.
+ ///
+ public static bool IsActionInRange(int fileType, int body, int action)
+ {
+ if (body < 0 || action < 0 || action >= Animations.GetActionCapacity(body, fileType))
+ {
+ return false;
+ }
+
+ AnimIdx[] cache = GetCache(fileType);
+ if (cache == null)
+ {
+ return false;
+ }
+
+ GetFileIndex(body, fileType, action, 4, out FileIndex _, out int index);
+
+ return index < cache.Length;
+ }
+
+ ///
+ /// Writes one action as a .vd file: the body .vd layout with 5 index entries, one per direction.
+ ///
+ public static void ExportActionToVD(int fileType, int body, int action, string file)
+ {
+ using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Write))
+ using (var bin = new BinaryWriter(fs))
+ {
+ bin.Write((short)6);
+ bin.Write(ActionVdType);
+ long indexPos = bin.BaseStream.Position;
+ long animPos = bin.BaseStream.Position + (12 * 5);
+ for (int dir = 0; dir < 5; ++dir)
+ {
+ GetAnimation(fileType, body, action, dir).ExportToVD(bin, ref indexPos, ref animPos);
+ }
+ }
+ }
+
+ ///
+ /// Reads a single-action .vd, positioned after its 4-byte header, into the given action.
+ /// Directions the file leaves empty are cleared. Nothing changes if the file can't be read.
+ ///
+ public static void LoadActionFromVD(int fileType, int body, int action, BinaryReader bin)
+ {
+ var entries = new Entry3D[5];
+ for (int dir = 0; dir < 5; ++dir)
+ {
+ entries[dir].Lookup = bin.ReadInt32();
+ entries[dir].Length = bin.ReadInt32();
+ entries[dir].Extra = bin.ReadInt32();
+ }
+
+ var loaded = new AnimIdx[5];
+ for (int dir = 0; dir < 5; ++dir)
+ {
+ Entry3D entry = entries[dir];
+ if ((entry.Lookup > 0) && (entry.Lookup < bin.BaseStream.Length) && (entry.Length > 0))
+ {
+ bin.BaseStream.Seek(entry.Lookup, SeekOrigin.Begin);
+ loaded[dir] = new AnimIdx(bin, entry.Extra);
+ }
+ }
+
+ for (int dir = 0; dir < 5; ++dir)
+ {
+ AnimIdx target = GetAnimation(fileType, body, action, dir);
+ if (loaded[dir] != null)
+ {
+ target.CopyFrom(loaded[dir]);
+ }
+ else
+ {
+ target.ClearFrames();
+ }
+ }
+ }
+
+ ///
+ /// Copies the frames and palettes of all 5 directions of an action to another action, unchanged.
+ ///
+ public static void CopyAction(int fileType, int sourceBody, int sourceAction, int targetBody, int targetAction)
+ {
+ if (sourceBody == targetBody && sourceAction == targetAction)
+ {
+ return;
+ }
+
+ for (int dir = 0; dir < 5; ++dir)
+ {
+ AnimIdx source = GetAnimation(fileType, sourceBody, sourceAction, dir);
+ GetAnimation(fileType, targetBody, targetAction, dir).CopyFrom(source);
+ }
+ }
+
public static void Save(int fileType, string path)
{
string filename;
@@ -392,7 +494,7 @@ public sealed class AnimIdx
{
public readonly int PaletteCapacity = 0x100;
- private readonly int _idxExtra;
+ private int _idxExtra;
public ushort[] Palette { get; private set; }
public List Frames { get; private set; }
@@ -563,6 +665,21 @@ public void ClearFrames()
Frames?.Clear();
}
+ ///
+ /// Makes this a copy of : palette, frames and idx extra. Nothing is shared.
+ ///
+ public void CopyFrom(AnimIdx source)
+ {
+ if (ReferenceEquals(source, this))
+ {
+ return;
+ }
+
+ Array.Copy(source.Palette, Palette, PaletteCapacity);
+ Frames = source.Frames?.ConvertAll(frame => new FrameEdit(frame));
+ _idxExtra = source._idxExtra;
+ }
+
public void ExportPalette(string filename, int type)
{
switch (type)
@@ -758,6 +875,25 @@ public FrameEdit(BinaryReader bin)
Center = new Point(xCenter, yCenter);
}
+ public FrameEdit(FrameEdit source)
+ {
+ Center = source.Center;
+ Width = source.Width;
+ Height = source.Height;
+
+ if (source.RawData == null)
+ {
+ return;
+ }
+
+ RawData = new Raw[source.RawData.Length];
+ for (int i = 0; i < RawData.Length; ++i)
+ {
+ RawData[i] = source.RawData[i];
+ RawData[i].data = (byte[])source.RawData[i].data.Clone();
+ }
+ }
+
public unsafe FrameEdit(Bitmap bit, ushort[] palette, int centerX, int centerY)
{
Center = new Point(centerX, centerY);
diff --git a/UoFiddler.Controls/Forms/AnimationCopyActionForm.Designer.cs b/UoFiddler.Controls/Forms/AnimationCopyActionForm.Designer.cs
new file mode 100644
index 00000000..f57c2c0c
--- /dev/null
+++ b/UoFiddler.Controls/Forms/AnimationCopyActionForm.Designer.cs
@@ -0,0 +1,159 @@
+/***************************************************************************
+ *
+ * $Author: Turley
+ *
+ * "THE BEER-WARE LICENSE"
+ * As long as you retain this notice you can do whatever you want with
+ * this stuff. If we meet some day, and you think this stuff is worth it,
+ * you can buy me a beer in return.
+ *
+ ***************************************************************************/
+
+namespace UoFiddler.Controls.Forms
+{
+ partial class AnimationCopyActionForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ SourceLabel = new System.Windows.Forms.Label();
+ BodyLabel = new System.Windows.Forms.Label();
+ BodyNumericUpDown = new System.Windows.Forms.NumericUpDown();
+ ActionLabel = new System.Windows.Forms.Label();
+ ActionNumericUpDown = new System.Windows.Forms.NumericUpDown();
+ CopyButton = new System.Windows.Forms.Button();
+ CloseButton = new System.Windows.Forms.Button();
+ ((System.ComponentModel.ISupportInitialize)BodyNumericUpDown).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)ActionNumericUpDown).BeginInit();
+ SuspendLayout();
+ //
+ // SourceLabel
+ //
+ SourceLabel.AutoSize = true;
+ SourceLabel.Location = new System.Drawing.Point(14, 10);
+ SourceLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ SourceLabel.Name = "SourceLabel";
+ SourceLabel.Size = new System.Drawing.Size(73, 15);
+ SourceLabel.TabIndex = 0;
+ SourceLabel.Text = "Copy action";
+ //
+ // BodyLabel
+ //
+ BodyLabel.AutoSize = true;
+ BodyLabel.Location = new System.Drawing.Point(14, 44);
+ BodyLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ BodyLabel.Name = "BodyLabel";
+ BodyLabel.Size = new System.Drawing.Size(34, 15);
+ BodyLabel.TabIndex = 1;
+ BodyLabel.Text = "Body";
+ //
+ // BodyNumericUpDown
+ //
+ BodyNumericUpDown.Location = new System.Drawing.Point(74, 42);
+ BodyNumericUpDown.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ BodyNumericUpDown.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
+ BodyNumericUpDown.Name = "BodyNumericUpDown";
+ BodyNumericUpDown.Size = new System.Drawing.Size(200, 23);
+ BodyNumericUpDown.TabIndex = 2;
+ //
+ // ActionLabel
+ //
+ ActionLabel.AutoSize = true;
+ ActionLabel.Location = new System.Drawing.Point(14, 76);
+ ActionLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ ActionLabel.Name = "ActionLabel";
+ ActionLabel.Size = new System.Drawing.Size(42, 15);
+ ActionLabel.TabIndex = 3;
+ ActionLabel.Text = "Action";
+ //
+ // ActionNumericUpDown
+ //
+ ActionNumericUpDown.Location = new System.Drawing.Point(74, 74);
+ ActionNumericUpDown.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ ActionNumericUpDown.Maximum = new decimal(new int[] { 34, 0, 0, 0 });
+ ActionNumericUpDown.Name = "ActionNumericUpDown";
+ ActionNumericUpDown.Size = new System.Drawing.Size(200, 23);
+ ActionNumericUpDown.TabIndex = 4;
+ //
+ // CopyButton
+ //
+ CopyButton.Location = new System.Drawing.Point(74, 110);
+ CopyButton.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ CopyButton.Name = "CopyButton";
+ CopyButton.Size = new System.Drawing.Size(96, 27);
+ CopyButton.TabIndex = 5;
+ CopyButton.Text = "Copy";
+ CopyButton.UseVisualStyleBackColor = true;
+ CopyButton.Click += OnClickCopy;
+ //
+ // CloseButton
+ //
+ CloseButton.Location = new System.Drawing.Point(178, 110);
+ CloseButton.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ CloseButton.Name = "CloseButton";
+ CloseButton.Size = new System.Drawing.Size(96, 27);
+ CloseButton.TabIndex = 6;
+ CloseButton.Text = "Cancel";
+ CloseButton.UseVisualStyleBackColor = true;
+ CloseButton.Click += OnClickCancel;
+ //
+ // AnimationCopyActionForm
+ //
+ AcceptButton = CopyButton;
+ AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ CancelButton = CloseButton;
+ ClientSize = new System.Drawing.Size(290, 150);
+ Controls.Add(CloseButton);
+ Controls.Add(CopyButton);
+ Controls.Add(ActionNumericUpDown);
+ Controls.Add(ActionLabel);
+ Controls.Add(BodyNumericUpDown);
+ Controls.Add(BodyLabel);
+ Controls.Add(SourceLabel);
+ DoubleBuffered = true;
+ FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
+ Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ Name = "AnimationCopyActionForm";
+ StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ Text = "Copy Action";
+ ((System.ComponentModel.ISupportInitialize)BodyNumericUpDown).EndInit();
+ ((System.ComponentModel.ISupportInitialize)ActionNumericUpDown).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label SourceLabel;
+ private System.Windows.Forms.Label BodyLabel;
+ private System.Windows.Forms.NumericUpDown BodyNumericUpDown;
+ private System.Windows.Forms.Label ActionLabel;
+ private System.Windows.Forms.NumericUpDown ActionNumericUpDown;
+ private System.Windows.Forms.Button CopyButton;
+ private System.Windows.Forms.Button CloseButton;
+ }
+}
diff --git a/UoFiddler.Controls/Forms/AnimationCopyActionForm.cs b/UoFiddler.Controls/Forms/AnimationCopyActionForm.cs
new file mode 100644
index 00000000..fb6d24b6
--- /dev/null
+++ b/UoFiddler.Controls/Forms/AnimationCopyActionForm.cs
@@ -0,0 +1,83 @@
+/***************************************************************************
+ *
+ * $Author: Turley
+ *
+ * "THE BEER-WARE LICENSE"
+ * As long as you retain this notice you can do whatever you want with
+ * this stuff. If we meet some day, and you think this stuff is worth it,
+ * you can buy me a beer in return.
+ *
+ ***************************************************************************/
+
+using System;
+using System.Windows.Forms;
+using Ultima;
+using UoFiddler.Controls.Classes;
+
+namespace UoFiddler.Controls.Forms
+{
+ ///
+ /// Picks the body and action to copy an action to. Closes with OK only for a valid target.
+ ///
+ public partial class AnimationCopyActionForm : Form
+ {
+ private readonly int _fileType;
+ private readonly int _sourceBody;
+ private readonly int _sourceAction;
+
+ public AnimationCopyActionForm(int fileType, int sourceBody, int sourceAction)
+ {
+ InitializeComponent();
+ Icon = Options.GetFiddlerIcon();
+
+ _fileType = fileType;
+ _sourceBody = sourceBody;
+ _sourceAction = sourceAction;
+
+ SourceLabel.Text = $"Copy body {sourceBody}, action {sourceAction} (all directions) to:";
+ BodyNumericUpDown.Maximum = Math.Max(0, Animations.GetAnimCount(fileType) - 1);
+ BodyNumericUpDown.Value = Math.Min(sourceBody, BodyNumericUpDown.Maximum);
+ ActionNumericUpDown.Value = sourceAction;
+ }
+
+ public int TargetBody => (int)BodyNumericUpDown.Value;
+
+ public int TargetAction => (int)ActionNumericUpDown.Value;
+
+ private void OnClickCopy(object sender, EventArgs e)
+ {
+ int body = TargetBody;
+ int action = TargetAction;
+
+ if (body == _sourceBody && action == _sourceAction)
+ {
+ MessageBox.Show("Choose a different body or action than the source.", "Copy Action",
+ MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ if (!AnimationEdit.IsActionInRange(_fileType, body, action))
+ {
+ MessageBox.Show($"Body {body} has no action {action} in this anim file.", "Copy Action",
+ MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ if (AnimationEdit.IsActionDefined(_fileType, body, action) &&
+ MessageBox.Show($"Action {action} of body {body} already has frames. Replace them?", "Copy Action",
+ MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
+ {
+ return;
+ }
+
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+
+ private void OnClickCancel(object sender, EventArgs e)
+ {
+ DialogResult = DialogResult.Cancel;
+ Close();
+ }
+ }
+}
diff --git a/UoFiddler.Controls/Forms/AnimationCopyActionForm.resx b/UoFiddler.Controls/Forms/AnimationCopyActionForm.resx
new file mode 100644
index 00000000..6dae11dd
--- /dev/null
+++ b/UoFiddler.Controls/Forms/AnimationCopyActionForm.resx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs b/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs
index b0a1642f..bab1b61d 100644
--- a/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs
+++ b/UoFiddler.Controls/Forms/AnimationEditForm.Designer.cs
@@ -33,7 +33,6 @@ private void InitializeComponent()
MainSplitContainer = new System.Windows.Forms.SplitContainer();
AnimationListTreeView = new System.Windows.Forms.TreeView();
ContextMenuStripTreeView = new System.Windows.Forms.ContextMenuStrip(components);
- addToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
extractImagesToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
asBmpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -42,6 +41,9 @@ private void InitializeComponent()
asPngToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
importToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
fromvdToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ actionFromVdToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ actionToVdToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ copyActionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
exportToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
tovdToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
AnimationFileToolStrip = new System.Windows.Forms.ToolStrip();
@@ -243,16 +245,10 @@ private void InitializeComponent()
// ContextMenuStripTreeView
//
ContextMenuStripTreeView.ImageScalingSize = new System.Drawing.Size(20, 20);
- ContextMenuStripTreeView.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { addToolStripMenuItem, removeToolStripMenuItem, extractImagesToolStripMenuItem1, importToolStripMenuItem1, exportToolStripMenuItem1 });
+ ContextMenuStripTreeView.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { removeToolStripMenuItem, copyActionToolStripMenuItem, extractImagesToolStripMenuItem1, importToolStripMenuItem1, exportToolStripMenuItem1 });
ContextMenuStripTreeView.Name = "contextMenuStrip2";
- ContextMenuStripTreeView.Size = new System.Drawing.Size(158, 114);
- //
- // addToolStripMenuItem
- //
- addToolStripMenuItem.Name = "addToolStripMenuItem";
- addToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
- addToolStripMenuItem.Text = "Replace";
- addToolStripMenuItem.Visible = false;
+ ContextMenuStripTreeView.Size = new System.Drawing.Size(158, 136);
+ ContextMenuStripTreeView.Opening += OnTreeViewContextMenuOpening;
//
// removeToolStripMenuItem
//
@@ -260,7 +256,15 @@ private void InitializeComponent()
removeToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
removeToolStripMenuItem.Text = "Remove";
removeToolStripMenuItem.Click += OnClickRemoveAction;
- //
+ //
+ // copyActionToolStripMenuItem
+ //
+ copyActionToolStripMenuItem.Name = "copyActionToolStripMenuItem";
+ copyActionToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
+ copyActionToolStripMenuItem.Text = "Copy Action To..";
+ copyActionToolStripMenuItem.ToolTipText = "Copy the selected action, all directions, to another body or action";
+ copyActionToolStripMenuItem.Click += OnClickCopyAction;
+ //
// extractImagesToolStripMenuItem1
//
extractImagesToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { asBmpToolStripMenuItem, asTiffToolStripMenuItem, asJpgToolStripMenuItem, asPngToolStripMenuItem });
@@ -302,7 +306,7 @@ private void InitializeComponent()
//
// importToolStripMenuItem1
//
- importToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { fromvdToolStripMenuItem });
+ importToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { fromvdToolStripMenuItem, actionFromVdToolStripMenuItem });
importToolStripMenuItem1.Name = "importToolStripMenuItem1";
importToolStripMenuItem1.Size = new System.Drawing.Size(157, 22);
importToolStripMenuItem1.Text = "Import..";
@@ -310,13 +314,21 @@ private void InitializeComponent()
// fromvdToolStripMenuItem
//
fromvdToolStripMenuItem.Name = "fromvdToolStripMenuItem";
- fromvdToolStripMenuItem.Size = new System.Drawing.Size(121, 22);
- fromvdToolStripMenuItem.Text = "From .vd";
+ fromvdToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
+ fromvdToolStripMenuItem.Text = "Body from .vd";
fromvdToolStripMenuItem.Click += OnClickImportFromVD;
+ //
+ // actionFromVdToolStripMenuItem
+ //
+ actionFromVdToolStripMenuItem.Name = "actionFromVdToolStripMenuItem";
+ actionFromVdToolStripMenuItem.Size = new System.Drawing.Size(165, 22);
+ actionFromVdToolStripMenuItem.Text = "Action from .vd";
+ actionFromVdToolStripMenuItem.ToolTipText = "Replace the selected action, all directions, with a single-action .vd";
+ actionFromVdToolStripMenuItem.Click += OnClickImportActionFromVD;
//
// exportToolStripMenuItem1
//
- exportToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { tovdToolStripMenuItem });
+ exportToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { tovdToolStripMenuItem, actionToVdToolStripMenuItem });
exportToolStripMenuItem1.Name = "exportToolStripMenuItem1";
exportToolStripMenuItem1.Size = new System.Drawing.Size(157, 22);
exportToolStripMenuItem1.Text = "Export..";
@@ -324,9 +336,17 @@ private void InitializeComponent()
// tovdToolStripMenuItem
//
tovdToolStripMenuItem.Name = "tovdToolStripMenuItem";
- tovdToolStripMenuItem.Size = new System.Drawing.Size(105, 22);
- tovdToolStripMenuItem.Text = "To .vd";
+ tovdToolStripMenuItem.Size = new System.Drawing.Size(149, 22);
+ tovdToolStripMenuItem.Text = "Body to .vd";
tovdToolStripMenuItem.Click += OnClickExportToVD;
+ //
+ // actionToVdToolStripMenuItem
+ //
+ actionToVdToolStripMenuItem.Name = "actionToVdToolStripMenuItem";
+ actionToVdToolStripMenuItem.Size = new System.Drawing.Size(149, 22);
+ actionToVdToolStripMenuItem.Text = "Action to .vd";
+ actionToVdToolStripMenuItem.ToolTipText = "Export the selected action, all directions, as a single-action .vd";
+ actionToVdToolStripMenuItem.Click += OnClickExportActionToVD;
//
// AnimationFileToolStrip
//
@@ -1659,7 +1679,6 @@ private void InitializeComponent()
#endregion
private System.Windows.Forms.ToolStripMenuItem addDirectionsAddWithCanvasUniqueImageToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem addToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem addToolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem addWithCanvasToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem allDirectionsAddToolStripMenuItem;
@@ -1681,6 +1700,9 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem fromImageToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fromTxtToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem fromvdToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem actionFromVdToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem actionToVdToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem copyActionToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem1;
private System.Windows.Forms.Label BackgroundRedLabel;
diff --git a/UoFiddler.Controls/Forms/AnimationEditForm.cs b/UoFiddler.Controls/Forms/AnimationEditForm.cs
index 89006bfc..44fdff17 100644
--- a/UoFiddler.Controls/Forms/AnimationEditForm.cs
+++ b/UoFiddler.Controls/Forms/AnimationEditForm.cs
@@ -1708,6 +1708,13 @@ private void OnClickImportFromVD(object sender, EventArgs e)
return;
}
+ if (animType == AnimationEdit.ActionVdType)
+ {
+ MessageBox.Show("This .vd holds a single action. Use Import.. > Action from .vd on an action instead.",
+ "Import", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
if (animType != currentType)
{
MessageBox.Show("Wrong Anim Id ( Type )", "Import", MessageBoxButtons.OK,
@@ -1759,6 +1766,158 @@ private void OnClickExportToVD(object sender, EventArgs e)
FileSavedDialog.Show(FindForm(), Options.OutputPath, "Animation saved successfully.");
}
+ private void OnTreeViewContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ bool actionSelected = _fileType != 0 && TryGetSelectedAction(out _, out _);
+ copyActionToolStripMenuItem.Enabled = actionSelected;
+ actionFromVdToolStripMenuItem.Enabled = actionSelected;
+ actionToVdToolStripMenuItem.Enabled = actionSelected;
+ }
+
+ private bool TryGetSelectedAction(out int body, out int action)
+ {
+ TreeNode node = AnimationListTreeView.SelectedNode;
+ if (node?.Parent == null)
+ {
+ body = -1;
+ action = -1;
+ return false;
+ }
+
+ body = (int)node.Parent.Tag;
+ action = (int)node.Tag;
+ return true;
+ }
+
+ private void OnClickExportActionToVD(object sender, EventArgs e)
+ {
+ if (_fileType == 0 || !TryGetSelectedAction(out int body, out int action))
+ {
+ return;
+ }
+
+ string fileName = Path.Combine(Options.OutputPath, $"anim{_fileType}_{Utils.FormatExportId(body)}_action{action}.vd");
+ AnimationEdit.ExportActionToVD(_fileType, body, action, fileName);
+
+ FileSavedDialog.Show(FindForm(), Options.OutputPath, "Action saved successfully.");
+ }
+
+ private void OnClickImportActionFromVD(object sender, EventArgs e)
+ {
+ if (_fileType == 0 || !TryGetSelectedAction(out int body, out int action))
+ {
+ return;
+ }
+
+ using (OpenFileDialog dialog = new OpenFileDialog())
+ {
+ dialog.Multiselect = false;
+ dialog.Title = $"Choose a single-action .vd to replace action {action} of body {body}";
+ dialog.CheckFileExists = true;
+ dialog.Filter = "vd files (*.vd)|*.vd";
+ if (dialog.ShowDialog() != DialogResult.OK)
+ {
+ return;
+ }
+
+ using (FileStream fs = new FileStream(dialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (BinaryReader bin = new BinaryReader(fs))
+ {
+ int fileType = bin.ReadInt16();
+ int animType = bin.ReadInt16();
+ if (fileType != 6)
+ {
+ MessageBox.Show("Not an Anim File.", "Import", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ if (animType != AnimationEdit.ActionVdType)
+ {
+ MessageBox.Show("This .vd holds a whole body. Use Import.. > Body from .vd on the body instead.",
+ "Import", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return;
+ }
+
+ if (AnimationEdit.IsActionDefined(_fileType, body, action) &&
+ MessageBox.Show($"Action {action} of body {body} already has frames. Replace them?", "Import",
+ MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
+ {
+ return;
+ }
+
+ try
+ {
+ AnimationEdit.LoadActionFromVD(_fileType, body, action, bin);
+ }
+ catch (Exception ex) when (ex is IOException || ex is ArgumentException || ex is OverflowException)
+ {
+ MessageBox.Show($"Could not read {Path.GetFileName(dialog.FileName)}: {ex.Message}", "Import",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+ }
+ }
+
+ UpdateBodyNode(body);
+ Options.ChangedUltimaClass["Animations"] = true;
+ AfterSelectTreeView(this, null);
+ }
+
+ private void OnClickCopyAction(object sender, EventArgs e)
+ {
+ if (_fileType == 0 || !TryGetSelectedAction(out int body, out int action))
+ {
+ return;
+ }
+
+ using (var form = new AnimationCopyActionForm(_fileType, body, action))
+ {
+ if (form.ShowDialog(this) != DialogResult.OK)
+ {
+ return;
+ }
+
+ AnimationEdit.CopyAction(_fileType, body, action, form.TargetBody, form.TargetAction);
+ UpdateBodyNode(form.TargetBody);
+ }
+
+ Options.ChangedUltimaClass["Animations"] = true;
+ AfterSelectTreeView(this, null);
+ }
+
+ ///
+ /// Recolours a body's tree nodes after its actions changed.
+ ///
+ private void UpdateBodyNode(int body)
+ {
+ TreeNode node = _showOnlyValid
+ ? AnimationListTreeView.Nodes.Cast().FirstOrDefault(n => (int)n.Tag == body)
+ : body < AnimationListTreeView.Nodes.Count ? AnimationListTreeView.Nodes[body] : null;
+ if (node == null)
+ {
+ return;
+ }
+
+ bool valid = false;
+ foreach (TreeNode child in node.Nodes)
+ {
+ if (AnimationEdit.IsActionDefined(_fileType, body, (int)child.Tag))
+ {
+ valid = true;
+ if (child.ForeColor == _invalidColor)
+ {
+ child.ForeColor = Color.Empty;
+ }
+ }
+ else
+ {
+ child.ForeColor = _invalidColor;
+ }
+ }
+
+ node.ForeColor = valid ? Color.Empty : _invalidColor;
+ }
+
private void OnClickShowOnlyValid(object sender, EventArgs e)
{
_showOnlyValid = !_showOnlyValid;