From 810691e2c027346aef2e633dd41de869476962f1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 12:40:04 +0000
Subject: [PATCH 01/36] Initial plan
From ca26b612205a6ca34e5bfae2a93d691301c7180b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 12:46:52 +0000
Subject: [PATCH 02/36] Add WayPointInstance class with auto-naming, property
form, and editor integration
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Command.cs | 5 +
.../ContextMenus/SectorContextMenu.cs | 5 +
.../SelectedGeometryContextMenu.cs | 5 +
TombEditor/EditorActions.cs | 7 +
TombEditor/EditorCommands.cs | 5 +
TombEditor/Forms/FormWayPoint.Designer.cs | 329 ++++++++++++++++++
TombEditor/Forms/FormWayPoint.cs | 54 +++
.../Rendering/ServiceObjectTextures.cs | 2 +
.../ServiceObjectTextures/waypoint.png | Bin 0 -> 2034 bytes
.../TombLib.Rendering.csproj | 1 +
TombLib/TombLib.Test/WayPointInstanceTests.cs | 139 ++++++++
.../LevelData/Instances/WayPointInstance.cs | 141 ++++++++
12 files changed, 693 insertions(+)
create mode 100644 TombEditor/Forms/FormWayPoint.Designer.cs
create mode 100644 TombEditor/Forms/FormWayPoint.cs
create mode 100644 TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures/waypoint.png
create mode 100644 TombLib/TombLib.Test/WayPointInstanceTests.cs
create mode 100644 TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs
index 55457428f..168de67a5 100644
--- a/TombEditor/Command.cs
+++ b/TombEditor/Command.cs
@@ -1152,6 +1152,11 @@ static CommandHandler()
args.Editor.Action = new EditorActionPlace(false, (l, r) => new FlybyCameraInstance(args.Editor.SelectedObject));
});
+ AddCommand("AddWayPoint", "Add waypoint", CommandType.Objects, delegate (CommandArgs args)
+ {
+ args.Editor.Action = new EditorActionPlace(false, (l, r) => new WayPointInstance(args.Editor.SelectedObject));
+ });
+
AddCommand("AddSink", "Add sink", CommandType.Objects, delegate (CommandArgs args)
{
args.Editor.Action = new EditorActionPlace(false, (l, r) => new SinkInstance());
diff --git a/TombEditor/Controls/ContextMenus/SectorContextMenu.cs b/TombEditor/Controls/ContextMenus/SectorContextMenu.cs
index 652b98726..edce1b34a 100644
--- a/TombEditor/Controls/ContextMenus/SectorContextMenu.cs
+++ b/TombEditor/Controls/ContextMenus/SectorContextMenu.cs
@@ -49,6 +49,11 @@ public SectorContextMenu(Editor editor, IWin32Window owner, Room targetRoom, Vec
EditorActions.PlaceObject(targetRoom, targetSector, new FlybyCameraInstance(editor.SelectedObject));
}));
+ Items.Add(new ToolStripMenuItem("Add waypoint", Properties.Resources.objects_movie_projector_16, (o, e) =>
+ {
+ EditorActions.PlaceObject(targetRoom, targetSector, new WayPointInstance(editor.SelectedObject));
+ }));
+
Items.Add(new ToolStripMenuItem("Add sink", Properties.Resources.objects_tornado_16, (o, e) =>
{
EditorActions.PlaceObject(targetRoom, targetSector, new SinkInstance());
diff --git a/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs b/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs
index abd8da4a9..0184aa30b 100644
--- a/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs
+++ b/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs
@@ -56,6 +56,11 @@ public SelectedGeometryContextMenu(Editor editor, IWin32Window owner, Room targe
EditorActions.PlaceObject(targetRoom, targetSector, new FlybyCameraInstance(editor.SelectedObject));
}));
+ Items.Add(new ToolStripMenuItem("Add waypoint", Properties.Resources.objects_movie_projector_16, (o, e) =>
+ {
+ EditorActions.PlaceObject(targetRoom, targetSector, new WayPointInstance(editor.SelectedObject));
+ }));
+
Items.Add(new ToolStripMenuItem("Add sink", Properties.Resources.objects_tornado_16, (o, e) =>
{
EditorActions.PlaceObject(targetRoom, targetSector, new SinkInstance());
diff --git a/TombEditor/EditorActions.cs b/TombEditor/EditorActions.cs
index b3ea287e3..7c54d769f 100644
--- a/TombEditor/EditorActions.cs
+++ b/TombEditor/EditorActions.cs
@@ -1129,6 +1129,13 @@ public static void EditObject(ObjectInstance instance, IWin32Window owner)
return;
_editor.ObjectChange(instance, ObjectChangeType.Change);
}
+ else if (instance is WayPointInstance)
+ {
+ using (var formWayPoint = GetObjectSetupWindow((WayPointInstance)instance))
+ if (formWayPoint.ShowDialog(owner) != DialogResult.OK)
+ return;
+ _editor.ObjectChange(instance, ObjectChangeType.Change);
+ }
else if (instance is CameraInstance)
{
using (var formCamera = GetObjectSetupWindow((CameraInstance)instance))
diff --git a/TombEditor/EditorCommands.cs b/TombEditor/EditorCommands.cs
index a2c624dc2..0fceca166 100644
--- a/TombEditor/EditorCommands.cs
+++ b/TombEditor/EditorCommands.cs
@@ -777,6 +777,11 @@ private void AddCommands()
_editor.Action = new EditorActionPlace(false, (l, r) => new FlybyCameraInstance());
});
+ AddCommand("AddWayPoint", "Add waypoint", CommandType.Objects, delegate ()
+ {
+ _editor.Action = new EditorActionPlace(false, (l, r) => new WayPointInstance());
+ });
+
AddCommand("AddSink", "Add sink", CommandType.Objects, delegate ()
{
_editor.Action = new EditorActionPlace(false, (l, r) => new SinkInstance());
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
new file mode 100644
index 000000000..d56b3624e
--- /dev/null
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -0,0 +1,329 @@
+using DarkUI.Controls;
+
+namespace TombEditor.Forms
+{
+ partial class FormWayPoint
+ {
+ ///
+ /// 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()
+ {
+ this.butCancel = new DarkUI.Controls.DarkButton();
+ this.butOK = new DarkUI.Controls.DarkButton();
+ this.lblName = new DarkUI.Controls.DarkLabel();
+ this.lblSequence = new DarkUI.Controls.DarkLabel();
+ this.lblNumber = new DarkUI.Controls.DarkLabel();
+ this.lblPathType = new DarkUI.Controls.DarkLabel();
+ this.lblRotationX = new DarkUI.Controls.DarkLabel();
+ this.lblRotationY = new DarkUI.Controls.DarkLabel();
+ this.lblRoll = new DarkUI.Controls.DarkLabel();
+ this.txtName = new DarkUI.Controls.DarkTextBox();
+ this.numSequence = new DarkUI.Controls.DarkNumericUpDown();
+ this.numNumber = new DarkUI.Controls.DarkNumericUpDown();
+ this.cmbPathType = new DarkUI.Controls.DarkComboBox();
+ this.numRotationX = new DarkUI.Controls.DarkNumericUpDown();
+ this.numRotationY = new DarkUI.Controls.DarkNumericUpDown();
+ this.numRoll = new DarkUI.Controls.DarkNumericUpDown();
+ ((System.ComponentModel.ISupportInitialize)(this.numSequence)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numNumber)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRotationX)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRotationY)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRoll)).BeginInit();
+ this.SuspendLayout();
+ //
+ // butCancel
+ //
+ this.butCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.butCancel.Checked = false;
+ this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.butCancel.Location = new System.Drawing.Point(239, 220);
+ this.butCancel.Name = "butCancel";
+ this.butCancel.Size = new System.Drawing.Size(80, 23);
+ this.butCancel.TabIndex = 8;
+ this.butCancel.Text = "Cancel";
+ this.butCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+ this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
+ //
+ // butOK
+ //
+ this.butOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.butOK.Checked = false;
+ this.butOK.Location = new System.Drawing.Point(153, 220);
+ this.butOK.Name = "butOK";
+ this.butOK.Size = new System.Drawing.Size(80, 23);
+ this.butOK.TabIndex = 7;
+ this.butOK.Text = "OK";
+ this.butOK.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+ this.butOK.Click += new System.EventHandler(this.butOK_Click);
+ //
+ // lblName
+ //
+ this.lblName.AutoSize = true;
+ this.lblName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblName.Location = new System.Drawing.Point(12, 15);
+ this.lblName.Name = "lblName";
+ this.lblName.Size = new System.Drawing.Size(39, 13);
+ this.lblName.TabIndex = 0;
+ this.lblName.Text = "Name:";
+ //
+ // lblSequence
+ //
+ this.lblSequence.AutoSize = true;
+ this.lblSequence.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblSequence.Location = new System.Drawing.Point(12, 41);
+ this.lblSequence.Name = "lblSequence";
+ this.lblSequence.Size = new System.Drawing.Size(60, 13);
+ this.lblSequence.TabIndex = 2;
+ this.lblSequence.Text = "Sequence:";
+ //
+ // lblNumber
+ //
+ this.lblNumber.AutoSize = true;
+ this.lblNumber.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblNumber.Location = new System.Drawing.Point(12, 67);
+ this.lblNumber.Name = "lblNumber";
+ this.lblNumber.Size = new System.Drawing.Size(51, 13);
+ this.lblNumber.TabIndex = 4;
+ this.lblNumber.Text = "Number:";
+ //
+ // lblPathType
+ //
+ this.lblPathType.AutoSize = true;
+ this.lblPathType.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblPathType.Location = new System.Drawing.Point(12, 93);
+ this.lblPathType.Name = "lblPathType";
+ this.lblPathType.Size = new System.Drawing.Size(59, 13);
+ this.lblPathType.TabIndex = 6;
+ this.lblPathType.Text = "Path Type:";
+ //
+ // lblRotationX
+ //
+ this.lblRotationX.AutoSize = true;
+ this.lblRotationX.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblRotationX.Location = new System.Drawing.Point(12, 119);
+ this.lblRotationX.Name = "lblRotationX";
+ this.lblRotationX.Size = new System.Drawing.Size(64, 13);
+ this.lblRotationX.TabIndex = 8;
+ this.lblRotationX.Text = "Rotation X:";
+ //
+ // lblRotationY
+ //
+ this.lblRotationY.AutoSize = true;
+ this.lblRotationY.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblRotationY.Location = new System.Drawing.Point(12, 145);
+ this.lblRotationY.Name = "lblRotationY";
+ this.lblRotationY.Size = new System.Drawing.Size(63, 13);
+ this.lblRotationY.TabIndex = 10;
+ this.lblRotationY.Text = "Rotation Y:";
+ //
+ // lblRoll
+ //
+ this.lblRoll.AutoSize = true;
+ this.lblRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblRoll.Location = new System.Drawing.Point(12, 171);
+ this.lblRoll.Name = "lblRoll";
+ this.lblRoll.Size = new System.Drawing.Size(30, 13);
+ this.lblRoll.TabIndex = 12;
+ this.lblRoll.Text = "Roll:";
+ //
+ // txtName
+ //
+ this.txtName.Location = new System.Drawing.Point(82, 12);
+ this.txtName.Name = "txtName";
+ this.txtName.Size = new System.Drawing.Size(237, 20);
+ this.txtName.TabIndex = 1;
+ //
+ // numSequence
+ //
+ this.numSequence.IncrementAlternate = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 65536});
+ this.numSequence.Location = new System.Drawing.Point(82, 38);
+ this.numSequence.LoopValues = false;
+ this.numSequence.Maximum = new decimal(new int[] {
+ 65535,
+ 0,
+ 0,
+ 0});
+ this.numSequence.Name = "numSequence";
+ this.numSequence.Size = new System.Drawing.Size(237, 22);
+ this.numSequence.TabIndex = 3;
+ //
+ // numNumber
+ //
+ this.numNumber.IncrementAlternate = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 65536});
+ this.numNumber.Location = new System.Drawing.Point(82, 64);
+ this.numNumber.LoopValues = false;
+ this.numNumber.Maximum = new decimal(new int[] {
+ 65535,
+ 0,
+ 0,
+ 0});
+ this.numNumber.Name = "numNumber";
+ this.numNumber.Size = new System.Drawing.Size(237, 22);
+ this.numNumber.TabIndex = 5;
+ //
+ // cmbPathType
+ //
+ this.cmbPathType.FormattingEnabled = true;
+ this.cmbPathType.Items.AddRange(new object[] {
+ "Linear",
+ "Curved",
+ "Bezier"});
+ this.cmbPathType.Location = new System.Drawing.Point(82, 90);
+ this.cmbPathType.Name = "cmbPathType";
+ this.cmbPathType.Size = new System.Drawing.Size(237, 23);
+ this.cmbPathType.TabIndex = 7;
+ //
+ // numRotationX
+ //
+ this.numRotationX.DecimalPlaces = 2;
+ this.numRotationX.IncrementAlternate = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 65536});
+ this.numRotationX.Location = new System.Drawing.Point(82, 116);
+ this.numRotationX.LoopValues = false;
+ this.numRotationX.Maximum = new decimal(new int[] {
+ 90,
+ 0,
+ 0,
+ 0});
+ this.numRotationX.Minimum = new decimal(new int[] {
+ 90,
+ 0,
+ 0,
+ -2147483648});
+ this.numRotationX.Name = "numRotationX";
+ this.numRotationX.Size = new System.Drawing.Size(237, 22);
+ this.numRotationX.TabIndex = 9;
+ //
+ // numRotationY
+ //
+ this.numRotationY.DecimalPlaces = 2;
+ this.numRotationY.IncrementAlternate = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 65536});
+ this.numRotationY.Location = new System.Drawing.Point(82, 142);
+ this.numRotationY.LoopValues = false;
+ this.numRotationY.Maximum = new decimal(new int[] {
+ 360,
+ 0,
+ 0,
+ 0});
+ this.numRotationY.Name = "numRotationY";
+ this.numRotationY.Size = new System.Drawing.Size(237, 22);
+ this.numRotationY.TabIndex = 11;
+ //
+ // numRoll
+ //
+ this.numRoll.DecimalPlaces = 2;
+ this.numRoll.IncrementAlternate = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 65536});
+ this.numRoll.Location = new System.Drawing.Point(82, 168);
+ this.numRoll.LoopValues = false;
+ this.numRoll.Maximum = new decimal(new int[] {
+ 360,
+ 0,
+ 0,
+ 0});
+ this.numRoll.Name = "numRoll";
+ this.numRoll.Size = new System.Drawing.Size(237, 22);
+ this.numRoll.TabIndex = 13;
+ //
+ // FormWayPoint
+ //
+ this.AcceptButton = this.butOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.butCancel;
+ this.ClientSize = new System.Drawing.Size(331, 255);
+ this.Controls.Add(this.numRoll);
+ this.Controls.Add(this.numRotationY);
+ this.Controls.Add(this.numRotationX);
+ this.Controls.Add(this.cmbPathType);
+ this.Controls.Add(this.numNumber);
+ this.Controls.Add(this.numSequence);
+ this.Controls.Add(this.txtName);
+ this.Controls.Add(this.lblRoll);
+ this.Controls.Add(this.lblRotationY);
+ this.Controls.Add(this.lblRotationX);
+ this.Controls.Add(this.lblPathType);
+ this.Controls.Add(this.lblNumber);
+ this.Controls.Add(this.lblSequence);
+ this.Controls.Add(this.lblName);
+ this.Controls.Add(this.butCancel);
+ this.Controls.Add(this.butOK);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "FormWayPoint";
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "WayPoint";
+ this.Load += new System.EventHandler(this.FormWayPoint_Load);
+ ((System.ComponentModel.ISupportInitialize)(this.numSequence)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numNumber)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRotationX)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRotationY)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRoll)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private DarkButton butOK;
+ private DarkButton butCancel;
+ private DarkLabel lblName;
+ private DarkLabel lblSequence;
+ private DarkLabel lblNumber;
+ private DarkLabel lblPathType;
+ private DarkLabel lblRotationX;
+ private DarkLabel lblRotationY;
+ private DarkLabel lblRoll;
+ private DarkTextBox txtName;
+ private DarkNumericUpDown numSequence;
+ private DarkNumericUpDown numNumber;
+ private DarkComboBox cmbPathType;
+ private DarkNumericUpDown numRotationX;
+ private DarkNumericUpDown numRotationY;
+ private DarkNumericUpDown numRoll;
+ }
+}
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
new file mode 100644
index 000000000..a2bc65dde
--- /dev/null
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Windows.Forms;
+using DarkUI.Forms;
+using TombLib.LevelData;
+
+namespace TombEditor.Forms
+{
+ public partial class FormWayPoint : DarkForm
+ {
+ public bool IsNew { get; set; }
+
+ private readonly WayPointInstance _wayPoint;
+ private readonly Editor _editor;
+
+ public FormWayPoint(WayPointInstance wayPoint)
+ {
+ _wayPoint = wayPoint;
+ _editor = Editor.Instance;
+
+ InitializeComponent();
+ }
+
+ private void butCancel_Click(object sender, EventArgs e)
+ {
+ DialogResult = DialogResult.Cancel;
+ Close();
+ }
+
+ private void FormWayPoint_Load(object sender, EventArgs e)
+ {
+ txtName.Text = _wayPoint.Name;
+ numSequence.Value = _wayPoint.Sequence;
+ numNumber.Value = _wayPoint.Number;
+ cmbPathType.SelectedIndex = (int)_wayPoint.PathType;
+ numRotationX.Value = (decimal)_wayPoint.RotationX;
+ numRotationY.Value = (decimal)_wayPoint.RotationY;
+ numRoll.Value = (decimal)_wayPoint.Roll;
+ }
+
+ private void butOK_Click(object sender, EventArgs e)
+ {
+ _wayPoint.Name = txtName.Text;
+ _wayPoint.Sequence = (ushort)numSequence.Value;
+ _wayPoint.Number = (ushort)numNumber.Value;
+ _wayPoint.PathType = (PathType)cmbPathType.SelectedIndex;
+ _wayPoint.RotationX = (float)numRotationX.Value;
+ _wayPoint.RotationY = (float)numRotationY.Value;
+ _wayPoint.Roll = (float)numRoll.Value;
+
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+ }
+}
diff --git a/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures.cs b/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures.cs
index a439c0665..c8829746e 100644
--- a/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures.cs
+++ b/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures.cs
@@ -14,6 +14,7 @@ public enum ServiceObjectTexture
{
camera,
flyby_camera,
+ waypoint,
imp_geo,
sink,
sound_source,
@@ -134,6 +135,7 @@ public static ServiceObjectTexture GetType(ISpatial instance)
else if (instance is VolumeInstance) type = ServiceObjectTexture.volume;
else if (instance is GhostBlockInstance) type = ServiceObjectTexture.ghost_block;
else if (instance is FlybyCameraInstance) type = ServiceObjectTexture.flyby_camera;
+ else if (instance is WayPointInstance) type = ServiceObjectTexture.waypoint;
else if (instance is SoundSourceInstance) type = ServiceObjectTexture.sound_source;
else if (instance is ImportedGeometryInstance) type = ServiceObjectTexture.imp_geo;
else type = ServiceObjectTexture.unknown;
diff --git a/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures/waypoint.png b/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures/waypoint.png
new file mode 100644
index 0000000000000000000000000000000000000000..84e9fb1e0a0e2d11f061b5d0550f12ca3dfcf964
GIT binary patch
literal 2034
zcmV_bktLya)kRPjUV9-y-js+Jp$4TiYUj~RqL{KsqmT}xW|}jOGt8*7pZDVX+OOMF
zBX3)sas6Sz-fOSZ{<+q(*Lv0(B}4=!aplJlFvtut6NAhkuO-PAdLWTV*tBDbA|eUv
z$;@0Z$V_6`Y#YM;I6wz|_#=w5
zUOw&1moJf>osAzqe(-ToQBgR0^eAFtVxUr~;BYwb_U&6VH#g(UmoI#}(9lq{w6q{0
zAwg6tFxv~a(9X!R_2lmEZaQ(|1Z~{7ao*t`5fMS>&z~oY#X@$woe~lf1kcZ$IYTCs
zY2LC;CKDAG7YowI#Kh3Z$jI6;7x(Vnqs^N)FKSbNhr@>tQ&CZoAg-dK!lQc1%E|=!
zu3fuE9LM=SU2y*=+W0$iu_Kl$e;vm*MB$n)Z5$3r*S%+)Y;id`T6;C8m&|+sj;zfNp&_h
zHj+}QBmg9pN~x)-$+scjx^+tc`0CZG3#PSLEL2!nD5x_wHkO8lhL%+4;NT#|#>Vnx
zR904sW->g5Eb5By{{8#>wN9r)d3pJQY1L{qDk>@v6&1zDg@uJ7BqU@>b%ut9A}lP7
zzqi?JaJ${UyR3fy{yl%K*XyBDsTNJYYu7F$Cnxjw9LFtr>NsO0^3yC&H$1%(^4Qqe
z|JLp@83F?X|GdVR$q*hM&fgCX4#I3UFR4zm*^GgK0sda2(LgGd`Yz<(egux=V7J>n
z%44xu1S8?$!-q?%^TC4${0LGg6oLhgPx>y7967@0&&kR8;|J*b_wN)P9WAIgF)@*B
zHk(KF*=#o2w{IVx_u#>UG&(x!+p~-7*RS*W6$%AaS645ZzO=M--nnu?LBStaZzCfk
zR8UYL$amw$4c}k!O-xKsT3Q-khFYzr>gsCx`SYhB-KS5V=B(CchxGJxG8harF)=Zx
z4uio!dc9td{?esOtHwbjsz2FkwF;(SR;v{yB_(KRXh3ReDgpumFf=rTwzf9(^zO6h7GXUY%rNjXl-r9e{bHhhTnC{<#MX4tMh0vVzF4Lrly86Gc#$=o;}`UF4SuEvICwSE?l@k
zQ&Uq5w(;G&cT`?pPVw>aynkAyQhATLShsE+tX3-|5(y$BBe7-67QwLo+mcG9;5ZI_
zeSNT6t^71@_JYQ2Hsi^YC#bEh#fJ|c@a@|-!E=p9<2`$Petv%Zi$;
zYNoHEzrP1qDD
zsi_I|_4R_d-zH(_&Yd`U@+3~5K8=Qk20VZMeC-|y_%E86m=O3zA|oS_nwpA&f&wHb
zCnF#r007`{I7BmLJ`WiHf`fyxd-rbS=H?P2RnVFd@3V2OGw%hG2yd;OoUAK@9T@{
zf?X57i{I+*?nZZaHv$6#k(HH&^z?LOWo02aI2a)zA-wadAja
zPKHvcIusQZAulfvw{PFZ=g*%-gS;#~SuU3g4grd-;NW2X`q;5!C@CpH
zPfrhOYHINK@nZ}R5A)BBMk9vya2yAh%O&1a$`sHhb`5^(wQ
zWtyIzUa*ZWmy2G#dPP-LRg{#JL;y&m(Rk01fQuI|qN=J24u@meXA&-#3vqFA$j{GT
z^aPPYp@3Gag;uM@@#DwQ)zyU;FJ5?$x$t#)YA%?Ai1{uUWF`igL1vJd7-R;Si9u$N
z8Du5~nL%cdnZ%aI{^XVZpAoDlGcm{v@*0qYZ7Z>!%pfx{$P6-r%=B0F55A`$8008h
Q1ONa407*qoM6N<$f}*wGVE_OC
literal 0
HcmV?d00001
diff --git a/TombLib/TombLib.Rendering/TombLib.Rendering.csproj b/TombLib/TombLib.Rendering/TombLib.Rendering.csproj
index eaaed83bd..d3631a1a8 100644
--- a/TombLib/TombLib.Rendering/TombLib.Rendering.csproj
+++ b/TombLib/TombLib.Rendering/TombLib.Rendering.csproj
@@ -151,6 +151,7 @@
Always
+
diff --git a/TombLib/TombLib.Test/WayPointInstanceTests.cs b/TombLib/TombLib.Test/WayPointInstanceTests.cs
new file mode 100644
index 000000000..895945d86
--- /dev/null
+++ b/TombLib/TombLib.Test/WayPointInstanceTests.cs
@@ -0,0 +1,139 @@
+using TombLib.LevelData;
+
+namespace TombLib.Test
+{
+ [TestClass]
+ public class WayPointInstanceTests
+ {
+ [TestMethod]
+ public void WayPoint_AutoNaming_DefaultName()
+ {
+ // Arrange & Act
+ var wayPoint = new WayPointInstance();
+
+ // Assert
+ Assert.AreEqual("WayPoint_0", wayPoint.Name, "Default WayPoint name should be 'WayPoint_0'");
+ }
+
+ [TestMethod]
+ public void WayPoint_AutoNaming_CustomBaseName()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+
+ // Act
+ wayPoint.Name = "Camera";
+ wayPoint.Number = 0;
+
+ // Assert
+ Assert.AreEqual("Camera_0", wayPoint.Name, "Custom name should be 'Camera_0'");
+ }
+
+ [TestMethod]
+ public void WayPoint_AutoNaming_SequenceChange()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+ wayPoint.Name = "MyPath";
+
+ // Act
+ wayPoint.Number = 5;
+
+ // Assert
+ Assert.AreEqual("MyPath_5", wayPoint.Name, "Name should update to 'MyPath_5' when number changes");
+ }
+
+ [TestMethod]
+ public void WayPoint_AutoNaming_NumberChange()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+ wayPoint.Name = "Camera";
+ wayPoint.Number = 3;
+
+ // Act
+ wayPoint.Number = 7;
+
+ // Assert
+ Assert.AreEqual("Camera_7", wayPoint.Name, "Name should update to 'Camera_7' when number changes to 7");
+ }
+
+ [TestMethod]
+ public void WayPoint_AutoNaming_PreservesBaseName()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+ wayPoint.Name = "PatrolPath";
+ wayPoint.Number = 0;
+
+ // Act
+ wayPoint.Number = 10;
+
+ // Assert
+ Assert.AreEqual("PatrolPath_10", wayPoint.Name, "Base name 'PatrolPath' should be preserved");
+ }
+
+ [TestMethod]
+ public void WayPoint_DefaultPathType()
+ {
+ // Arrange & Act
+ var wayPoint = new WayPointInstance();
+
+ // Assert
+ Assert.AreEqual(PathType.Linear, wayPoint.PathType, "Default PathType should be Linear");
+ }
+
+ [TestMethod]
+ public void WayPoint_PathTypeCanBeSet()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+
+ // Act
+ wayPoint.PathType = PathType.Bezier;
+
+ // Assert
+ Assert.AreEqual(PathType.Bezier, wayPoint.PathType, "PathType should be settable to Bezier");
+ }
+
+ [TestMethod]
+ public void WayPoint_RotationXClamping()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+
+ // Act & Assert
+ wayPoint.RotationX = 100;
+ Assert.AreEqual(90, wayPoint.RotationX, "RotationX should be clamped to 90");
+
+ wayPoint.RotationX = -100;
+ Assert.AreEqual(-90, wayPoint.RotationX, "RotationX should be clamped to -90");
+ }
+
+ [TestMethod]
+ public void WayPoint_RotationYWrapping()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+
+ // Act
+ wayPoint.RotationY = 400;
+
+ // Assert
+ Assert.AreEqual(40, wayPoint.RotationY, 0.01, "RotationY should wrap around 360 degrees");
+ }
+
+ [TestMethod]
+ public void WayPoint_RollWrapping()
+ {
+ // Arrange
+ var wayPoint = new WayPointInstance();
+
+ // Act
+ wayPoint.Roll = 720;
+
+ // Assert
+ Assert.AreEqual(0, wayPoint.Roll, 0.01, "Roll should wrap around 360 degrees");
+ }
+ }
+}
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
new file mode 100644
index 000000000..fca8984b4
--- /dev/null
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Linq;
+
+namespace TombLib.LevelData
+{
+ public enum PathType
+ {
+ Linear,
+ Curved,
+ Bezier
+ }
+
+ public class WayPointInstance : PositionAndScriptBasedObjectInstance, IRotateableYXRoll
+ {
+ private string _baseName = "WayPoint";
+ private ushort _sequence;
+ private ushort _number;
+
+ public string Name
+ {
+ get { return _baseName + "_" + _number; }
+ set
+ {
+ // When setting name, extract the base name (strip trailing _number if present)
+ if (!string.IsNullOrEmpty(value))
+ {
+ int lastUnderscore = value.LastIndexOf('_');
+ if (lastUnderscore >= 0)
+ {
+ string suffix = value.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ _baseName = value.Substring(0, lastUnderscore);
+ }
+ else
+ {
+ _baseName = value;
+ }
+ }
+ else
+ {
+ _baseName = value;
+ }
+ }
+ else
+ {
+ _baseName = "WayPoint";
+ }
+ }
+ }
+
+ public ushort Sequence
+ {
+ get { return _sequence; }
+ set { _sequence = value; }
+ }
+
+ public ushort Number
+ {
+ get { return _number; }
+ set { _number = value; }
+ }
+
+ public PathType PathType { get; set; } = PathType.Linear;
+
+ private float _rotationX { get; set; }
+ private float _rotationY { get; set; }
+ private float _roll { get; set; }
+
+ public WayPointInstance(ObjectInstance selectedObject = null)
+ {
+ if (selectedObject != null && selectedObject is WayPointInstance)
+ {
+ var prevWayPoint = (WayPointInstance)selectedObject;
+ var currSeq = prevWayPoint.Sequence;
+ var currNum = (ushort)(prevWayPoint.Number + 1);
+
+ // Push next waypoints in sequence forward
+ var level = selectedObject.Room.Level;
+ foreach (var room in level.ExistingRooms)
+ foreach (var instance in room.Objects.OfType())
+ if (instance.Sequence == currSeq && instance.Number >= currNum)
+ instance.Number++;
+
+ Sequence = currSeq;
+ Number = currNum;
+ _baseName = prevWayPoint._baseName;
+ PathType = prevWayPoint.PathType;
+
+ // Additionally copy last waypoint parameters
+ RotationX = prevWayPoint.RotationX;
+ RotationY = prevWayPoint.RotationY;
+ Roll = prevWayPoint.Roll;
+ }
+ }
+
+ /// Degrees in the range [-90, 90]
+ public float RotationX
+ {
+ get { return _rotationX; }
+ set { _rotationX = Math.Max(-90, Math.Min(90, value)); }
+ }
+
+ /// Degrees in the range [0, 360)
+ public float RotationY
+ {
+ get { return _rotationY; }
+ set { _rotationY = (float)(value - Math.Floor(value / 360.0) * 360.0); }
+ }
+
+ /// Degrees in the range [0, 360)
+ public float Roll
+ {
+ get { return _roll; }
+ set { _roll = (float)(value - Math.Floor(value / 360.0) * 360.0); }
+ }
+
+ public override bool CopyToAlternateRooms => false;
+
+ public override string ToString()
+ {
+ return "WayPoint " +
+ ", Name = " + Name +
+ ", Sequence = " + Sequence +
+ ", Number = " + Number +
+ ", PathType = " + PathType +
+ " (" + (Room?.ToString() ?? "NULL") + ")" +
+ ", X = " + SectorPosition.X +
+ ", Z = " + SectorPosition.Y +
+ GetScriptIDOrName(false);
+ }
+
+ public string ShortName() => "WayPoint (" + Sequence + ":" + Number + ")" + GetScriptIDOrName() + " (" + (Room?.ToString() ?? "NULL") + ")";
+
+ public override void CopyDependentLevelSettings(Room.CopyDependentLevelSettingsArgs args)
+ {
+ base.CopyDependentLevelSettings(args);
+ Sequence = args.ReassociateFlyBySequence(Sequence);
+ }
+ }
+}
From 2bdb2c921062b9ce58fdd1a16fb703646aa640a4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 12:48:28 +0000
Subject: [PATCH 03/36] Add WayPoint serialization support for save/load
functionality
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombLib/TombLib/LevelData/IO/Prj2Chunks.cs | 1 +
TombLib/TombLib/LevelData/IO/Prj2Loader.cs | 14 ++++++++++++++
TombLib/TombLib/LevelData/IO/Prj2Writer.cs | 15 +++++++++++++++
3 files changed, 30 insertions(+)
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Chunks.cs b/TombLib/TombLib/LevelData/IO/Prj2Chunks.cs
index ba995a2cc..af88f211a 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Chunks.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Chunks.cs
@@ -190,6 +190,7 @@ internal static class Prj2Chunks
/**********/public static readonly ChunkId ObjectFlyBy = ChunkId.FromString("TeFly");
/**********/public static readonly ChunkId ObjectFlyBy2 = ChunkId.FromString("TeFly2");
/**********/public static readonly ChunkId ObjectFlyBy2LuaScript = ChunkId.FromString("TeFly2Lua");
+ /**********/public static readonly ChunkId ObjectWayPoint = ChunkId.FromString("TeWayPt");
/**********/public static readonly ChunkId ObjectMemo = ChunkId.FromString("TeMemo");
/**********/public static readonly ChunkId ObjectMemo2 = ChunkId.FromString("TeMemo2");
/**********/public static readonly ChunkId ObjectSink = ChunkId.FromString("TeSin");
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
index 0613f0545..f6096319a 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
@@ -1402,6 +1402,20 @@ private static bool LoadObjects(ChunkReader chunkIO, ChunkId idOuter, LevelSetti
addObject(instance);
newObjects.TryAdd(objectID, instance);
}
+ else if (id3 == Prj2Chunks.ObjectWayPoint)
+ {
+ var instance = new WayPointInstance();
+ instance.Position = chunkIO.Raw.ReadVector3();
+ instance.SetArbitaryRotationsYX(chunkIO.Raw.ReadSingle(), chunkIO.Raw.ReadSingle());
+ instance.Roll = chunkIO.Raw.ReadSingle();
+ instance.ScriptId = ReadOptionalLEB128Int(chunkIO.Raw);
+ instance.Name = chunkIO.Raw.ReadStringUTF8();
+ instance.Number = LEB128.ReadUShort(chunkIO.Raw);
+ instance.Sequence = LEB128.ReadUShort(chunkIO.Raw);
+ instance.PathType = (PathType)LEB128.ReadInt(chunkIO.Raw);
+ addObject(instance);
+ newObjects.TryAdd(objectID, instance);
+ }
else if (id3 == Prj2Chunks.ObjectFlyBy2) // Obsolete; LuaScript is unused with new script concept.
{
var instance = new FlybyCameraInstance();
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
index 01ab4e49c..9bc23f11c 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
@@ -679,6 +679,21 @@ private static void WriteObjects(ChunkWriter chunkIO, IEnumerable
+ {
+ var instance = (WayPointInstance)o;
+ LEB128.Write(chunkIO.Raw, objectInstanceLookup.TryGetOrDefault(instance, -1));
+ chunkIO.Raw.Write(instance.Position);
+ chunkIO.Raw.Write(instance.RotationY);
+ chunkIO.Raw.Write(instance.RotationX);
+ chunkIO.Raw.Write(instance.Roll);
+ LEB128.Write(chunkIO.Raw, ((long?)instance.ScriptId ?? -1));
+ chunkIO.Raw.WriteStringUTF8(instance.Name);
+ LEB128.Write(chunkIO.Raw, instance.Number);
+ LEB128.Write(chunkIO.Raw, instance.Sequence);
+ LEB128.Write(chunkIO.Raw, (int)instance.PathType);
+ });
else if (o is MemoInstance)
using (var chunk = chunkIO.WriteChunk(Prj2Chunks.ObjectMemo2, LEB128.MaximumSize3Byte))
{
From 4fe8bc69d9e5f0dc208475a7baecf9617b96ff54 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 12:50:38 +0000
Subject: [PATCH 04/36] Add WayPoint rendering support with path visualization
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3D.cs | 2 +
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 45 ++++++++++
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 84 +++++++++++++++++++
3 files changed, 131 insertions(+)
diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs
index 61eca6e9f..11d0f60dd 100644
--- a/TombEditor/Controls/Panel3D/Panel3D.cs
+++ b/TombEditor/Controls/Panel3D/Panel3D.cs
@@ -134,6 +134,7 @@ public bool DisablePickingForHiddenRooms
private bool _drawHeightLine;
private Buffer _objectHeightLineVertexBuffer;
private Buffer _flybyPathVertexBuffer;
+ private Buffer _wayPointPathVertexBuffer;
private Buffer _ghostBlockVertexBuffer;
private Buffer _boxVertexBuffer;
@@ -217,6 +218,7 @@ protected override void Dispose(bool disposing)
_rasterizerWireframe?.Dispose();
_objectHeightLineVertexBuffer?.Dispose();
_flybyPathVertexBuffer?.Dispose();
+ _wayPointPathVertexBuffer?.Dispose();
_gizmo?.Dispose();
_sphere?.Dispose();
_cone?.Dispose();
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index a29d32b1d..7c86503ce 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -196,6 +196,19 @@ private void DrawFlybyPath(Effect effect)
effect.CurrentTechnique.Passes[0].Apply();
_legacyDevice.Draw(PrimitiveType.TriangleList, _flybyPathVertexBuffer.ElementCount);
}
+
+ // Add the path of waypoints
+ if (_editor.SelectedObject is WayPointInstance &&
+ AddWayPointPath(((WayPointInstance)_editor.SelectedObject).Sequence))
+ {
+ _legacyDevice.SetRasterizerState(_legacyDevice.RasterizerStates.CullNone);
+ _legacyDevice.SetVertexBuffer(_wayPointPathVertexBuffer);
+ _legacyDevice.SetVertexInputLayout(VertexInputLayout.FromBuffer(0, _wayPointPathVertexBuffer));
+ effect.Parameters["ModelViewProjection"].SetValue(_viewProjection.ToSharpDX());
+ effect.Parameters["Color"].SetValue(new Vector4(1.0f, 0.5f, 0.0f, 1.0f)); // Orange for waypoints
+ effect.CurrentTechnique.Passes[0].Apply();
+ _legacyDevice.Draw(PrimitiveType.TriangleList, _wayPointPathVertexBuffer.ElementCount);
+ }
}
private void DrawSectorSplitHighlights(Effect effect)
@@ -1138,6 +1151,38 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
DrawOrQueueServiceObject(instance, _littleCube, color, effect, sprites);
}
+ if (group.Key == typeof(WayPointInstance))
+ foreach (WayPointInstance instance in group)
+ {
+ _legacyDevice.SetRasterizerState(_legacyDevice.RasterizerStates.CullBack);
+
+ var color = new Vector4(1.0f, 0.5f, 0.0f, 1.0f); // Orange color for waypoints
+
+ if (_editor.SelectedObject is WayPointInstance && (_editor.SelectedObject as WayPointInstance).Sequence == instance.Sequence)
+ color = MathC.GetRandomColorByIndex(instance.Sequence, 32, 0.7f);
+
+ if (_highlightedObjects.Contains(instance))
+ {
+ color = _editor.Configuration.UI_ColorScheme.ColorSelection;
+ _legacyDevice.SetRasterizerState(_rasterizerWireframe);
+
+ if (_editor.SelectedObject == instance)
+ {
+ // Add text message
+ textToDraw.Add(CreateTextTagForObject(
+ instance.RotationPositionMatrix * _viewProjection,
+ "WayPoint (" + instance.Sequence + ":" + instance.Number + ") " +
+ instance.GetScriptIDOrName() + "\n" +
+ GetObjectPositionString(instance.Room, instance) + GetObjectTriggerString(instance)));
+
+ // Add the line height of the object
+ AddObjectHeightLine(instance.Room, instance.Position);
+ }
+ }
+
+ DrawOrQueueServiceObject(instance, _littleCube, color, effect, sprites);
+ }
+
if (group.Key == typeof(MemoInstance))
foreach (MemoInstance instance in group)
{
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index 4f61f9d47..91610f911 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -183,6 +183,90 @@ private bool AddFlybyPath(int sequence)
return true;
}
+ private bool AddWayPointPath(int sequence)
+ {
+ // Collect all waypoints
+ var wayPoints = new List();
+
+ foreach (var room in _editor.Level.ExistingRooms)
+ foreach (var instance in room.Objects.OfType())
+ {
+ if (instance.Sequence == sequence)
+ wayPoints.Add(instance);
+ }
+
+ // Is it actually necessary to show the path?
+ if (wayPoints.Count < 2)
+ return false;
+
+ // Sort waypoints
+ wayPoints.Sort((x, y) => x.Number.CompareTo(y.Number));
+
+ // Initialize variables for vertex buffer preparation
+ var vertices = new List();
+ var startColor = MathC.GetRandomColorByIndex(sequence, 32, 0.7f);
+ var endColor = MathC.GetRandomColorByIndex(sequence, 32, 0.3f);
+
+ float th = _flybyPathThickness;
+
+ // Process waypoints to calculate paths
+ var pointList = new List();
+ for (int i = 0; i < wayPoints.Count; i++)
+ {
+ var wp = wayPoints[i];
+ pointList.Add(wp.Position + wp.Room.WorldPos);
+ }
+
+ // Calculate the spline path based on PathType
+ List interpolatedPoints;
+ if (wayPoints[0].PathType == PathType.Linear)
+ {
+ // For linear, just use the points as-is
+ interpolatedPoints = pointList;
+ }
+ else
+ {
+ // For Curved and Bezier, use spline interpolation
+ interpolatedPoints = Spline.Calculate(pointList, pointList.Count * _flybyPathSmoothness);
+ }
+
+ // Add vertices for the path
+ for (int j = 0; j < interpolatedPoints.Count - 1; j++)
+ {
+ var color = Vector4.Lerp(startColor, endColor, j / (float)interpolatedPoints.Count);
+ var points = new List()
+ {
+ new Vector3[]
+ {
+ interpolatedPoints[j],
+ new Vector3(interpolatedPoints[j].X + th, interpolatedPoints[j].Y + th, interpolatedPoints[j].Z + th),
+ new Vector3(interpolatedPoints[j].X - th, interpolatedPoints[j].Y + th, interpolatedPoints[j].Z + th)
+ },
+ new Vector3[]
+ {
+ interpolatedPoints[j + 1],
+ new Vector3(interpolatedPoints[j + 1].X + th, interpolatedPoints[j + 1].Y + th, interpolatedPoints[j + 1].Z + th),
+ new Vector3(interpolatedPoints[j + 1].X - th, interpolatedPoints[j + 1].Y + th, interpolatedPoints[j + 1].Z + th)
+ }
+ };
+
+ for (int k = 0; k < _flybyPathIndices.Count; k++)
+ {
+ var v = new SolidVertex();
+ v.Position = points[_flybyPathIndices[k].Y][_flybyPathIndices[k].X];
+ v.Color = color;
+ vertices.Add(v);
+ }
+ }
+
+ // Prepare the Vertex Buffer
+ if (_wayPointPathVertexBuffer != null)
+ _wayPointPathVertexBuffer.Dispose();
+ _wayPointPathVertexBuffer = SharpDX.Toolkit.Graphics.Buffer.Vertex.New(_legacyDevice, vertices.ToArray(), SharpDX.Direct3D11.ResourceUsage.Dynamic);
+
+ return true;
+ }
+
private class Comparer : IComparer, IComparer, IComparer
{
public int Compare(StaticInstance x, StaticInstance y)
From 1391f22eb398f678a8717e217d8fe5a72f7fa1ab Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 12:52:01 +0000
Subject: [PATCH 05/36] Address code review feedback: improve pattern matching,
disposal, and PathType consistency
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 12 +++++++++---
.../TombLib/LevelData/Instances/WayPointInstance.cs | 3 +--
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index 91610f911..adba12355 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -209,6 +209,12 @@ private bool AddWayPointPath(int sequence)
float th = _flybyPathThickness;
+ // Determine the PathType to use for the sequence (use the most common one)
+ var pathTypeCounts = wayPoints.GroupBy(wp => wp.PathType)
+ .OrderByDescending(g => g.Count())
+ .ToList();
+ var pathType = pathTypeCounts.FirstOrDefault()?.Key ?? PathType.Linear;
+
// Process waypoints to calculate paths
var pointList = new List();
for (int i = 0; i < wayPoints.Count; i++)
@@ -219,7 +225,7 @@ private bool AddWayPointPath(int sequence)
// Calculate the spline path based on PathType
List interpolatedPoints;
- if (wayPoints[0].PathType == PathType.Linear)
+ if (pathType == PathType.Linear)
{
// For linear, just use the points as-is
interpolatedPoints = pointList;
@@ -260,8 +266,8 @@ private bool AddWayPointPath(int sequence)
}
// Prepare the Vertex Buffer
- if (_wayPointPathVertexBuffer != null)
- _wayPointPathVertexBuffer.Dispose();
+ _wayPointPathVertexBuffer?.Dispose();
+ _wayPointPathVertexBuffer = null;
_wayPointPathVertexBuffer = SharpDX.Toolkit.Graphics.Buffer.Vertex.New(_legacyDevice, vertices.ToArray(), SharpDX.Direct3D11.ResourceUsage.Dynamic);
return true;
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
index fca8984b4..a3c783dea 100644
--- a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -69,9 +69,8 @@ public ushort Number
public WayPointInstance(ObjectInstance selectedObject = null)
{
- if (selectedObject != null && selectedObject is WayPointInstance)
+ if (selectedObject is WayPointInstance prevWayPoint)
{
- var prevWayPoint = (WayPointInstance)selectedObject;
var currSeq = prevWayPoint.Sequence;
var currNum = (ushort)(prevWayPoint.Number + 1);
From 46bca0fd9f5c183d84f5c2085e18727f3a7dd9ae Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 13:19:33 +0000
Subject: [PATCH 06/36] Restrict WayPoint to TombEngine and add compiler
support
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Command.cs | 2 +
.../ContextMenus/SectorContextMenu.cs | 1 +
.../SelectedGeometryContextMenu.cs | 1 +
TombEditor/EditorCommands.cs | 2 +
.../TombEngine/LevelCompilerTombEngine.cs | 51 +++++++++++++++++++
.../LevelData/Compilers/TombEngine/Structs.cs | 17 +++++++
.../Compilers/TombEngine/TombEngine.cs | 17 +++++++
7 files changed, 91 insertions(+)
diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs
index 168de67a5..76a09aed1 100644
--- a/TombEditor/Command.cs
+++ b/TombEditor/Command.cs
@@ -1154,6 +1154,8 @@ static CommandHandler()
AddCommand("AddWayPoint", "Add waypoint", CommandType.Objects, delegate (CommandArgs args)
{
+ if (!EditorActions.VersionCheck(args.Editor.Level.IsTombEngine, "WayPoint"))
+ return;
args.Editor.Action = new EditorActionPlace(false, (l, r) => new WayPointInstance(args.Editor.SelectedObject));
});
diff --git a/TombEditor/Controls/ContextMenus/SectorContextMenu.cs b/TombEditor/Controls/ContextMenus/SectorContextMenu.cs
index edce1b34a..29b6f8ffa 100644
--- a/TombEditor/Controls/ContextMenus/SectorContextMenu.cs
+++ b/TombEditor/Controls/ContextMenus/SectorContextMenu.cs
@@ -49,6 +49,7 @@ public SectorContextMenu(Editor editor, IWin32Window owner, Room targetRoom, Vec
EditorActions.PlaceObject(targetRoom, targetSector, new FlybyCameraInstance(editor.SelectedObject));
}));
+ if (editor.Level.IsTombEngine)
Items.Add(new ToolStripMenuItem("Add waypoint", Properties.Resources.objects_movie_projector_16, (o, e) =>
{
EditorActions.PlaceObject(targetRoom, targetSector, new WayPointInstance(editor.SelectedObject));
diff --git a/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs b/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs
index 0184aa30b..d6eb36da0 100644
--- a/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs
+++ b/TombEditor/Controls/ContextMenus/SelectedGeometryContextMenu.cs
@@ -56,6 +56,7 @@ public SelectedGeometryContextMenu(Editor editor, IWin32Window owner, Room targe
EditorActions.PlaceObject(targetRoom, targetSector, new FlybyCameraInstance(editor.SelectedObject));
}));
+ if (editor.Level.IsTombEngine)
Items.Add(new ToolStripMenuItem("Add waypoint", Properties.Resources.objects_movie_projector_16, (o, e) =>
{
EditorActions.PlaceObject(targetRoom, targetSector, new WayPointInstance(editor.SelectedObject));
diff --git a/TombEditor/EditorCommands.cs b/TombEditor/EditorCommands.cs
index 0fceca166..8635e6ac1 100644
--- a/TombEditor/EditorCommands.cs
+++ b/TombEditor/EditorCommands.cs
@@ -779,6 +779,8 @@ private void AddCommands()
AddCommand("AddWayPoint", "Add waypoint", CommandType.Objects, delegate ()
{
+ if (!VersionCheck(_editor.Level.IsTombEngine, "WayPoint"))
+ return;
_editor.Action = new EditorActionPlace(false, (l, r) => new WayPointInstance());
});
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index 1b5835b98..446db8593 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -56,6 +56,7 @@ public int Compare(TombEngineBucket x, TombEngineBucket y)
private readonly List _cameras = new List();
private readonly List _sinks = new List();
private readonly List _flyByCameras = new List();
+ private readonly List _wayPoints = new List();
private readonly List _soundSources = new List();
private List _boxes = new List();
private List _overlaps = new List();
@@ -74,6 +75,7 @@ public int Compare(TombEngineBucket x, TombEngineBucket y)
private Dictionary _aiObjectsTable;
private Dictionary _soundSourcesTable;
private Dictionary _flybyTable;
+ private Dictionary _wayPointTable;
// Collected game limits
private Dictionary _limits;
@@ -228,10 +230,12 @@ private void BuildCamerasAndSinks()
int sinkID = 0;
int camID = 0;
int flybyID = 0;
+ int wayPointID = 0;
_cameraTable = new Dictionary(new ReferenceEqualityComparer());
_sinkTable = new Dictionary(new ReferenceEqualityComparer());
_flybyTable = new Dictionary(new ReferenceEqualityComparer());
+ _wayPointTable = new Dictionary(new ReferenceEqualityComparer());
foreach (var room in _level.ExistingRooms)
{
@@ -241,6 +245,8 @@ private void BuildCamerasAndSinks()
_flybyTable.Add(obj, flybyID++);
foreach (var obj in room.Objects.OfType())
_sinkTable.Add(obj, sinkID++);
+ foreach (var obj in room.Objects.OfType())
+ _wayPointTable.Add(obj, wayPointID++);
}
}
@@ -332,8 +338,53 @@ private void BuildCamerasAndSinks()
lastIndex = _flyByCameras[i].Index;
}
+ // Collect waypoints
+ foreach (var instance in _wayPointTable.Keys)
+ {
+ Vector3 position = instance.Room.WorldPos + instance.Position;
+ _wayPoints.Add(new TombEngineWayPoint
+ {
+ X = (int)Math.Round(position.X),
+ Y = (int)Math.Round(-position.Y),
+ Z = (int)Math.Round(position.Z),
+ Room = _roomRemapping[instance.Room],
+ RotationX = instance.RotationX,
+ RotationY = instance.RotationY,
+ Roll = instance.Roll,
+ Sequence = instance.Sequence,
+ Number = instance.Number,
+ PathType = (int)instance.PathType,
+ Name = instance.Name,
+ LuaName = instance.ScriptId.HasValue ? (_scriptingIdsTable.TryGetOrDefault(instance.ScriptId.Value, string.Empty) ?? string.Empty) : string.Empty
+ });
+ }
+ _wayPoints.Sort((x, y) =>
+ {
+ int seqCompare = x.Sequence.CompareTo(y.Sequence);
+ if (seqCompare != 0) return seqCompare;
+ return x.Number.CompareTo(y.Number);
+ });
+
+ // Check waypoint duplicates
+ lastSeq = -1;
+ lastIndex = -1;
+
+ for (int i = 0; i < _wayPoints.Count; i++)
+ {
+ if(_wayPoints[i].Sequence != lastSeq)
+ {
+ lastSeq = _wayPoints[i].Sequence;
+ lastIndex = -1;
+ }
+
+ if (_wayPoints[i].Number == lastIndex && _wayPoints[i].Sequence == lastSeq)
+ _progressReporter.ReportWarn("Warning: waypoint sequence " + _wayPoints[i].Sequence + " has duplicated waypoint with ID " + lastIndex);
+ lastIndex = _wayPoints[i].Number;
+ }
+
ReportProgress(47, " Number of cameras: " + _cameraTable.Count);
ReportProgress(47, " Number of flyby cameras: " + _flyByCameras.Count);
+ ReportProgress(47, " Number of waypoints: " + _wayPointTable.Count);
ReportProgress(47, " Number of sinks: " + _sinkTable.Count);
}
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
index a00161107..e8f4e2541 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
@@ -585,6 +585,23 @@ public struct TombEngineSink
public string LuaName;
}
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct TombEngineWayPoint
+ {
+ public int X;
+ public int Y;
+ public int Z;
+ public int Room;
+ public float RotationX;
+ public float RotationY;
+ public float Roll;
+ public ushort Sequence;
+ public ushort Number;
+ public int PathType;
+ public string Name;
+ public string LuaName;
+ }
+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TombEngineSoundSource
{
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
index 67650844a..630977c73 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
@@ -77,6 +77,23 @@ private void WriteLevelTombEngine()
writer.Write((uint)_flyByCameras.Count);
writer.WriteBlockArray(_flyByCameras);
+ writer.Write((uint)_wayPoints.Count);
+ foreach (var waypoint in _wayPoints)
+ {
+ writer.Write(waypoint.X);
+ writer.Write(waypoint.Y);
+ writer.Write(waypoint.Z);
+ writer.Write(waypoint.Room);
+ writer.Write(waypoint.RotationX);
+ writer.Write(waypoint.RotationY);
+ writer.Write(waypoint.Roll);
+ writer.Write(waypoint.Sequence);
+ writer.Write(waypoint.Number);
+ writer.Write(waypoint.PathType);
+ writer.Write(waypoint.Name);
+ writer.Write(waypoint.LuaName);
+ }
+
writer.Write((uint)_sinks.Count);
foreach (var sink in _sinks)
{
From 4a60133c481ac566134913934eac303d3959d974 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 13:21:04 +0000
Subject: [PATCH 07/36] Fix code formatting in waypoint duplicate detection
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
.../LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index 446db8593..f4cac0a05 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -371,14 +371,14 @@ private void BuildCamerasAndSinks()
for (int i = 0; i < _wayPoints.Count; i++)
{
- if(_wayPoints[i].Sequence != lastSeq)
+ if (_wayPoints[i].Sequence != lastSeq)
{
lastSeq = _wayPoints[i].Sequence;
lastIndex = -1;
}
if (_wayPoints[i].Number == lastIndex && _wayPoints[i].Sequence == lastSeq)
- _progressReporter.ReportWarn("Warning: waypoint sequence " + _wayPoints[i].Sequence + " has duplicated waypoint with ID " + lastIndex);
+ _progressReporter.ReportWarn($"Warning: waypoint sequence {_wayPoints[i].Sequence} has duplicated waypoint with ID {lastIndex}");
lastIndex = _wayPoints[i].Number;
}
From 34ba61fb7626d7296d3dfe79b243691d4d3689e2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 13:46:46 +0000
Subject: [PATCH 08/36] Add Circle/Ellipse shape support with Radius1 and
Radius2 properties
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.Designer.cs | 148 ++++++++++++++++--
TombEditor/Forms/FormWayPoint.cs | 20 +++
.../TombEngine/LevelCompilerTombEngine.cs | 3 +
.../LevelData/Compilers/TombEngine/Structs.cs | 3 +
.../Compilers/TombEngine/TombEngine.cs | 3 +
TombLib/TombLib/LevelData/IO/Prj2Loader.cs | 3 +
TombLib/TombLib/LevelData/IO/Prj2Writer.cs | 3 +
.../LevelData/Instances/WayPointInstance.cs | 13 ++
8 files changed, 183 insertions(+), 13 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index d56b3624e..830977278 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -36,6 +36,9 @@ private void InitializeComponent()
this.lblSequence = new DarkUI.Controls.DarkLabel();
this.lblNumber = new DarkUI.Controls.DarkLabel();
this.lblPathType = new DarkUI.Controls.DarkLabel();
+ this.lblShape = new DarkUI.Controls.DarkLabel();
+ this.lblRadius1 = new DarkUI.Controls.DarkLabel();
+ this.lblRadius2 = new DarkUI.Controls.DarkLabel();
this.lblRotationX = new DarkUI.Controls.DarkLabel();
this.lblRotationY = new DarkUI.Controls.DarkLabel();
this.lblRoll = new DarkUI.Controls.DarkLabel();
@@ -43,11 +46,16 @@ private void InitializeComponent()
this.numSequence = new DarkUI.Controls.DarkNumericUpDown();
this.numNumber = new DarkUI.Controls.DarkNumericUpDown();
this.cmbPathType = new DarkUI.Controls.DarkComboBox();
+ this.cmbShape = new DarkUI.Controls.DarkComboBox();
+ this.numRadius1 = new DarkUI.Controls.DarkNumericUpDown();
+ this.numRadius2 = new DarkUI.Controls.DarkNumericUpDown();
this.numRotationX = new DarkUI.Controls.DarkNumericUpDown();
this.numRotationY = new DarkUI.Controls.DarkNumericUpDown();
this.numRoll = new DarkUI.Controls.DarkNumericUpDown();
((System.ComponentModel.ISupportInitialize)(this.numSequence)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numNumber)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRadius1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRadius2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationX)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationY)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numRoll)).BeginInit();
@@ -58,10 +66,10 @@ private void InitializeComponent()
this.butCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.butCancel.Checked = false;
this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.butCancel.Location = new System.Drawing.Point(239, 220);
+ this.butCancel.Location = new System.Drawing.Point(239, 298);
this.butCancel.Name = "butCancel";
this.butCancel.Size = new System.Drawing.Size(80, 23);
- this.butCancel.TabIndex = 8;
+ this.butCancel.TabIndex = 12;
this.butCancel.Text = "Cancel";
this.butCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
@@ -70,10 +78,10 @@ private void InitializeComponent()
//
this.butOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.butOK.Checked = false;
- this.butOK.Location = new System.Drawing.Point(153, 220);
+ this.butOK.Location = new System.Drawing.Point(153, 298);
this.butOK.Name = "butOK";
this.butOK.Size = new System.Drawing.Size(80, 23);
- this.butOK.TabIndex = 7;
+ this.butOK.TabIndex = 11;
this.butOK.Text = "OK";
this.butOK.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
this.butOK.Click += new System.EventHandler(this.butOK_Click);
@@ -118,34 +126,64 @@ private void InitializeComponent()
this.lblPathType.TabIndex = 6;
this.lblPathType.Text = "Path Type:";
//
+ // lblShape
+ //
+ this.lblShape.AutoSize = true;
+ this.lblShape.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblShape.Location = new System.Drawing.Point(12, 119);
+ this.lblShape.Name = "lblShape";
+ this.lblShape.Size = new System.Drawing.Size(42, 13);
+ this.lblShape.TabIndex = 8;
+ this.lblShape.Text = "Shape:";
+ //
+ // lblRadius1
+ //
+ this.lblRadius1.AutoSize = true;
+ this.lblRadius1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblRadius1.Location = new System.Drawing.Point(12, 145);
+ this.lblRadius1.Name = "lblRadius1";
+ this.lblRadius1.Size = new System.Drawing.Size(52, 13);
+ this.lblRadius1.TabIndex = 10;
+ this.lblRadius1.Text = "Radius 1:";
+ //
+ // lblRadius2
+ //
+ this.lblRadius2.AutoSize = true;
+ this.lblRadius2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblRadius2.Location = new System.Drawing.Point(12, 171);
+ this.lblRadius2.Name = "lblRadius2";
+ this.lblRadius2.Size = new System.Drawing.Size(52, 13);
+ this.lblRadius2.TabIndex = 12;
+ this.lblRadius2.Text = "Radius 2:";
+ //
// lblRotationX
//
this.lblRotationX.AutoSize = true;
this.lblRotationX.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationX.Location = new System.Drawing.Point(12, 119);
+ this.lblRotationX.Location = new System.Drawing.Point(12, 197);
this.lblRotationX.Name = "lblRotationX";
this.lblRotationX.Size = new System.Drawing.Size(64, 13);
- this.lblRotationX.TabIndex = 8;
+ this.lblRotationX.TabIndex = 14;
this.lblRotationX.Text = "Rotation X:";
//
// lblRotationY
//
this.lblRotationY.AutoSize = true;
this.lblRotationY.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationY.Location = new System.Drawing.Point(12, 145);
+ this.lblRotationY.Location = new System.Drawing.Point(12, 223);
this.lblRotationY.Name = "lblRotationY";
this.lblRotationY.Size = new System.Drawing.Size(63, 13);
- this.lblRotationY.TabIndex = 10;
+ this.lblRotationY.TabIndex = 16;
this.lblRotationY.Text = "Rotation Y:";
//
// lblRoll
//
this.lblRoll.AutoSize = true;
this.lblRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRoll.Location = new System.Drawing.Point(12, 171);
+ this.lblRoll.Location = new System.Drawing.Point(12, 249);
this.lblRoll.Name = "lblRoll";
this.lblRoll.Size = new System.Drawing.Size(30, 13);
- this.lblRoll.TabIndex = 12;
+ this.lblRoll.TabIndex = 18;
this.lblRoll.Text = "Roll:";
//
// txtName
@@ -203,6 +241,76 @@ private void InitializeComponent()
this.cmbPathType.Size = new System.Drawing.Size(237, 23);
this.cmbPathType.TabIndex = 7;
//
+ // cmbShape
+ //
+ this.cmbShape.FormattingEnabled = true;
+ this.cmbShape.Items.AddRange(new object[] {
+ "Circle",
+ "Ellipse"});
+ this.cmbShape.Location = new System.Drawing.Point(82, 116);
+ this.cmbShape.Name = "cmbShape";
+ this.cmbShape.Size = new System.Drawing.Size(237, 23);
+ this.cmbShape.TabIndex = 9;
+ this.cmbShape.SelectedIndexChanged += new System.EventHandler(this.cmbShape_SelectedIndexChanged);
+ //
+ // numRadius1
+ //
+ this.numRadius1.DecimalPlaces = 2;
+ this.numRadius1.IncrementAlternate = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ this.numRadius1.Location = new System.Drawing.Point(82, 142);
+ this.numRadius1.LoopValues = false;
+ this.numRadius1.Maximum = new decimal(new int[] {
+ 100000,
+ 0,
+ 0,
+ 0});
+ this.numRadius1.Minimum = new decimal(new int[] {
+ 0,
+ 0,
+ 0,
+ 0});
+ this.numRadius1.Name = "numRadius1";
+ this.numRadius1.Size = new System.Drawing.Size(237, 22);
+ this.numRadius1.TabIndex = 11;
+ this.numRadius1.Value = new decimal(new int[] {
+ 1024,
+ 0,
+ 0,
+ 0});
+ //
+ // numRadius2
+ //
+ this.numRadius2.DecimalPlaces = 2;
+ this.numRadius2.IncrementAlternate = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ this.numRadius2.Location = new System.Drawing.Point(82, 168);
+ this.numRadius2.LoopValues = false;
+ this.numRadius2.Maximum = new decimal(new int[] {
+ 100000,
+ 0,
+ 0,
+ 0});
+ this.numRadius2.Minimum = new decimal(new int[] {
+ 0,
+ 0,
+ 0,
+ 0});
+ this.numRadius2.Name = "numRadius2";
+ this.numRadius2.Size = new System.Drawing.Size(237, 22);
+ this.numRadius2.TabIndex = 13;
+ this.numRadius2.Value = new decimal(new int[] {
+ 1024,
+ 0,
+ 0,
+ 0});
+ //
// numRotationX
//
this.numRotationX.DecimalPlaces = 2;
@@ -211,7 +319,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRotationX.Location = new System.Drawing.Point(82, 116);
+ this.numRotationX.Location = new System.Drawing.Point(82, 194);
this.numRotationX.LoopValues = false;
this.numRotationX.Maximum = new decimal(new int[] {
90,
@@ -225,7 +333,7 @@ private void InitializeComponent()
-2147483648});
this.numRotationX.Name = "numRotationX";
this.numRotationX.Size = new System.Drawing.Size(237, 22);
- this.numRotationX.TabIndex = 9;
+ this.numRotationX.TabIndex = 15;
//
// numRotationY
//
@@ -235,7 +343,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRotationY.Location = new System.Drawing.Point(82, 142);
+ this.numRotationY.Location = new System.Drawing.Point(82, 220);
this.numRotationY.LoopValues = false;
this.numRotationY.Maximum = new decimal(new int[] {
360,
@@ -275,6 +383,9 @@ private void InitializeComponent()
this.Controls.Add(this.numRoll);
this.Controls.Add(this.numRotationY);
this.Controls.Add(this.numRotationX);
+ this.Controls.Add(this.numRadius2);
+ this.Controls.Add(this.numRadius1);
+ this.Controls.Add(this.cmbShape);
this.Controls.Add(this.cmbPathType);
this.Controls.Add(this.numNumber);
this.Controls.Add(this.numSequence);
@@ -282,6 +393,9 @@ private void InitializeComponent()
this.Controls.Add(this.lblRoll);
this.Controls.Add(this.lblRotationY);
this.Controls.Add(this.lblRotationX);
+ this.Controls.Add(this.lblRadius2);
+ this.Controls.Add(this.lblRadius1);
+ this.Controls.Add(this.lblShape);
this.Controls.Add(this.lblPathType);
this.Controls.Add(this.lblNumber);
this.Controls.Add(this.lblSequence);
@@ -299,6 +413,8 @@ private void InitializeComponent()
this.Load += new System.EventHandler(this.FormWayPoint_Load);
((System.ComponentModel.ISupportInitialize)(this.numSequence)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numNumber)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRadius1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numRadius2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationX)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationY)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numRoll)).EndInit();
@@ -315,6 +431,9 @@ private void InitializeComponent()
private DarkLabel lblSequence;
private DarkLabel lblNumber;
private DarkLabel lblPathType;
+ private DarkLabel lblShape;
+ private DarkLabel lblRadius1;
+ private DarkLabel lblRadius2;
private DarkLabel lblRotationX;
private DarkLabel lblRotationY;
private DarkLabel lblRoll;
@@ -322,6 +441,9 @@ private void InitializeComponent()
private DarkNumericUpDown numSequence;
private DarkNumericUpDown numNumber;
private DarkComboBox cmbPathType;
+ private DarkComboBox cmbShape;
+ private DarkNumericUpDown numRadius1;
+ private DarkNumericUpDown numRadius2;
private DarkNumericUpDown numRotationX;
private DarkNumericUpDown numRotationY;
private DarkNumericUpDown numRoll;
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index a2bc65dde..3989021bc 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -32,9 +32,26 @@ private void FormWayPoint_Load(object sender, EventArgs e)
numSequence.Value = _wayPoint.Sequence;
numNumber.Value = _wayPoint.Number;
cmbPathType.SelectedIndex = (int)_wayPoint.PathType;
+ cmbShape.SelectedIndex = (int)_wayPoint.Shape;
+ numRadius1.Value = (decimal)_wayPoint.Radius1;
+ numRadius2.Value = (decimal)_wayPoint.Radius2;
numRotationX.Value = (decimal)_wayPoint.RotationX;
numRotationY.Value = (decimal)_wayPoint.RotationY;
numRoll.Value = (decimal)_wayPoint.Roll;
+
+ UpdateRadiusVisibility();
+ }
+
+ private void cmbShape_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ UpdateRadiusVisibility();
+ }
+
+ private void UpdateRadiusVisibility()
+ {
+ bool isEllipse = cmbShape.SelectedIndex == (int)WayPointShape.Ellipse;
+ lblRadius2.Visible = isEllipse;
+ numRadius2.Visible = isEllipse;
}
private void butOK_Click(object sender, EventArgs e)
@@ -43,6 +60,9 @@ private void butOK_Click(object sender, EventArgs e)
_wayPoint.Sequence = (ushort)numSequence.Value;
_wayPoint.Number = (ushort)numNumber.Value;
_wayPoint.PathType = (PathType)cmbPathType.SelectedIndex;
+ _wayPoint.Shape = (WayPointShape)cmbShape.SelectedIndex;
+ _wayPoint.Radius1 = (float)numRadius1.Value;
+ _wayPoint.Radius2 = (float)numRadius2.Value;
_wayPoint.RotationX = (float)numRotationX.Value;
_wayPoint.RotationY = (float)numRotationY.Value;
_wayPoint.Roll = (float)numRoll.Value;
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index f4cac0a05..4cf6800af 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -354,6 +354,9 @@ private void BuildCamerasAndSinks()
Sequence = instance.Sequence,
Number = instance.Number,
PathType = (int)instance.PathType,
+ Shape = (int)instance.Shape,
+ Radius1 = instance.Radius1,
+ Radius2 = instance.Radius2,
Name = instance.Name,
LuaName = instance.ScriptId.HasValue ? (_scriptingIdsTable.TryGetOrDefault(instance.ScriptId.Value, string.Empty) ?? string.Empty) : string.Empty
});
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
index e8f4e2541..fff6b1cc0 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
@@ -598,6 +598,9 @@ public struct TombEngineWayPoint
public ushort Sequence;
public ushort Number;
public int PathType;
+ public int Shape;
+ public float Radius1;
+ public float Radius2;
public string Name;
public string LuaName;
}
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
index 630977c73..49256c392 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
@@ -90,6 +90,9 @@ private void WriteLevelTombEngine()
writer.Write(waypoint.Sequence);
writer.Write(waypoint.Number);
writer.Write(waypoint.PathType);
+ writer.Write(waypoint.Shape);
+ writer.Write(waypoint.Radius1);
+ writer.Write(waypoint.Radius2);
writer.Write(waypoint.Name);
writer.Write(waypoint.LuaName);
}
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
index f6096319a..62dd479b3 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
@@ -1413,6 +1413,9 @@ private static bool LoadObjects(ChunkReader chunkIO, ChunkId idOuter, LevelSetti
instance.Number = LEB128.ReadUShort(chunkIO.Raw);
instance.Sequence = LEB128.ReadUShort(chunkIO.Raw);
instance.PathType = (PathType)LEB128.ReadInt(chunkIO.Raw);
+ instance.Shape = (WayPointShape)LEB128.ReadInt(chunkIO.Raw);
+ instance.Radius1 = chunkIO.Raw.ReadSingle();
+ instance.Radius2 = chunkIO.Raw.ReadSingle();
addObject(instance);
newObjects.TryAdd(objectID, instance);
}
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
index 9bc23f11c..61156f106 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
@@ -693,6 +693,9 @@ private static void WriteObjects(ChunkWriter chunkIO, IEnumerable
Date: Sat, 24 Jan 2026 19:52:29 +0000
Subject: [PATCH 09/36] Fix compilation error: use LuaName property instead of
TryGetOrDefault
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
.../LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index 4cf6800af..e69ab2e21 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -358,7 +358,7 @@ private void BuildCamerasAndSinks()
Radius1 = instance.Radius1,
Radius2 = instance.Radius2,
Name = instance.Name,
- LuaName = instance.ScriptId.HasValue ? (_scriptingIdsTable.TryGetOrDefault(instance.ScriptId.Value, string.Empty) ?? string.Empty) : string.Empty
+ LuaName = instance.LuaName ?? string.Empty
});
}
_wayPoints.Sort((x, y) =>
From c25a5c21954da969dc6767cc926e8b38510b638b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:05:11 +0000
Subject: [PATCH 10/36] Refactor WayPoint to use single Type enum and update
UI/serialization
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 13 ++-
TombEditor/Forms/FormWayPoint.Designer.cs | 109 +++++++-----------
TombEditor/Forms/FormWayPoint.cs | 49 +++++---
.../TombEngine/LevelCompilerTombEngine.cs | 3 +-
.../LevelData/Compilers/TombEngine/Structs.cs | 3 +-
.../Compilers/TombEngine/TombEngine.cs | 3 +-
TombLib/TombLib/LevelData/IO/Prj2Loader.cs | 5 +-
TombLib/TombLib/LevelData/IO/Prj2Writer.cs | 5 +-
.../LevelData/Instances/WayPointInstance.cs | 99 +++++++++++-----
9 files changed, 164 insertions(+), 125 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index adba12355..6d6d14095 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -195,6 +195,9 @@ private bool AddWayPointPath(int sequence)
wayPoints.Add(instance);
}
+ // Filter to only multi-point types (Linear, Bezier)
+ wayPoints = wayPoints.Where(wp => wp.Type == WayPointType.Linear || wp.Type == WayPointType.Bezier).ToList();
+
// Is it actually necessary to show the path?
if (wayPoints.Count < 2)
return false;
@@ -209,11 +212,11 @@ private bool AddWayPointPath(int sequence)
float th = _flybyPathThickness;
- // Determine the PathType to use for the sequence (use the most common one)
- var pathTypeCounts = wayPoints.GroupBy(wp => wp.PathType)
+ // Determine the Type to use for the sequence (use the most common one)
+ var typeCounts = wayPoints.GroupBy(wp => wp.Type)
.OrderByDescending(g => g.Count())
.ToList();
- var pathType = pathTypeCounts.FirstOrDefault()?.Key ?? PathType.Linear;
+ var wpType = typeCounts.FirstOrDefault()?.Key ?? WayPointType.Linear;
// Process waypoints to calculate paths
var pointList = new List();
@@ -223,9 +226,9 @@ private bool AddWayPointPath(int sequence)
pointList.Add(wp.Position + wp.Room.WorldPos);
}
- // Calculate the spline path based on PathType
+ // Calculate the spline path based on Type
List interpolatedPoints;
- if (pathType == PathType.Linear)
+ if (wpType == WayPointType.Linear)
{
// For linear, just use the points as-is
interpolatedPoints = pointList;
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index 830977278..c72e6506e 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -35,8 +35,7 @@ private void InitializeComponent()
this.lblName = new DarkUI.Controls.DarkLabel();
this.lblSequence = new DarkUI.Controls.DarkLabel();
this.lblNumber = new DarkUI.Controls.DarkLabel();
- this.lblPathType = new DarkUI.Controls.DarkLabel();
- this.lblShape = new DarkUI.Controls.DarkLabel();
+ this.lblType = new DarkUI.Controls.DarkLabel();
this.lblRadius1 = new DarkUI.Controls.DarkLabel();
this.lblRadius2 = new DarkUI.Controls.DarkLabel();
this.lblRotationX = new DarkUI.Controls.DarkLabel();
@@ -45,8 +44,7 @@ private void InitializeComponent()
this.txtName = new DarkUI.Controls.DarkTextBox();
this.numSequence = new DarkUI.Controls.DarkNumericUpDown();
this.numNumber = new DarkUI.Controls.DarkNumericUpDown();
- this.cmbPathType = new DarkUI.Controls.DarkComboBox();
- this.cmbShape = new DarkUI.Controls.DarkComboBox();
+ this.cmbType = new DarkUI.Controls.DarkComboBox();
this.numRadius1 = new DarkUI.Controls.DarkNumericUpDown();
this.numRadius2 = new DarkUI.Controls.DarkNumericUpDown();
this.numRotationX = new DarkUI.Controls.DarkNumericUpDown();
@@ -116,74 +114,64 @@ private void InitializeComponent()
this.lblNumber.TabIndex = 4;
this.lblNumber.Text = "Number:";
//
- // lblPathType
+ // lblType
//
- this.lblPathType.AutoSize = true;
- this.lblPathType.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblPathType.Location = new System.Drawing.Point(12, 93);
- this.lblPathType.Name = "lblPathType";
- this.lblPathType.Size = new System.Drawing.Size(59, 13);
- this.lblPathType.TabIndex = 6;
- this.lblPathType.Text = "Path Type:";
- //
- // lblShape
- //
- this.lblShape.AutoSize = true;
- this.lblShape.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblShape.Location = new System.Drawing.Point(12, 119);
- this.lblShape.Name = "lblShape";
- this.lblShape.Size = new System.Drawing.Size(42, 13);
- this.lblShape.TabIndex = 8;
- this.lblShape.Text = "Shape:";
+ this.lblType.AutoSize = true;
+ this.lblType.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblType.Location = new System.Drawing.Point(12, 93);
+ this.lblType.Name = "lblType";
+ this.lblType.Size = new System.Drawing.Size(34, 13);
+ this.lblType.TabIndex = 6;
+ this.lblType.Text = "Type:";
//
// lblRadius1
//
this.lblRadius1.AutoSize = true;
this.lblRadius1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRadius1.Location = new System.Drawing.Point(12, 145);
+ this.lblRadius1.Location = new System.Drawing.Point(12, 119);
this.lblRadius1.Name = "lblRadius1";
this.lblRadius1.Size = new System.Drawing.Size(52, 13);
- this.lblRadius1.TabIndex = 10;
+ this.lblRadius1.TabIndex = 8;
this.lblRadius1.Text = "Radius 1:";
//
// lblRadius2
//
this.lblRadius2.AutoSize = true;
this.lblRadius2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRadius2.Location = new System.Drawing.Point(12, 171);
+ this.lblRadius2.Location = new System.Drawing.Point(12, 145);
this.lblRadius2.Name = "lblRadius2";
this.lblRadius2.Size = new System.Drawing.Size(52, 13);
- this.lblRadius2.TabIndex = 12;
+ this.lblRadius2.TabIndex = 10;
this.lblRadius2.Text = "Radius 2:";
//
// lblRotationX
//
this.lblRotationX.AutoSize = true;
this.lblRotationX.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationX.Location = new System.Drawing.Point(12, 197);
+ this.lblRotationX.Location = new System.Drawing.Point(12, 171);
this.lblRotationX.Name = "lblRotationX";
this.lblRotationX.Size = new System.Drawing.Size(64, 13);
- this.lblRotationX.TabIndex = 14;
+ this.lblRotationX.TabIndex = 12;
this.lblRotationX.Text = "Rotation X:";
//
// lblRotationY
//
this.lblRotationY.AutoSize = true;
this.lblRotationY.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationY.Location = new System.Drawing.Point(12, 223);
+ this.lblRotationY.Location = new System.Drawing.Point(12, 197);
this.lblRotationY.Name = "lblRotationY";
this.lblRotationY.Size = new System.Drawing.Size(63, 13);
- this.lblRotationY.TabIndex = 16;
+ this.lblRotationY.TabIndex = 14;
this.lblRotationY.Text = "Rotation Y:";
//
// lblRoll
//
this.lblRoll.AutoSize = true;
this.lblRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRoll.Location = new System.Drawing.Point(12, 249);
+ this.lblRoll.Location = new System.Drawing.Point(12, 223);
this.lblRoll.Name = "lblRoll";
this.lblRoll.Size = new System.Drawing.Size(30, 13);
- this.lblRoll.TabIndex = 18;
+ this.lblRoll.TabIndex = 16;
this.lblRoll.Text = "Roll:";
//
// txtName
@@ -229,29 +217,22 @@ private void InitializeComponent()
this.numNumber.Size = new System.Drawing.Size(237, 22);
this.numNumber.TabIndex = 5;
//
- // cmbPathType
+ // cmbType
//
- this.cmbPathType.FormattingEnabled = true;
- this.cmbPathType.Items.AddRange(new object[] {
+ this.cmbType.FormattingEnabled = true;
+ this.cmbType.Items.AddRange(new object[] {
+ "Point",
+ "Circle",
+ "Ellipse",
+ "Square",
+ "Rectangle",
"Linear",
- "Curved",
"Bezier"});
- this.cmbPathType.Location = new System.Drawing.Point(82, 90);
- this.cmbPathType.Name = "cmbPathType";
- this.cmbPathType.Size = new System.Drawing.Size(237, 23);
- this.cmbPathType.TabIndex = 7;
- //
- // cmbShape
- //
- this.cmbShape.FormattingEnabled = true;
- this.cmbShape.Items.AddRange(new object[] {
- "Circle",
- "Ellipse"});
- this.cmbShape.Location = new System.Drawing.Point(82, 116);
- this.cmbShape.Name = "cmbShape";
- this.cmbShape.Size = new System.Drawing.Size(237, 23);
- this.cmbShape.TabIndex = 9;
- this.cmbShape.SelectedIndexChanged += new System.EventHandler(this.cmbShape_SelectedIndexChanged);
+ this.cmbType.Location = new System.Drawing.Point(82, 90);
+ this.cmbType.Name = "cmbType";
+ this.cmbType.Size = new System.Drawing.Size(237, 23);
+ this.cmbType.TabIndex = 7;
+ this.cmbType.SelectedIndexChanged += new System.EventHandler(this.cmbType_SelectedIndexChanged);
//
// numRadius1
//
@@ -261,7 +242,7 @@ private void InitializeComponent()
0,
0,
0});
- this.numRadius1.Location = new System.Drawing.Point(82, 142);
+ this.numRadius1.Location = new System.Drawing.Point(82, 116);
this.numRadius1.LoopValues = false;
this.numRadius1.Maximum = new decimal(new int[] {
100000,
@@ -275,7 +256,7 @@ private void InitializeComponent()
0});
this.numRadius1.Name = "numRadius1";
this.numRadius1.Size = new System.Drawing.Size(237, 22);
- this.numRadius1.TabIndex = 11;
+ this.numRadius1.TabIndex = 9;
this.numRadius1.Value = new decimal(new int[] {
1024,
0,
@@ -290,7 +271,7 @@ private void InitializeComponent()
0,
0,
0});
- this.numRadius2.Location = new System.Drawing.Point(82, 168);
+ this.numRadius2.Location = new System.Drawing.Point(82, 142);
this.numRadius2.LoopValues = false;
this.numRadius2.Maximum = new decimal(new int[] {
100000,
@@ -304,7 +285,7 @@ private void InitializeComponent()
0});
this.numRadius2.Name = "numRadius2";
this.numRadius2.Size = new System.Drawing.Size(237, 22);
- this.numRadius2.TabIndex = 13;
+ this.numRadius2.TabIndex = 11;
this.numRadius2.Value = new decimal(new int[] {
1024,
0,
@@ -319,7 +300,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRotationX.Location = new System.Drawing.Point(82, 194);
+ this.numRotationX.Location = new System.Drawing.Point(82, 168);
this.numRotationX.LoopValues = false;
this.numRotationX.Maximum = new decimal(new int[] {
90,
@@ -333,7 +314,7 @@ private void InitializeComponent()
-2147483648});
this.numRotationX.Name = "numRotationX";
this.numRotationX.Size = new System.Drawing.Size(237, 22);
- this.numRotationX.TabIndex = 15;
+ this.numRotationX.TabIndex = 13;
//
// numRotationY
//
@@ -385,8 +366,7 @@ private void InitializeComponent()
this.Controls.Add(this.numRotationX);
this.Controls.Add(this.numRadius2);
this.Controls.Add(this.numRadius1);
- this.Controls.Add(this.cmbShape);
- this.Controls.Add(this.cmbPathType);
+ this.Controls.Add(this.cmbType);
this.Controls.Add(this.numNumber);
this.Controls.Add(this.numSequence);
this.Controls.Add(this.txtName);
@@ -395,8 +375,7 @@ private void InitializeComponent()
this.Controls.Add(this.lblRotationX);
this.Controls.Add(this.lblRadius2);
this.Controls.Add(this.lblRadius1);
- this.Controls.Add(this.lblShape);
- this.Controls.Add(this.lblPathType);
+ this.Controls.Add(this.lblType);
this.Controls.Add(this.lblNumber);
this.Controls.Add(this.lblSequence);
this.Controls.Add(this.lblName);
@@ -430,8 +409,7 @@ private void InitializeComponent()
private DarkLabel lblName;
private DarkLabel lblSequence;
private DarkLabel lblNumber;
- private DarkLabel lblPathType;
- private DarkLabel lblShape;
+ private DarkLabel lblType;
private DarkLabel lblRadius1;
private DarkLabel lblRadius2;
private DarkLabel lblRotationX;
@@ -440,8 +418,7 @@ private void InitializeComponent()
private DarkTextBox txtName;
private DarkNumericUpDown numSequence;
private DarkNumericUpDown numNumber;
- private DarkComboBox cmbPathType;
- private DarkComboBox cmbShape;
+ private DarkComboBox cmbType;
private DarkNumericUpDown numRadius1;
private DarkNumericUpDown numRadius2;
private DarkNumericUpDown numRotationX;
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 3989021bc..442042e65 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -28,39 +28,62 @@ private void butCancel_Click(object sender, EventArgs e)
private void FormWayPoint_Load(object sender, EventArgs e)
{
- txtName.Text = _wayPoint.Name;
+ txtName.Text = _wayPoint.BaseName;
numSequence.Value = _wayPoint.Sequence;
numNumber.Value = _wayPoint.Number;
- cmbPathType.SelectedIndex = (int)_wayPoint.PathType;
- cmbShape.SelectedIndex = (int)_wayPoint.Shape;
+ cmbType.SelectedIndex = (int)_wayPoint.Type;
numRadius1.Value = (decimal)_wayPoint.Radius1;
numRadius2.Value = (decimal)_wayPoint.Radius2;
numRotationX.Value = (decimal)_wayPoint.RotationX;
numRotationY.Value = (decimal)_wayPoint.RotationY;
numRoll.Value = (decimal)_wayPoint.Roll;
- UpdateRadiusVisibility();
+ UpdateFieldVisibility();
}
- private void cmbShape_SelectedIndexChanged(object sender, EventArgs e)
+ private void cmbType_SelectedIndexChanged(object sender, EventArgs e)
{
- UpdateRadiusVisibility();
+ UpdateFieldVisibility();
}
- private void UpdateRadiusVisibility()
+ private void UpdateFieldVisibility()
{
- bool isEllipse = cmbShape.SelectedIndex == (int)WayPointShape.Ellipse;
- lblRadius2.Visible = isEllipse;
- numRadius2.Visible = isEllipse;
+ WayPointType type = (WayPointType)cmbType.SelectedIndex;
+
+ // Check if this is a singular type
+ bool isSingular = type == WayPointType.Point ||
+ type == WayPointType.Circle ||
+ type == WayPointType.Ellipse ||
+ type == WayPointType.Square ||
+ type == WayPointType.Rectangle;
+
+ // Number field only for multi-point types
+ lblNumber.Visible = !isSingular;
+ numNumber.Visible = !isSingular;
+
+ // Radius fields only for shape types
+ bool requiresRadius = type == WayPointType.Circle ||
+ type == WayPointType.Ellipse ||
+ type == WayPointType.Square ||
+ type == WayPointType.Rectangle;
+
+ lblRadius1.Visible = requiresRadius;
+ numRadius1.Visible = requiresRadius;
+
+ // Radius2 only for ellipse and rectangle
+ bool requiresTwoRadii = type == WayPointType.Ellipse ||
+ type == WayPointType.Rectangle;
+
+ lblRadius2.Visible = requiresTwoRadii;
+ numRadius2.Visible = requiresTwoRadii;
}
private void butOK_Click(object sender, EventArgs e)
{
- _wayPoint.Name = txtName.Text;
+ _wayPoint.BaseName = txtName.Text;
_wayPoint.Sequence = (ushort)numSequence.Value;
_wayPoint.Number = (ushort)numNumber.Value;
- _wayPoint.PathType = (PathType)cmbPathType.SelectedIndex;
- _wayPoint.Shape = (WayPointShape)cmbShape.SelectedIndex;
+ _wayPoint.Type = (WayPointType)cmbType.SelectedIndex;
_wayPoint.Radius1 = (float)numRadius1.Value;
_wayPoint.Radius2 = (float)numRadius2.Value;
_wayPoint.RotationX = (float)numRotationX.Value;
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index e69ab2e21..00975b1a6 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -353,8 +353,7 @@ private void BuildCamerasAndSinks()
Roll = instance.Roll,
Sequence = instance.Sequence,
Number = instance.Number,
- PathType = (int)instance.PathType,
- Shape = (int)instance.Shape,
+ Type = (int)instance.Type,
Radius1 = instance.Radius1,
Radius2 = instance.Radius2,
Name = instance.Name,
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
index fff6b1cc0..69a833cba 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
@@ -597,8 +597,7 @@ public struct TombEngineWayPoint
public float Roll;
public ushort Sequence;
public ushort Number;
- public int PathType;
- public int Shape;
+ public int Type;
public float Radius1;
public float Radius2;
public string Name;
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
index 49256c392..b0afe65e8 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
@@ -89,8 +89,7 @@ private void WriteLevelTombEngine()
writer.Write(waypoint.Roll);
writer.Write(waypoint.Sequence);
writer.Write(waypoint.Number);
- writer.Write(waypoint.PathType);
- writer.Write(waypoint.Shape);
+ writer.Write(waypoint.Type);
writer.Write(waypoint.Radius1);
writer.Write(waypoint.Radius2);
writer.Write(waypoint.Name);
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
index 62dd479b3..fc79e3504 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
@@ -1409,11 +1409,10 @@ private static bool LoadObjects(ChunkReader chunkIO, ChunkId idOuter, LevelSetti
instance.SetArbitaryRotationsYX(chunkIO.Raw.ReadSingle(), chunkIO.Raw.ReadSingle());
instance.Roll = chunkIO.Raw.ReadSingle();
instance.ScriptId = ReadOptionalLEB128Int(chunkIO.Raw);
- instance.Name = chunkIO.Raw.ReadStringUTF8();
+ instance.BaseName = chunkIO.Raw.ReadStringUTF8();
instance.Number = LEB128.ReadUShort(chunkIO.Raw);
instance.Sequence = LEB128.ReadUShort(chunkIO.Raw);
- instance.PathType = (PathType)LEB128.ReadInt(chunkIO.Raw);
- instance.Shape = (WayPointShape)LEB128.ReadInt(chunkIO.Raw);
+ instance.Type = (WayPointType)LEB128.ReadInt(chunkIO.Raw);
instance.Radius1 = chunkIO.Raw.ReadSingle();
instance.Radius2 = chunkIO.Raw.ReadSingle();
addObject(instance);
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
index 61156f106..1e31a1cd9 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
@@ -689,11 +689,10 @@ private static void WriteObjects(ChunkWriter chunkIO, IEnumerable= 0)
+ if (lastUnderscore >= 0 && !IsSingularType())
{
string suffix = value.Substring(lastUnderscore + 1);
if (ushort.TryParse(suffix, out _))
@@ -67,15 +79,42 @@ public ushort Number
set { _number = value; }
}
- public PathType PathType { get; set; } = PathType.Linear;
- public WayPointShape Shape { get; set; } = WayPointShape.Circle;
+ public WayPointType Type
+ {
+ get { return _type; }
+ set { _type = value; }
+ }
+
public float Radius1 { get; set; } = 1024.0f; // Default radius in units
- public float Radius2 { get; set; } = 1024.0f; // Default radius in units (same as Radius1 for circle)
+ public float Radius2 { get; set; } = 1024.0f; // Default radius in units
private float _rotationX { get; set; }
private float _rotationY { get; set; }
private float _roll { get; set; }
+ public bool IsSingularType()
+ {
+ return _type == WayPointType.Point ||
+ _type == WayPointType.Circle ||
+ _type == WayPointType.Ellipse ||
+ _type == WayPointType.Square ||
+ _type == WayPointType.Rectangle;
+ }
+
+ public bool RequiresRadius()
+ {
+ return _type == WayPointType.Circle ||
+ _type == WayPointType.Ellipse ||
+ _type == WayPointType.Square ||
+ _type == WayPointType.Rectangle;
+ }
+
+ public bool RequiresTwoRadii()
+ {
+ return _type == WayPointType.Ellipse ||
+ _type == WayPointType.Rectangle;
+ }
+
public WayPointInstance(ObjectInstance selectedObject = null)
{
if (selectedObject is WayPointInstance prevWayPoint)
@@ -83,18 +122,21 @@ public WayPointInstance(ObjectInstance selectedObject = null)
var currSeq = prevWayPoint.Sequence;
var currNum = (ushort)(prevWayPoint.Number + 1);
- // Push next waypoints in sequence forward
- var level = selectedObject.Room.Level;
- foreach (var room in level.ExistingRooms)
- foreach (var instance in room.Objects.OfType())
- if (instance.Sequence == currSeq && instance.Number >= currNum)
- instance.Number++;
+ // Only push forward if it's a multi-point type
+ if (!prevWayPoint.IsSingularType())
+ {
+ // Push next waypoints in sequence forward
+ var level = selectedObject.Room.Level;
+ foreach (var room in level.ExistingRooms)
+ foreach (var instance in room.Objects.OfType())
+ if (instance.Sequence == currSeq && instance.Number >= currNum)
+ instance.Number++;
+ }
Sequence = currSeq;
- Number = currNum;
+ Number = prevWayPoint.IsSingularType() ? (ushort)0 : currNum;
_baseName = prevWayPoint._baseName;
- PathType = prevWayPoint.PathType;
- Shape = prevWayPoint.Shape;
+ Type = prevWayPoint.Type;
Radius1 = prevWayPoint.Radius1;
Radius2 = prevWayPoint.Radius2;
@@ -133,16 +175,15 @@ public override string ToString()
return "WayPoint " +
", Name = " + Name +
", Sequence = " + Sequence +
- ", Number = " + Number +
- ", PathType = " + PathType +
- ", Shape = " + Shape +
+ (IsSingularType() ? "" : ", Number = " + Number) +
+ ", Type = " + Type +
" (" + (Room?.ToString() ?? "NULL") + ")" +
", X = " + SectorPosition.X +
", Z = " + SectorPosition.Y +
GetScriptIDOrName(false);
}
- public string ShortName() => "WayPoint (" + Sequence + ":" + Number + ")" + GetScriptIDOrName() + " (" + (Room?.ToString() ?? "NULL") + ")";
+ public string ShortName() => "WayPoint " + (IsSingularType() ? "" : "(" + Sequence + ":" + Number + ") ") + GetScriptIDOrName() + " (" + (Room?.ToString() ?? "NULL") + ")";
public override void CopyDependentLevelSettings(Room.CopyDependentLevelSettingsArgs args)
{
From c6e14266684457e1ecfc2a6c95333f7886364f01 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:06:03 +0000
Subject: [PATCH 11/36] Add circle, ellipse, square, and rectangle rendering
for WayPoints
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 95 ++++++++++++++++++++++
1 file changed, 95 insertions(+)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 7c86503ce..18eb276f9 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1181,6 +1181,12 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
}
DrawOrQueueServiceObject(instance, _littleCube, color, effect, sprites);
+
+ // Draw shape for shape types (Circle, Ellipse, Square, Rectangle)
+ if (instance.RequiresRadius())
+ {
+ DrawWayPointShape(instance, color);
+ }
}
if (group.Key == typeof(MemoInstance))
@@ -1469,6 +1475,95 @@ private void DrawOrQueueServiceObject(ISpatial instance, GeometricPrimitive prim
_legacyDevice.DrawIndexed(PrimitiveType.TriangleList, primitive.IndexBuffer.ElementCount);
}
+ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
+ {
+ // Get world position
+ Vector3 position = instance.Position + instance.Room.WorldPos;
+
+ // Create transformation matrix for the shape orientation
+ Matrix4x4 rotation = Matrix4x4.CreateRotationY(instance.RotationY * (float)Math.PI / 180.0f) *
+ Matrix4x4.CreateRotationZ(instance.Roll * (float)Math.PI / 180.0f);
+
+ // Number of segments for circles/ellipses
+ int segments = 32;
+ var vertices = new List();
+
+ switch (instance.Type)
+ {
+ case WayPointType.Circle:
+ // Draw circle with Radius1
+ for (int i = 0; i <= segments; i++)
+ {
+ float angle = (i / (float)segments) * 2.0f * (float)Math.PI;
+ float x = (float)Math.Cos(angle) * instance.Radius1;
+ float z = (float)Math.Sin(angle) * instance.Radius1;
+ Vector3 point = Vector3.Transform(new Vector3(x, 0, z), rotation) + position;
+ vertices.Add(point);
+ }
+ break;
+
+ case WayPointType.Ellipse:
+ // Draw ellipse with Radius1 and Radius2
+ for (int i = 0; i <= segments; i++)
+ {
+ float angle = (i / (float)segments) * 2.0f * (float)Math.PI;
+ float x = (float)Math.Cos(angle) * instance.Radius1;
+ float z = (float)Math.Sin(angle) * instance.Radius2;
+ Vector3 point = Vector3.Transform(new Vector3(x, 0, z), rotation) + position;
+ vertices.Add(point);
+ }
+ break;
+
+ case WayPointType.Square:
+ // Draw square with Radius1
+ {
+ float r = instance.Radius1;
+ Vector3[] corners = new Vector3[]
+ {
+ new Vector3(-r, 0, -r),
+ new Vector3(r, 0, -r),
+ new Vector3(r, 0, r),
+ new Vector3(-r, 0, r),
+ new Vector3(-r, 0, -r) // Close the loop
+ };
+ foreach (var corner in corners)
+ {
+ vertices.Add(Vector3.Transform(corner, rotation) + position);
+ }
+ }
+ break;
+
+ case WayPointType.Rectangle:
+ // Draw rectangle with Radius1 and Radius2
+ {
+ float r1 = instance.Radius1;
+ float r2 = instance.Radius2;
+ Vector3[] corners = new Vector3[]
+ {
+ new Vector3(-r1, 0, -r2),
+ new Vector3(r1, 0, -r2),
+ new Vector3(r1, 0, r2),
+ new Vector3(-r1, 0, r2),
+ new Vector3(-r1, 0, -r2) // Close the loop
+ };
+ foreach (var corner in corners)
+ {
+ vertices.Add(Vector3.Transform(corner, rotation) + position);
+ }
+ }
+ break;
+ }
+
+ // Draw the shape using line rendering
+ if (vertices.Count > 1)
+ {
+ for (int i = 0; i < vertices.Count - 1; i++)
+ {
+ AddLine(vertices[i], vertices[i + 1], color.To4());
+ }
+ }
+ }
+
private void DrawCardinalDirections(List textToDraw)
{
string[] messages;
From 0957e3e827a64a9ec05e20232245dd3d20bcc51f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:11:32 +0000
Subject: [PATCH 12/36] Fix compilation errors: update DrawWayPointShape
rendering and tests, rename Roll to Z Axis, fix form layout
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 62 ++++++++++++--
TombEditor/Forms/FormWayPoint.Designer.cs | 14 ++--
TombLib/TombLib.Test/WayPointInstanceTests.cs | 84 +++++++++++++------
3 files changed, 119 insertions(+), 41 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 18eb276f9..a58a74d32 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1486,7 +1486,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
// Number of segments for circles/ellipses
int segments = 32;
- var vertices = new List();
+ var points = new List();
switch (instance.Type)
{
@@ -1498,7 +1498,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
float x = (float)Math.Cos(angle) * instance.Radius1;
float z = (float)Math.Sin(angle) * instance.Radius1;
Vector3 point = Vector3.Transform(new Vector3(x, 0, z), rotation) + position;
- vertices.Add(point);
+ points.Add(point);
}
break;
@@ -1510,7 +1510,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
float x = (float)Math.Cos(angle) * instance.Radius1;
float z = (float)Math.Sin(angle) * instance.Radius2;
Vector3 point = Vector3.Transform(new Vector3(x, 0, z), rotation) + position;
- vertices.Add(point);
+ points.Add(point);
}
break;
@@ -1528,7 +1528,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
};
foreach (var corner in corners)
{
- vertices.Add(Vector3.Transform(corner, rotation) + position);
+ points.Add(Vector3.Transform(corner, rotation) + position);
}
}
break;
@@ -1548,18 +1548,62 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
};
foreach (var corner in corners)
{
- vertices.Add(Vector3.Transform(corner, rotation) + position);
+ points.Add(Vector3.Transform(corner, rotation) + position);
}
}
break;
}
- // Draw the shape using line rendering
- if (vertices.Count > 1)
+ // Convert points to line vertices using the same approach as flyby paths
+ if (points.Count > 1)
{
- for (int i = 0; i < vertices.Count - 1; i++)
+ var vertices = new List();
+ float th = 4.0f; // Line thickness
+
+ for (int i = 0; i < points.Count - 1; i++)
{
- AddLine(vertices[i], vertices[i + 1], color.To4());
+ var linePoints = new List()
+ {
+ new Vector3[]
+ {
+ points[i],
+ new Vector3(points[i].X + th, points[i].Y + th, points[i].Z + th),
+ new Vector3(points[i].X - th, points[i].Y + th, points[i].Z + th)
+ },
+ new Vector3[]
+ {
+ points[i + 1],
+ new Vector3(points[i + 1].X + th, points[i + 1].Y + th, points[i + 1].Z + th),
+ new Vector3(points[i + 1].X - th, points[i + 1].Y + th, points[i + 1].Z + th)
+ }
+ };
+
+ // Add triangles to form the line segment
+ for (int k = 0; k < _flybyPathIndices.Count; k++)
+ {
+ var v = new SolidVertex();
+ v.Position = linePoints[_flybyPathIndices[k].Y][_flybyPathIndices[k].X];
+ v.Color = color;
+ vertices.Add(v);
+ }
+ }
+
+ // Create temporary vertex buffer for this shape
+ if (vertices.Count > 0)
+ {
+ var shapeBuffer = SharpDX.Toolkit.Graphics.Buffer.Vertex.New(_legacyDevice,
+ vertices.ToArray(), SharpDX.Direct3D11.ResourceUsage.Dynamic);
+
+ // Draw the shape
+ var effect = _legacyDevice.Extensions.DeviceManager.___LegacyEffects["Solid"];
+ effect.Parameters["ModelViewProjection"].SetValue(_viewProjection.ToSharpDX());
+ effect.Parameters["Color"].SetValue(color);
+ effect.Techniques[0].Passes[0].Apply();
+ _legacyDevice.SetVertexBuffer(shapeBuffer);
+ _legacyDevice.SetVertexInputLayout(VertexInputLayout.FromBuffer(0, shapeBuffer));
+ _legacyDevice.Draw(PrimitiveType.TriangleList, vertices.Count);
+
+ shapeBuffer.Dispose();
}
}
}
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index c72e6506e..6074ccc6d 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -170,9 +170,9 @@ private void InitializeComponent()
this.lblRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.lblRoll.Location = new System.Drawing.Point(12, 223);
this.lblRoll.Name = "lblRoll";
- this.lblRoll.Size = new System.Drawing.Size(30, 13);
+ this.lblRoll.Size = new System.Drawing.Size(42, 13);
this.lblRoll.TabIndex = 16;
- this.lblRoll.Text = "Roll:";
+ this.lblRoll.Text = "Z Axis:";
//
// txtName
//
@@ -324,7 +324,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRotationY.Location = new System.Drawing.Point(82, 220);
+ this.numRotationY.Location = new System.Drawing.Point(82, 194);
this.numRotationY.LoopValues = false;
this.numRotationY.Maximum = new decimal(new int[] {
360,
@@ -333,7 +333,7 @@ private void InitializeComponent()
0});
this.numRotationY.Name = "numRotationY";
this.numRotationY.Size = new System.Drawing.Size(237, 22);
- this.numRotationY.TabIndex = 11;
+ this.numRotationY.TabIndex = 15;
//
// numRoll
//
@@ -343,7 +343,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRoll.Location = new System.Drawing.Point(82, 168);
+ this.numRoll.Location = new System.Drawing.Point(82, 220);
this.numRoll.LoopValues = false;
this.numRoll.Maximum = new decimal(new int[] {
360,
@@ -352,7 +352,7 @@ private void InitializeComponent()
0});
this.numRoll.Name = "numRoll";
this.numRoll.Size = new System.Drawing.Size(237, 22);
- this.numRoll.TabIndex = 13;
+ this.numRoll.TabIndex = 17;
//
// FormWayPoint
//
@@ -360,7 +360,7 @@ private void InitializeComponent()
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.butCancel;
- this.ClientSize = new System.Drawing.Size(331, 255);
+ this.ClientSize = new System.Drawing.Size(331, 333);
this.Controls.Add(this.numRoll);
this.Controls.Add(this.numRotationY);
this.Controls.Add(this.numRotationX);
diff --git a/TombLib/TombLib.Test/WayPointInstanceTests.cs b/TombLib/TombLib.Test/WayPointInstanceTests.cs
index 895945d86..5f7cd2320 100644
--- a/TombLib/TombLib.Test/WayPointInstanceTests.cs
+++ b/TombLib/TombLib.Test/WayPointInstanceTests.cs
@@ -6,49 +6,60 @@ namespace TombLib.Test
public class WayPointInstanceTests
{
[TestMethod]
- public void WayPoint_AutoNaming_DefaultName()
+ public void WayPoint_DefaultType()
{
// Arrange & Act
var wayPoint = new WayPointInstance();
// Assert
- Assert.AreEqual("WayPoint_0", wayPoint.Name, "Default WayPoint name should be 'WayPoint_0'");
+ Assert.AreEqual(WayPointType.Point, wayPoint.Type, "Default Type should be Point");
}
[TestMethod]
- public void WayPoint_AutoNaming_CustomBaseName()
+ public void WayPoint_TypeCanBeSet()
{
// Arrange
var wayPoint = new WayPointInstance();
// Act
- wayPoint.Name = "Camera";
- wayPoint.Number = 0;
+ wayPoint.Type = WayPointType.Bezier;
// Assert
- Assert.AreEqual("Camera_0", wayPoint.Name, "Custom name should be 'Camera_0'");
+ Assert.AreEqual(WayPointType.Bezier, wayPoint.Type, "Type should be settable to Bezier");
}
[TestMethod]
- public void WayPoint_AutoNaming_SequenceChange()
+ public void WayPoint_AutoNaming_SingularType()
{
- // Arrange
+ // Arrange & Act
var wayPoint = new WayPointInstance();
- wayPoint.Name = "MyPath";
+ wayPoint.Type = WayPointType.Circle;
+ wayPoint.BaseName = "Patrol";
- // Act
+ // Assert
+ Assert.AreEqual("Patrol", wayPoint.Name, "Singular type should use base name only");
+ }
+
+ [TestMethod]
+ public void WayPoint_AutoNaming_MultiPointType()
+ {
+ // Arrange & Act
+ var wayPoint = new WayPointInstance();
+ wayPoint.Type = WayPointType.Linear;
+ wayPoint.BaseName = "Path";
wayPoint.Number = 5;
// Assert
- Assert.AreEqual("MyPath_5", wayPoint.Name, "Name should update to 'MyPath_5' when number changes");
+ Assert.AreEqual("Path_5", wayPoint.Name, "Multi-point type should use BaseName_Number format");
}
[TestMethod]
- public void WayPoint_AutoNaming_NumberChange()
+ public void WayPoint_AutoNaming_NumberChangeLinear()
{
// Arrange
var wayPoint = new WayPointInstance();
- wayPoint.Name = "Camera";
+ wayPoint.Type = WayPointType.Linear;
+ wayPoint.BaseName = "Camera";
wayPoint.Number = 3;
// Act
@@ -59,41 +70,64 @@ public void WayPoint_AutoNaming_NumberChange()
}
[TestMethod]
- public void WayPoint_AutoNaming_PreservesBaseName()
+ public void WayPoint_AutoNaming_TypeChangeToSingular()
{
// Arrange
var wayPoint = new WayPointInstance();
- wayPoint.Name = "PatrolPath";
- wayPoint.Number = 0;
+ wayPoint.Type = WayPointType.Bezier;
+ wayPoint.BaseName = "Target";
+ wayPoint.Number = 3;
+ Assert.AreEqual("Target_3", wayPoint.Name, "Initially should be Target_3");
// Act
- wayPoint.Number = 10;
+ wayPoint.Type = WayPointType.Ellipse;
// Assert
- Assert.AreEqual("PatrolPath_10", wayPoint.Name, "Base name 'PatrolPath' should be preserved");
+ Assert.AreEqual("Target", wayPoint.Name, "After changing to singular type, name should be just 'Target'");
}
[TestMethod]
- public void WayPoint_DefaultPathType()
+ public void WayPoint_RequiresRadius_Circle()
{
// Arrange & Act
var wayPoint = new WayPointInstance();
+ wayPoint.Type = WayPointType.Circle;
// Assert
- Assert.AreEqual(PathType.Linear, wayPoint.PathType, "Default PathType should be Linear");
+ Assert.IsTrue(wayPoint.RequiresRadius(), "Circle should require radius");
}
[TestMethod]
- public void WayPoint_PathTypeCanBeSet()
+ public void WayPoint_RequiresTwoRadii_Ellipse()
{
- // Arrange
+ // Arrange & Act
var wayPoint = new WayPointInstance();
+ wayPoint.Type = WayPointType.Ellipse;
- // Act
- wayPoint.PathType = PathType.Bezier;
+ // Assert
+ Assert.IsTrue(wayPoint.RequiresTwoRadii(), "Ellipse should require two radii");
+ }
+
+ [TestMethod]
+ public void WayPoint_IsSingularType_Point()
+ {
+ // Arrange & Act
+ var wayPoint = new WayPointInstance();
+ wayPoint.Type = WayPointType.Point;
+
+ // Assert
+ Assert.IsTrue(wayPoint.IsSingularType(), "Point should be a singular type");
+ }
+
+ [TestMethod]
+ public void WayPoint_IsSingularType_Linear()
+ {
+ // Arrange & Act
+ var wayPoint = new WayPointInstance();
+ wayPoint.Type = WayPointType.Linear;
// Assert
- Assert.AreEqual(PathType.Bezier, wayPoint.PathType, "PathType should be settable to Bezier");
+ Assert.IsFalse(wayPoint.IsSingularType(), "Linear should not be a singular type");
}
[TestMethod]
From 3e533e8bdc3ef2ba9172e0e436184605fc79191c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:12:39 +0000
Subject: [PATCH 13/36] Implement batch type updates and selective compilation
for WayPoints
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.cs | 24 +++++++-
.../TombEngine/LevelCompilerTombEngine.cs | 59 +++++++++++++------
2 files changed, 63 insertions(+), 20 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 442042e65..b99f05d1b 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Windows.Forms;
using DarkUI.Forms;
using TombLib.LevelData;
@@ -80,16 +81,35 @@ private void UpdateFieldVisibility()
private void butOK_Click(object sender, EventArgs e)
{
+ var oldType = _wayPoint.Type;
+ var newType = (WayPointType)cmbType.SelectedIndex;
+ var sequence = (ushort)numSequence.Value;
+
_wayPoint.BaseName = txtName.Text;
- _wayPoint.Sequence = (ushort)numSequence.Value;
+ _wayPoint.Sequence = sequence;
_wayPoint.Number = (ushort)numNumber.Value;
- _wayPoint.Type = (WayPointType)cmbType.SelectedIndex;
+ _wayPoint.Type = newType;
_wayPoint.Radius1 = (float)numRadius1.Value;
_wayPoint.Radius2 = (float)numRadius2.Value;
_wayPoint.RotationX = (float)numRotationX.Value;
_wayPoint.RotationY = (float)numRotationY.Value;
_wayPoint.Roll = (float)numRoll.Value;
+ // Batch type update: if type changed, update all waypoints in the same sequence
+ if (oldType != newType && _editor?.Level != null)
+ {
+ foreach (var room in _editor.Level.ExistingRooms)
+ {
+ foreach (var obj in room.Objects.OfType())
+ {
+ if (obj.Sequence == sequence && obj != _wayPoint)
+ {
+ obj.Type = newType;
+ }
+ }
+ }
+ }
+
DialogResult = DialogResult.OK;
Close();
}
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index 00975b1a6..f66213e93 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -338,27 +338,50 @@ private void BuildCamerasAndSinks()
lastIndex = _flyByCameras[i].Index;
}
- // Collect waypoints
+ // Collect waypoints with selective compilation logic
+ // If any waypoint in a sequence is singular type, only compile that one
+ var waypointsBySequence = new Dictionary>();
foreach (var instance in _wayPointTable.Keys)
{
- Vector3 position = instance.Room.WorldPos + instance.Position;
- _wayPoints.Add(new TombEngineWayPoint
+ if (!waypointsBySequence.ContainsKey(instance.Sequence))
+ waypointsBySequence[instance.Sequence] = new List();
+ waypointsBySequence[instance.Sequence].Add(instance);
+ }
+
+ foreach (var sequencePair in waypointsBySequence)
+ {
+ var waypoints = sequencePair.Value;
+
+ // Check if any waypoint in this sequence is singular type
+ bool hasSingularType = waypoints.Any(wp => wp.IsSingularType());
+
+ // If there's a singular type, only compile singular type waypoints
+ // Otherwise, compile all waypoints
+ var waypointsToCompile = hasSingularType
+ ? waypoints.Where(wp => wp.IsSingularType()).ToList()
+ : waypoints;
+
+ foreach (var instance in waypointsToCompile)
{
- X = (int)Math.Round(position.X),
- Y = (int)Math.Round(-position.Y),
- Z = (int)Math.Round(position.Z),
- Room = _roomRemapping[instance.Room],
- RotationX = instance.RotationX,
- RotationY = instance.RotationY,
- Roll = instance.Roll,
- Sequence = instance.Sequence,
- Number = instance.Number,
- Type = (int)instance.Type,
- Radius1 = instance.Radius1,
- Radius2 = instance.Radius2,
- Name = instance.Name,
- LuaName = instance.LuaName ?? string.Empty
- });
+ Vector3 position = instance.Room.WorldPos + instance.Position;
+ _wayPoints.Add(new TombEngineWayPoint
+ {
+ X = (int)Math.Round(position.X),
+ Y = (int)Math.Round(-position.Y),
+ Z = (int)Math.Round(position.Z),
+ Room = _roomRemapping[instance.Room],
+ RotationX = instance.RotationX,
+ RotationY = instance.RotationY,
+ Roll = instance.Roll,
+ Sequence = instance.Sequence,
+ Number = instance.Number,
+ Type = (int)instance.Type,
+ Radius1 = instance.Radius1,
+ Radius2 = instance.Radius2,
+ Name = instance.Name,
+ LuaName = instance.LuaName ?? string.Empty
+ });
+ }
}
_wayPoints.Sort((x, y) =>
{
From 0286387494ff58ecd3ae5c32985cf587d7a68796 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:18:32 +0000
Subject: [PATCH 14/36] Fix GraphicsDevice.Extensions compilation error in
DrawWayPointShape
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index a58a74d32..47138e874 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1595,7 +1595,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
vertices.ToArray(), SharpDX.Direct3D11.ResourceUsage.Dynamic);
// Draw the shape
- var effect = _legacyDevice.Extensions.DeviceManager.___LegacyEffects["Solid"];
+ var effect = DeviceManager.DefaultDeviceManager.___LegacyEffects["Solid"];
effect.Parameters["ModelViewProjection"].SetValue(_viewProjection.ToSharpDX());
effect.Parameters["Color"].SetValue(color);
effect.Techniques[0].Passes[0].Apply();
From eae8208ea62927b3089cae36f0a2e786177245e7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:31:06 +0000
Subject: [PATCH 15/36] Major refactor: Remove Sequence, use Name as
identifier, disable fields instead of hiding, rename Radius to Dimension, add
name validation
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.Designer.cs | 210 ++++++++----------
TombEditor/Forms/FormWayPoint.cs | 134 +++++++++--
TombLib/TombLib.Test/WayPointInstanceTests.cs | 10 +-
.../TombEngine/LevelCompilerTombEngine.cs | 66 ++++--
.../LevelData/Compilers/TombEngine/Structs.cs | 1 -
.../Compilers/TombEngine/TombEngine.cs | 1 -
TombLib/TombLib/LevelData/IO/Prj2Loader.cs | 7 +-
TombLib/TombLib/LevelData/IO/Prj2Writer.cs | 19 +-
.../LevelData/Instances/WayPointInstance.cs | 47 ++--
9 files changed, 287 insertions(+), 208 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index 6074ccc6d..692793127 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -33,27 +33,24 @@ private void InitializeComponent()
this.butCancel = new DarkUI.Controls.DarkButton();
this.butOK = new DarkUI.Controls.DarkButton();
this.lblName = new DarkUI.Controls.DarkLabel();
- this.lblSequence = new DarkUI.Controls.DarkLabel();
this.lblNumber = new DarkUI.Controls.DarkLabel();
this.lblType = new DarkUI.Controls.DarkLabel();
- this.lblRadius1 = new DarkUI.Controls.DarkLabel();
- this.lblRadius2 = new DarkUI.Controls.DarkLabel();
+ this.lblDimension1 = new DarkUI.Controls.DarkLabel();
+ this.lblDimension2 = new DarkUI.Controls.DarkLabel();
this.lblRotationX = new DarkUI.Controls.DarkLabel();
this.lblRotationY = new DarkUI.Controls.DarkLabel();
this.lblRoll = new DarkUI.Controls.DarkLabel();
this.txtName = new DarkUI.Controls.DarkTextBox();
- this.numSequence = new DarkUI.Controls.DarkNumericUpDown();
this.numNumber = new DarkUI.Controls.DarkNumericUpDown();
this.cmbType = new DarkUI.Controls.DarkComboBox();
- this.numRadius1 = new DarkUI.Controls.DarkNumericUpDown();
- this.numRadius2 = new DarkUI.Controls.DarkNumericUpDown();
+ this.numDimension1 = new DarkUI.Controls.DarkNumericUpDown();
+ this.numDimension2 = new DarkUI.Controls.DarkNumericUpDown();
this.numRotationX = new DarkUI.Controls.DarkNumericUpDown();
this.numRotationY = new DarkUI.Controls.DarkNumericUpDown();
this.numRoll = new DarkUI.Controls.DarkNumericUpDown();
- ((System.ComponentModel.ISupportInitialize)(this.numSequence)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numNumber)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRadius1)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRadius2)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numDimension1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numDimension2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationX)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationY)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numRoll)).BeginInit();
@@ -64,10 +61,10 @@ private void InitializeComponent()
this.butCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.butCancel.Checked = false;
this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.butCancel.Location = new System.Drawing.Point(239, 298);
+ this.butCancel.Location = new System.Drawing.Point(239, 222);
this.butCancel.Name = "butCancel";
this.butCancel.Size = new System.Drawing.Size(80, 23);
- this.butCancel.TabIndex = 12;
+ this.butCancel.TabIndex = 17;
this.butCancel.Text = "Cancel";
this.butCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
@@ -76,10 +73,10 @@ private void InitializeComponent()
//
this.butOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.butOK.Checked = false;
- this.butOK.Location = new System.Drawing.Point(153, 298);
+ this.butOK.Location = new System.Drawing.Point(153, 222);
this.butOK.Name = "butOK";
this.butOK.Size = new System.Drawing.Size(80, 23);
- this.butOK.TabIndex = 11;
+ this.butOK.TabIndex = 16;
this.butOK.Text = "OK";
this.butOK.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
this.butOK.Click += new System.EventHandler(this.butOK_Click);
@@ -90,115 +87,87 @@ private void InitializeComponent()
this.lblName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.lblName.Location = new System.Drawing.Point(12, 15);
this.lblName.Name = "lblName";
- this.lblName.Size = new System.Drawing.Size(39, 13);
+ this.lblName.Size = new System.Drawing.Point(39, 13);
this.lblName.TabIndex = 0;
this.lblName.Text = "Name:";
//
- // lblSequence
- //
- this.lblSequence.AutoSize = true;
- this.lblSequence.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblSequence.Location = new System.Drawing.Point(12, 41);
- this.lblSequence.Name = "lblSequence";
- this.lblSequence.Size = new System.Drawing.Size(60, 13);
- this.lblSequence.TabIndex = 2;
- this.lblSequence.Text = "Sequence:";
- //
// lblNumber
//
this.lblNumber.AutoSize = true;
this.lblNumber.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblNumber.Location = new System.Drawing.Point(12, 67);
+ this.lblNumber.Location = new System.Drawing.Point(12, 41);
this.lblNumber.Name = "lblNumber";
this.lblNumber.Size = new System.Drawing.Size(51, 13);
- this.lblNumber.TabIndex = 4;
+ this.lblNumber.TabIndex = 2;
this.lblNumber.Text = "Number:";
//
// lblType
//
this.lblType.AutoSize = true;
this.lblType.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblType.Location = new System.Drawing.Point(12, 93);
+ this.lblType.Location = new System.Drawing.Point(12, 67);
this.lblType.Name = "lblType";
this.lblType.Size = new System.Drawing.Size(34, 13);
- this.lblType.TabIndex = 6;
+ this.lblType.TabIndex = 4;
this.lblType.Text = "Type:";
//
- // lblRadius1
+ // lblDimension1
//
- this.lblRadius1.AutoSize = true;
- this.lblRadius1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRadius1.Location = new System.Drawing.Point(12, 119);
- this.lblRadius1.Name = "lblRadius1";
- this.lblRadius1.Size = new System.Drawing.Size(52, 13);
- this.lblRadius1.TabIndex = 8;
- this.lblRadius1.Text = "Radius 1:";
+ this.lblDimension1.AutoSize = true;
+ this.lblDimension1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblDimension1.Location = new System.Drawing.Point(12, 93);
+ this.lblDimension1.Name = "lblDimension1";
+ this.lblDimension1.Size = new System.Drawing.Size(71, 13);
+ this.lblDimension1.TabIndex = 6;
+ this.lblDimension1.Text = "Dimension 1:";
//
- // lblRadius2
+ // lblDimension2
//
- this.lblRadius2.AutoSize = true;
- this.lblRadius2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRadius2.Location = new System.Drawing.Point(12, 145);
- this.lblRadius2.Name = "lblRadius2";
- this.lblRadius2.Size = new System.Drawing.Size(52, 13);
- this.lblRadius2.TabIndex = 10;
- this.lblRadius2.Text = "Radius 2:";
+ this.lblDimension2.AutoSize = true;
+ this.lblDimension2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
+ this.lblDimension2.Location = new System.Drawing.Point(12, 119);
+ this.lblDimension2.Name = "lblDimension2";
+ this.lblDimension2.Size = new System.Drawing.Size(71, 13);
+ this.lblDimension2.TabIndex = 8;
+ this.lblDimension2.Text = "Dimension 2:";
//
// lblRotationX
//
this.lblRotationX.AutoSize = true;
this.lblRotationX.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationX.Location = new System.Drawing.Point(12, 171);
+ this.lblRotationX.Location = new System.Drawing.Point(12, 145);
this.lblRotationX.Name = "lblRotationX";
this.lblRotationX.Size = new System.Drawing.Size(64, 13);
- this.lblRotationX.TabIndex = 12;
+ this.lblRotationX.TabIndex = 10;
this.lblRotationX.Text = "Rotation X:";
//
// lblRotationY
//
this.lblRotationY.AutoSize = true;
this.lblRotationY.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationY.Location = new System.Drawing.Point(12, 197);
+ this.lblRotationY.Location = new System.Drawing.Point(12, 171);
this.lblRotationY.Name = "lblRotationY";
this.lblRotationY.Size = new System.Drawing.Size(63, 13);
- this.lblRotationY.TabIndex = 14;
+ this.lblRotationY.TabIndex = 12;
this.lblRotationY.Text = "Rotation Y:";
//
// lblRoll
//
this.lblRoll.AutoSize = true;
this.lblRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRoll.Location = new System.Drawing.Point(12, 223);
+ this.lblRoll.Location = new System.Drawing.Point(12, 197);
this.lblRoll.Name = "lblRoll";
this.lblRoll.Size = new System.Drawing.Size(42, 13);
- this.lblRoll.TabIndex = 16;
+ this.lblRoll.TabIndex = 14;
this.lblRoll.Text = "Z Axis:";
//
// txtName
//
- this.txtName.Location = new System.Drawing.Point(82, 12);
+ this.txtName.Location = new System.Drawing.Point(92, 12);
this.txtName.Name = "txtName";
- this.txtName.Size = new System.Drawing.Size(237, 20);
+ this.txtName.Size = new System.Drawing.Size(227, 20);
this.txtName.TabIndex = 1;
//
- // numSequence
- //
- this.numSequence.IncrementAlternate = new decimal(new int[] {
- 10,
- 0,
- 0,
- 65536});
- this.numSequence.Location = new System.Drawing.Point(82, 38);
- this.numSequence.LoopValues = false;
- this.numSequence.Maximum = new decimal(new int[] {
- 65535,
- 0,
- 0,
- 0});
- this.numSequence.Name = "numSequence";
- this.numSequence.Size = new System.Drawing.Size(237, 22);
- this.numSequence.TabIndex = 3;
- //
// numNumber
//
this.numNumber.IncrementAlternate = new decimal(new int[] {
@@ -206,7 +175,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numNumber.Location = new System.Drawing.Point(82, 64);
+ this.numNumber.Location = new System.Drawing.Point(92, 38);
this.numNumber.LoopValues = false;
this.numNumber.Maximum = new decimal(new int[] {
65535,
@@ -214,8 +183,8 @@ private void InitializeComponent()
0,
0});
this.numNumber.Name = "numNumber";
- this.numNumber.Size = new System.Drawing.Size(237, 22);
- this.numNumber.TabIndex = 5;
+ this.numNumber.Size = new System.Drawing.Size(227, 22);
+ this.numNumber.TabIndex = 3;
//
// cmbType
//
@@ -228,65 +197,65 @@ private void InitializeComponent()
"Rectangle",
"Linear",
"Bezier"});
- this.cmbType.Location = new System.Drawing.Point(82, 90);
+ this.cmbType.Location = new System.Drawing.Point(92, 64);
this.cmbType.Name = "cmbType";
- this.cmbType.Size = new System.Drawing.Size(237, 23);
- this.cmbType.TabIndex = 7;
+ this.cmbType.Size = new System.Drawing.Size(227, 23);
+ this.cmbType.TabIndex = 5;
this.cmbType.SelectedIndexChanged += new System.EventHandler(this.cmbType_SelectedIndexChanged);
//
- // numRadius1
+ // numDimension1
//
- this.numRadius1.DecimalPlaces = 2;
- this.numRadius1.IncrementAlternate = new decimal(new int[] {
+ this.numDimension1.DecimalPlaces = 2;
+ this.numDimension1.IncrementAlternate = new decimal(new int[] {
100,
0,
0,
0});
- this.numRadius1.Location = new System.Drawing.Point(82, 116);
- this.numRadius1.LoopValues = false;
- this.numRadius1.Maximum = new decimal(new int[] {
+ this.numDimension1.Location = new System.Drawing.Point(92, 90);
+ this.numDimension1.LoopValues = false;
+ this.numDimension1.Maximum = new decimal(new int[] {
100000,
0,
0,
0});
- this.numRadius1.Minimum = new decimal(new int[] {
+ this.numDimension1.Minimum = new decimal(new int[] {
0,
0,
0,
0});
- this.numRadius1.Name = "numRadius1";
- this.numRadius1.Size = new System.Drawing.Size(237, 22);
- this.numRadius1.TabIndex = 9;
- this.numRadius1.Value = new decimal(new int[] {
+ this.numDimension1.Name = "numDimension1";
+ this.numDimension1.Size = new System.Drawing.Size(227, 22);
+ this.numDimension1.TabIndex = 7;
+ this.numDimension1.Value = new decimal(new int[] {
1024,
0,
0,
0});
//
- // numRadius2
+ // numDimension2
//
- this.numRadius2.DecimalPlaces = 2;
- this.numRadius2.IncrementAlternate = new decimal(new int[] {
+ this.numDimension2.DecimalPlaces = 2;
+ this.numDimension2.IncrementAlternate = new decimal(new int[] {
100,
0,
0,
0});
- this.numRadius2.Location = new System.Drawing.Point(82, 142);
- this.numRadius2.LoopValues = false;
- this.numRadius2.Maximum = new decimal(new int[] {
+ this.numDimension2.Location = new System.Drawing.Point(92, 116);
+ this.numDimension2.LoopValues = false;
+ this.numDimension2.Maximum = new decimal(new int[] {
100000,
0,
0,
0});
- this.numRadius2.Minimum = new decimal(new int[] {
+ this.numDimension2.Minimum = new decimal(new int[] {
0,
0,
0,
0});
- this.numRadius2.Name = "numRadius2";
- this.numRadius2.Size = new System.Drawing.Size(237, 22);
- this.numRadius2.TabIndex = 11;
- this.numRadius2.Value = new decimal(new int[] {
+ this.numDimension2.Name = "numDimension2";
+ this.numDimension2.Size = new System.Drawing.Size(227, 22);
+ this.numDimension2.TabIndex = 9;
+ this.numDimension2.Value = new decimal(new int[] {
1024,
0,
0,
@@ -300,7 +269,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRotationX.Location = new System.Drawing.Point(82, 168);
+ this.numRotationX.Location = new System.Drawing.Point(92, 142);
this.numRotationX.LoopValues = false;
this.numRotationX.Maximum = new decimal(new int[] {
90,
@@ -313,8 +282,8 @@ private void InitializeComponent()
0,
-2147483648});
this.numRotationX.Name = "numRotationX";
- this.numRotationX.Size = new System.Drawing.Size(237, 22);
- this.numRotationX.TabIndex = 13;
+ this.numRotationX.Size = new System.Drawing.Size(227, 22);
+ this.numRotationX.TabIndex = 11;
//
// numRotationY
//
@@ -324,7 +293,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRotationY.Location = new System.Drawing.Point(82, 194);
+ this.numRotationY.Location = new System.Drawing.Point(92, 168);
this.numRotationY.LoopValues = false;
this.numRotationY.Maximum = new decimal(new int[] {
360,
@@ -332,8 +301,8 @@ private void InitializeComponent()
0,
0});
this.numRotationY.Name = "numRotationY";
- this.numRotationY.Size = new System.Drawing.Size(237, 22);
- this.numRotationY.TabIndex = 15;
+ this.numRotationY.Size = new System.Drawing.Size(227, 22);
+ this.numRotationY.TabIndex = 13;
//
// numRoll
//
@@ -343,7 +312,7 @@ private void InitializeComponent()
0,
0,
65536});
- this.numRoll.Location = new System.Drawing.Point(82, 220);
+ this.numRoll.Location = new System.Drawing.Point(92, 194);
this.numRoll.LoopValues = false;
this.numRoll.Maximum = new decimal(new int[] {
360,
@@ -351,8 +320,8 @@ private void InitializeComponent()
0,
0});
this.numRoll.Name = "numRoll";
- this.numRoll.Size = new System.Drawing.Size(237, 22);
- this.numRoll.TabIndex = 17;
+ this.numRoll.Size = new System.Drawing.Size(227, 22);
+ this.numRoll.TabIndex = 15;
//
// FormWayPoint
//
@@ -360,24 +329,22 @@ private void InitializeComponent()
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.butCancel;
- this.ClientSize = new System.Drawing.Size(331, 333);
+ this.ClientSize = new System.Drawing.Size(331, 255);
this.Controls.Add(this.numRoll);
this.Controls.Add(this.numRotationY);
this.Controls.Add(this.numRotationX);
- this.Controls.Add(this.numRadius2);
- this.Controls.Add(this.numRadius1);
+ this.Controls.Add(this.numDimension2);
+ this.Controls.Add(this.numDimension1);
this.Controls.Add(this.cmbType);
this.Controls.Add(this.numNumber);
- this.Controls.Add(this.numSequence);
this.Controls.Add(this.txtName);
this.Controls.Add(this.lblRoll);
this.Controls.Add(this.lblRotationY);
this.Controls.Add(this.lblRotationX);
- this.Controls.Add(this.lblRadius2);
- this.Controls.Add(this.lblRadius1);
+ this.Controls.Add(this.lblDimension2);
+ this.Controls.Add(this.lblDimension1);
this.Controls.Add(this.lblType);
this.Controls.Add(this.lblNumber);
- this.Controls.Add(this.lblSequence);
this.Controls.Add(this.lblName);
this.Controls.Add(this.butCancel);
this.Controls.Add(this.butOK);
@@ -390,10 +357,9 @@ private void InitializeComponent()
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "WayPoint";
this.Load += new System.EventHandler(this.FormWayPoint_Load);
- ((System.ComponentModel.ISupportInitialize)(this.numSequence)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numNumber)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRadius1)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRadius2)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numDimension1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.numDimension2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationX)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numRotationY)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numRoll)).EndInit();
@@ -407,20 +373,18 @@ private void InitializeComponent()
private DarkButton butOK;
private DarkButton butCancel;
private DarkLabel lblName;
- private DarkLabel lblSequence;
private DarkLabel lblNumber;
private DarkLabel lblType;
- private DarkLabel lblRadius1;
- private DarkLabel lblRadius2;
+ private DarkLabel lblDimension1;
+ private DarkLabel lblDimension2;
private DarkLabel lblRotationX;
private DarkLabel lblRotationY;
private DarkLabel lblRoll;
private DarkTextBox txtName;
- private DarkNumericUpDown numSequence;
private DarkNumericUpDown numNumber;
private DarkComboBox cmbType;
- private DarkNumericUpDown numRadius1;
- private DarkNumericUpDown numRadius2;
+ private DarkNumericUpDown numDimension1;
+ private DarkNumericUpDown numDimension2;
private DarkNumericUpDown numRotationX;
private DarkNumericUpDown numRotationY;
private DarkNumericUpDown numRoll;
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index b99f05d1b..e82e8bb85 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -29,12 +29,26 @@ private void butCancel_Click(object sender, EventArgs e)
private void FormWayPoint_Load(object sender, EventArgs e)
{
- txtName.Text = _wayPoint.BaseName;
- numSequence.Value = _wayPoint.Sequence;
+ // Extract base name from full name (remove _number suffix if present)
+ string baseName = _wayPoint.Name;
+ if (!_wayPoint.IsSingularType())
+ {
+ int lastUnderscore = baseName.LastIndexOf('_');
+ if (lastUnderscore >= 0)
+ {
+ string suffix = baseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ baseName = baseName.Substring(0, lastUnderscore);
+ }
+ }
+ }
+
+ txtName.Text = baseName;
numNumber.Value = _wayPoint.Number;
cmbType.SelectedIndex = (int)_wayPoint.Type;
- numRadius1.Value = (decimal)_wayPoint.Radius1;
- numRadius2.Value = (decimal)_wayPoint.Radius2;
+ numDimension1.Value = (decimal)_wayPoint.Radius1;
+ numDimension2.Value = (decimal)_wayPoint.Radius2;
numRotationX.Value = (decimal)_wayPoint.RotationX;
numRotationY.Value = (decimal)_wayPoint.RotationY;
numRoll.Value = (decimal)_wayPoint.Roll;
@@ -58,53 +72,127 @@ private void UpdateFieldVisibility()
type == WayPointType.Square ||
type == WayPointType.Rectangle;
- // Number field only for multi-point types
- lblNumber.Visible = !isSingular;
- numNumber.Visible = !isSingular;
+ // Number field only for multi-point types - disable instead of hide
+ numNumber.Enabled = !isSingular;
- // Radius fields only for shape types
- bool requiresRadius = type == WayPointType.Circle ||
+ // Dimension fields only for shape types - disable instead of hide
+ bool requiresDimension = type == WayPointType.Circle ||
type == WayPointType.Ellipse ||
type == WayPointType.Square ||
type == WayPointType.Rectangle;
- lblRadius1.Visible = requiresRadius;
- numRadius1.Visible = requiresRadius;
+ numDimension1.Enabled = requiresDimension;
- // Radius2 only for ellipse and rectangle
- bool requiresTwoRadii = type == WayPointType.Ellipse ||
+ // Dimension2 only for ellipse and rectangle - disable instead of hide
+ bool requiresTwoDimensions = type == WayPointType.Ellipse ||
type == WayPointType.Rectangle;
- lblRadius2.Visible = requiresTwoRadii;
- numRadius2.Visible = requiresTwoRadii;
+ numDimension2.Enabled = requiresTwoDimensions;
}
private void butOK_Click(object sender, EventArgs e)
{
+ // Validate name is not empty
+ string newName = txtName.Text.Trim();
+ if (string.IsNullOrEmpty(newName))
+ {
+ DarkMessageBox.Show(this, "WayPoint name cannot be empty.", "Validation Error",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
var oldType = _wayPoint.Type;
var newType = (WayPointType)cmbType.SelectedIndex;
- var sequence = (ushort)numSequence.Value;
+
+ // Extract old base name for comparison
+ string oldBaseName = _wayPoint.Name;
+ if (!_wayPoint.IsSingularType())
+ {
+ int lastUnderscore = oldBaseName.LastIndexOf('_');
+ if (lastUnderscore >= 0)
+ {
+ string suffix = oldBaseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ oldBaseName = oldBaseName.Substring(0, lastUnderscore);
+ }
+ }
+ }
+
+ // Check for duplicate names only if name changed
+ bool nameChanged = oldBaseName != newName;
+ if (nameChanged && _editor?.Level != null)
+ {
+ foreach (var room in _editor.Level.ExistingRooms)
+ {
+ foreach (var obj in room.Objects.OfType())
+ {
+ if (obj != _wayPoint)
+ {
+ // Extract base name from existing waypoint
+ string existingBaseName = obj.Name;
+ if (!obj.IsSingularType())
+ {
+ int lastUnderscore = existingBaseName.LastIndexOf('_');
+ if (lastUnderscore >= 0)
+ {
+ string suffix = existingBaseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ existingBaseName = existingBaseName.Substring(0, lastUnderscore);
+ }
+ }
+ }
+
+ if (existingBaseName == newName)
+ {
+ DarkMessageBox.Show(this, $"A WayPoint with the name '{newName}' already exists.", "Duplicate Name",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+ }
+ }
+ }
+ }
- _wayPoint.BaseName = txtName.Text;
- _wayPoint.Sequence = sequence;
+ // Update waypoint properties
+ _wayPoint.Name = newName;
_wayPoint.Number = (ushort)numNumber.Value;
_wayPoint.Type = newType;
- _wayPoint.Radius1 = (float)numRadius1.Value;
- _wayPoint.Radius2 = (float)numRadius2.Value;
+ _wayPoint.Radius1 = (float)numDimension1.Value;
+ _wayPoint.Radius2 = (float)numDimension2.Value;
_wayPoint.RotationX = (float)numRotationX.Value;
_wayPoint.RotationY = (float)numRotationY.Value;
_wayPoint.Roll = (float)numRoll.Value;
- // Batch type update: if type changed, update all waypoints in the same sequence
+ // Batch type update: if type changed, update all waypoints with the same name
if (oldType != newType && _editor?.Level != null)
{
foreach (var room in _editor.Level.ExistingRooms)
{
foreach (var obj in room.Objects.OfType())
{
- if (obj.Sequence == sequence && obj != _wayPoint)
+ if (obj != _wayPoint)
{
- obj.Type = newType;
+ // Extract base name
+ string objBaseName = obj.Name;
+ if (!obj.IsSingularType())
+ {
+ int lastUnderscore = objBaseName.LastIndexOf('_');
+ if (lastUnderscore >= 0)
+ {
+ string suffix = objBaseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ objBaseName = objBaseName.Substring(0, lastUnderscore);
+ }
+ }
+ }
+
+ if (objBaseName == newName)
+ {
+ obj.Type = newType;
+ }
}
}
}
diff --git a/TombLib/TombLib.Test/WayPointInstanceTests.cs b/TombLib/TombLib.Test/WayPointInstanceTests.cs
index 5f7cd2320..7f19491db 100644
--- a/TombLib/TombLib.Test/WayPointInstanceTests.cs
+++ b/TombLib/TombLib.Test/WayPointInstanceTests.cs
@@ -34,7 +34,7 @@ public void WayPoint_AutoNaming_SingularType()
// Arrange & Act
var wayPoint = new WayPointInstance();
wayPoint.Type = WayPointType.Circle;
- wayPoint.BaseName = "Patrol";
+ wayPoint.Name = "Patrol";
// Assert
Assert.AreEqual("Patrol", wayPoint.Name, "Singular type should use base name only");
@@ -46,11 +46,11 @@ public void WayPoint_AutoNaming_MultiPointType()
// Arrange & Act
var wayPoint = new WayPointInstance();
wayPoint.Type = WayPointType.Linear;
- wayPoint.BaseName = "Path";
+ wayPoint.Name = "Path";
wayPoint.Number = 5;
// Assert
- Assert.AreEqual("Path_5", wayPoint.Name, "Multi-point type should use BaseName_Number format");
+ Assert.AreEqual("Path_5", wayPoint.Name, "Multi-point type should use Name_Number format");
}
[TestMethod]
@@ -59,7 +59,7 @@ public void WayPoint_AutoNaming_NumberChangeLinear()
// Arrange
var wayPoint = new WayPointInstance();
wayPoint.Type = WayPointType.Linear;
- wayPoint.BaseName = "Camera";
+ wayPoint.Name = "Camera";
wayPoint.Number = 3;
// Act
@@ -75,7 +75,7 @@ public void WayPoint_AutoNaming_TypeChangeToSingular()
// Arrange
var wayPoint = new WayPointInstance();
wayPoint.Type = WayPointType.Bezier;
- wayPoint.BaseName = "Target";
+ wayPoint.Name = "Target";
wayPoint.Number = 3;
Assert.AreEqual("Target_3", wayPoint.Name, "Initially should be Target_3");
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index f66213e93..6fd2041c5 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -338,21 +338,40 @@ private void BuildCamerasAndSinks()
lastIndex = _flyByCameras[i].Index;
}
- // Collect waypoints with selective compilation logic
- // If any waypoint in a sequence is singular type, only compile that one
- var waypointsBySequence = new Dictionary>();
+ // Collect waypoints with name-based grouping and selective compilation logic
+ // Skip waypoints without a name
+ // If any waypoint with a name is singular type, only compile singular types with that name
+ var waypointsByName = new Dictionary>();
foreach (var instance in _wayPointTable.Keys)
{
- if (!waypointsBySequence.ContainsKey(instance.Sequence))
- waypointsBySequence[instance.Sequence] = new List();
- waypointsBySequence[instance.Sequence].Add(instance);
+ // Extract base name
+ string baseName = instance.Name;
+ if (string.IsNullOrEmpty(baseName))
+ continue; // Skip waypoints without a name
+
+ if (!instance.IsSingularType())
+ {
+ int lastUnderscore = baseName.LastIndexOf('_');
+ if (lastUnderscore >= 0)
+ {
+ string suffix = baseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ baseName = baseName.Substring(0, lastUnderscore);
+ }
+ }
+ }
+
+ if (!waypointsByName.ContainsKey(baseName))
+ waypointsByName[baseName] = new List();
+ waypointsByName[baseName].Add(instance);
}
- foreach (var sequencePair in waypointsBySequence)
+ foreach (var namePair in waypointsByName)
{
- var waypoints = sequencePair.Value;
+ var waypoints = namePair.Value;
- // Check if any waypoint in this sequence is singular type
+ // Check if any waypoint with this name is singular type
bool hasSingularType = waypoints.Any(wp => wp.IsSingularType());
// If there's a singular type, only compile singular type waypoints
@@ -373,7 +392,6 @@ private void BuildCamerasAndSinks()
RotationX = instance.RotationX,
RotationY = instance.RotationY,
Roll = instance.Roll,
- Sequence = instance.Sequence,
Number = instance.Number,
Type = (int)instance.Type,
Radius1 = instance.Radius1,
@@ -383,27 +401,41 @@ private void BuildCamerasAndSinks()
});
}
}
+
+ // Sort by name, then number
_wayPoints.Sort((x, y) =>
{
- int seqCompare = x.Sequence.CompareTo(y.Sequence);
- if (seqCompare != 0) return seqCompare;
+ int nameCompare = string.Compare(x.Name, y.Name, StringComparison.Ordinal);
+ if (nameCompare != 0) return nameCompare;
return x.Number.CompareTo(y.Number);
});
// Check waypoint duplicates
- lastSeq = -1;
+ string lastName = "";
lastIndex = -1;
for (int i = 0; i < _wayPoints.Count; i++)
{
- if (_wayPoints[i].Sequence != lastSeq)
+ // Extract base name for comparison
+ string baseName = _wayPoints[i].Name;
+ if (baseName.Contains("_"))
+ {
+ int lastUnderscore = baseName.LastIndexOf('_');
+ string suffix = baseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ baseName = baseName.Substring(0, lastUnderscore);
+ }
+ }
+
+ if (baseName != lastName)
{
- lastSeq = _wayPoints[i].Sequence;
+ lastName = baseName;
lastIndex = -1;
}
- if (_wayPoints[i].Number == lastIndex && _wayPoints[i].Sequence == lastSeq)
- _progressReporter.ReportWarn($"Warning: waypoint sequence {_wayPoints[i].Sequence} has duplicated waypoint with ID {lastIndex}");
+ if (_wayPoints[i].Number == lastIndex && baseName == lastName)
+ _progressReporter.ReportWarn($"Warning: waypoint '{baseName}' has duplicated waypoint with number {lastIndex}");
lastIndex = _wayPoints[i].Number;
}
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
index 69a833cba..cc1b805a8 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
@@ -595,7 +595,6 @@ public struct TombEngineWayPoint
public float RotationX;
public float RotationY;
public float Roll;
- public ushort Sequence;
public ushort Number;
public int Type;
public float Radius1;
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
index b0afe65e8..6bcd879ec 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
@@ -87,7 +87,6 @@ private void WriteLevelTombEngine()
writer.Write(waypoint.RotationX);
writer.Write(waypoint.RotationY);
writer.Write(waypoint.Roll);
- writer.Write(waypoint.Sequence);
writer.Write(waypoint.Number);
writer.Write(waypoint.Type);
writer.Write(waypoint.Radius1);
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
index fc79e3504..ff7aed0ea 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
@@ -1409,12 +1409,15 @@ private static bool LoadObjects(ChunkReader chunkIO, ChunkId idOuter, LevelSetti
instance.SetArbitaryRotationsYX(chunkIO.Raw.ReadSingle(), chunkIO.Raw.ReadSingle());
instance.Roll = chunkIO.Raw.ReadSingle();
instance.ScriptId = ReadOptionalLEB128Int(chunkIO.Raw);
- instance.BaseName = chunkIO.Raw.ReadStringUTF8();
+ string baseName = chunkIO.Raw.ReadStringUTF8();
instance.Number = LEB128.ReadUShort(chunkIO.Raw);
- instance.Sequence = LEB128.ReadUShort(chunkIO.Raw);
instance.Type = (WayPointType)LEB128.ReadInt(chunkIO.Raw);
instance.Radius1 = chunkIO.Raw.ReadSingle();
instance.Radius2 = chunkIO.Raw.ReadSingle();
+
+ // Set the name (will auto-format with _number if multi-point type)
+ instance.Name = baseName;
+
addObject(instance);
newObjects.TryAdd(objectID, instance);
}
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
index 1e31a1cd9..88a4aa7e0 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
@@ -689,9 +689,24 @@ private static void WriteObjects(ChunkWriter chunkIO, IEnumerable= 0)
+ {
+ string suffix = baseName.Substring(lastUnderscore + 1);
+ if (ushort.TryParse(suffix, out _))
+ {
+ baseName = baseName.Substring(0, lastUnderscore);
+ }
+ }
+ }
+
+ chunkIO.Raw.WriteStringUTF8(baseName);
LEB128.Write(chunkIO.Raw, instance.Number);
- LEB128.Write(chunkIO.Raw, instance.Sequence);
LEB128.Write(chunkIO.Raw, (int)instance.Type);
chunkIO.Raw.Write(instance.Radius1);
chunkIO.Raw.Write(instance.Radius2);
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
index 33fd4c653..33860f763 100644
--- a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -16,26 +16,19 @@ public enum WayPointType
public class WayPointInstance : PositionAndScriptBasedObjectInstance, IRotateableYXRoll
{
- private string _baseName = "WayPoint";
- private ushort _sequence;
+ private string _name = "";
private ushort _number;
private WayPointType _type = WayPointType.Point;
- public string BaseName
- {
- get { return _baseName; }
- set { _baseName = value ?? "WayPoint"; }
- }
-
public string Name
{
get
{
- // Singular types don't have number suffix
+ // Singular types use name as-is
if (IsSingularType())
- return _baseName;
+ return _name;
else
- return _baseName + "_" + _number;
+ return _name + "_" + _number;
}
set
{
@@ -48,31 +41,25 @@ public string Name
string suffix = value.Substring(lastUnderscore + 1);
if (ushort.TryParse(suffix, out _))
{
- _baseName = value.Substring(0, lastUnderscore);
+ _name = value.Substring(0, lastUnderscore);
}
else
{
- _baseName = value;
+ _name = value;
}
}
else
{
- _baseName = value;
+ _name = value;
}
}
else
{
- _baseName = "WayPoint";
+ _name = "";
}
}
}
- public ushort Sequence
- {
- get { return _sequence; }
- set { _sequence = value; }
- }
-
public ushort Number
{
get { return _number; }
@@ -119,23 +106,22 @@ public WayPointInstance(ObjectInstance selectedObject = null)
{
if (selectedObject is WayPointInstance prevWayPoint)
{
- var currSeq = prevWayPoint.Sequence;
var currNum = (ushort)(prevWayPoint.Number + 1);
// Only push forward if it's a multi-point type
if (!prevWayPoint.IsSingularType())
{
- // Push next waypoints in sequence forward
+ // Push next waypoints with same name forward
var level = selectedObject.Room.Level;
+ var prevName = prevWayPoint._name;
foreach (var room in level.ExistingRooms)
foreach (var instance in room.Objects.OfType())
- if (instance.Sequence == currSeq && instance.Number >= currNum)
+ if (instance._name == prevName && instance.Number >= currNum)
instance.Number++;
}
- Sequence = currSeq;
Number = prevWayPoint.IsSingularType() ? (ushort)0 : currNum;
- _baseName = prevWayPoint._baseName;
+ _name = prevWayPoint._name;
Type = prevWayPoint.Type;
Radius1 = prevWayPoint.Radius1;
Radius2 = prevWayPoint.Radius2;
@@ -174,7 +160,6 @@ public override string ToString()
{
return "WayPoint " +
", Name = " + Name +
- ", Sequence = " + Sequence +
(IsSingularType() ? "" : ", Number = " + Number) +
", Type = " + Type +
" (" + (Room?.ToString() ?? "NULL") + ")" +
@@ -183,12 +168,6 @@ public override string ToString()
GetScriptIDOrName(false);
}
- public string ShortName() => "WayPoint " + (IsSingularType() ? "" : "(" + Sequence + ":" + Number + ") ") + GetScriptIDOrName() + " (" + (Room?.ToString() ?? "NULL") + ")";
-
- public override void CopyDependentLevelSettings(Room.CopyDependentLevelSettingsArgs args)
- {
- base.CopyDependentLevelSettings(args);
- Sequence = args.ReassociateFlyBySequence(Sequence);
- }
+ public string ShortName() => "WayPoint " + Name + (IsSingularType() ? "" : " (" + Number + ")") + " " + GetScriptIDOrName() + " (" + (Room?.ToString() ?? "NULL") + ")";
}
}
From 9015a2148ba695bb964edbab913f1b9567ddc9a1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:45:14 +0000
Subject: [PATCH 16/36] Fix compilation errors: remove Sequence references and
fix Size type in Designer
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 8 ++++----
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 4 ++--
TombEditor/Forms/FormWayPoint.Designer.cs | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 47138e874..3eb8f41e7 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -198,8 +198,8 @@ private void DrawFlybyPath(Effect effect)
}
// Add the path of waypoints
- if (_editor.SelectedObject is WayPointInstance &&
- AddWayPointPath(((WayPointInstance)_editor.SelectedObject).Sequence))
+ if (_editor.SelectedObject is WayPointInstance waypoint &&
+ AddWayPointPath(waypoint.Name))
{
_legacyDevice.SetRasterizerState(_legacyDevice.RasterizerStates.CullNone);
_legacyDevice.SetVertexBuffer(_wayPointPathVertexBuffer);
@@ -1158,8 +1158,8 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
var color = new Vector4(1.0f, 0.5f, 0.0f, 1.0f); // Orange color for waypoints
- if (_editor.SelectedObject is WayPointInstance && (_editor.SelectedObject as WayPointInstance).Sequence == instance.Sequence)
- color = MathC.GetRandomColorByIndex(instance.Sequence, 32, 0.7f);
+ if (_editor.SelectedObject is WayPointInstance selectedWaypoint && selectedWaypoint.Name == instance.Name)
+ color = MathC.GetRandomColorByIndex(instance.Name.GetHashCode(), 32, 0.7f);
if (_highlightedObjects.Contains(instance))
{
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index 6d6d14095..36c7f1622 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -183,7 +183,7 @@ private bool AddFlybyPath(int sequence)
return true;
}
- private bool AddWayPointPath(int sequence)
+ private bool AddWayPointPath(string name)
{
// Collect all waypoints
var wayPoints = new List();
@@ -191,7 +191,7 @@ private bool AddWayPointPath(int sequence)
foreach (var room in _editor.Level.ExistingRooms)
foreach (var instance in room.Objects.OfType())
{
- if (instance.Sequence == sequence)
+ if (instance.Name == name)
wayPoints.Add(instance);
}
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index 692793127..49cce114a 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -87,7 +87,7 @@ private void InitializeComponent()
this.lblName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
this.lblName.Location = new System.Drawing.Point(12, 15);
this.lblName.Name = "lblName";
- this.lblName.Size = new System.Drawing.Point(39, 13);
+ this.lblName.Size = new System.Drawing.Size(39, 13);
this.lblName.TabIndex = 0;
this.lblName.Text = "Name:";
//
From d1d63c559848f53d4ae20b9358b9ce8cded8497a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:49:33 +0000
Subject: [PATCH 17/36] Fix remaining compilation errors: remove last Sequence
reference and fix variable names
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 6 +++++-
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 6 ++++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 3eb8f41e7..795b549aa 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1169,9 +1169,13 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
if (_editor.SelectedObject == instance)
{
// Add text message
+ string label = instance.IsSingularType() ?
+ $"WayPoint ({instance.Name}) " :
+ $"WayPoint ({instance.Name}:{instance.Number}) ";
+
textToDraw.Add(CreateTextTagForObject(
instance.RotationPositionMatrix * _viewProjection,
- "WayPoint (" + instance.Sequence + ":" + instance.Number + ") " +
+ label +
instance.GetScriptIDOrName() + "\n" +
GetObjectPositionString(instance.Room, instance) + GetObjectTriggerString(instance)));
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index 36c7f1622..31c353137 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -207,8 +207,10 @@ private bool AddWayPointPath(string name)
// Initialize variables for vertex buffer preparation
var vertices = new List();
- var startColor = MathC.GetRandomColorByIndex(sequence, 32, 0.7f);
- var endColor = MathC.GetRandomColorByIndex(sequence, 32, 0.3f);
+ // Use name hash for color selection
+ int nameHash = name.GetHashCode();
+ var startColor = MathC.GetRandomColorByIndex(nameHash, 32, 0.7f);
+ var endColor = MathC.GetRandomColorByIndex(nameHash, 32, 0.3f);
float th = _flybyPathThickness;
From 0fb2dd5b14649f66e3db7b24ae693ce94d2fdefd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:53:53 +0000
Subject: [PATCH 18/36] Fix crash: ensure name hash is positive and non-zero
for GetRandomColorByIndex
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 6 +++++-
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 5 +++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 795b549aa..4b756f4fb 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1159,7 +1159,11 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
var color = new Vector4(1.0f, 0.5f, 0.0f, 1.0f); // Orange color for waypoints
if (_editor.SelectedObject is WayPointInstance selectedWaypoint && selectedWaypoint.Name == instance.Name)
- color = MathC.GetRandomColorByIndex(instance.Name.GetHashCode(), 32, 0.7f);
+ {
+ int nameHash = Math.Abs(instance.Name?.GetHashCode() ?? 1);
+ if (nameHash == 0) nameHash = 1;
+ color = MathC.GetRandomColorByIndex(nameHash, 32, 0.7f);
+ }
if (_highlightedObjects.Contains(instance))
{
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index 31c353137..0605cb70f 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -207,8 +207,9 @@ private bool AddWayPointPath(string name)
// Initialize variables for vertex buffer preparation
var vertices = new List();
- // Use name hash for color selection
- int nameHash = name.GetHashCode();
+ // Use name hash for color selection (ensure positive and non-zero)
+ int nameHash = Math.Abs(name?.GetHashCode() ?? 1);
+ if (nameHash == 0) nameHash = 1;
var startColor = MathC.GetRandomColorByIndex(nameHash, 32, 0.7f);
var endColor = MathC.GetRandomColorByIndex(nameHash, 32, 0.3f);
From f378c8645f5253cbe63444d1885399e9222f9f62 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 21:02:43 +0000
Subject: [PATCH 19/36] Fix waypoint copy bugs: sync LuaName with Name, improve
label format, expose BaseName property
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 6 ++---
.../LevelData/Instances/WayPointInstance.cs | 26 +++++++++++++++++--
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 4b756f4fb..1dcf31f5a 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1172,10 +1172,10 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
if (_editor.SelectedObject == instance)
{
- // Add text message
+ // Add text message with format: Name (Number) for multi-point, just Name for singular
string label = instance.IsSingularType() ?
- $"WayPoint ({instance.Name}) " :
- $"WayPoint ({instance.Name}:{instance.Number}) ";
+ $"{instance.BaseName} " :
+ $"{instance.BaseName} ({instance.Number}) ";
textToDraw.Add(CreateTextTagForObject(
instance.RotationPositionMatrix * _viewProjection,
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
index 33860f763..d325fb13b 100644
--- a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -20,6 +20,12 @@ public class WayPointInstance : PositionAndScriptBasedObjectInstance, IRotateabl
private ushort _number;
private WayPointType _type = WayPointType.Point;
+ // Public property to access the base name (without number suffix)
+ public string BaseName
+ {
+ get { return _name; }
+ }
+
public string Name
{
get
@@ -57,19 +63,32 @@ public string Name
{
_name = "";
}
+
+ // Update LuaName to match Name
+ LuaName = Name;
}
}
public ushort Number
{
get { return _number; }
- set { _number = value; }
+ set
+ {
+ _number = value;
+ // Update LuaName when number changes
+ LuaName = Name;
+ }
}
public WayPointType Type
{
get { return _type; }
- set { _type = value; }
+ set
+ {
+ _type = value;
+ // Update LuaName when type changes (affects Name format)
+ LuaName = Name;
+ }
}
public float Radius1 { get; set; } = 1024.0f; // Default radius in units
@@ -130,6 +149,9 @@ public WayPointInstance(ObjectInstance selectedObject = null)
RotationX = prevWayPoint.RotationX;
RotationY = prevWayPoint.RotationY;
Roll = prevWayPoint.Roll;
+
+ // Set LuaName to match Name
+ LuaName = Name;
}
}
From 1cdc2fcd0db00ce49097ca9cfe944e155aef5476 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 21:09:06 +0000
Subject: [PATCH 20/36] Fix path preview and batch type update: use BaseName
for comparisons
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DHelpers.cs | 2 +-
TombEditor/Forms/FormWayPoint.cs | 24 +++----------------
2 files changed, 4 insertions(+), 22 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
index 0605cb70f..b699dad9e 100644
--- a/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DHelpers.cs
@@ -191,7 +191,7 @@ private bool AddWayPointPath(string name)
foreach (var room in _editor.Level.ExistingRooms)
foreach (var instance in room.Objects.OfType())
{
- if (instance.Name == name)
+ if (instance.BaseName == name)
wayPoints.Add(instance);
}
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index e82e8bb85..aa4c22033 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -165,34 +165,16 @@ private void butOK_Click(object sender, EventArgs e)
_wayPoint.RotationY = (float)numRotationY.Value;
_wayPoint.Roll = (float)numRoll.Value;
- // Batch type update: if type changed, update all waypoints with the same name
+ // Batch type update: if type changed, update all waypoints with the same original base name
if (oldType != newType && _editor?.Level != null)
{
foreach (var room in _editor.Level.ExistingRooms)
{
foreach (var obj in room.Objects.OfType())
{
- if (obj != _wayPoint)
+ if (obj != _wayPoint && obj.BaseName == oldBaseName)
{
- // Extract base name
- string objBaseName = obj.Name;
- if (!obj.IsSingularType())
- {
- int lastUnderscore = objBaseName.LastIndexOf('_');
- if (lastUnderscore >= 0)
- {
- string suffix = objBaseName.Substring(lastUnderscore + 1);
- if (ushort.TryParse(suffix, out _))
- {
- objBaseName = objBaseName.Substring(0, lastUnderscore);
- }
- }
- }
-
- if (objBaseName == newName)
- {
- obj.Type = newType;
- }
+ obj.Type = newType;
}
}
}
From 6246ea565322e65f4f84175c30ef61e7361b039b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 21:16:36 +0000
Subject: [PATCH 21/36] Increase waypoint shape thickness from 4.0f to 16.0f
for better visibility
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 1dcf31f5a..5247c1cb4 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1566,7 +1566,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
if (points.Count > 1)
{
var vertices = new List();
- float th = 4.0f; // Line thickness
+ float th = 16.0f; // Line thickness (increased for better visibility)
for (int i = 0; i < points.Count - 1; i++)
{
From 7e20ff9eb30424b119836cced0885af336c7ce53 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 22:03:24 +0000
Subject: [PATCH 22/36] Fix path preview, duplicate name validation, and add
double-sided shape rendering
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 13 ++++++-
TombEditor/Forms/FormWayPoint.cs | 44 ++++++++++------------
2 files changed, 30 insertions(+), 27 deletions(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 5247c1cb4..f237f55cb 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -199,7 +199,7 @@ private void DrawFlybyPath(Effect effect)
// Add the path of waypoints
if (_editor.SelectedObject is WayPointInstance waypoint &&
- AddWayPointPath(waypoint.Name))
+ AddWayPointPath(waypoint.BaseName))
{
_legacyDevice.SetRasterizerState(_legacyDevice.RasterizerStates.CullNone);
_legacyDevice.SetVertexBuffer(_wayPointPathVertexBuffer);
@@ -1586,7 +1586,7 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
}
};
- // Add triangles to form the line segment
+ // Add triangles to form the line segment (both sides for double-sided rendering)
for (int k = 0; k < _flybyPathIndices.Count; k++)
{
var v = new SolidVertex();
@@ -1594,6 +1594,15 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
v.Color = color;
vertices.Add(v);
}
+
+ // Add reversed triangles for the back side
+ for (int k = _flybyPathIndices.Count - 1; k >= 0; k--)
+ {
+ var v = new SolidVertex();
+ v.Position = linePoints[_flybyPathIndices[k].Y][_flybyPathIndices[k].X];
+ v.Color = color;
+ vertices.Add(v);
+ }
}
// Create temporary vertex buffer for this shape
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index aa4c22033..5aaa5bc51 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -119,37 +119,31 @@ private void butOK_Click(object sender, EventArgs e)
}
}
- // Check for duplicate names only if name changed
- bool nameChanged = oldBaseName != newName;
- if (nameChanged && _editor?.Level != null)
+ // Check for duplicate full names (base name + number combination)
+ // Only check if this creates an actual duplicate, not just same base name
+ bool nameOrNumberChanged = oldBaseName != newName || _wayPoint.Number != (ushort)numNumber.Value;
+ if (nameOrNumberChanged && _editor?.Level != null)
{
+ // Build the full name that will be generated
+ ushort newNumber = (ushort)numNumber.Value;
+ var newTypeToCheck = (WayPointType)cmbType.SelectedIndex;
+ bool isSingularNew = newTypeToCheck == WayPointType.Point ||
+ newTypeToCheck == WayPointType.Circle ||
+ newTypeToCheck == WayPointType.Ellipse ||
+ newTypeToCheck == WayPointType.Square ||
+ newTypeToCheck == WayPointType.Rectangle;
+
+ string fullNameToCheck = isSingularNew ? newName : $"{newName}_{newNumber}";
+
foreach (var room in _editor.Level.ExistingRooms)
{
foreach (var obj in room.Objects.OfType())
{
- if (obj != _wayPoint)
+ if (obj != _wayPoint && obj.Name == fullNameToCheck)
{
- // Extract base name from existing waypoint
- string existingBaseName = obj.Name;
- if (!obj.IsSingularType())
- {
- int lastUnderscore = existingBaseName.LastIndexOf('_');
- if (lastUnderscore >= 0)
- {
- string suffix = existingBaseName.Substring(lastUnderscore + 1);
- if (ushort.TryParse(suffix, out _))
- {
- existingBaseName = existingBaseName.Substring(0, lastUnderscore);
- }
- }
- }
-
- if (existingBaseName == newName)
- {
- DarkMessageBox.Show(this, $"A WayPoint with the name '{newName}' already exists.", "Duplicate Name",
- MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
+ DarkMessageBox.Show(this, $"A WayPoint with the name '{fullNameToCheck}' already exists.", "Duplicate Name",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
}
}
}
From 73638236b9aadbca8c302c2113926137be0c77bc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 22:15:39 +0000
Subject: [PATCH 23/36] Enforce unique base names: prevent different types with
same name unless numbers differ
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.cs | 39 ++++++++++++++++++--------------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 5aaa5bc51..463672ceb 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -119,31 +119,36 @@ private void butOK_Click(object sender, EventArgs e)
}
}
- // Check for duplicate full names (base name + number combination)
- // Only check if this creates an actual duplicate, not just same base name
- bool nameOrNumberChanged = oldBaseName != newName || _wayPoint.Number != (ushort)numNumber.Value;
- if (nameOrNumberChanged && _editor?.Level != null)
+ // Check for duplicate names with validation rules:
+ // 1. Base name must be unique across all waypoints
+ // 2. Exception: same base name allowed ONLY if numbers are different
+ // (e.g., "test_0" and "test_1" are OK, but "test" Circle and "test" Point are NOT OK)
+ bool nameChanged = oldBaseName != newName;
+ bool numberChanged = _wayPoint.Number != (ushort)numNumber.Value;
+
+ if ((nameChanged || numberChanged) && _editor?.Level != null)
{
- // Build the full name that will be generated
ushort newNumber = (ushort)numNumber.Value;
- var newTypeToCheck = (WayPointType)cmbType.SelectedIndex;
- bool isSingularNew = newTypeToCheck == WayPointType.Point ||
- newTypeToCheck == WayPointType.Circle ||
- newTypeToCheck == WayPointType.Ellipse ||
- newTypeToCheck == WayPointType.Square ||
- newTypeToCheck == WayPointType.Rectangle;
-
- string fullNameToCheck = isSingularNew ? newName : $"{newName}_{newNumber}";
foreach (var room in _editor.Level.ExistingRooms)
{
foreach (var obj in room.Objects.OfType())
{
- if (obj != _wayPoint && obj.Name == fullNameToCheck)
+ if (obj == _wayPoint)
+ continue;
+
+ // Check if another waypoint uses this base name
+ if (obj.BaseName == newName)
{
- DarkMessageBox.Show(this, $"A WayPoint with the name '{fullNameToCheck}' already exists.", "Duplicate Name",
- MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
+ // Same base name found - only allowed if numbers are different
+ if (obj.Number == newNumber)
+ {
+ // Same base name AND same number = duplicate
+ DarkMessageBox.Show(this, $"A WayPoint with the name '{newName}' and number {newNumber} already exists.", "Duplicate Name",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+ // Different numbers - this is OK
}
}
}
From 1e10cdae9234e8c3c5f59c98c1988928d5d7d48e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 22:35:39 +0000
Subject: [PATCH 24/36] Add scale gizmo, fix copy behavior, reset number on
type change, and check LuaName conflicts
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.cs | 92 ++++++++++++++++++-
.../LevelData/Instances/WayPointInstance.cs | 41 ++++++++-
2 files changed, 131 insertions(+), 2 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 463672ceb..bf0388da7 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -58,6 +58,28 @@ private void FormWayPoint_Load(object sender, EventArgs e)
private void cmbType_SelectedIndexChanged(object sender, EventArgs e)
{
+ WayPointType newType = (WayPointType)cmbType.SelectedIndex;
+ WayPointType oldType = _wayPoint.Type;
+
+ // If type changed to singular, reset number to 0
+ bool newIsSingular = newType == WayPointType.Point ||
+ newType == WayPointType.Circle ||
+ newType == WayPointType.Ellipse ||
+ newType == WayPointType.Square ||
+ newType == WayPointType.Rectangle;
+
+ bool oldIsSingular = oldType == WayPointType.Point ||
+ oldType == WayPointType.Circle ||
+ oldType == WayPointType.Ellipse ||
+ oldType == WayPointType.Square ||
+ oldType == WayPointType.Rectangle;
+
+ // Reset number to 0 when changing to singular type from multi-point type
+ if (newIsSingular && !oldIsSingular)
+ {
+ numNumber.Value = 0;
+ }
+
UpdateFieldVisibility();
}
@@ -154,9 +176,77 @@ private void butOK_Click(object sender, EventArgs e)
}
}
+ // Generate the new full name that will be used
+ string fullNewName = newName;
+ ushort newNumber = (ushort)numNumber.Value;
+
+ bool newIsSingular = newType == WayPointType.Point ||
+ newType == WayPointType.Circle ||
+ newType == WayPointType.Ellipse ||
+ newType == WayPointType.Square ||
+ newType == WayPointType.Rectangle;
+
+ if (!newIsSingular)
+ {
+ fullNewName = newName + "_" + newNumber;
+ }
+
+ // Check if a LuaName with this value already exists
+ if (_editor?.Level != null)
+ {
+ foreach (var room in _editor.Level.ExistingRooms)
+ {
+ foreach (var obj in room.Objects)
+ {
+ if (obj == _wayPoint)
+ continue;
+
+ if (obj is PositionAndScriptBasedObjectInstance scriptObj)
+ {
+ if (!string.IsNullOrEmpty(scriptObj.LuaName) && scriptObj.LuaName == fullNewName)
+ {
+ // LuaName conflict - clear the LuaName for this waypoint
+ _wayPoint.LuaName = "";
+ DarkMessageBox.Show(this, $"A LuaName '{fullNewName}' already exists for another object. The LuaName for this waypoint has been cleared. Please generate a new LuaName.", "LuaName Conflict",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+
+ // Update waypoint properties but with cleared LuaName
+ _wayPoint.Name = newName;
+ _wayPoint.Number = newNumber;
+ _wayPoint.Type = newType;
+ _wayPoint.Radius1 = (float)numDimension1.Value;
+ _wayPoint.Radius2 = (float)numDimension2.Value;
+ _wayPoint.RotationX = (float)numRotationX.Value;
+ _wayPoint.RotationY = (float)numRotationY.Value;
+ _wayPoint.Roll = (float)numRoll.Value;
+
+ // Batch type update
+ if (oldType != newType && _editor?.Level != null)
+ {
+ foreach (var r in _editor.Level.ExistingRooms)
+ {
+ foreach (var o in r.Objects.OfType())
+ {
+ if (o != _wayPoint && o.BaseName == oldBaseName)
+ {
+ o.Type = newType;
+ }
+ }
+ }
+ }
+
+ DialogResult = DialogResult.OK;
+ Close();
+ return;
+ }
+ }
+ }
+ }
+ }
+
// Update waypoint properties
_wayPoint.Name = newName;
- _wayPoint.Number = (ushort)numNumber.Value;
+ _wayPoint.Number = newNumber;
_wayPoint.Type = newType;
_wayPoint.Radius1 = (float)numDimension1.Value;
_wayPoint.Radius2 = (float)numDimension2.Value;
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
index d325fb13b..162020bd6 100644
--- a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -14,7 +14,7 @@ public enum WayPointType
Bezier // Multi-point bezier path
}
- public class WayPointInstance : PositionAndScriptBasedObjectInstance, IRotateableYXRoll
+ public class WayPointInstance : PositionAndScriptBasedObjectInstance, IRotateableYXRoll, ISizeable
{
private string _name = "";
private ushort _number;
@@ -94,6 +94,26 @@ public WayPointType Type
public float Radius1 { get; set; } = 1024.0f; // Default radius in units
public float Radius2 { get; set; } = 1024.0f; // Default radius in units
+ // ISizeable implementation for scale gizmo support on X and Z axes
+ public Vector3 DefaultSize => new Vector3(Radius1 * 2, 0, Radius2 * 2);
+
+ public Vector3 Size
+ {
+ get => new Vector3(Radius1 * 2, 0, Radius2 * 2);
+ set
+ {
+ // Only allow scaling on X and Z axes for shapes
+ if (RequiresRadius())
+ {
+ Radius1 = Math.Max(0.01f, value.X / 2);
+ if (RequiresTwoRadii())
+ Radius2 = Math.Max(0.01f, value.Z / 2);
+ else
+ Radius2 = Radius1; // Keep them synchronized for single-radius shapes
+ }
+ }
+ }
+
private float _rotationX { get; set; }
private float _rotationY { get; set; }
private float _roll { get; set; }
@@ -155,6 +175,25 @@ public WayPointInstance(ObjectInstance selectedObject = null)
}
}
+ public override ObjectInstance Clone()
+ {
+ var clone = (WayPointInstance)base.Clone();
+
+ // For singular types, clear the name so user must provide a new one
+ if (IsSingularType())
+ {
+ clone._name = "";
+ clone.LuaName = "";
+ }
+ else
+ {
+ // For multi-point types, increment the number
+ clone.Number = (ushort)(Number + 1);
+ }
+
+ return clone;
+ }
+
/// Degrees in the range [-90, 90]
public float RotationX
{
From f08346de46cc299f88b7c0bab5aaaf11320e6a36 Mon Sep 17 00:00:00 2001
From: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date: Sat, 24 Jan 2026 17:39:18 -0500
Subject: [PATCH 25/36] Fix label
---
TombEditor/Forms/FormWayPoint.Designer.cs | 480 +++++++++-------------
TombEditor/Forms/FormWayPoint.resx | 120 ++++++
2 files changed, 321 insertions(+), 279 deletions(-)
create mode 100644 TombEditor/Forms/FormWayPoint.resx
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index 49cce114a..04b30bc7a 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -30,342 +30,264 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
- this.butCancel = new DarkUI.Controls.DarkButton();
- this.butOK = new DarkUI.Controls.DarkButton();
- this.lblName = new DarkUI.Controls.DarkLabel();
- this.lblNumber = new DarkUI.Controls.DarkLabel();
- this.lblType = new DarkUI.Controls.DarkLabel();
- this.lblDimension1 = new DarkUI.Controls.DarkLabel();
- this.lblDimension2 = new DarkUI.Controls.DarkLabel();
- this.lblRotationX = new DarkUI.Controls.DarkLabel();
- this.lblRotationY = new DarkUI.Controls.DarkLabel();
- this.lblRoll = new DarkUI.Controls.DarkLabel();
- this.txtName = new DarkUI.Controls.DarkTextBox();
- this.numNumber = new DarkUI.Controls.DarkNumericUpDown();
- this.cmbType = new DarkUI.Controls.DarkComboBox();
- this.numDimension1 = new DarkUI.Controls.DarkNumericUpDown();
- this.numDimension2 = new DarkUI.Controls.DarkNumericUpDown();
- this.numRotationX = new DarkUI.Controls.DarkNumericUpDown();
- this.numRotationY = new DarkUI.Controls.DarkNumericUpDown();
- this.numRoll = new DarkUI.Controls.DarkNumericUpDown();
- ((System.ComponentModel.ISupportInitialize)(this.numNumber)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numDimension1)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numDimension2)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRotationX)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRotationY)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRoll)).BeginInit();
- this.SuspendLayout();
+ butCancel = new DarkButton();
+ butOK = new DarkButton();
+ lblName = new DarkLabel();
+ lblNumber = new DarkLabel();
+ lblType = new DarkLabel();
+ lblDimension1 = new DarkLabel();
+ lblDimension2 = new DarkLabel();
+ lblRotationX = new DarkLabel();
+ lblRotationY = new DarkLabel();
+ lblRoll = new DarkLabel();
+ txtName = new DarkTextBox();
+ numNumber = new DarkNumericUpDown();
+ cmbType = new DarkComboBox();
+ numDimension1 = new DarkNumericUpDown();
+ numDimension2 = new DarkNumericUpDown();
+ numRotationX = new DarkNumericUpDown();
+ numRotationY = new DarkNumericUpDown();
+ numRoll = new DarkNumericUpDown();
+ ((System.ComponentModel.ISupportInitialize)numNumber).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)numDimension1).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)numDimension2).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)numRotationX).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)numRotationY).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)numRoll).BeginInit();
+ SuspendLayout();
//
// butCancel
//
- this.butCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.butCancel.Checked = false;
- this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.butCancel.Location = new System.Drawing.Point(239, 222);
- this.butCancel.Name = "butCancel";
- this.butCancel.Size = new System.Drawing.Size(80, 23);
- this.butCancel.TabIndex = 17;
- this.butCancel.Text = "Cancel";
- this.butCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
- this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
+ butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
+ butCancel.Checked = false;
+ butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ butCancel.Location = new System.Drawing.Point(239, 222);
+ butCancel.Name = "butCancel";
+ butCancel.Size = new System.Drawing.Size(80, 23);
+ butCancel.TabIndex = 17;
+ butCancel.Text = "Cancel";
+ butCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+ butCancel.Click += butCancel_Click;
//
// butOK
//
- this.butOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.butOK.Checked = false;
- this.butOK.Location = new System.Drawing.Point(153, 222);
- this.butOK.Name = "butOK";
- this.butOK.Size = new System.Drawing.Size(80, 23);
- this.butOK.TabIndex = 16;
- this.butOK.Text = "OK";
- this.butOK.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
- this.butOK.Click += new System.EventHandler(this.butOK_Click);
+ butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
+ butOK.Checked = false;
+ butOK.Location = new System.Drawing.Point(153, 222);
+ butOK.Name = "butOK";
+ butOK.Size = new System.Drawing.Size(80, 23);
+ butOK.TabIndex = 16;
+ butOK.Text = "OK";
+ butOK.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+ butOK.Click += butOK_Click;
//
// lblName
//
- this.lblName.AutoSize = true;
- this.lblName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblName.Location = new System.Drawing.Point(12, 15);
- this.lblName.Name = "lblName";
- this.lblName.Size = new System.Drawing.Size(39, 13);
- this.lblName.TabIndex = 0;
- this.lblName.Text = "Name:";
+ lblName.AutoSize = true;
+ lblName.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblName.Location = new System.Drawing.Point(12, 15);
+ lblName.Name = "lblName";
+ lblName.Size = new System.Drawing.Size(39, 13);
+ lblName.TabIndex = 0;
+ lblName.Text = "Name:";
//
// lblNumber
//
- this.lblNumber.AutoSize = true;
- this.lblNumber.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblNumber.Location = new System.Drawing.Point(12, 41);
- this.lblNumber.Name = "lblNumber";
- this.lblNumber.Size = new System.Drawing.Size(51, 13);
- this.lblNumber.TabIndex = 2;
- this.lblNumber.Text = "Number:";
+ lblNumber.AutoSize = true;
+ lblNumber.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblNumber.Location = new System.Drawing.Point(12, 41);
+ lblNumber.Name = "lblNumber";
+ lblNumber.Size = new System.Drawing.Size(51, 13);
+ lblNumber.TabIndex = 2;
+ lblNumber.Text = "Number:";
//
// lblType
//
- this.lblType.AutoSize = true;
- this.lblType.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblType.Location = new System.Drawing.Point(12, 67);
- this.lblType.Name = "lblType";
- this.lblType.Size = new System.Drawing.Size(34, 13);
- this.lblType.TabIndex = 4;
- this.lblType.Text = "Type:";
+ lblType.AutoSize = true;
+ lblType.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblType.Location = new System.Drawing.Point(12, 67);
+ lblType.Name = "lblType";
+ lblType.Size = new System.Drawing.Size(32, 13);
+ lblType.TabIndex = 4;
+ lblType.Text = "Type:";
//
// lblDimension1
//
- this.lblDimension1.AutoSize = true;
- this.lblDimension1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblDimension1.Location = new System.Drawing.Point(12, 93);
- this.lblDimension1.Name = "lblDimension1";
- this.lblDimension1.Size = new System.Drawing.Size(71, 13);
- this.lblDimension1.TabIndex = 6;
- this.lblDimension1.Text = "Dimension 1:";
+ lblDimension1.AutoSize = true;
+ lblDimension1.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblDimension1.Location = new System.Drawing.Point(12, 93);
+ lblDimension1.Name = "lblDimension1";
+ lblDimension1.Size = new System.Drawing.Size(74, 13);
+ lblDimension1.TabIndex = 6;
+ lblDimension1.Text = "Dimension 1:";
//
// lblDimension2
//
- this.lblDimension2.AutoSize = true;
- this.lblDimension2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblDimension2.Location = new System.Drawing.Point(12, 119);
- this.lblDimension2.Name = "lblDimension2";
- this.lblDimension2.Size = new System.Drawing.Size(71, 13);
- this.lblDimension2.TabIndex = 8;
- this.lblDimension2.Text = "Dimension 2:";
+ lblDimension2.AutoSize = true;
+ lblDimension2.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblDimension2.Location = new System.Drawing.Point(12, 119);
+ lblDimension2.Name = "lblDimension2";
+ lblDimension2.Size = new System.Drawing.Size(74, 13);
+ lblDimension2.TabIndex = 8;
+ lblDimension2.Text = "Dimension 2:";
//
// lblRotationX
//
- this.lblRotationX.AutoSize = true;
- this.lblRotationX.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationX.Location = new System.Drawing.Point(12, 145);
- this.lblRotationX.Name = "lblRotationX";
- this.lblRotationX.Size = new System.Drawing.Size(64, 13);
- this.lblRotationX.TabIndex = 10;
- this.lblRotationX.Text = "Rotation X:";
+ lblRotationX.AutoSize = true;
+ lblRotationX.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblRotationX.Location = new System.Drawing.Point(12, 145);
+ lblRotationX.Name = "lblRotationX";
+ lblRotationX.Size = new System.Drawing.Size(64, 13);
+ lblRotationX.TabIndex = 10;
+ lblRotationX.Text = "Rotation X:";
//
// lblRotationY
//
- this.lblRotationY.AutoSize = true;
- this.lblRotationY.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRotationY.Location = new System.Drawing.Point(12, 171);
- this.lblRotationY.Name = "lblRotationY";
- this.lblRotationY.Size = new System.Drawing.Size(63, 13);
- this.lblRotationY.TabIndex = 12;
- this.lblRotationY.Text = "Rotation Y:";
+ lblRotationY.AutoSize = true;
+ lblRotationY.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblRotationY.Location = new System.Drawing.Point(12, 171);
+ lblRotationY.Name = "lblRotationY";
+ lblRotationY.Size = new System.Drawing.Size(63, 13);
+ lblRotationY.TabIndex = 12;
+ lblRotationY.Text = "Rotation Y:";
//
// lblRoll
//
- this.lblRoll.AutoSize = true;
- this.lblRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(220)))), ((int)(((byte)(220)))));
- this.lblRoll.Location = new System.Drawing.Point(12, 197);
- this.lblRoll.Name = "lblRoll";
- this.lblRoll.Size = new System.Drawing.Size(42, 13);
- this.lblRoll.TabIndex = 14;
- this.lblRoll.Text = "Z Axis:";
+ lblRoll.AutoSize = true;
+ lblRoll.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblRoll.Location = new System.Drawing.Point(12, 197);
+ lblRoll.Name = "lblRoll";
+ lblRoll.Size = new System.Drawing.Size(64, 13);
+ lblRoll.TabIndex = 14;
+ lblRoll.Text = "Rotation Z:";
//
// txtName
//
- this.txtName.Location = new System.Drawing.Point(92, 12);
- this.txtName.Name = "txtName";
- this.txtName.Size = new System.Drawing.Size(227, 20);
- this.txtName.TabIndex = 1;
+ txtName.Location = new System.Drawing.Point(92, 12);
+ txtName.Name = "txtName";
+ txtName.Size = new System.Drawing.Size(227, 22);
+ txtName.TabIndex = 1;
//
// numNumber
//
- this.numNumber.IncrementAlternate = new decimal(new int[] {
- 10,
- 0,
- 0,
- 65536});
- this.numNumber.Location = new System.Drawing.Point(92, 38);
- this.numNumber.LoopValues = false;
- this.numNumber.Maximum = new decimal(new int[] {
- 65535,
- 0,
- 0,
- 0});
- this.numNumber.Name = "numNumber";
- this.numNumber.Size = new System.Drawing.Size(227, 22);
- this.numNumber.TabIndex = 3;
+ numNumber.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
+ numNumber.Location = new System.Drawing.Point(92, 38);
+ numNumber.LoopValues = false;
+ numNumber.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
+ numNumber.Name = "numNumber";
+ numNumber.Size = new System.Drawing.Size(227, 22);
+ numNumber.TabIndex = 3;
//
// cmbType
//
- this.cmbType.FormattingEnabled = true;
- this.cmbType.Items.AddRange(new object[] {
- "Point",
- "Circle",
- "Ellipse",
- "Square",
- "Rectangle",
- "Linear",
- "Bezier"});
- this.cmbType.Location = new System.Drawing.Point(92, 64);
- this.cmbType.Name = "cmbType";
- this.cmbType.Size = new System.Drawing.Size(227, 23);
- this.cmbType.TabIndex = 5;
- this.cmbType.SelectedIndexChanged += new System.EventHandler(this.cmbType_SelectedIndexChanged);
+ cmbType.FormattingEnabled = true;
+ cmbType.Items.AddRange(new object[] { "Point", "Circle", "Ellipse", "Square", "Rectangle", "Linear", "Bezier" });
+ cmbType.Location = new System.Drawing.Point(92, 64);
+ cmbType.Name = "cmbType";
+ cmbType.Size = new System.Drawing.Size(227, 23);
+ cmbType.TabIndex = 5;
+ cmbType.SelectedIndexChanged += cmbType_SelectedIndexChanged;
//
// numDimension1
//
- this.numDimension1.DecimalPlaces = 2;
- this.numDimension1.IncrementAlternate = new decimal(new int[] {
- 100,
- 0,
- 0,
- 0});
- this.numDimension1.Location = new System.Drawing.Point(92, 90);
- this.numDimension1.LoopValues = false;
- this.numDimension1.Maximum = new decimal(new int[] {
- 100000,
- 0,
- 0,
- 0});
- this.numDimension1.Minimum = new decimal(new int[] {
- 0,
- 0,
- 0,
- 0});
- this.numDimension1.Name = "numDimension1";
- this.numDimension1.Size = new System.Drawing.Size(227, 22);
- this.numDimension1.TabIndex = 7;
- this.numDimension1.Value = new decimal(new int[] {
- 1024,
- 0,
- 0,
- 0});
+ numDimension1.DecimalPlaces = 2;
+ numDimension1.IncrementAlternate = new decimal(new int[] { 100, 0, 0, 0 });
+ numDimension1.Location = new System.Drawing.Point(92, 90);
+ numDimension1.LoopValues = false;
+ numDimension1.Maximum = new decimal(new int[] { 100000, 0, 0, 0 });
+ numDimension1.Name = "numDimension1";
+ numDimension1.Size = new System.Drawing.Size(227, 22);
+ numDimension1.TabIndex = 7;
+ numDimension1.Value = new decimal(new int[] { 1024, 0, 0, 0 });
//
// numDimension2
//
- this.numDimension2.DecimalPlaces = 2;
- this.numDimension2.IncrementAlternate = new decimal(new int[] {
- 100,
- 0,
- 0,
- 0});
- this.numDimension2.Location = new System.Drawing.Point(92, 116);
- this.numDimension2.LoopValues = false;
- this.numDimension2.Maximum = new decimal(new int[] {
- 100000,
- 0,
- 0,
- 0});
- this.numDimension2.Minimum = new decimal(new int[] {
- 0,
- 0,
- 0,
- 0});
- this.numDimension2.Name = "numDimension2";
- this.numDimension2.Size = new System.Drawing.Size(227, 22);
- this.numDimension2.TabIndex = 9;
- this.numDimension2.Value = new decimal(new int[] {
- 1024,
- 0,
- 0,
- 0});
+ numDimension2.DecimalPlaces = 2;
+ numDimension2.IncrementAlternate = new decimal(new int[] { 100, 0, 0, 0 });
+ numDimension2.Location = new System.Drawing.Point(92, 116);
+ numDimension2.LoopValues = false;
+ numDimension2.Maximum = new decimal(new int[] { 100000, 0, 0, 0 });
+ numDimension2.Name = "numDimension2";
+ numDimension2.Size = new System.Drawing.Size(227, 22);
+ numDimension2.TabIndex = 9;
+ numDimension2.Value = new decimal(new int[] { 1024, 0, 0, 0 });
//
// numRotationX
//
- this.numRotationX.DecimalPlaces = 2;
- this.numRotationX.IncrementAlternate = new decimal(new int[] {
- 10,
- 0,
- 0,
- 65536});
- this.numRotationX.Location = new System.Drawing.Point(92, 142);
- this.numRotationX.LoopValues = false;
- this.numRotationX.Maximum = new decimal(new int[] {
- 90,
- 0,
- 0,
- 0});
- this.numRotationX.Minimum = new decimal(new int[] {
- 90,
- 0,
- 0,
- -2147483648});
- this.numRotationX.Name = "numRotationX";
- this.numRotationX.Size = new System.Drawing.Size(227, 22);
- this.numRotationX.TabIndex = 11;
+ numRotationX.DecimalPlaces = 2;
+ numRotationX.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
+ numRotationX.Location = new System.Drawing.Point(92, 142);
+ numRotationX.LoopValues = false;
+ numRotationX.Maximum = new decimal(new int[] { 90, 0, 0, 0 });
+ numRotationX.Minimum = new decimal(new int[] { 90, 0, 0, int.MinValue });
+ numRotationX.Name = "numRotationX";
+ numRotationX.Size = new System.Drawing.Size(227, 22);
+ numRotationX.TabIndex = 11;
//
// numRotationY
//
- this.numRotationY.DecimalPlaces = 2;
- this.numRotationY.IncrementAlternate = new decimal(new int[] {
- 10,
- 0,
- 0,
- 65536});
- this.numRotationY.Location = new System.Drawing.Point(92, 168);
- this.numRotationY.LoopValues = false;
- this.numRotationY.Maximum = new decimal(new int[] {
- 360,
- 0,
- 0,
- 0});
- this.numRotationY.Name = "numRotationY";
- this.numRotationY.Size = new System.Drawing.Size(227, 22);
- this.numRotationY.TabIndex = 13;
+ numRotationY.DecimalPlaces = 2;
+ numRotationY.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
+ numRotationY.Location = new System.Drawing.Point(92, 168);
+ numRotationY.LoopValues = false;
+ numRotationY.Maximum = new decimal(new int[] { 360, 0, 0, 0 });
+ numRotationY.Name = "numRotationY";
+ numRotationY.Size = new System.Drawing.Size(227, 22);
+ numRotationY.TabIndex = 13;
//
// numRoll
//
- this.numRoll.DecimalPlaces = 2;
- this.numRoll.IncrementAlternate = new decimal(new int[] {
- 10,
- 0,
- 0,
- 65536});
- this.numRoll.Location = new System.Drawing.Point(92, 194);
- this.numRoll.LoopValues = false;
- this.numRoll.Maximum = new decimal(new int[] {
- 360,
- 0,
- 0,
- 0});
- this.numRoll.Name = "numRoll";
- this.numRoll.Size = new System.Drawing.Size(227, 22);
- this.numRoll.TabIndex = 15;
+ numRoll.DecimalPlaces = 2;
+ numRoll.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
+ numRoll.Location = new System.Drawing.Point(92, 194);
+ numRoll.LoopValues = false;
+ numRoll.Maximum = new decimal(new int[] { 360, 0, 0, 0 });
+ numRoll.Name = "numRoll";
+ numRoll.Size = new System.Drawing.Size(227, 22);
+ numRoll.TabIndex = 15;
//
// FormWayPoint
//
- this.AcceptButton = this.butOK;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.CancelButton = this.butCancel;
- this.ClientSize = new System.Drawing.Size(331, 255);
- this.Controls.Add(this.numRoll);
- this.Controls.Add(this.numRotationY);
- this.Controls.Add(this.numRotationX);
- this.Controls.Add(this.numDimension2);
- this.Controls.Add(this.numDimension1);
- this.Controls.Add(this.cmbType);
- this.Controls.Add(this.numNumber);
- this.Controls.Add(this.txtName);
- this.Controls.Add(this.lblRoll);
- this.Controls.Add(this.lblRotationY);
- this.Controls.Add(this.lblRotationX);
- this.Controls.Add(this.lblDimension2);
- this.Controls.Add(this.lblDimension1);
- this.Controls.Add(this.lblType);
- this.Controls.Add(this.lblNumber);
- this.Controls.Add(this.lblName);
- this.Controls.Add(this.butCancel);
- this.Controls.Add(this.butOK);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "FormWayPoint";
- this.ShowIcon = false;
- this.ShowInTaskbar = false;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "WayPoint";
- this.Load += new System.EventHandler(this.FormWayPoint_Load);
- ((System.ComponentModel.ISupportInitialize)(this.numNumber)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numDimension1)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numDimension2)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRotationX)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRotationY)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.numRoll)).EndInit();
- this.ResumeLayout(false);
- this.PerformLayout();
-
+ AcceptButton = butOK;
+ AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ CancelButton = butCancel;
+ ClientSize = new System.Drawing.Size(331, 255);
+ Controls.Add(numRoll);
+ Controls.Add(numRotationY);
+ Controls.Add(numRotationX);
+ Controls.Add(numDimension2);
+ Controls.Add(numDimension1);
+ Controls.Add(cmbType);
+ Controls.Add(numNumber);
+ Controls.Add(txtName);
+ Controls.Add(lblRoll);
+ Controls.Add(lblRotationY);
+ Controls.Add(lblRotationX);
+ Controls.Add(lblDimension2);
+ Controls.Add(lblDimension1);
+ Controls.Add(lblType);
+ Controls.Add(lblNumber);
+ Controls.Add(lblName);
+ Controls.Add(butCancel);
+ Controls.Add(butOK);
+ FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ MaximizeBox = false;
+ MinimizeBox = false;
+ Name = "FormWayPoint";
+ ShowIcon = false;
+ ShowInTaskbar = false;
+ StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ Text = "WayPoint";
+ Load += FormWayPoint_Load;
+ ((System.ComponentModel.ISupportInitialize)numNumber).EndInit();
+ ((System.ComponentModel.ISupportInitialize)numDimension1).EndInit();
+ ((System.ComponentModel.ISupportInitialize)numDimension2).EndInit();
+ ((System.ComponentModel.ISupportInitialize)numRotationX).EndInit();
+ ((System.ComponentModel.ISupportInitialize)numRotationY).EndInit();
+ ((System.ComponentModel.ISupportInitialize)numRoll).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
}
#endregion
diff --git a/TombEditor/Forms/FormWayPoint.resx b/TombEditor/Forms/FormWayPoint.resx
new file mode 100644
index 000000000..8b2ff64a1
--- /dev/null
+++ b/TombEditor/Forms/FormWayPoint.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
From 5eaa7509889809ca9dcc468b3c256c246d871302 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 24 Jan 2026 22:42:51 +0000
Subject: [PATCH 26/36] Fix compilation errors: add System.Numerics using and
fix variable name conflict
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.cs | 8 ++++----
TombLib/TombLib/LevelData/Instances/WayPointInstance.cs | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index bf0388da7..9827d6651 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -146,11 +146,11 @@ private void butOK_Click(object sender, EventArgs e)
// 2. Exception: same base name allowed ONLY if numbers are different
// (e.g., "test_0" and "test_1" are OK, but "test" Circle and "test" Point are NOT OK)
bool nameChanged = oldBaseName != newName;
- bool numberChanged = _wayPoint.Number != (ushort)numNumber.Value;
+ ushort currentNumber = (ushort)numNumber.Value;
+ bool numberChanged = _wayPoint.Number != currentNumber;
if ((nameChanged || numberChanged) && _editor?.Level != null)
{
- ushort newNumber = (ushort)numNumber.Value;
foreach (var room in _editor.Level.ExistingRooms)
{
@@ -163,10 +163,10 @@ private void butOK_Click(object sender, EventArgs e)
if (obj.BaseName == newName)
{
// Same base name found - only allowed if numbers are different
- if (obj.Number == newNumber)
+ if (obj.Number == currentNumber)
{
// Same base name AND same number = duplicate
- DarkMessageBox.Show(this, $"A WayPoint with the name '{newName}' and number {newNumber} already exists.", "Duplicate Name",
+ DarkMessageBox.Show(this, $"A WayPoint with the name '{newName}' and number {currentNumber} already exists.", "Duplicate Name",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
index 162020bd6..e94dc2bd2 100644
--- a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Numerics;
namespace TombLib.LevelData
{
From 4aac58f2ce9f98b80ff28c5e5dcb54098a4e193b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 02:21:14 +0000
Subject: [PATCH 27/36] Fix shape rendering: add RotationX to transformation
matrix
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index f237f55cb..74ed7681e 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1489,7 +1489,9 @@ private void DrawWayPointShape(WayPointInstance instance, Vector4 color)
Vector3 position = instance.Position + instance.Room.WorldPos;
// Create transformation matrix for the shape orientation
- Matrix4x4 rotation = Matrix4x4.CreateRotationY(instance.RotationY * (float)Math.PI / 180.0f) *
+ // Apply rotations in order: X, Y, Z (Roll)
+ Matrix4x4 rotation = Matrix4x4.CreateRotationX(instance.RotationX * (float)Math.PI / 180.0f) *
+ Matrix4x4.CreateRotationY(instance.RotationY * (float)Math.PI / 180.0f) *
Matrix4x4.CreateRotationZ(instance.Roll * (float)Math.PI / 180.0f);
// Number of segments for circles/ellipses
From b09d92dd36f66eb38dfffb50dbb82fe9f2d1055b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 25 Jan 2026 17:38:36 +0000
Subject: [PATCH 28/36] Add back Sequence field as alternative waypoint
identifier alongside Name
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.Designer.cs | 94 ++++++++++++-------
TombEditor/Forms/FormWayPoint.cs | 13 ++-
.../TombEngine/LevelCompilerTombEngine.cs | 1 +
.../LevelData/Compilers/TombEngine/Structs.cs | 1 +
.../Compilers/TombEngine/TombEngine.cs | 1 +
TombLib/TombLib/LevelData/IO/Prj2Loader.cs | 1 +
TombLib/TombLib/LevelData/IO/Prj2Writer.cs | 1 +
.../LevelData/Instances/WayPointInstance.cs | 7 ++
8 files changed, 83 insertions(+), 36 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.Designer.cs b/TombEditor/Forms/FormWayPoint.Designer.cs
index 04b30bc7a..df187b449 100644
--- a/TombEditor/Forms/FormWayPoint.Designer.cs
+++ b/TombEditor/Forms/FormWayPoint.Designer.cs
@@ -33,6 +33,7 @@ private void InitializeComponent()
butCancel = new DarkButton();
butOK = new DarkButton();
lblName = new DarkLabel();
+ lblSequence = new DarkLabel();
lblNumber = new DarkLabel();
lblType = new DarkLabel();
lblDimension1 = new DarkLabel();
@@ -41,6 +42,7 @@ private void InitializeComponent()
lblRotationY = new DarkLabel();
lblRoll = new DarkLabel();
txtName = new DarkTextBox();
+ numSequence = new DarkNumericUpDown();
numNumber = new DarkNumericUpDown();
cmbType = new DarkComboBox();
numDimension1 = new DarkNumericUpDown();
@@ -48,6 +50,7 @@ private void InitializeComponent()
numRotationX = new DarkNumericUpDown();
numRotationY = new DarkNumericUpDown();
numRoll = new DarkNumericUpDown();
+ ((System.ComponentModel.ISupportInitialize)numSequence).BeginInit();
((System.ComponentModel.ISupportInitialize)numNumber).BeginInit();
((System.ComponentModel.ISupportInitialize)numDimension1).BeginInit();
((System.ComponentModel.ISupportInitialize)numDimension2).BeginInit();
@@ -61,10 +64,10 @@ private void InitializeComponent()
butCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
butCancel.Checked = false;
butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- butCancel.Location = new System.Drawing.Point(239, 222);
+ butCancel.Location = new System.Drawing.Point(239, 248);
butCancel.Name = "butCancel";
butCancel.Size = new System.Drawing.Size(80, 23);
- butCancel.TabIndex = 17;
+ butCancel.TabIndex = 19;
butCancel.Text = "Cancel";
butCancel.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
butCancel.Click += butCancel_Click;
@@ -73,10 +76,10 @@ private void InitializeComponent()
//
butOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
butOK.Checked = false;
- butOK.Location = new System.Drawing.Point(153, 222);
+ butOK.Location = new System.Drawing.Point(153, 248);
butOK.Name = "butOK";
butOK.Size = new System.Drawing.Size(80, 23);
- butOK.TabIndex = 16;
+ butOK.TabIndex = 18;
butOK.Text = "OK";
butOK.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
butOK.Click += butOK_Click;
@@ -91,74 +94,84 @@ private void InitializeComponent()
lblName.TabIndex = 0;
lblName.Text = "Name:";
//
+ // lblSequence
+ //
+ lblSequence.AutoSize = true;
+ lblSequence.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ lblSequence.Location = new System.Drawing.Point(12, 41);
+ lblSequence.Name = "lblSequence";
+ lblSequence.Size = new System.Drawing.Size(61, 13);
+ lblSequence.TabIndex = 2;
+ lblSequence.Text = "Sequence:";
+ //
// lblNumber
//
lblNumber.AutoSize = true;
lblNumber.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblNumber.Location = new System.Drawing.Point(12, 41);
+ lblNumber.Location = new System.Drawing.Point(12, 67);
lblNumber.Name = "lblNumber";
lblNumber.Size = new System.Drawing.Size(51, 13);
- lblNumber.TabIndex = 2;
+ lblNumber.TabIndex = 4;
lblNumber.Text = "Number:";
//
// lblType
//
lblType.AutoSize = true;
lblType.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblType.Location = new System.Drawing.Point(12, 67);
+ lblType.Location = new System.Drawing.Point(12, 93);
lblType.Name = "lblType";
lblType.Size = new System.Drawing.Size(32, 13);
- lblType.TabIndex = 4;
+ lblType.TabIndex = 6;
lblType.Text = "Type:";
//
// lblDimension1
//
lblDimension1.AutoSize = true;
lblDimension1.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblDimension1.Location = new System.Drawing.Point(12, 93);
+ lblDimension1.Location = new System.Drawing.Point(12, 119);
lblDimension1.Name = "lblDimension1";
lblDimension1.Size = new System.Drawing.Size(74, 13);
- lblDimension1.TabIndex = 6;
+ lblDimension1.TabIndex = 8;
lblDimension1.Text = "Dimension 1:";
//
// lblDimension2
//
lblDimension2.AutoSize = true;
lblDimension2.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblDimension2.Location = new System.Drawing.Point(12, 119);
+ lblDimension2.Location = new System.Drawing.Point(12, 145);
lblDimension2.Name = "lblDimension2";
lblDimension2.Size = new System.Drawing.Size(74, 13);
- lblDimension2.TabIndex = 8;
+ lblDimension2.TabIndex = 10;
lblDimension2.Text = "Dimension 2:";
//
// lblRotationX
//
lblRotationX.AutoSize = true;
lblRotationX.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblRotationX.Location = new System.Drawing.Point(12, 145);
+ lblRotationX.Location = new System.Drawing.Point(12, 171);
lblRotationX.Name = "lblRotationX";
lblRotationX.Size = new System.Drawing.Size(64, 13);
- lblRotationX.TabIndex = 10;
+ lblRotationX.TabIndex = 12;
lblRotationX.Text = "Rotation X:";
//
// lblRotationY
//
lblRotationY.AutoSize = true;
lblRotationY.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblRotationY.Location = new System.Drawing.Point(12, 171);
+ lblRotationY.Location = new System.Drawing.Point(12, 197);
lblRotationY.Name = "lblRotationY";
lblRotationY.Size = new System.Drawing.Size(63, 13);
- lblRotationY.TabIndex = 12;
+ lblRotationY.TabIndex = 14;
lblRotationY.Text = "Rotation Y:";
//
// lblRoll
//
lblRoll.AutoSize = true;
lblRoll.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
- lblRoll.Location = new System.Drawing.Point(12, 197);
+ lblRoll.Location = new System.Drawing.Point(12, 223);
lblRoll.Name = "lblRoll";
lblRoll.Size = new System.Drawing.Size(64, 13);
- lblRoll.TabIndex = 14;
+ lblRoll.TabIndex = 16;
lblRoll.Text = "Rotation Z:";
//
// txtName
@@ -168,83 +181,93 @@ private void InitializeComponent()
txtName.Size = new System.Drawing.Size(227, 22);
txtName.TabIndex = 1;
//
+ // numSequence
+ //
+ numSequence.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
+ numSequence.Location = new System.Drawing.Point(92, 38);
+ numSequence.LoopValues = false;
+ numSequence.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
+ numSequence.Name = "numSequence";
+ numSequence.Size = new System.Drawing.Size(227, 22);
+ numSequence.TabIndex = 3;
+ //
// numNumber
//
numNumber.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
- numNumber.Location = new System.Drawing.Point(92, 38);
+ numNumber.Location = new System.Drawing.Point(92, 64);
numNumber.LoopValues = false;
numNumber.Maximum = new decimal(new int[] { 65535, 0, 0, 0 });
numNumber.Name = "numNumber";
numNumber.Size = new System.Drawing.Size(227, 22);
- numNumber.TabIndex = 3;
+ numNumber.TabIndex = 5;
//
// cmbType
//
cmbType.FormattingEnabled = true;
cmbType.Items.AddRange(new object[] { "Point", "Circle", "Ellipse", "Square", "Rectangle", "Linear", "Bezier" });
- cmbType.Location = new System.Drawing.Point(92, 64);
+ cmbType.Location = new System.Drawing.Point(92, 90);
cmbType.Name = "cmbType";
cmbType.Size = new System.Drawing.Size(227, 23);
- cmbType.TabIndex = 5;
+ cmbType.TabIndex = 7;
cmbType.SelectedIndexChanged += cmbType_SelectedIndexChanged;
//
// numDimension1
//
numDimension1.DecimalPlaces = 2;
numDimension1.IncrementAlternate = new decimal(new int[] { 100, 0, 0, 0 });
- numDimension1.Location = new System.Drawing.Point(92, 90);
+ numDimension1.Location = new System.Drawing.Point(92, 116);
numDimension1.LoopValues = false;
numDimension1.Maximum = new decimal(new int[] { 100000, 0, 0, 0 });
numDimension1.Name = "numDimension1";
numDimension1.Size = new System.Drawing.Size(227, 22);
- numDimension1.TabIndex = 7;
+ numDimension1.TabIndex = 9;
numDimension1.Value = new decimal(new int[] { 1024, 0, 0, 0 });
//
// numDimension2
//
numDimension2.DecimalPlaces = 2;
numDimension2.IncrementAlternate = new decimal(new int[] { 100, 0, 0, 0 });
- numDimension2.Location = new System.Drawing.Point(92, 116);
+ numDimension2.Location = new System.Drawing.Point(92, 142);
numDimension2.LoopValues = false;
numDimension2.Maximum = new decimal(new int[] { 100000, 0, 0, 0 });
numDimension2.Name = "numDimension2";
numDimension2.Size = new System.Drawing.Size(227, 22);
- numDimension2.TabIndex = 9;
+ numDimension2.TabIndex = 11;
numDimension2.Value = new decimal(new int[] { 1024, 0, 0, 0 });
//
// numRotationX
//
numRotationX.DecimalPlaces = 2;
numRotationX.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
- numRotationX.Location = new System.Drawing.Point(92, 142);
+ numRotationX.Location = new System.Drawing.Point(92, 168);
numRotationX.LoopValues = false;
numRotationX.Maximum = new decimal(new int[] { 90, 0, 0, 0 });
numRotationX.Minimum = new decimal(new int[] { 90, 0, 0, int.MinValue });
numRotationX.Name = "numRotationX";
numRotationX.Size = new System.Drawing.Size(227, 22);
- numRotationX.TabIndex = 11;
+ numRotationX.TabIndex = 13;
//
// numRotationY
//
numRotationY.DecimalPlaces = 2;
numRotationY.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
- numRotationY.Location = new System.Drawing.Point(92, 168);
+ numRotationY.Location = new System.Drawing.Point(92, 194);
numRotationY.LoopValues = false;
numRotationY.Maximum = new decimal(new int[] { 360, 0, 0, 0 });
numRotationY.Name = "numRotationY";
numRotationY.Size = new System.Drawing.Size(227, 22);
- numRotationY.TabIndex = 13;
+ numRotationY.TabIndex = 15;
//
// numRoll
//
numRoll.DecimalPlaces = 2;
numRoll.IncrementAlternate = new decimal(new int[] { 10, 0, 0, 65536 });
- numRoll.Location = new System.Drawing.Point(92, 194);
+ numRoll.Location = new System.Drawing.Point(92, 220);
numRoll.LoopValues = false;
numRoll.Maximum = new decimal(new int[] { 360, 0, 0, 0 });
numRoll.Name = "numRoll";
numRoll.Size = new System.Drawing.Size(227, 22);
- numRoll.TabIndex = 15;
+ numRoll.TabIndex = 17;
//
// FormWayPoint
//
@@ -252,7 +275,7 @@ private void InitializeComponent()
AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
CancelButton = butCancel;
- ClientSize = new System.Drawing.Size(331, 255);
+ ClientSize = new System.Drawing.Size(331, 281);
Controls.Add(numRoll);
Controls.Add(numRotationY);
Controls.Add(numRotationX);
@@ -260,6 +283,7 @@ private void InitializeComponent()
Controls.Add(numDimension1);
Controls.Add(cmbType);
Controls.Add(numNumber);
+ Controls.Add(numSequence);
Controls.Add(txtName);
Controls.Add(lblRoll);
Controls.Add(lblRotationY);
@@ -268,6 +292,7 @@ private void InitializeComponent()
Controls.Add(lblDimension1);
Controls.Add(lblType);
Controls.Add(lblNumber);
+ Controls.Add(lblSequence);
Controls.Add(lblName);
Controls.Add(butCancel);
Controls.Add(butOK);
@@ -280,6 +305,7 @@ private void InitializeComponent()
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
Text = "WayPoint";
Load += FormWayPoint_Load;
+ ((System.ComponentModel.ISupportInitialize)numSequence).EndInit();
((System.ComponentModel.ISupportInitialize)numNumber).EndInit();
((System.ComponentModel.ISupportInitialize)numDimension1).EndInit();
((System.ComponentModel.ISupportInitialize)numDimension2).EndInit();
@@ -295,6 +321,7 @@ private void InitializeComponent()
private DarkButton butOK;
private DarkButton butCancel;
private DarkLabel lblName;
+ private DarkLabel lblSequence;
private DarkLabel lblNumber;
private DarkLabel lblType;
private DarkLabel lblDimension1;
@@ -303,6 +330,7 @@ private void InitializeComponent()
private DarkLabel lblRotationY;
private DarkLabel lblRoll;
private DarkTextBox txtName;
+ private DarkNumericUpDown numSequence;
private DarkNumericUpDown numNumber;
private DarkComboBox cmbType;
private DarkNumericUpDown numDimension1;
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 9827d6651..2f37a2036 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -45,6 +45,7 @@ private void FormWayPoint_Load(object sender, EventArgs e)
}
txtName.Text = baseName;
+ numSequence.Value = _wayPoint.Sequence;
numNumber.Value = _wayPoint.Number;
cmbType.SelectedIndex = (int)_wayPoint.Type;
numDimension1.Value = (decimal)_wayPoint.Radius1;
@@ -212,6 +213,7 @@ private void butOK_Click(object sender, EventArgs e)
// Update waypoint properties but with cleared LuaName
_wayPoint.Name = newName;
+ _wayPoint.Sequence = (ushort)numSequence.Value;
_wayPoint.Number = newNumber;
_wayPoint.Type = newType;
_wayPoint.Radius1 = (float)numDimension1.Value;
@@ -223,11 +225,12 @@ private void butOK_Click(object sender, EventArgs e)
// Batch type update
if (oldType != newType && _editor?.Level != null)
{
+ var oldSequence = _wayPoint.Sequence;
foreach (var r in _editor.Level.ExistingRooms)
{
foreach (var o in r.Objects.OfType())
{
- if (o != _wayPoint && o.BaseName == oldBaseName)
+ if (o != _wayPoint && (o.BaseName == oldBaseName || o.Sequence == oldSequence))
{
o.Type = newType;
}
@@ -246,6 +249,7 @@ private void butOK_Click(object sender, EventArgs e)
// Update waypoint properties
_wayPoint.Name = newName;
+ _wayPoint.Sequence = (ushort)numSequence.Value;
_wayPoint.Number = newNumber;
_wayPoint.Type = newType;
_wayPoint.Radius1 = (float)numDimension1.Value;
@@ -254,14 +258,17 @@ private void butOK_Click(object sender, EventArgs e)
_wayPoint.RotationY = (float)numRotationY.Value;
_wayPoint.Roll = (float)numRoll.Value;
- // Batch type update: if type changed, update all waypoints with the same original base name
+ // Batch type update: if type changed, update all waypoints with either:
+ // 1. Same original base name OR
+ // 2. Same sequence number
if (oldType != newType && _editor?.Level != null)
{
+ var oldSequence = _wayPoint.Sequence;
foreach (var room in _editor.Level.ExistingRooms)
{
foreach (var obj in room.Objects.OfType())
{
- if (obj != _wayPoint && obj.BaseName == oldBaseName)
+ if (obj != _wayPoint && (obj.BaseName == oldBaseName || obj.Sequence == oldSequence))
{
obj.Type = newType;
}
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index 6fd2041c5..a1bdeb9c9 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -392,6 +392,7 @@ private void BuildCamerasAndSinks()
RotationX = instance.RotationX,
RotationY = instance.RotationY,
Roll = instance.Roll,
+ Sequence = instance.Sequence,
Number = instance.Number,
Type = (int)instance.Type,
Radius1 = instance.Radius1,
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
index cc1b805a8..69a833cba 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
@@ -595,6 +595,7 @@ public struct TombEngineWayPoint
public float RotationX;
public float RotationY;
public float Roll;
+ public ushort Sequence;
public ushort Number;
public int Type;
public float Radius1;
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
index 6bcd879ec..b0afe65e8 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
@@ -87,6 +87,7 @@ private void WriteLevelTombEngine()
writer.Write(waypoint.RotationX);
writer.Write(waypoint.RotationY);
writer.Write(waypoint.Roll);
+ writer.Write(waypoint.Sequence);
writer.Write(waypoint.Number);
writer.Write(waypoint.Type);
writer.Write(waypoint.Radius1);
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
index ff7aed0ea..85e9aebf6 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
@@ -1410,6 +1410,7 @@ private static bool LoadObjects(ChunkReader chunkIO, ChunkId idOuter, LevelSetti
instance.Roll = chunkIO.Raw.ReadSingle();
instance.ScriptId = ReadOptionalLEB128Int(chunkIO.Raw);
string baseName = chunkIO.Raw.ReadStringUTF8();
+ instance.Sequence = LEB128.ReadUShort(chunkIO.Raw);
instance.Number = LEB128.ReadUShort(chunkIO.Raw);
instance.Type = (WayPointType)LEB128.ReadInt(chunkIO.Raw);
instance.Radius1 = chunkIO.Raw.ReadSingle();
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
index 88a4aa7e0..5b01d89a9 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
@@ -706,6 +706,7 @@ private static void WriteObjects(ChunkWriter chunkIO, IEnumerable
Date: Mon, 26 Jan 2026 00:50:09 +0000
Subject: [PATCH 29/36] Add WayPoint button to toolbar (TombEngine only)
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Properties/Resources.Designer.cs | 10 ++++++++++
TombEditor/Properties/Resources.resx | 3 +++
.../icons_objects/objects_WayPoint-16.png | Bin 0 -> 2034 bytes
TombEditor/ToolWindows/MainView.Designer.cs | 15 ++++++++++++++-
TombEditor/ToolWindows/MainView.cs | 1 +
5 files changed, 28 insertions(+), 1 deletion(-)
create mode 100644 TombEditor/Resources/icons_objects/objects_WayPoint-16.png
diff --git a/TombEditor/Properties/Resources.Designer.cs b/TombEditor/Properties/Resources.Designer.cs
index f53d05977..3a7de26cd 100644
--- a/TombEditor/Properties/Resources.Designer.cs
+++ b/TombEditor/Properties/Resources.Designer.cs
@@ -860,6 +860,16 @@ internal static System.Drawing.Bitmap objects_volume_sphere_16 {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap objects_WayPoint_16 {
+ get {
+ object obj = ResourceManager.GetObject("objects_WayPoint_16", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/TombEditor/Properties/Resources.resx b/TombEditor/Properties/Resources.resx
index f4fa1b3d4..59793f457 100644
--- a/TombEditor/Properties/Resources.resx
+++ b/TombEditor/Properties/Resources.resx
@@ -247,6 +247,9 @@
..\Resources\icons_objects\objects_volume-sphere-16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\icons_objects\objects_WayPoint-16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\icons_general\general_animcommand-16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/TombEditor/Resources/icons_objects/objects_WayPoint-16.png b/TombEditor/Resources/icons_objects/objects_WayPoint-16.png
new file mode 100644
index 0000000000000000000000000000000000000000..84e9fb1e0a0e2d11f061b5d0550f12ca3dfcf964
GIT binary patch
literal 2034
zcmV_bktLya)kRPjUV9-y-js+Jp$4TiYUj~RqL{KsqmT}xW|}jOGt8*7pZDVX+OOMF
zBX3)sas6Sz-fOSZ{<+q(*Lv0(B}4=!aplJlFvtut6NAhkuO-PAdLWTV*tBDbA|eUv
z$;@0Z$V_6`Y#YM;I6wz|_#=w5
zUOw&1moJf>osAzqe(-ToQBgR0^eAFtVxUr~;BYwb_U&6VH#g(UmoI#}(9lq{w6q{0
zAwg6tFxv~a(9X!R_2lmEZaQ(|1Z~{7ao*t`5fMS>&z~oY#X@$woe~lf1kcZ$IYTCs
zY2LC;CKDAG7YowI#Kh3Z$jI6;7x(Vnqs^N)FKSbNhr@>tQ&CZoAg-dK!lQc1%E|=!
zu3fuE9LM=SU2y*=+W0$iu_Kl$e;vm*MB$n)Z5$3r*S%+)Y;id`T6;C8m&|+sj;zfNp&_h
zHj+}QBmg9pN~x)-$+scjx^+tc`0CZG3#PSLEL2!nD5x_wHkO8lhL%+4;NT#|#>Vnx
zR904sW->g5Eb5By{{8#>wN9r)d3pJQY1L{qDk>@v6&1zDg@uJ7BqU@>b%ut9A}lP7
zzqi?JaJ${UyR3fy{yl%K*XyBDsTNJYYu7F$Cnxjw9LFtr>NsO0^3yC&H$1%(^4Qqe
z|JLp@83F?X|GdVR$q*hM&fgCX4#I3UFR4zm*^GgK0sda2(LgGd`Yz<(egux=V7J>n
z%44xu1S8?$!-q?%^TC4${0LGg6oLhgPx>y7967@0&&kR8;|J*b_wN)P9WAIgF)@*B
zHk(KF*=#o2w{IVx_u#>UG&(x!+p~-7*RS*W6$%AaS645ZzO=M--nnu?LBStaZzCfk
zR8UYL$amw$4c}k!O-xKsT3Q-khFYzr>gsCx`SYhB-KS5V=B(CchxGJxG8harF)=Zx
z4uio!dc9td{?esOtHwbjsz2FkwF;(SR;v{yB_(KRXh3ReDgpumFf=rTwzf9(^zO6h7GXUY%rNjXl-r9e{bHhhTnC{<#MX4tMh0vVzF4Lrly86Gc#$=o;}`UF4SuEvICwSE?l@k
zQ&Uq5w(;G&cT`?pPVw>aynkAyQhATLShsE+tX3-|5(y$BBe7-67QwLo+mcG9;5ZI_
zeSNT6t^71@_JYQ2Hsi^YC#bEh#fJ|c@a@|-!E=p9<2`$Petv%Zi$;
zYNoHEzrP1qDD
zsi_I|_4R_d-zH(_&Yd`U@+3~5K8=Qk20VZMeC-|y_%E86m=O3zA|oS_nwpA&f&wHb
zCnF#r007`{I7BmLJ`WiHf`fyxd-rbS=H?P2RnVFd@3V2OGw%hG2yd;OoUAK@9T@{
zf?X57i{I+*?nZZaHv$6#k(HH&^z?LOWo02aI2a)zA-wadAja
zPKHvcIusQZAulfvw{PFZ=g*%-gS;#~SuU3g4grd-;NW2X`q;5!C@CpH
zPfrhOYHINK@nZ}R5A)BBMk9vya2yAh%O&1a$`sHhb`5^(wQ
zWtyIzUa*ZWmy2G#dPP-LRg{#JL;y&m(Rk01fQuI|qN=J24u@meXA&-#3vqFA$j{GT
z^aPPYp@3Gag;uM@@#DwQ)zyU;FJ5?$x$t#)YA%?Ai1{uUWF`igL1vJd7-R;Si9u$N
z8Du5~nL%cdnZ%aI{^XVZpAoDlGcm{v@*0qYZ7Z>!%pfx{$P6-r%=B0F55A`$8008h
Q1ONa407*qoM6N<$f}*wGVE_OC
literal 0
HcmV?d00001
diff --git a/TombEditor/ToolWindows/MainView.Designer.cs b/TombEditor/ToolWindows/MainView.Designer.cs
index 05032bdee..ce0d33a23 100644
--- a/TombEditor/ToolWindows/MainView.Designer.cs
+++ b/TombEditor/ToolWindows/MainView.Designer.cs
@@ -61,6 +61,7 @@ private void InitializeComponent()
butAddCamera = new System.Windows.Forms.ToolStripButton();
butAddSprite = new System.Windows.Forms.ToolStripButton();
butAddFlybyCamera = new System.Windows.Forms.ToolStripButton();
+ butAddWayPoint = new System.Windows.Forms.ToolStripButton();
butAddSink = new System.Windows.Forms.ToolStripButton();
butAddSoundSource = new System.Windows.Forms.ToolStripButton();
butAddImportedGeometry = new System.Windows.Forms.ToolStripButton();
@@ -97,7 +98,7 @@ private void InitializeComponent()
toolStrip.BackColor = System.Drawing.Color.FromArgb(60, 63, 65);
toolStrip.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
toolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
- toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { but2D, but3D, butFaceEdit, butLightingMode, butUndo, butRedo, butCenterCamera, butDrawPortals, butDrawAllRooms, butDrawHorizon, butDrawRoomNames, butDrawCardinalDirections, butDrawExtraBlendingModes, butHideTransparentFaces, butBilinearFilter, butDrawWhiteLighting, butDrawStaticTint, butDrawIllegalSlopes, butDrawSlideDirections, butDisableGeometryPicking, butDisableHiddenRoomPicking, butDrawObjects, butFlipMap, butCopy, butPaste, butStamp, butOpacityNone, butOpacitySolidFaces, butOpacityTraversableFaces, butMirror, butAddCamera, butAddSprite, butAddFlybyCamera, butAddSink, butAddSoundSource, butAddImportedGeometry, butAddGhostBlock, butAddMemo, butCompileLevel, butCompileLevelAndPlay, butCompileAndPlayPreview, butAddBoxVolume, butAddSphereVolume, butTextureFloor, butTextureCeiling, butTextureWalls, butEditLevelSettings, butToggleFlyMode, butSearch, butSearchAndReplaceObjects });
+ toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { but2D, but3D, butFaceEdit, butLightingMode, butUndo, butRedo, butCenterCamera, butDrawPortals, butDrawAllRooms, butDrawHorizon, butDrawRoomNames, butDrawCardinalDirections, butDrawExtraBlendingModes, butHideTransparentFaces, butBilinearFilter, butDrawWhiteLighting, butDrawStaticTint, butDrawIllegalSlopes, butDrawSlideDirections, butDisableGeometryPicking, butDisableHiddenRoomPicking, butDrawObjects, butFlipMap, butCopy, butPaste, butStamp, butOpacityNone, butOpacitySolidFaces, butOpacityTraversableFaces, butMirror, butAddCamera, butAddSprite, butAddFlybyCamera, butAddWayPoint, butAddSink, butAddSoundSource, butAddImportedGeometry, butAddGhostBlock, butAddMemo, butCompileLevel, butCompileLevelAndPlay, butCompileAndPlayPreview, butAddBoxVolume, butAddSphereVolume, butTextureFloor, butTextureCeiling, butTextureWalls, butEditLevelSettings, butToggleFlyMode, butSearch, butSearchAndReplaceObjects });
toolStrip.Location = new System.Drawing.Point(0, 0);
toolStrip.Name = "toolStrip";
toolStrip.Padding = new System.Windows.Forms.Padding(6, 0, 1, 0);
@@ -567,6 +568,17 @@ private void InitializeComponent()
butAddFlybyCamera.Size = new System.Drawing.Size(23, 29);
butAddFlybyCamera.Tag = "AddFlybyCamera";
//
+ // butAddWayPoint
+ //
+ butAddWayPoint.BackColor = System.Drawing.Color.FromArgb(60, 63, 65);
+ butAddWayPoint.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ butAddWayPoint.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220);
+ butAddWayPoint.Image = Properties.Resources.objects_WayPoint_16;
+ butAddWayPoint.ImageTransparentColor = System.Drawing.Color.Magenta;
+ butAddWayPoint.Name = "butAddWayPoint";
+ butAddWayPoint.Size = new System.Drawing.Size(23, 29);
+ butAddWayPoint.Tag = "AddWayPoint";
+ //
// butAddSink
//
butAddSink.BackColor = System.Drawing.Color.FromArgb(60, 63, 65);
@@ -902,6 +914,7 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripButton butOpacityTraversableFaces;
private System.Windows.Forms.ToolStripButton butAddCamera;
private System.Windows.Forms.ToolStripButton butAddFlybyCamera;
+ private System.Windows.Forms.ToolStripButton butAddWayPoint;
private System.Windows.Forms.ToolStripButton butAddSoundSource;
private System.Windows.Forms.ToolStripButton butAddSink;
private System.Windows.Forms.ToolStripButton butAddGhostBlock;
diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs
index 711e19edc..eac3e6375 100644
--- a/TombEditor/ToolWindows/MainView.cs
+++ b/TombEditor/ToolWindows/MainView.cs
@@ -190,6 +190,7 @@ obj is Editor.LevelChangedEvent ||
{
butAddBoxVolume.Enabled = _editor.Level.IsTombEngine;
butAddSphereVolume.Enabled = _editor.Level.IsTombEngine;
+ butAddWayPoint.Enabled = _editor.Level.IsTombEngine;
butDrawVolumes.Enabled = _editor.Level.IsTombEngine; // We may safely hide it because it's not customizable
butAddSprite.Enabled = _editor.Level.Settings.GameVersion.Native() <= TRVersion.Game.TR2;
From 7525247e0c317311b0eaf399e1e5cb7091dfa307 Mon Sep 17 00:00:00 2001
From: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date: Sun, 25 Jan 2026 19:34:05 -0500
Subject: [PATCH 30/36] UpdateLogo
---
.../ServiceObjectTextures/waypoint.png | Bin 2034 -> 4564 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures/waypoint.png b/TombLib/TombLib.Rendering/Rendering/ServiceObjectTextures/waypoint.png
index 84e9fb1e0a0e2d11f061b5d0550f12ca3dfcf964..879947ae51df855aaed5ac89f11f0780bf1b4736 100644
GIT binary patch
literal 4564
zcmeHKYfuzd79K<%0hy@yg4MDaHAtXmdV1asgFFUd76uTIh%fBv?wRQ@FMAqhKqLrS
z#?>Xdt1I}R5@S~58>21@VvLV$j7AZoan0g_f+8D2NsLrh410S(#M)GCQMLKURP}WC
zx!?KjIo~<=R^JWjsfhu8!G1g*FF>25$pH5p?(+2p{|o)!Jqm807iBGCGjJzlXKV)A
zOhIg+oq{MAZQ$`-cP!}(_~#+t=e?66%f@6VE0)!hirbHU;&vANJ-Il#@Ilzt=RcHe
zTkjb>xpU(&^exY@uyKJIo&%KW7R;_8k85&C4g(9lAFg>%WJ$KHo*xJhC+3jV+z=
zn`}Z;5!tcPpC9zMML4M^5q?{*P%qp2s>mK0aL=AO9jF
zkjJjoTN;wjr%$?=midujvQK2vgiGnWzuP0I+xF&^&*JFt6P+u1nnb3i&3kK=en-4~
z=1n=s7hbKyYW>PueBOPhR2f#6w4ry-x+&u2uG2FgHalG1(J`j%s<)qB;4S=Q6Dtrt
z=sBownH;^=W0O8r|L5u}5r)>B+}^Ks`BU3-?yi34R%r$j)DpOXNKr)G3%FOMOMZAd
zZMvS)hD3)}BOPTI-dnnTS;_sS%%Ya6kXbvcL-`XnekA-;edV@*ZLS^4J^O>({Z-p1
zUtO~6=G(hZc3;Y!7GVkxI&vd8c1&`Tz-t29x&M7X{{?qs87X@oMSb7;dgRZO{z@_n
z&wf!;8l06>F|+UWl54jjQgwCO&yw6s5{wh8-rabV^1
z*$vmP3lh(^$n7heueYgN7@uh+O>^Rz+4JBJ7LC1otz&1;*+#hW(+5`%udcY^10{d5
z_>2dizvl5o>gcM|^+#7(`=6e8*z5VvET$>o%Jp{8dIdCSu|>KRjIddRIBC;ULYKu3
zdXLA8n&-0PL>|RLddf&!)q?K&PX!Q7ss(S!bcoI#PnqbXB8JK=O3fmQ@(2|vm>2CA
z<-!1gg<^5YWieYFm`g2i^J3tcGm8X}8^Y$P1&eg)P`r(yAgNF)MBoG$U4ROr{h%m@
zG+-H;`GXYTO)W67tQ`}HoKC0EDG}Njqe!e$sYD1WLQxnXU`L^q#a*z~F^i)Z;LuPG
zf}!m!ZL>lgC$6{Uvuc3=j6*Ntv)FaIA$qH0Pz9ie$c5WQVj&{3SVY4;9Be`XAQ=qk
z7d;$VV5N#OD2FYdA*h4`%F50fPC*hw{`P#v>`sRyM3kAb0H_1ZDt=|kM6E7;$b*x>
zNL%b~FCg|SNR~Fd6zi4PxDj_c!vg{CL%gq`U*zrv1C&mOX>3G37oJw57I5=pq>Z3S
z%>9T+5S)<630R7#q_9+lNMOBKWq@%dq1O|L0asAyFeXmYG7)2UGgRaEQ!=vJ;NI)f}D7l=J%U~Jsf~9&y
z38Fz2umq9mQI(2Ps8EBOiX^c4HpYU3a?%#uNQvxLqkDi89E(lYss*SJd1*;Ew=_yvoRT8B_E|JMpQVFVj3CgAz2k1qPRg4HFa`y;V7!0HX
zq{X>T1q5z8$OemND4ex1SvH$lE#QKJIM1PB9avB#&f*%Jr2r{{$}vQYp|UKo3X@1M
znG8l`7%~iRBWXk7|3Y)i2Z|afdJ^pb^B1~J18XXiS~>7G@M@;riwT0<%L2oRffO8g
z0X0xAz%?*Lm~g9+0{h2c!@jW7zt9W{${>~@h#aQmQUxrPD+pMrBnen1R-rP5OrjSL
zW;Mj_uo+k<&QP&Npd-)M#S(i$poUoWJTPL@gmzO
z(SPX?yU>$zVDIjQck`d`UD%Yr7BT`^Q;L+|H
zm1{%_j0im1UH><^{9YcWC@c5^a)P7Mf#yjTaLn@7Cnsunjl8YAgPLdim8?>>D5UgvessCNH+8
zT%L8JEn=6VBU?il&i_=gZyrDVr;ES4d-7a+qEQ{QuQDw1&%x>X^
V_e+kS`W}>qr%g!J9En>|_8+Y2h2#JL
delta 2020
zcmVJ(xqsHP*Lv0(B}4=!aplJl
zFvtut6NAhkuO-PAdLWTV*tBDbA|eUv$;@0Z$V_6`Fn9FB$vx+cAI7fe}8{EckUeZ_4O@V+vZ_3Kg*SZJUKZ@
zPN$RHZuf#|j7B4E-@bj$%gAIhQYw`sl}hJ4+p%K@8I8tO1zEC?cWw60$lvh7xm+%E
zb#>vaoqwH;A3uKZaZyoGIC}IbVq#*T
zQmNo@IPmuETQoN}R0NQ-6Pl!-o%3QBjc~uA-vCqk789$^`kYUAsmc$N4_w&dyHKXf%Sdwr$%+r%s)s
z($Z4O&(EijkdOtT>vXzB?eSG_Z1!!)!^6Xrn3%|y;pgW^6%`fK
z+uO^haXOvU+1W|?`T27ityC(hv9WPUbv8COl7CXEBmg9pN~x)-$+scjx^+tc`0CZG
z3#PSLEL2!nD5x_wHkO8lhL%+4;NT#|#>VnxR904sW->g5Eb5By{{8#>wN9r)d3pJQ
zY1L{qDk>@v6&1zDg@uJ7BqU@>b%ut9A}lP7zqi?JaJ${UyR3fy{yl%K*XyBDsTNJY
zYk$`+Bqt~H_Z-J9dFnW0B=XZNPd7Zh67tyC*#Fk
zHZQ48v)PP+fdT$rqtQSrmHIB^-+ly+<6yVjJ<4OTSOg>C;lqbZs`J5v2mA<9C=`MP
zj!*h7jvP6{=g-N>`Qr!Z`}gk@9UU#GH-9lPk!&`bNA=lkHrlsuAD{Q&!Gkn9I_lfA
zi|f~~^Z6AD1yxs9FPgrzv~=FNazR1CA6IW9BO_E$P$0;6EK_sd_*?(%a
z3Z`IIs}&_BC1_}9Kx%3#0s;asG&F>^wl?(i^ekFddwV;cJ$r_Xj10uZ#bLvS4Y1j4
zFquqfZEeNmFw3)PHLAvICwSE?l@kQ&Uq5w(;G&cT`?pPVw>aynkAy
zQhATLShsE+tX3-|5(y$BBe7-67QwLo+mcG9;5ZI_eSNT6t^71@_JYQ2Hsi^YC#bEh
z#fJ|c@a@|-!E=p9<2`$Petv%Zi({Ry&)vOZBbDPg1Ox;iC@5%IUTUVVqQAc%H*emA!C*j3OAG9F
zyP#*w<#O!bzaKK03>_UEn3|gMPRK+gs+pLr>FH_yx~ZuN_4W0FxZfsW=gyrtdGaJq
zpFWL-h6X%;{(S8o3HUFXn17fM_(mclBaxb#ih_ayBqt{$ARqt$;BYuZGi5#x832NV
zgRy(}Zsg|XA~!b|TeoigqxX(MUV;uBIDjixt{@{L!#BNft3jEWnJWr-O+dEW?Q=eQ
z46;bBTn5?4#UL}t46;{9CX+!fmovy>YHVx_*$o2L0aDO-)$ji%vR;xus
zL3UKj@sH<)YjG_DJcor+1b$PbVy80giQAD>x=1vT@${G-|Ft}Mt65N0s{k)
zm6e6`^mJrpWg$2?7$G4cy#L!PZ88yw+EZq0W?gY{aY#;1hEl2I<9_}6g}S;r6crUA
zFE0vya2yAh%O&1a$7W{{c0mdF0&
zmHwX*tS2)u$PDrtkc4e3v7XEzGcm{vGK0+YSM?9Rryv;QC|d*o0000
Date: Sun, 25 Jan 2026 19:58:44 -0500
Subject: [PATCH 31/36] Add toolbar icon
---
.../icons_objects/objects_WayPoint-16.png | Bin 2034 -> 4596 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/TombEditor/Resources/icons_objects/objects_WayPoint-16.png b/TombEditor/Resources/icons_objects/objects_WayPoint-16.png
index 84e9fb1e0a0e2d11f061b5d0550f12ca3dfcf964..081f4aaef6a65c0f8869952fe5908fcdf08a7f86 100644
GIT binary patch
literal 4596
zcmeHKYfuzd7H*#)DCjB=Ezq`kL<~Lsn0JQ}kr`mbQG{U^)ZK8W`wp}+4|*DAfK{@h
zF~n>ERhtlvn>FjBQdU+?bh9z8kHn~zb<0??YFJ}@gn+s+!Dy0QCF|av;aN%57FF>d
zQ#I4w=brQ3bH4MPThmPiOLM10%!G9{T!^kym2db&^gHL?BY-X^>+B_RA@Ji~
z$>S6&AuiW?Ar1qg6T<@kbQP?O8aFq*w|jqf;S>oJ+
zui`T5=#;$LcE_&mxo-)pen0Q*>*FT1AJ6Ma&QDx7w%*$N(w5at|2?qu9mDeKm#CZv
zlflyy@~`jH?+D~QFn`e*o)npMJ*^=AAO9iKcVGS{vi^ito_%w>&UwpObog`e-krpb
ztJAttlW)!|bT;2FTDWJrBz*YE7UM^M+~HuFsLrdeo_&V88Cz<^ew9{?VPiIm+1Ulw
z?Ck!6P#N3SwjRzuQ844wvE7R;^CN0nZnV6{byzG3`A^2aw@7^Q;O#Q0Jx(L$6|PE{
zaA|#G^T|Dpn-A=b+PfbAH+DCA>Jyf@w7Z||JV%@w*Y@zU!`)A9tB=ADu3n;j=y+yN
zX-?|QxO*FmnVyI_?_*sI!4xfnalW1x_SOGw_dA=vUh!#d!>N`+&CF}loA@RA^cz!d
z9J1wi|8-e{BgQ&+!4t2tuV>xJ-FrrLtV;D%de7FmNp04to1QxUQ_8vV@xrZ{BI1gzt|$_{!VgudTd%XZrC+
zlUIqqY&c@c?@d0^ae>G^-lOwYUAfR{x+aCi*Pl^rH&fgtGgw@vD5@
z7i&-78rQO;l)Q?CZ8|P&S-r2mw|=|d{isXs`fl92TImFF^^1RDSX8ly6jowe!toxL
z3J4wtR0UjKBs~nv$P9P^zXr;<1D11x!Q`mqYi8nIiC@i@e4}l-LUJY>xzSjY^}UNlQSi
zVTlD1c!nf6xk7VJ9|U?b5f!rR-YNWD(0)f)|JHK4%|D688i12+#9C4q7ER#f}N>Dj8D+NkGP_h=*21K{u@%sjs?4k=6$w1+gK}4txMus-(P(fP5pwrKx8C&wKGOeYv5QT
zNozTJh`&b=owfhZTNxibBh>Uf(TDc04VpqTRRpU-Poc+3F*r;(9vlk}@SzfXpau%T
zJP}rCiLU_ea){24KEd|e#UBxaLjXFB9ymw{fu7Vjd6slQMj&;9hD8m8(4Y&EG=T2&
zIAuSOU{*Qe5pjhC8srL}*Vn4F;o0~rpwb6K8A&r_A7xsO(Gt|-FAuPKAn4Gz7z8zo
z6m9^d!Kl}ffCr48;hn5n6H?7jTb`nb{_-*?oS=x7kPV&8=%A+t
zPWnE+P_B>j(&EuI2%lzaISXQq}JzN+!;Cky_oT1WA`49DS7_o)Z$ps
zrVamc(SCa4x9)}JH)nj1xnaCXJX-dB>R(=QM4j*^U7WIdN!8}8iH2|g(7pR%wfWaM
z8(4qU%ZD5rB73hThIKR~T#xa@J6mVZ?)i;1_SR?9j>VU^nli3O??`{ExlQ^|jO{oX
z6Kgo_oY?Vxb=u{W6(3hTFMEqxj9>0=pV6VRto-~9_gH)JH!;%=u_tu@?yf_XU{=dg^WH^e>;DI@(PIGs
literal 2034
zcmV_bktLya)kRPjUV9-y-js+Jp$4TiYUj~RqL{KsqmT}xW|}jOGt8*7pZDVX+OOMF
zBX3)sas6Sz-fOSZ{<+q(*Lv0(B}4=!aplJlFvtut6NAhkuO-PAdLWTV*tBDbA|eUv
z$;@0Z$V_6`Y#YM;I6wz|_#=w5
zUOw&1moJf>osAzqe(-ToQBgR0^eAFtVxUr~;BYwb_U&6VH#g(UmoI#}(9lq{w6q{0
zAwg6tFxv~a(9X!R_2lmEZaQ(|1Z~{7ao*t`5fMS>&z~oY#X@$woe~lf1kcZ$IYTCs
zY2LC;CKDAG7YowI#Kh3Z$jI6;7x(Vnqs^N)FKSbNhr@>tQ&CZoAg-dK!lQc1%E|=!
zu3fuE9LM=SU2y*=+W0$iu_Kl$e;vm*MB$n)Z5$3r*S%+)Y;id`T6;C8m&|+sj;zfNp&_h
zHj+}QBmg9pN~x)-$+scjx^+tc`0CZG3#PSLEL2!nD5x_wHkO8lhL%+4;NT#|#>Vnx
zR904sW->g5Eb5By{{8#>wN9r)d3pJQY1L{qDk>@v6&1zDg@uJ7BqU@>b%ut9A}lP7
zzqi?JaJ${UyR3fy{yl%K*XyBDsTNJYYu7F$Cnxjw9LFtr>NsO0^3yC&H$1%(^4Qqe
z|JLp@83F?X|GdVR$q*hM&fgCX4#I3UFR4zm*^GgK0sda2(LgGd`Yz<(egux=V7J>n
z%44xu1S8?$!-q?%^TC4${0LGg6oLhgPx>y7967@0&&kR8;|J*b_wN)P9WAIgF)@*B
zHk(KF*=#o2w{IVx_u#>UG&(x!+p~-7*RS*W6$%AaS645ZzO=M--nnu?LBStaZzCfk
zR8UYL$amw$4c}k!O-xKsT3Q-khFYzr>gsCx`SYhB-KS5V=B(CchxGJxG8harF)=Zx
z4uio!dc9td{?esOtHwbjsz2FkwF;(SR;v{yB_(KRXh3ReDgpumFf=rTwzf9(^zO6h7GXUY%rNjXl-r9e{bHhhTnC{<#MX4tMh0vVzF4Lrly86Gc#$=o;}`UF4SuEvICwSE?l@k
zQ&Uq5w(;G&cT`?pPVw>aynkAyQhATLShsE+tX3-|5(y$BBe7-67QwLo+mcG9;5ZI_
zeSNT6t^71@_JYQ2Hsi^YC#bEh#fJ|c@a@|-!E=p9<2`$Petv%Zi$;
zYNoHEzrP1qDD
zsi_I|_4R_d-zH(_&Yd`U@+3~5K8=Qk20VZMeC-|y_%E86m=O3zA|oS_nwpA&f&wHb
zCnF#r007`{I7BmLJ`WiHf`fyxd-rbS=H?P2RnVFd@3V2OGw%hG2yd;OoUAK@9T@{
zf?X57i{I+*?nZZaHv$6#k(HH&^z?LOWo02aI2a)zA-wadAja
zPKHvcIusQZAulfvw{PFZ=g*%-gS;#~SuU3g4grd-;NW2X`q;5!C@CpH
zPfrhOYHINK@nZ}R5A)BBMk9vya2yAh%O&1a$`sHhb`5^(wQ
zWtyIzUa*ZWmy2G#dPP-LRg{#JL;y&m(Rk01fQuI|qN=J24u@meXA&-#3vqFA$j{GT
z^aPPYp@3Gag;uM@@#DwQ)zyU;FJ5?$x$t#)YA%?Ai1{uUWF`igL1vJd7-R;Si9u$N
z8Du5~nL%cdnZ%aI{^XVZpAoDlGcm{v@*0qYZ7Z>!%pfx{$P6-r%=B0F55A`$8008h
Q1ONa407*qoM6N<$f}*wGVE_OC
From 1c198f42ddbeeaf61a9553ee4744089684ccccec Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 26 Jan 2026 01:14:02 +0000
Subject: [PATCH 32/36] Remove LuaName functionality from WayPoints - waypoints
now use Name only
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
.../ContextMenus/MaterialObjectContextMenu.cs | 6 +-
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 1 -
TombEditor/Forms/FormWayPoint.cs | 55 -------------------
.../TombEngine/LevelCompilerTombEngine.cs | 3 +-
.../LevelData/Compilers/TombEngine/Structs.cs | 1 -
.../Compilers/TombEngine/TombEngine.cs | 1 -
.../LevelData/Instances/WayPointInstance.cs | 9 +--
7 files changed, 5 insertions(+), 71 deletions(-)
diff --git a/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs b/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs
index 0063c126c..e288719b1 100644
--- a/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs
+++ b/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs
@@ -9,7 +9,7 @@ class MaterialObjectContextMenu : BaseContextMenu
public MaterialObjectContextMenu(Editor editor, IWin32Window owner, ObjectInstance targetObject)
: base(editor, owner)
{
- if (targetObject is IHasScriptID)
+ if (targetObject is IHasScriptID && !(targetObject is WayPointInstance))
{
if (_editor.Level.IsNG && targetObject == editor.SelectedObject)
{
@@ -33,7 +33,7 @@ public MaterialObjectContextMenu(Editor editor, IWin32Window owner, ObjectInstan
}
}
- if (!(targetObject is LightInstance || targetObject is GhostBlockInstance))
+ if (!(targetObject is LightInstance || targetObject is GhostBlockInstance || targetObject is WayPointInstance))
{
Items.Add(new ToolStripMenuItem("Edit object", Properties.Resources.general_edit_16, (o, e) =>
{
@@ -137,7 +137,7 @@ public MaterialObjectContextMenu(Editor editor, IWin32Window owner, ObjectInstan
}));
}
- if (targetObject is PositionAndScriptBasedObjectInstance && _editor.Level.Settings.GameVersion == TRVersion.Game.TombEngine)
+ if (targetObject is PositionAndScriptBasedObjectInstance && !(targetObject is WayPointInstance) && _editor.Level.Settings.GameVersion == TRVersion.Game.TombEngine)
{
Items.Add(new ToolStripMenuItem("Copy Lua name to clipboard", null, (o, e) =>
{
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index 74ed7681e..d64494a5e 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1180,7 +1180,6 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
textToDraw.Add(CreateTextTagForObject(
instance.RotationPositionMatrix * _viewProjection,
label +
- instance.GetScriptIDOrName() + "\n" +
GetObjectPositionString(instance.Room, instance) + GetObjectTriggerString(instance)));
// Add the line height of the object
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 2f37a2036..10592b26d 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -192,61 +192,6 @@ private void butOK_Click(object sender, EventArgs e)
fullNewName = newName + "_" + newNumber;
}
- // Check if a LuaName with this value already exists
- if (_editor?.Level != null)
- {
- foreach (var room in _editor.Level.ExistingRooms)
- {
- foreach (var obj in room.Objects)
- {
- if (obj == _wayPoint)
- continue;
-
- if (obj is PositionAndScriptBasedObjectInstance scriptObj)
- {
- if (!string.IsNullOrEmpty(scriptObj.LuaName) && scriptObj.LuaName == fullNewName)
- {
- // LuaName conflict - clear the LuaName for this waypoint
- _wayPoint.LuaName = "";
- DarkMessageBox.Show(this, $"A LuaName '{fullNewName}' already exists for another object. The LuaName for this waypoint has been cleared. Please generate a new LuaName.", "LuaName Conflict",
- MessageBoxButtons.OK, MessageBoxIcon.Warning);
-
- // Update waypoint properties but with cleared LuaName
- _wayPoint.Name = newName;
- _wayPoint.Sequence = (ushort)numSequence.Value;
- _wayPoint.Number = newNumber;
- _wayPoint.Type = newType;
- _wayPoint.Radius1 = (float)numDimension1.Value;
- _wayPoint.Radius2 = (float)numDimension2.Value;
- _wayPoint.RotationX = (float)numRotationX.Value;
- _wayPoint.RotationY = (float)numRotationY.Value;
- _wayPoint.Roll = (float)numRoll.Value;
-
- // Batch type update
- if (oldType != newType && _editor?.Level != null)
- {
- var oldSequence = _wayPoint.Sequence;
- foreach (var r in _editor.Level.ExistingRooms)
- {
- foreach (var o in r.Objects.OfType())
- {
- if (o != _wayPoint && (o.BaseName == oldBaseName || o.Sequence == oldSequence))
- {
- o.Type = newType;
- }
- }
- }
- }
-
- DialogResult = DialogResult.OK;
- Close();
- return;
- }
- }
- }
- }
- }
-
// Update waypoint properties
_wayPoint.Name = newName;
_wayPoint.Sequence = (ushort)numSequence.Value;
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
index a1bdeb9c9..fb89588b7 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/LevelCompilerTombEngine.cs
@@ -397,8 +397,7 @@ private void BuildCamerasAndSinks()
Type = (int)instance.Type,
Radius1 = instance.Radius1,
Radius2 = instance.Radius2,
- Name = instance.Name,
- LuaName = instance.LuaName ?? string.Empty
+ Name = instance.Name
});
}
}
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
index 69a833cba..af681ed32 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/Structs.cs
@@ -601,7 +601,6 @@ public struct TombEngineWayPoint
public float Radius1;
public float Radius2;
public string Name;
- public string LuaName;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
diff --git a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
index b0afe65e8..b33e8461a 100644
--- a/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
+++ b/TombLib/TombLib/LevelData/Compilers/TombEngine/TombEngine.cs
@@ -93,7 +93,6 @@ private void WriteLevelTombEngine()
writer.Write(waypoint.Radius1);
writer.Write(waypoint.Radius2);
writer.Write(waypoint.Name);
- writer.Write(waypoint.LuaName);
}
writer.Write((uint)_sinks.Count);
diff --git a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
index 5ff1607ec..4812e000f 100644
--- a/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
+++ b/TombLib/TombLib/LevelData/Instances/WayPointInstance.cs
@@ -15,7 +15,7 @@ public enum WayPointType
Bezier // Multi-point bezier path
}
- public class WayPointInstance : PositionAndScriptBasedObjectInstance, IRotateableYXRoll, ISizeable
+ public class WayPointInstance : PositionBasedObjectInstance, IRotateableYXRoll, ISizeable
{
private string _name = "";
private ushort _sequence;
@@ -71,9 +71,6 @@ public string Name
{
_name = "";
}
-
- // Update LuaName to match Name
- LuaName = Name;
}
}
@@ -83,8 +80,6 @@ public ushort Number
set
{
_number = value;
- // Update LuaName when number changes
- LuaName = Name;
}
}
@@ -94,8 +89,6 @@ public WayPointType Type
set
{
_type = value;
- // Update LuaName when type changes (affects Name format)
- LuaName = Name;
}
}
From 026ae2aebe44db3b5c41d2c486c0508afa72b0eb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 26 Jan 2026 01:33:00 +0000
Subject: [PATCH 33/36] Fix WayPoint context menu and add rotation display
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
.../Controls/ContextMenus/MaterialObjectContextMenu.cs | 2 +-
TombEditor/Controls/Panel3D/Panel3DDraw.cs | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs b/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs
index e288719b1..d9a31c4e6 100644
--- a/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs
+++ b/TombEditor/Controls/ContextMenus/MaterialObjectContextMenu.cs
@@ -33,7 +33,7 @@ public MaterialObjectContextMenu(Editor editor, IWin32Window owner, ObjectInstan
}
}
- if (!(targetObject is LightInstance || targetObject is GhostBlockInstance || targetObject is WayPointInstance))
+ if (!(targetObject is LightInstance || targetObject is GhostBlockInstance))
{
Items.Add(new ToolStripMenuItem("Edit object", Properties.Resources.general_edit_16, (o, e) =>
{
diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
index d64494a5e..765d03fc3 100644
--- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs
+++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs
@@ -1177,10 +1177,14 @@ private void DrawPlaceholders(Effect effect, Room[] roomsWhoseObjectsToDraw, Lis
$"{instance.BaseName} " :
$"{instance.BaseName} ({instance.Number}) ";
+ string rotationInfo = GetObjectRotationString(instance.Room, instance);
+ if (!string.IsNullOrEmpty(rotationInfo))
+ rotationInfo = "\n" + rotationInfo;
+
textToDraw.Add(CreateTextTagForObject(
instance.RotationPositionMatrix * _viewProjection,
label +
- GetObjectPositionString(instance.Room, instance) + GetObjectTriggerString(instance)));
+ GetObjectPositionString(instance.Room, instance) + rotationInfo + GetObjectTriggerString(instance)));
// Add the line height of the object
AddObjectHeightLine(instance.Room, instance.Position);
From da368c1a134b42579ce1ad6fbfee7ef4fcab0fc5 Mon Sep 17 00:00:00 2001
From: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date: Sun, 25 Jan 2026 20:41:25 -0500
Subject: [PATCH 34/36] fix bugs
---
TombLib/TombLib/LevelData/IO/Prj2Loader.cs | 1 -
TombLib/TombLib/LevelData/IO/Prj2Writer.cs | 1 -
TombLib/TombLib/LevelData/Instances/WayPointInstance.cs | 8 ++------
3 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
index 85e9aebf6..90ae3ab23 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Loader.cs
@@ -1408,7 +1408,6 @@ private static bool LoadObjects(ChunkReader chunkIO, ChunkId idOuter, LevelSetti
instance.Position = chunkIO.Raw.ReadVector3();
instance.SetArbitaryRotationsYX(chunkIO.Raw.ReadSingle(), chunkIO.Raw.ReadSingle());
instance.Roll = chunkIO.Raw.ReadSingle();
- instance.ScriptId = ReadOptionalLEB128Int(chunkIO.Raw);
string baseName = chunkIO.Raw.ReadStringUTF8();
instance.Sequence = LEB128.ReadUShort(chunkIO.Raw);
instance.Number = LEB128.ReadUShort(chunkIO.Raw);
diff --git a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
index 5b01d89a9..d0a4e6025 100644
--- a/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
+++ b/TombLib/TombLib/LevelData/IO/Prj2Writer.cs
@@ -688,7 +688,6 @@ private static void WriteObjects(ChunkWriter chunkIO, IEnumerable "WayPoint " + Name + (IsSingularType() ? "" : " (" + Number + ")") + " " + " (" + (Room?.ToString() ?? "NULL") + ")";
}
}
From 6e1457fd8c04a041e4e8d1d58ff94bf89cf0c973 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 26 Jan 2026 01:57:17 +0000
Subject: [PATCH 35/36] Add sequence number uniqueness validation
Co-authored-by: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
---
TombEditor/Forms/FormWayPoint.cs | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index 10592b26d..ab95f67cf 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -149,8 +149,10 @@ private void butOK_Click(object sender, EventArgs e)
bool nameChanged = oldBaseName != newName;
ushort currentNumber = (ushort)numNumber.Value;
bool numberChanged = _wayPoint.Number != currentNumber;
+ ushort currentSequence = (ushort)numSequence.Value;
+ bool sequenceChanged = _wayPoint.Sequence != currentSequence;
- if ((nameChanged || numberChanged) && _editor?.Level != null)
+ if (((nameChanged || numberChanged) || sequenceChanged) && _editor?.Level != null)
{
foreach (var room in _editor.Level.ExistingRooms)
@@ -173,6 +175,16 @@ private void butOK_Click(object sender, EventArgs e)
}
// Different numbers - this is OK
}
+
+ // Check for duplicate sequence numbers
+ // Sequence must be unique UNLESS the base name is the same
+ if (obj.Sequence == currentSequence && obj.BaseName != newName)
+ {
+ // Same sequence but different base name = NOT allowed
+ DarkMessageBox.Show(this, $"A WayPoint with sequence number {currentSequence} already exists with a different name ('{obj.BaseName}'). Sequence numbers must be unique unless waypoints share the same base name.", "Duplicate Sequence",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
}
}
}
From f0a00891d7a7c618d7c76c0483d483f374dc0ebf Mon Sep 17 00:00:00 2001
From: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date: Mon, 26 Jan 2026 13:53:21 -0500
Subject: [PATCH 36/36] Update Validation code for Sequence/Name and Number
---
TombEditor/Forms/FormWayPoint.cs | 72 ++++++++++++++++++++++----------
1 file changed, 50 insertions(+), 22 deletions(-)
diff --git a/TombEditor/Forms/FormWayPoint.cs b/TombEditor/Forms/FormWayPoint.cs
index ab95f67cf..992329a77 100644
--- a/TombEditor/Forms/FormWayPoint.cs
+++ b/TombEditor/Forms/FormWayPoint.cs
@@ -119,14 +119,14 @@ private void butOK_Click(object sender, EventArgs e)
string newName = txtName.Text.Trim();
if (string.IsNullOrEmpty(newName))
{
- DarkMessageBox.Show(this, "WayPoint name cannot be empty.", "Validation Error",
+ DarkMessageBox.Show(this, "Waypoint name cannot be empty.", "Validation Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var oldType = _wayPoint.Type;
var newType = (WayPointType)cmbType.SelectedIndex;
-
+
// Extract old base name for comparison
string oldBaseName = _wayPoint.Name;
if (!_wayPoint.IsSingularType())
@@ -142,46 +142,57 @@ private void butOK_Click(object sender, EventArgs e)
}
}
- // Check for duplicate names with validation rules:
- // 1. Base name must be unique across all waypoints
- // 2. Exception: same base name allowed ONLY if numbers are different
- // (e.g., "test_0" and "test_1" are OK, but "test" Circle and "test" Point are NOT OK)
+ // Check for changes
bool nameChanged = oldBaseName != newName;
ushort currentNumber = (ushort)numNumber.Value;
bool numberChanged = _wayPoint.Number != currentNumber;
ushort currentSequence = (ushort)numSequence.Value;
bool sequenceChanged = _wayPoint.Sequence != currentSequence;
-
- if (((nameChanged || numberChanged) || sequenceChanged) && _editor?.Level != null)
+
+ // Validation rules:
+ // 1. Same name + same sequence can only exist if numbers are different
+ // 2. Same name + different sequence is NOT allowed
+ // 3. Different name + same sequence is NOT allowed
+ // 4. Same sequence + same name + same number is NOT allowed
+ // Always validate if any of these changed (removed the condition check)
+ if (_editor?.Level != null)
{
-
foreach (var room in _editor.Level.ExistingRooms)
{
foreach (var obj in room.Objects.OfType())
{
if (obj == _wayPoint)
continue;
-
- // Check if another waypoint uses this base name
- if (obj.BaseName == newName)
+
+ // Rule 1 & 4: Check for same name + same sequence
+ if (obj.BaseName == newName && obj.Sequence == currentSequence)
{
- // Same base name found - only allowed if numbers are different
+ // Only allowed if numbers are different
if (obj.Number == currentNumber)
{
- // Same base name AND same number = duplicate
- DarkMessageBox.Show(this, $"A WayPoint with the name '{newName}' and number {currentNumber} already exists.", "Duplicate Name",
+ DarkMessageBox.Show(this,
+ $"A waypoint with name '{newName}', sequence {currentSequence}, and number {currentNumber} already exists.",
+ "Validation Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
- // Different numbers - this is OK
+ // Different numbers - this is allowed
+ }
+ // Rule 2: Check for same name + different sequence
+ else if (obj.BaseName == newName && obj.Sequence != currentSequence)
+ {
+ DarkMessageBox.Show(this,
+ $"A waypoint with name '{newName}' already exists with a different sequence ({obj.Sequence}). The same name cannot be used with different sequence numbers.",
+ "Validation Error",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
}
-
- // Check for duplicate sequence numbers
- // Sequence must be unique UNLESS the base name is the same
- if (obj.Sequence == currentSequence && obj.BaseName != newName)
+ // Rule 3: Check for different name + same sequence
+ else if (obj.BaseName != newName && obj.Sequence == currentSequence)
{
- // Same sequence but different base name = NOT allowed
- DarkMessageBox.Show(this, $"A WayPoint with sequence number {currentSequence} already exists with a different name ('{obj.BaseName}'). Sequence numbers must be unique unless waypoints share the same base name.", "Duplicate Sequence",
+ DarkMessageBox.Show(this,
+ $"A waypoint with sequence {currentSequence} already exists with a different name ('{obj.BaseName}'). The same sequence number cannot be used with different names.",
+ "Validation Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
@@ -189,6 +200,23 @@ private void butOK_Click(object sender, EventArgs e)
}
}
+ // Batch type update: if type changed, update all waypoints with the same name/sequence pair
+ if (oldType != newType && _editor?.Level != null)
+ {
+ var oldSequence = _wayPoint.Sequence;
+ foreach (var room in _editor.Level.ExistingRooms)
+ {
+ foreach (var obj in room.Objects.OfType())
+ {
+ // Update all waypoints that share the same base name (which implies same sequence)
+ if (obj != _wayPoint && obj.BaseName == oldBaseName)
+ {
+ obj.Type = newType;
+ }
+ }
+ }
+ }
+
// Generate the new full name that will be used
string fullNewName = newName;
ushort newNumber = (ushort)numNumber.Value;