Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 253 additions & 25 deletions CentrED/Tools/LargeScale/Operations/CopyMove.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public class CopyMove : RemoteLargeScaleTool
// New: current area for conversions
private RectU16 _currentArea;
private bool _hasArea = false;

private bool useAlternateSource = false;
private string alternateMapPath = "";
private string alternateStaIdxPath = "";
private string alternateStaticsPath = "";
private int alternateMapWidth = 7168; // Default to Map0 dimensions
private int alternateMapHeight = 4096;

public void SetArea(RectU16 area)
{
Expand All @@ -36,33 +43,199 @@ public void SetArea(RectU16 area)
public override bool DrawUI()
{
var changed = false;

// Copy / Move
changed |= ImGui.RadioButton(LangManager.Get(COPY), ref copyMove_type, (int)LSO.CopyMove.Copy);
ImGui.SameLine();
changed |= ImGui.RadioButton(LangManager.Get(MOVE), ref copyMove_type, (int)LSO.CopyMove.Move);

// Copy/Move radio buttons - DISABLE Move when using alternate source
if (useAlternateSource)
{
// Force Copy mode when using alternate source
if (copyMove_type != (int)LSO.CopyMove.Copy)
{
copyMove_type = (int)LSO.CopyMove.Copy;
changed = true;
}

// Draw Copy as enabled
changed |= ImGui.RadioButton(LangManager.Get(COPY), ref copyMove_type, (int)LSO.CopyMove.Copy);

// Draw Move as disabled
ImGui.BeginDisabled();
ImGui.SameLine();
int disabledMove = (int)LSO.CopyMove.Move;
ImGui.RadioButton(LangManager.Get(MOVE), ref disabledMove, (int)LSO.CopyMove.Move);
ImGui.EndDisabled();

// Show tooltip on disabled Move button
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Move is not supported when using a different source map.\nOnly Copy is available.");
}
}
else
{
// Normal mode - both Copy and Move enabled
changed |= ImGui.RadioButton(LangManager.Get(COPY), ref copyMove_type, (int)LSO.CopyMove.Copy);
ImGui.SameLine();
changed |= ImGui.RadioButton(LangManager.Get(MOVE), ref copyMove_type, (int)LSO.CopyMove.Move);
}

ImGui.Separator();

// Relative / Absolute toggle with auto-conversion
int prevMode = copyMove_coordMode;
changed |= ImGui.RadioButton(LangManager.Get(COORD_MODE_RELATIVE), ref copyMove_coordMode, 0);
ImGui.SameLine();
changed |= ImGui.RadioButton(LangManager.Get(COORD_MODE_ABSOLUTE), ref copyMove_coordMode, 1);

if (prevMode != copyMove_coordMode && _hasArea)

// NEW: Checkbox for alternate source
changed |= ImGui.Checkbox("Use Different Source Map", ref useAlternateSource);

if (useAlternateSource)
{
ImGui.Text("Source Map Configuration:");
ImGui.Separator();

// File paths
ImGui.InputText("Map File##alt", ref alternateMapPath, 256);
ImGui.SameLine();
if (ImGui.Button("Browse...##map"))
{
TinyFileDialogs.TryOpenFile
("Select Map File", Environment.CurrentDirectory, ["*.mul"], null, false, out var result);
if (!string.IsNullOrEmpty(result))
{
alternateMapPath = result;
// Auto-populate related files
var basePath = alternateMapPath.Replace(".mul", "");
var mapNumber = basePath[basePath.Length - 1]; // Get map number
var directory = Path.GetDirectoryName(alternateMapPath);
var baseFileName = Path.GetFileNameWithoutExtension(alternateMapPath)
.Replace("Map", "").Replace("map", "");

alternateStaIdxPath = Path.Combine(directory, $"Staidx{mapNumber}.mul");
alternateStaticsPath = Path.Combine(directory, $"Statics{mapNumber}.mul");

// Set default dimensions based on common UO map sizes
switch(mapNumber)
{
case '0': // OLD Felucca/Trammel
alternateMapWidth = 6144;
alternateMapHeight = 4096;
break;
case '1': // Felucca/Trammel
alternateMapWidth = 7168;
alternateMapHeight = 4096;
break;
case '2': // Ilshenar
alternateMapWidth = 2304;
alternateMapHeight = 1600;
break;
case '3': // Malas
alternateMapWidth = 2560;
alternateMapHeight = 2048;
break;
case '4': // Tokuno
alternateMapWidth = 1448;
alternateMapHeight = 1448;
break;
case '5': // TerMur
alternateMapWidth = 1280;
alternateMapHeight = 4096;
break;
case '6': // Custom
alternateMapWidth = 1280;
alternateMapHeight = 4096;
break;
default:
// Keep current values
break;
}
}
}

ImGui.InputText("StaIdx File##alt", ref alternateStaIdxPath, 256);
ImGui.InputText("Statics File##alt", ref alternateStaticsPath, 256);

ImGui.Separator();

ImGui.Text("Map Dimensions (in tiles):");
changed |= ImGuiEx.DragInt("Width##alt", ref alternateMapWidth, 1, 64, 8192);
ImGui.SameLine();
ImGui.TextDisabled("(?)");
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Width of the source map in tiles.\nMap0: 6144, Map1: 7168, Map2: 2304, etc...");
}

changed |= ImGuiEx.DragInt("Height##alt", ref alternateMapHeight, 1, 64, 8192);
ImGui.SameLine();
ImGui.TextDisabled("(?)");
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Height of the source map in tiles.\nMap0: 4096, Map1: 4096, Map2: 1600, etc...");
}

