mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2025-02-06 07:41:01 +00:00
449 lines
14 KiB
C#
449 lines
14 KiB
C#
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Windows.Forms;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.Editing;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
using System.Drawing;
|
|
|
|
#endregion
|
|
|
|
//JBR Parallel Linedef mode
|
|
namespace CodeImp.DoomBuilder.BuilderModes
|
|
{
|
|
[EditMode(DisplayName = "Parallel Linedef",
|
|
AllowCopyPaste = false,
|
|
Volatile = true)]
|
|
public sealed class ParallelLinedefMode : BaseClassicMode
|
|
{
|
|
#region ================== Constants
|
|
|
|
private const float LINE_THICKNESS = 0.6f;
|
|
private const int LINKED_LINEDEFS = 0;
|
|
private const int UNLINKED_LINEDEFS = 1;
|
|
private const int LINEDEFS = 1;
|
|
private const int VERTICES = 2;
|
|
private const int THINGS = 3;
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
private EditMode basemode;
|
|
private ICollection<Linedef> selected;
|
|
private TracksTracer trackstracer;
|
|
private bool clearlinedefs;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
// Just keep the base mode button checked
|
|
public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
// Constructor
|
|
public ParallelLinedefMode(EditMode basemode)
|
|
{
|
|
this.basemode = basemode;
|
|
}
|
|
|
|
// Disposer
|
|
public override void Dispose()
|
|
{
|
|
// Not already disposed?
|
|
if (!isdisposed)
|
|
{
|
|
// Clean up
|
|
|
|
// Done
|
|
base.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Methods
|
|
|
|
public List<Linedef> GetSelectedLinedefFromVertex(Vertex v)
|
|
{
|
|
List<Linedef> list = new List<Linedef>();
|
|
foreach (Linedef ld in v.Linedefs)
|
|
{
|
|
if (ld.Selected) list.Add(ld);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Events
|
|
|
|
public override void OnHelp()
|
|
{
|
|
General.ShowHelp("e_parallellinedef.html");
|
|
}
|
|
|
|
// Cancelled
|
|
public override void OnCancel()
|
|
{
|
|
// Cancel base class
|
|
base.OnCancel();
|
|
|
|
// Clear selection that is not the current mode
|
|
if (clearlinedefs) General.Map.Map.ClearSelectedLinedefs();
|
|
|
|
// Return to base mode
|
|
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
|
|
}
|
|
|
|
// Mode engages
|
|
public override void OnEngage()
|
|
{
|
|
base.OnEngage();
|
|
renderer.SetPresentation(Presentation.Standard);
|
|
|
|
// Make collections by selection
|
|
if (basemode is VerticesMode)
|
|
{
|
|
selected = new List<Linedef>();
|
|
ICollection<Vertex> selectedvertices = General.Map.Map.GetSelectedVertices(true);
|
|
foreach (Vertex v in selectedvertices)
|
|
{
|
|
foreach (Linedef ld in v.Linedefs)
|
|
{
|
|
if (ld.Start.Selected && ld.End.Selected)
|
|
{
|
|
ld.Selected = true;
|
|
if (!selected.Contains(ld)) selected.Add(ld);
|
|
}
|
|
}
|
|
}
|
|
clearlinedefs = true;
|
|
}
|
|
if (basemode is LinedefsMode)
|
|
{
|
|
selected = General.Map.Map.GetSelectedLinedefs(true);
|
|
}
|
|
if (basemode is SectorsMode)
|
|
{
|
|
selected = new List<Linedef>();
|
|
ICollection<Sector> selectedsectors = General.Map.Map.GetSelectedSectors(true);
|
|
foreach (Sector sc in selectedsectors)
|
|
{
|
|
foreach (Sidedef sd in sc.Sidedefs)
|
|
{
|
|
sd.Line.Selected = true;
|
|
if (!selected.Contains(sd.Line)) selected.Add(sd.Line);
|
|
}
|
|
}
|
|
clearlinedefs = true;
|
|
}
|
|
|
|
// Create the tracks tracer
|
|
trackstracer = new TracksTracer(selected);
|
|
|
|
// Check if tracing was valid
|
|
if (!trackstracer.IsValid)
|
|
{
|
|
DialogResult dr = MessageBox.Show("Detected intersections while tracing!\nMake sure you're selecting vertices with maximum of 2 linedefs connected to them.\nResults may not be desirable, continue anyway?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
|
if (dr != DialogResult.Yes)
|
|
{
|
|
OnCancel();
|
|
return;
|
|
}
|
|
}
|
|
BuilderPlug.Me.ParallelLinedefForm.SetNumOpenPaths(trackstracer.OpenPaths.Count);
|
|
BuilderPlug.Me.ParallelLinedefForm.SetNumClosePaths(trackstracer.ClosePaths.Count);
|
|
|
|
// Show toolbox window
|
|
BuilderPlug.Me.ParallelLinedefForm.Show((Form)General.Interface);
|
|
}
|
|
|
|
// Disenagaging
|
|
public override void OnDisengage()
|
|
{
|
|
base.OnDisengage();
|
|
|
|
// Hide toolbox window
|
|
BuilderPlug.Me.ParallelLinedefForm.Hide();
|
|
}
|
|
|
|
// This applies the curves and returns to the base mode
|
|
public override void OnAccept()
|
|
{
|
|
// Create undo
|
|
General.Map.UndoRedo.CreateUndo("Parallel (Linedef)");
|
|
|
|
int createas = BuilderPlug.Me.ParallelLinedefForm.CreateAs;
|
|
float distance = BuilderPlug.Me.ParallelLinedefForm.Distance;
|
|
bool closeopenpath = BuilderPlug.Me.ParallelLinedefForm.CloseOpenPath;
|
|
bool backwards = BuilderPlug.Me.ParallelLinedefForm.Backwards;
|
|
ThingBrowser2Form.Result thingresult = new ThingBrowser2Form.Result();
|
|
|
|
// Ask for thing type...
|
|
if (createas == THINGS)
|
|
{
|
|
BuilderPlug.Me.ParallelLinedefForm.Hide();
|
|
|
|
// Pick the thing to add...
|
|
thingresult = ThingBrowser2Form.BrowseThing(BuilderPlug.Me.ParallelLinedefForm);
|
|
if (!thingresult.OK)
|
|
{
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
General.Interface.DisplayStatus(StatusType.Warning, "Operation aborted.");
|
|
return;
|
|
}
|
|
|
|
BuilderPlug.Me.ParallelLinedefForm.Show();
|
|
BuilderPlug.Me.ParallelLinedefForm.Activate();
|
|
}
|
|
|
|
// Go for all selected lines
|
|
foreach (List<Vector2D> path in trackstracer.OpenPaths)
|
|
{
|
|
List<Vector2D> parallelpath = TracksTracer.ParallelizeOpenPath(path, distance, backwards);
|
|
if (createas == LINKED_LINEDEFS)
|
|
{
|
|
List<DrawnVertex> dvl = new List<DrawnVertex>();
|
|
if (closeopenpath) TracksTracer.AddPointToDrawnVertex(dvl, path[0], true, true);
|
|
TracksTracer.AddPathToDrawnVertex(dvl, parallelpath, true, true);
|
|
if (closeopenpath) TracksTracer.AddPointToDrawnVertex(dvl, path[path.Count - 1], true, true);
|
|
if (!Tools.DrawLines(dvl, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate))
|
|
{
|
|
// Drawing failed
|
|
// NOTE: I have to call this twice, because the first time only cancels this volatile mode
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
return;
|
|
}
|
|
}
|
|
else if (createas == UNLINKED_LINEDEFS)
|
|
{
|
|
// Unlinked linedef
|
|
Vertex oldv = General.Map.Map.CreateVertex(parallelpath[0]);
|
|
for (int i = 1; i < parallelpath.Count; i++)
|
|
{
|
|
Vertex newv = General.Map.Map.CreateVertex(parallelpath[i]);
|
|
General.Map.Map.CreateLinedef(oldv, newv);
|
|
oldv = newv;
|
|
}
|
|
if (closeopenpath)
|
|
{
|
|
Vertex vtf = General.Map.Map.CreateVertex(path[0]);
|
|
Vertex vtl = General.Map.Map.CreateVertex(path[path.Count - 1]);
|
|
Vertex vpf = General.Map.Map.CreateVertex(parallelpath[0]);
|
|
Vertex vpl = General.Map.Map.CreateVertex(parallelpath[parallelpath.Count - 1]);
|
|
General.Map.Map.CreateLinedef(vtf, vpf);
|
|
General.Map.Map.CreateLinedef(vpl, vtl);
|
|
}
|
|
}
|
|
else if (createas == VERTICES)
|
|
{
|
|
// Vertex
|
|
foreach (Vector2D pos in parallelpath)
|
|
{
|
|
General.Map.Map.CreateVertex(pos);
|
|
}
|
|
}
|
|
else if (createas == THINGS)
|
|
{
|
|
foreach (Vector2D pos in parallelpath)
|
|
{
|
|
Thing tp = General.Map.Map.CreateThing();
|
|
if (tp != null)
|
|
{
|
|
General.Settings.ApplyDefaultThingSettings(tp);
|
|
thingresult.Apply(tp);
|
|
tp.Move(pos);
|
|
tp.UpdateConfiguration();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach (List<Vector2D> path in trackstracer.ClosePaths)
|
|
{
|
|
List<Vector2D> parallelpath = TracksTracer.ParallelizeClosePath(path, distance, backwards);
|
|
if (createas == LINKED_LINEDEFS)
|
|
{
|
|
List<DrawnVertex> dvl = new List<DrawnVertex>();
|
|
TracksTracer.AddPathToDrawnVertex(dvl, parallelpath, true, true);
|
|
if (!Tools.DrawLines(dvl, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate))
|
|
{
|
|
// Drawing failed
|
|
// NOTE: I have to call this twice, because the first time only cancels this volatile mode
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
return;
|
|
}
|
|
}
|
|
else if (createas == UNLINKED_LINEDEFS)
|
|
{
|
|
// Unlinked linedef
|
|
Vertex oldv = General.Map.Map.CreateVertex(parallelpath[0]);
|
|
for (int i = 1; i < parallelpath.Count; i++)
|
|
{
|
|
Vertex newv = General.Map.Map.CreateVertex(parallelpath[i]);
|
|
General.Map.Map.CreateLinedef(oldv, newv);
|
|
oldv = newv;
|
|
}
|
|
}
|
|
else if (createas == VERTICES)
|
|
{
|
|
// Vertex
|
|
for (int i = 0; i < parallelpath.Count - 1; i++)
|
|
{
|
|
General.Map.Map.CreateVertex(parallelpath[i]);
|
|
}
|
|
}
|
|
else if (createas == THINGS)
|
|
{
|
|
// Thing
|
|
for (int i = 0; i < parallelpath.Count - 1; i++)
|
|
{
|
|
Thing tp = General.Map.Map.CreateThing();
|
|
if (tp != null)
|
|
{
|
|
General.Settings.ApplyDefaultThingSettings(tp);
|
|
thingresult.Apply(tp);
|
|
tp.Move(parallelpath[i]);
|
|
tp.UpdateConfiguration();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Announce how many we created
|
|
int num = trackstracer.OpenPaths.Count + trackstracer.ClosePaths.Count;
|
|
if (num == 0)
|
|
{
|
|
General.Interface.DisplayStatus(StatusType.Warning, "No parallel tracks created.");
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
return;
|
|
}
|
|
if (num > 1)
|
|
General.Interface.DisplayStatus(StatusType.Action, "Created " + num.ToString() + " parallel tracks.");
|
|
else
|
|
General.Interface.DisplayStatus(StatusType.Action, "Created a parallel track.");
|
|
|
|
if (createas == THINGS)
|
|
{
|
|
//Update Things filter
|
|
General.Map.ThingsFilter.Update();
|
|
}
|
|
|
|
// Snap to map format accuracy
|
|
General.Map.Map.SnapAllToAccuracy();
|
|
|
|
// Clear selection
|
|
General.Map.Map.ClearAllSelected();
|
|
|
|
// Update cached values
|
|
General.Map.Map.Update();
|
|
|
|
// Edit new sectors?
|
|
List<Sector> newsectors = General.Map.Map.GetMarkedSectors(true);
|
|
if (BuilderPlug.Me.EditNewSector && (newsectors.Count > 0))
|
|
General.Interface.ShowEditSectors(newsectors);
|
|
|
|
// Update the used textures
|
|
General.Map.Data.UpdateUsedTextures();
|
|
|
|
//mxd
|
|
General.Map.Renderer2D.UpdateExtraFloorFlag();
|
|
|
|
// Map is changed
|
|
General.Map.IsChanged = true;
|
|
|
|
// Return to base mode
|
|
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
|
|
}
|
|
|
|
// Redrawing display
|
|
public override void OnRedrawDisplay()
|
|
{
|
|
renderer.RedrawSurface();
|
|
float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale;
|
|
|
|
// Render lines
|
|
if (renderer.StartPlotter(true))
|
|
{
|
|
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
|
|
renderer.PlotVerticesSet(General.Map.Map.Vertices);
|
|
renderer.Finish();
|
|
}
|
|
|
|
// Render things
|
|
if (renderer.StartThings(true))
|
|
{
|
|
renderer.RenderThingSet(General.Map.Map.Things, Presentation.THINGS_ALPHA);
|
|
renderer.RenderNiGHTSPath();
|
|
renderer.Finish();
|
|
}
|
|
|
|
int createas = BuilderPlug.Me.ParallelLinedefForm.CreateAs;
|
|
float distance = BuilderPlug.Me.ParallelLinedefForm.Distance;
|
|
bool closeopenpath = BuilderPlug.Me.ParallelLinedefForm.CloseOpenPath;
|
|
bool backwards = BuilderPlug.Me.ParallelLinedefForm.Backwards;
|
|
|
|
// Render overlay
|
|
bool renderlines = (createas <= LINEDEFS);
|
|
if (renderer.StartOverlay(true))
|
|
{
|
|
foreach (List<Vector2D> path in trackstracer.OpenPaths)
|
|
{
|
|
// Selected path in info color
|
|
for (int i = 1; i < path.Count; i++)
|
|
renderer.RenderLine(path[i - 1], path[i], LINE_THICKNESS, General.Colors.InfoLine, true);
|
|
renderer.RenderRectangleFilled(new RectangleF(path[0].x - vsize, path[0].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.InfoLine, true);
|
|
renderer.RenderRectangleFilled(new RectangleF(path[path.Count - 1].x - vsize, path[path.Count - 1].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.InfoLine, true);
|
|
|
|
// Parallel path
|
|
List<Vector2D> parallelpath = TracksTracer.ParallelizeOpenPath(path, distance, backwards);
|
|
renderer.RenderRectangleFilled(new RectangleF(parallelpath[0].x - vsize, parallelpath[0].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true);
|
|
for (int i = 1; i < parallelpath.Count; i++)
|
|
{
|
|
if (renderlines) renderer.RenderLine(parallelpath[i - 1], parallelpath[i], LINE_THICKNESS, General.Colors.Highlight, true);
|
|
renderer.RenderRectangleFilled(new RectangleF(parallelpath[i].x - vsize, parallelpath[i].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true);
|
|
}
|
|
if (closeopenpath && renderlines)
|
|
{
|
|
renderer.RenderLine(path[0], parallelpath[0], LINE_THICKNESS, General.Colors.Highlight, true);
|
|
renderer.RenderLine(path[path.Count - 1], parallelpath[parallelpath.Count - 1], LINE_THICKNESS, General.Colors.Highlight, true);
|
|
}
|
|
}
|
|
foreach (List<Vector2D> path in trackstracer.ClosePaths)
|
|
{
|
|
// Selected path in info color
|
|
for (int i = 1; i < path.Count; i++)
|
|
renderer.RenderLine(path[i - 1], path[i], LINE_THICKNESS, General.Colors.InfoLine, true);
|
|
renderer.RenderRectangleFilled(new RectangleF(path[0].x - vsize, path[0].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.InfoLine, true);
|
|
renderer.RenderRectangleFilled(new RectangleF(path[path.Count - 1].x - vsize, path[path.Count - 1].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.InfoLine, true);
|
|
|
|
// Parallel path
|
|
List<Vector2D> parallelpath = TracksTracer.ParallelizeClosePath(path, distance, backwards);
|
|
renderer.RenderRectangleFilled(new RectangleF(parallelpath[0].x - vsize, parallelpath[0].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true);
|
|
for (int i = 1; i < parallelpath.Count; i++)
|
|
{
|
|
if (renderlines) renderer.RenderLine(parallelpath[i - 1], parallelpath[i], LINE_THICKNESS, General.Colors.Highlight, true);
|
|
renderer.RenderRectangleFilled(new RectangleF(parallelpath[i].x - vsize, parallelpath[i].y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.Highlight, true);
|
|
}
|
|
}
|
|
renderer.Finish();
|
|
}
|
|
|
|
renderer.Present();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|