ImGui.Separator();

// Validation warning
if (!string.IsNullOrEmpty(alternateMapPath) && !File.Exists(alternateMapPath))
{
ImGui.TextColored(new System.Numerics.Vector4(1, 0, 0, 1), "Warning: Map file not found!");
}
if (!string.IsNullOrEmpty(alternateStaIdxPath) && !File.Exists(alternateStaIdxPath))
{
ImGui.TextColored(new System.Numerics.Vector4(1, 0.5f, 0, 1), "Warning: StaIdx file not found!");
}
if (!string.IsNullOrEmpty(alternateStaticsPath) && !File.Exists(alternateStaticsPath))
{
ImGui.TextColored(new System.Numerics.Vector4(1, 0.5f, 0, 1), "Warning: Statics file not found!");
}
}

ImGui.Separator();

// DESTINATION COORDINATES SECTION
// When using alternate source, force Absolute mode and disable Relative
if (useAlternateSource)
{
if (copyMove_coordMode == 1)
// Force Absolute mode
if (copyMove_coordMode != 1)
{
// Relative -> Absolute
copyMove_inputX = _currentArea.X1 + copyMove_inputX;
copyMove_inputY = _currentArea.Y1 + copyMove_inputY;
copyMove_coordMode = 1;
changed = true;
}
else

// Draw Relative as disabled
ImGui.BeginDisabled();
int disabledRelative = 0;
ImGui.RadioButton(LangManager.Get(COORD_MODE_RELATIVE), ref disabledRelative, 0);
ImGui.EndDisabled();

// Show tooltip on disabled Relative button
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
// Absolute -> Relative
copyMove_inputX = copyMove_inputX - _currentArea.X1;
copyMove_inputY = copyMove_inputY - _currentArea.Y1;
ImGui.SetTooltip("Relative coordinates are not supported when using a different source map.\nOnly Absolute coordinates are available.");
}

ImGui.SameLine();
// Draw Absolute as enabled
changed |= ImGui.RadioButton(LangManager.Get(COORD_MODE_ABSOLUTE), ref copyMove_coordMode, 1);
}
else
{
// Normal mode - both Relative and Absolute enabled
int prevMode = copyMove_coordMode;
changed |= ImGui.RadioButton(LangManager.Get(COORD_MODE_RELATIVE), ref copyMove_coordMode, 0);
ImGui.SameLine();
changed |= ImGui.RadioButton(LangManager.Get(COORD_MODE_ABSOLUTE), ref copyMove_coordMode, 1);

if (prevMode != copyMove_coordMode && _hasArea)
{
if (copyMove_coordMode == 1)
{
// Relative -> Absolute
copyMove_inputX = _currentArea.X1 + copyMove_inputX;
copyMove_inputY = _currentArea.Y1 + copyMove_inputY;
}
else
{
// Absolute -> Relative
copyMove_inputX = copyMove_inputX - _currentArea.X1;
copyMove_inputY = copyMove_inputY - _currentArea.Y1;
}
}
}

Expand Down Expand Up @@ -104,7 +277,7 @@ public override bool DrawUI()

public override bool CanSubmit(RectU16 area)
{
// Ensure offsets computed from current inputs/mode/area
// Calculate offsets
if (copyMove_coordMode == 1)
{
copyMove_offsetX = copyMove_inputX - area.X1;
Expand All @@ -116,7 +289,40 @@ public override bool CanSubmit(RectU16 area)
copyMove_offsetY = copyMove_inputY;
}

// Existing bounds checks
// Validation for alternate source map dimensions
if (useAlternateSource)
{
// Check if source area exists within alternate map bounds
if (area.X1 >= alternateMapWidth || area.X2 >= alternateMapWidth)
{
_submitStatus = $"Source area exceeds alternate map width ({alternateMapWidth})";
return false;
}
if (area.Y1 >= alternateMapHeight || area.Y2 >= alternateMapHeight)
{
_submitStatus = $"Source area exceeds alternate map height ({alternateMapHeight})";
return false;
}

// Check if files exist
if (!File.Exists(alternateMapPath))
{
_submitStatus = "Alternate map file not found";
return false;
}
if (!File.Exists(alternateStaIdxPath))
{
_submitStatus = "Alternate StaIdx file not found";
return false;
}
if (!File.Exists(alternateStaticsPath))
{
_submitStatus = "Alternate Statics file not found";
return false;
}
}

// Existing destination validation
if (copyMove_offsetX < 0 && copyMove_offsetX + area.X1 < 0)
{
_submitStatus = LangManager.Get(INVALID_OFFSET_X);
Expand All @@ -142,7 +348,29 @@ public override bool CanSubmit(RectU16 area)

protected override ILargeScaleOperation SubmitLSO()
{
// Still submit offsets; absolute entries were converted above
return new LSOCopyMove((LSO.CopyMove)copyMove_type, copyMove_erase, copyMove_offsetX, copyMove_offsetY);
if (useAlternateSource)
{

return new LSOCopyMove(
(LSO.CopyMove)copyMove_type,
copyMove_erase,
copyMove_offsetX,
copyMove_offsetY,
alternateMapPath,
alternateStaIdxPath,
alternateStaticsPath,
alternateMapWidth,
alternateMapHeight
);
}
else
{
return new LSOCopyMove(
(LSO.CopyMove)copyMove_type,
copyMove_erase,
copyMove_offsetX,
copyMove_offsetY
);
}
}
}
55 changes: 53 additions & 2 deletions Client/Map/LargeScaleOperation.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using static CentrED.Network.LSO;
using CentrED.Utility;
using static CentrED.Network.LSO;

namespace CentrED.Client.Map;

Expand All @@ -13,22 +14,72 @@ public class LSOCopyMove : ILargeScaleOperation
private readonly int offsetX;
private readonly int offsetY;
private readonly bool erase;


// NEW: Alternate map source fields
private readonly bool useAlternateSource;
private readonly string alternateMapPath;
private readonly string alternateStaIdxPath;
private readonly string alternateStaticsPath;
private readonly int alternateMapWidth;
private readonly int alternateMapHeight;

// Original constructor (backwards compatible)
public LSOCopyMove(CopyMove type, bool erase, int offsetX, int offsetY)
{
this.type = type;
this.erase = erase;
this.offsetX = offsetX;
this.offsetY = offsetY;
this.useAlternateSource = false;
this.alternateMapPath = string.Empty;
this.alternateStaIdxPath = string.Empty;
this.alternateStaticsPath = string.Empty;
this.alternateMapWidth = 0;
this.alternateMapHeight = 0;
}

// NEW: Extended constructor with alternate map support
public LSOCopyMove(
CopyMove type,
bool erase,
int offsetX,
int offsetY,
string alternateMapPath,
string alternateStaIdxPath,
string alternateStaticsPath,
int alternateMapWidth,
int alternateMapHeight)
{
this.type = type;
this.erase = erase;
this.offsetX = offsetX;
this.offsetY = offsetY;
this.useAlternateSource = true;
this.alternateMapPath = alternateMapPath ?? string.Empty;
this.alternateStaIdxPath = alternateStaIdxPath ?? string.Empty;
this.alternateStaticsPath = alternateStaticsPath ?? string.Empty;
this.alternateMapWidth = alternateMapWidth;
this.alternateMapHeight = alternateMapHeight;
}

public void Write(BinaryWriter writer)
{
writer.Write((byte)type);
writer.Write(offsetX);
writer.Write(offsetY);
writer.Write(erase);

// NEW: Write alternate source flag and data
writer.Write(useAlternateSource);

if (useAlternateSource)
{
writer.WriteStringNull(alternateMapPath);
writer.WriteStringNull(alternateStaIdxPath);
writer.WriteStringNull(alternateStaticsPath);
writer.Write((ushort)alternateMapWidth);
writer.Write((ushort)alternateMapHeight);
}
}
}

Expand Down
Loading
Loading