diff --git a/Documents/todo.txt b/Documents/todo.txt index a8daefa3..c2ac42b9 100644 --- a/Documents/todo.txt +++ b/Documents/todo.txt @@ -27,3 +27,5 @@ - Make Plugin Development Kit - Add proper XML comments to classes, properties and methods +- Contact AlysiumX (AlysiumX@gmail.com) for official video tutorials + (see http://www.youtube.com/view_play_list?p=6FDD1F9F674419E8) diff --git a/Source/BuilderModes/BuilderModes.csproj b/Source/BuilderModes/BuilderModes.csproj index 0b16ca53..9221126c 100644 --- a/Source/BuilderModes/BuilderModes.csproj +++ b/Source/BuilderModes/BuilderModes.csproj @@ -33,6 +33,7 @@ + diff --git a/Source/BuilderModes/ClassicModes/BaseClassicMode.cs b/Source/BuilderModes/ClassicModes/BaseClassicMode.cs new file mode 100644 index 00000000..ea69abe5 --- /dev/null +++ b/Source/BuilderModes/ClassicModes/BaseClassicMode.cs @@ -0,0 +1,107 @@ + +#region ================== Copyright (c) 2007 Pascal vd Heiden + +/* + * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com + * This program is released under GNU General Public License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Reflection; +using CodeImp.DoomBuilder.Windows; +using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Rendering; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Editing; +using CodeImp.DoomBuilder.Actions; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.BuilderModes +{ + public abstract class BaseClassicMode : ClassicMode + { + #region ================== Constants + + #endregion + + #region ================== Variables + + #endregion + + #region ================== Properties + + #endregion + + #region ================== Constructor / Disposer + + // Constructor + public BaseClassicMode() + { + // Initialize + + // We have no destructor + GC.SuppressFinalize(this); + } + + // Disposer + public override void Dispose() + { + // Not already disposed? + if(!isdisposed) + { + // Clean up + + // Done + base.Dispose(); + } + } + + #endregion + + #region ================== Methods + + // This occurs when the user presses Copy. All selected geometry must be marked for copying! + public override bool OnCopyBegin() + { + General.Map.Map.MarkAllSelectedGeometry(true); + + // Return true when anything is selected so that the copy continues + // We only have to check vertices for the geometry, because without selected + // vertices, no complete structure can exist. + return (General.Map.Map.GetMarkedVertices(true).Count > 0) || + (General.Map.Map.GetMarkedThings(true).Count > 0); + } + + // This is called when something is pasted. + public override void OnPasteEnd() + { + General.Map.Map.ClearAllSelected(); + General.Map.Map.SelectMarkedGeometry(true, true); + + // Switch to EditSelectionMode + General.Map.ChangeMode("EditSelectionMode"); + } + + #endregion + } +} diff --git a/Source/BuilderModes/ClassicModes/CurveLinedefsMode.cs b/Source/BuilderModes/ClassicModes/CurveLinedefsMode.cs index 6daf9f84..d494304d 100644 --- a/Source/BuilderModes/ClassicModes/CurveLinedefsMode.cs +++ b/Source/BuilderModes/ClassicModes/CurveLinedefsMode.cs @@ -38,7 +38,7 @@ namespace CodeImp.DoomBuilder.BuilderModes { [EditMode(DisplayName = "Curve Linedefs", Volatile = true)] - public sealed class CurveLinedefsMode : ClassicMode + public sealed class CurveLinedefsMode : BaseClassicMode { #region ================== Constants @@ -48,9 +48,6 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Variables - // Mode to return to - private EditMode basemode; - // Collections private ICollection selectedlines; private ICollection unselectedlines; @@ -60,9 +57,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Properties // Just keep the base mode button checked - public override string EditModeButtonName { get { return basemode.GetType().Name; } } - - internal EditMode BaseMode { get { return basemode; } } + public override string EditModeButtonName { get { return General.Map.PreviousStableMode.Name; } } #endregion @@ -71,8 +66,6 @@ namespace CodeImp.DoomBuilder.BuilderModes // Constructor public CurveLinedefsMode(EditMode basemode) { - this.basemode = basemode; - // Make collections by selection selectedlines = General.Map.Map.GetSelectedLinedefs(true); unselectedlines = General.Map.Map.GetSelectedLinedefs(false); @@ -95,6 +88,73 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Methods + // This generates the vertices to split the line with, from start to end + private List GenerateCurve(Linedef line) + { + // Fetch settings from window + int vertices = BuilderPlug.Me.CurveLinedefsForm.Vertices; + float distance = BuilderPlug.Me.CurveLinedefsForm.Distance; + float angle = BuilderPlug.Me.CurveLinedefsForm.Angle; + bool fixedcurve = BuilderPlug.Me.CurveLinedefsForm.FixedCurve; + bool backwards = BuilderPlug.Me.CurveLinedefsForm.Backwards; + + // Make list + List points = new List(vertices); + + //Added by Anders Åstrand 2008-05-18 + //The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html + //c and theta are known (length of line and angle parameter). d, R and h are + //calculated from those two + //If the curve is not supposed to be a circular segment it's simply deformed to fit + //the value set for distance. + + //The vertices are generated to be evenly distributed (by angle) along the curve + //and lastly they are rotated and moved to fit with the original line + + //calculate some identities of a circle segment (refer to the graph in the url above) + double c = line.Length; + double theta = angle; + + double d = (c / Math.Tan(theta / 2)) / 2; + double R = d / Math.Cos(theta / 2); + double h = R - d; + + double yDeform = fixedcurve ? 1 : distance / h; + if(backwards) + yDeform = -yDeform; + + double a, x, y; + Vector2D vertex; + + for(int v = 1; v <= vertices; v++) + { + //calculate the angle for this vertex + //the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments + //this assumes the line is horisontal and on y = 0, the point is rotated and moved later + + a = (Math.PI - theta) / 2 + v * (theta / (vertices + 1)); + + //calculate the coordinates of the point, and distort the y coordinate + //using the deform factor calculated above + x = Math.Cos(a) * R; + y = (Math.Sin(a) * R - d) * yDeform; + + //rotate and transform to fit original line + vertex = new Vector2D((float)x, (float)y).GetRotated(line.Angle + Angle2D.PIHALF); + vertex = vertex.GetTransformed(line.GetCenterPoint().x, line.GetCenterPoint().y, 1, 1); + + points.Add(vertex); + } + + + // Done + return points; + } + + #endregion + + #region ================== Events + // Cancelled public override void OnCancel() { @@ -102,7 +162,7 @@ namespace CodeImp.DoomBuilder.BuilderModes base.OnCancel(); // Return to base mode - General.Map.ChangeMode(basemode); + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // Mode engages @@ -161,70 +221,7 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.IsChanged = true; // Return to base mode - General.Map.ChangeMode(basemode); - } - - // This generates the vertices to split the line with, from start to end - private List GenerateCurve(Linedef line) - { - // Fetch settings from window - int vertices = BuilderPlug.Me.CurveLinedefsForm.Vertices; - float distance = BuilderPlug.Me.CurveLinedefsForm.Distance; - float angle = BuilderPlug.Me.CurveLinedefsForm.Angle; - bool fixedcurve = BuilderPlug.Me.CurveLinedefsForm.FixedCurve; - bool backwards = BuilderPlug.Me.CurveLinedefsForm.Backwards; - - // Make list - List points = new List(vertices); - - //Added by Anders Åstrand 2008-05-18 - //The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html - //c and theta are known (length of line and angle parameter). d, R and h are - //calculated from those two - //If the curve is not supposed to be a circular segment it's simply deformed to fit - //the value set for distance. - - //The vertices are generated to be evenly distributed (by angle) along the curve - //and lastly they are rotated and moved to fit with the original line - - //calculate some identities of a circle segment (refer to the graph in the url above) - double c = line.Length; - double theta = angle; - - double d = (c / Math.Tan(theta / 2)) / 2; - double R = d / Math.Cos(theta / 2); - double h = R - d; - - double yDeform = fixedcurve ? 1 : distance / h; - if (backwards) - yDeform = -yDeform; - - double a, x, y; - Vector2D vertex; - - for (int v = 1; v <= vertices; v++) - { - //calculate the angle for this vertex - //the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments - //this assumes the line is horisontal and on y = 0, the point is rotated and moved later - - a = (Math.PI - theta)/2 + v * (theta / (vertices + 1)); - - //calculate the coordinates of the point, and distort the y coordinate - //using the deform factor calculated above - x = Math.Cos(a) * R; - y = (Math.Sin(a) * R - d) * yDeform; - - //rotate and transform to fit original line - vertex = new Vector2D((float)x, (float)y).GetRotated(line.Angle + Angle2D.PIHALF); - vertex = vertex.GetTransformed(line.GetCenterPoint().x, line.GetCenterPoint().y, 1, 1); - - points.Add(vertex); - } - - - // Done - return points; + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // Redrawing display diff --git a/Source/BuilderModes/ClassicModes/DragGeometryMode.cs b/Source/BuilderModes/ClassicModes/DragGeometryMode.cs index 67fc67b4..136e76a8 100644 --- a/Source/BuilderModes/ClassicModes/DragGeometryMode.cs +++ b/Source/BuilderModes/ClassicModes/DragGeometryMode.cs @@ -36,16 +36,13 @@ using CodeImp.DoomBuilder.Editing; namespace CodeImp.DoomBuilder.BuilderModes { - public abstract class DragGeometryMode : ClassicMode + public abstract class DragGeometryMode : BaseClassicMode { #region ================== Constants #endregion #region ================== Variables - - // Mode to return to - private EditMode basemode; // Mouse position on map where dragging started private Vector2D dragstartmappos; @@ -86,9 +83,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Properties // Just keep the base mode button checked - public override string EditModeButtonName { get { return basemode.GetType().Name; } } - - internal EditMode BaseMode { get { return basemode; } } + public override string EditModeButtonName { get { return General.Map.PreviousStableMode.Name; } } #endregion @@ -119,9 +114,6 @@ namespace CodeImp.DoomBuilder.BuilderModes // Initialize this.dragstartmappos = dragstartmappos; - // Create new instance of the previous mode - this.basemode = (EditMode)Assembly.GetCallingAssembly().CreateInstance(General.Map.Mode.GetType().FullName, false, BindingFlags.Default, null, null, CultureInfo.CurrentCulture, new object[0]); - Cursor.Current = Cursors.AppStarting; // Make list of selected vertices @@ -291,7 +283,7 @@ namespace CodeImp.DoomBuilder.BuilderModes base.OnCancel(); // Return to vertices mode - General.Map.ChangeMode(basemode); + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // Mode engages @@ -375,7 +367,7 @@ namespace CodeImp.DoomBuilder.BuilderModes protected override void OnEndEdit() { // Just return to base mode, Disengage will be called automatically. - General.Map.ChangeMode(basemode); + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); base.OnEndEdit(); } diff --git a/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs b/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs index c294b249..5ab74ff0 100644 --- a/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs +++ b/Source/BuilderModes/ClassicModes/DragLinedefsMode.cs @@ -68,7 +68,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public DragLinedefsMode(Vector2D dragstartmappos) { // Mark what we are dragging - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedLinedefs(true, true); ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); foreach(Vertex v in verts) v.Marked = true; diff --git a/Source/BuilderModes/ClassicModes/DragSectorsMode.cs b/Source/BuilderModes/ClassicModes/DragSectorsMode.cs index cfa79dc7..58b17b07 100644 --- a/Source/BuilderModes/ClassicModes/DragSectorsMode.cs +++ b/Source/BuilderModes/ClassicModes/DragSectorsMode.cs @@ -68,7 +68,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public DragSectorsMode(Vector2D dragstartmappos) { // Mark what we are dragging - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedLinedefs(true, true); ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); foreach(Vertex v in verts) v.Marked = true; diff --git a/Source/BuilderModes/ClassicModes/DragThingsMode.cs b/Source/BuilderModes/ClassicModes/DragThingsMode.cs index 4cf502d8..6d513868 100644 --- a/Source/BuilderModes/ClassicModes/DragThingsMode.cs +++ b/Source/BuilderModes/ClassicModes/DragThingsMode.cs @@ -45,7 +45,7 @@ namespace CodeImp.DoomBuilder.BuilderModes [EditMode(DisplayName = "Things", Volatile = true)] - public sealed class DragThingsMode : ClassicMode + public sealed class DragThingsMode : BaseClassicMode { #region ================== Constants @@ -104,7 +104,7 @@ namespace CodeImp.DoomBuilder.BuilderModes Cursor.Current = Cursors.AppStarting; // Mark what we are dragging - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedThings(true, true); // Get selected things diff --git a/Source/BuilderModes/ClassicModes/DragVerticesMode.cs b/Source/BuilderModes/ClassicModes/DragVerticesMode.cs index e1865ab6..97facacb 100644 --- a/Source/BuilderModes/ClassicModes/DragVerticesMode.cs +++ b/Source/BuilderModes/ClassicModes/DragVerticesMode.cs @@ -65,7 +65,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public DragVerticesMode(Vertex dragitem, Vector2D dragstartmappos) { // Mark what we are dragging - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedVertices(true, true); // Initialize diff --git a/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs b/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs index b0286b1d..1b682aea 100644 --- a/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs +++ b/Source/BuilderModes/ClassicModes/DrawGeometryMode.cs @@ -41,7 +41,7 @@ namespace CodeImp.DoomBuilder.BuilderModes SwitchAction = "drawlinesmode", Volatile = true)] - public class DrawGeometryMode : ClassicMode + public class DrawGeometryMode : BaseClassicMode { #region ================== Structures @@ -61,9 +61,6 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Variables - // Mode to return to - private EditMode basemode; - // Drawing points private List points; private List labels; @@ -82,9 +79,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Properties // Just keep the base mode button checked - public override string EditModeButtonName { get { return basemode.GetType().Name; } } - - internal EditMode BaseMode { get { return basemode; } } + public override string EditModeButtonName { get { return General.Map.PreviousStableMode.Name; } } #endregion @@ -94,13 +89,12 @@ namespace CodeImp.DoomBuilder.BuilderModes public DrawGeometryMode() { // Initialize - this.basemode = General.Map.Mode; points = new List(); labels = new List(); // No selection in this mode General.Map.Map.ClearAllSelected(); - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); // We have no destructor GC.SuppressFinalize(this); @@ -125,6 +119,215 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Methods + // This checks if the view offset/zoom changed and updates the check + protected bool CheckViewChanged() + { + bool viewchanged = false; + + // View changed? + if(renderer.OffsetX != lastoffsetx) viewchanged = true; + if(renderer.OffsetY != lastoffsety) viewchanged = true; + if(renderer.Scale != lastscale) viewchanged = true; + + // Keep view information + lastoffsetx = renderer.OffsetX; + lastoffsety = renderer.OffsetY; + lastscale = renderer.Scale; + + // Return result + return viewchanged; + } + + // This updates the dragging + private void Update() + { + PixelColor stitchcolor = General.Colors.Highlight; + PixelColor losecolor = General.Colors.Selection; + PixelColor color; + + snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; + snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; + + DrawnVertex lastp = new DrawnVertex(); + DrawnVertex curp = GetCurrentPosition(); + float vsize = ((float)renderer.VertexSize + 1.0f) / renderer.Scale; + float vsizeborder = ((float)renderer.VertexSize + 3.0f) / renderer.Scale; + + // The last label's end must go to the mouse cursor + if(labels.Count > 0) labels[labels.Count - 1].End = curp.pos; + + // Render drawing lines + if(renderer.StartOverlay(true)) + { + // Go for all points to draw lines + if(points.Count > 0) + { + // Render lines + lastp = points[0]; + for(int i = 1; i < points.Count; i++) + { + // Determine line color + if(lastp.stitch && points[i].stitch) color = stitchcolor; + else color = losecolor; + + // Render line + renderer.RenderLine(lastp.pos, points[i].pos, LINE_THICKNESS, color, true); + lastp = points[i]; + } + + // Determine line color + if(lastp.stitch && snaptonearest) color = stitchcolor; + else color = losecolor; + + // Render line to cursor + renderer.RenderLine(lastp.pos, curp.pos, LINE_THICKNESS, color, true); + + // Render vertices + for(int i = 0; i < points.Count; i++) + { + // Determine line color + if(points[i].stitch) color = stitchcolor; + else color = losecolor; + + // Render line + renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); + } + } + + // Determine point color + if(snaptonearest) color = stitchcolor; + else color = losecolor; + + // Render vertex at cursor + renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); + + // Go for all labels + foreach(LineLengthLabel l in labels) renderer.RenderText(l.TextLabel); + + // Done + renderer.Finish(); + } + + // Done + renderer.Present(); + } + + // This gets the aligned and snapped draw position + private DrawnVertex GetCurrentPosition() + { + DrawnVertex p = new DrawnVertex(); + + // Snap to nearest? + if(snaptonearest) + { + float vrange = VerticesMode.VERTEX_HIGHLIGHT_RANGE / renderer.Scale; + + // Go for all drawn points + foreach(DrawnVertex v in points) + { + Vector2D delta = mousemappos - v.pos; + if(delta.GetLengthSq() < (vrange * vrange)) + { + p.pos = v.pos; + p.stitch = true; + return p; + } + } + + // Try the nearest vertex + Vertex nv = General.Map.Map.NearestVertexSquareRange(mousemappos, vrange); + if(nv != null) + { + p.pos = nv.Position; + p.stitch = true; + return p; + } + + // Try the nearest linedef + Linedef nl = General.Map.Map.NearestLinedefRange(mousemappos, LinedefsMode.LINEDEF_HIGHLIGHT_RANGE / renderer.Scale); + if(nl != null) + { + // Snap to grid? + if(snaptogrid) + { + // Get grid intersection coordinates + List coords = nl.GetGridIntersections(); + + // Find nearest grid intersection + float found_distance = float.MaxValue; + Vector2D found_coord = new Vector2D(); + foreach(Vector2D v in coords) + { + Vector2D delta = mousemappos - v; + if(delta.GetLengthSq() < found_distance) + { + found_distance = delta.GetLengthSq(); + found_coord = v; + } + } + + // Align to the closest grid intersection + p.pos = found_coord; + p.stitch = true; + return p; + } + else + { + // Aligned to line + p.pos = nl.NearestOnLine(mousemappos); + p.stitch = true; + return p; + } + } + } + + // Snap to grid? + if(snaptogrid) + { + // Aligned to grid + p.pos = General.Map.Grid.SnappedToGrid(mousemappos); + p.stitch = snaptonearest; + return p; + } + else + { + // Normal position + p.pos = mousemappos; + p.stitch = snaptonearest; + return p; + } + } + + // This draws a point at a specific location + public void DrawPointAt(Vector2D pos, bool stitch) + { + DrawnVertex newpoint = new DrawnVertex(); + newpoint.pos = pos; + newpoint.stitch = stitch; + points.Add(newpoint); + labels.Add(new LineLengthLabel()); + labels[labels.Count - 1].Start = newpoint.pos; + if(labels.Count > 1) labels[labels.Count - 2].End = newpoint.pos; + Update(); + + // Check if point stitches with the first + if((points.Count > 1) && (points[points.Count - 1].stitch)) + { + Vector2D p1 = points[0].pos; + Vector2D p2 = points[points.Count - 1].pos; + Vector2D delta = p1 - p2; + if((Math.Abs(delta.x) <= 0.001f) && (Math.Abs(delta.y) <= 0.001f)) + { + // Finish drawing + FinishDraw(); + } + } + } + + #endregion + + #region ================== Events + // Engaging public override void OnEngage() { @@ -142,9 +345,7 @@ namespace CodeImp.DoomBuilder.BuilderModes base.OnCancel(); // Return to original mode - Type t = basemode.GetType(); - basemode = (EditMode)Activator.CreateInstance(t); - General.Map.ChangeMode(basemode); + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // Accepted @@ -577,28 +778,7 @@ namespace CodeImp.DoomBuilder.BuilderModes Cursor.Current = Cursors.Default; // Return to original mode - Type t = basemode.GetType(); - basemode = (EditMode)Activator.CreateInstance(t); - General.Map.ChangeMode(basemode); - } - - // This checks if the view offset/zoom changed and updates the check - protected bool CheckViewChanged() - { - bool viewchanged = false; - - // View changed? - if(renderer.OffsetX != lastoffsetx) viewchanged = true; - if(renderer.OffsetY != lastoffsety) viewchanged = true; - if(renderer.Scale != lastscale) viewchanged = true; - - // Keep view information - lastoffsetx = renderer.OffsetX; - lastoffsety = renderer.OffsetY; - lastscale = renderer.Scale; - - // Return result - return viewchanged; + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // This redraws the display @@ -623,166 +803,6 @@ namespace CodeImp.DoomBuilder.BuilderModes Update(); } - // This updates the dragging - private void Update() - { - PixelColor stitchcolor = General.Colors.Highlight; - PixelColor losecolor = General.Colors.Selection; - PixelColor color; - - snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid; - snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; - - DrawnVertex lastp = new DrawnVertex(); - DrawnVertex curp = GetCurrentPosition(); - float vsize = ((float)renderer.VertexSize + 1.0f) / renderer.Scale; - float vsizeborder = ((float)renderer.VertexSize + 3.0f) / renderer.Scale; - - // The last label's end must go to the mouse cursor - if(labels.Count > 0) labels[labels.Count - 1].End = curp.pos; - - // Render drawing lines - if(renderer.StartOverlay(true)) - { - // Go for all points to draw lines - if(points.Count > 0) - { - // Render lines - lastp = points[0]; - for(int i = 1; i < points.Count; i++) - { - // Determine line color - if(lastp.stitch && points[i].stitch) color = stitchcolor; - else color = losecolor; - - // Render line - renderer.RenderLine(lastp.pos, points[i].pos, LINE_THICKNESS, color, true); - lastp = points[i]; - } - - // Determine line color - if(lastp.stitch && snaptonearest) color = stitchcolor; - else color = losecolor; - - // Render line to cursor - renderer.RenderLine(lastp.pos, curp.pos, LINE_THICKNESS, color, true); - - // Render vertices - for(int i = 0; i < points.Count; i++) - { - // Determine line color - if(points[i].stitch) color = stitchcolor; - else color = losecolor; - - // Render line - renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); - } - } - - // Determine point color - if(snaptonearest) color = stitchcolor; - else color = losecolor; - - // Render vertex at cursor - renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); - - // Go for all labels - foreach(LineLengthLabel l in labels) renderer.RenderText(l.TextLabel); - - // Done - renderer.Finish(); - } - - // Done - renderer.Present(); - } - - // This gets the aligned and snapped draw position - private DrawnVertex GetCurrentPosition() - { - DrawnVertex p = new DrawnVertex(); - - // Snap to nearest? - if(snaptonearest) - { - float vrange = VerticesMode.VERTEX_HIGHLIGHT_RANGE / renderer.Scale; - - // Go for all drawn points - foreach(DrawnVertex v in points) - { - Vector2D delta = mousemappos - v.pos; - if(delta.GetLengthSq() < (vrange * vrange)) - { - p.pos = v.pos; - p.stitch = true; - return p; - } - } - - // Try the nearest vertex - Vertex nv = General.Map.Map.NearestVertexSquareRange(mousemappos, vrange); - if(nv != null) - { - p.pos = nv.Position; - p.stitch = true; - return p; - } - - // Try the nearest linedef - Linedef nl = General.Map.Map.NearestLinedefRange(mousemappos, LinedefsMode.LINEDEF_HIGHLIGHT_RANGE / renderer.Scale); - if(nl != null) - { - // Snap to grid? - if(snaptogrid) - { - // Get grid intersection coordinates - List coords = nl.GetGridIntersections(); - - // Find nearest grid intersection - float found_distance = float.MaxValue; - Vector2D found_coord = new Vector2D(); - foreach(Vector2D v in coords) - { - Vector2D delta = mousemappos - v; - if(delta.GetLengthSq() < found_distance) - { - found_distance = delta.GetLengthSq(); - found_coord = v; - } - } - - // Align to the closest grid intersection - p.pos = found_coord; - p.stitch = true; - return p; - } - else - { - // Aligned to line - p.pos = nl.NearestOnLine(mousemappos); - p.stitch = true; - return p; - } - } - } - - // Snap to grid? - if(snaptogrid) - { - // Aligned to grid - p.pos = General.Map.Grid.SnappedToGrid(mousemappos); - p.stitch = snaptonearest; - return p; - } - else - { - // Normal position - p.pos = mousemappos; - p.stitch = snaptonearest; - return p; - } - } - // Mouse moving public override void OnMouseMove(MouseEventArgs e) { @@ -790,31 +810,25 @@ namespace CodeImp.DoomBuilder.BuilderModes Update(); } - // This draws a point at a specific location - public void DrawPointAt(Vector2D pos, bool stitch) + // When a key is released + public override void OnKeyUp(KeyEventArgs e) { - DrawnVertex newpoint = new DrawnVertex(); - newpoint.pos = pos; - newpoint.stitch = stitch; - points.Add(newpoint); - labels.Add(new LineLengthLabel()); - labels[labels.Count - 1].Start = newpoint.pos; - if(labels.Count > 1) labels[labels.Count - 2].End = newpoint.pos; - Update(); - - // Check if point stitches with the first - if((points.Count > 1) && (points[points.Count - 1].stitch)) - { - Vector2D p1 = points[0].pos; - Vector2D p2 = points[points.Count - 1].pos; - Vector2D delta = p1 - p2; - if((Math.Abs(delta.x) <= 0.001f) && (Math.Abs(delta.y) <= 0.001f)) - { - // Finish drawing - FinishDraw(); - } - } + base.OnKeyUp(e); + if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); } + + // When a key is pressed + public override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + #endregion + + #region ================== Actions // Drawing a point [BeginAction("drawpoint")] @@ -850,22 +864,6 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.AcceptMode(); } - // When a key is released - public override void OnKeyUp(KeyEventArgs e) - { - base.OnKeyUp(e); - if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || - (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); - } - - // When a key is pressed - public override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || - (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); - } - #endregion } } diff --git a/Source/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/BuilderModes/ClassicModes/EditSelectionMode.cs index 357d6ce6..f1870676 100644 --- a/Source/BuilderModes/ClassicModes/EditSelectionMode.cs +++ b/Source/BuilderModes/ClassicModes/EditSelectionMode.cs @@ -43,7 +43,7 @@ namespace CodeImp.DoomBuilder.BuilderModes SwitchAction = "editselectionmode", // Action name used to switch to this mode Volatile = true)] - public class EditSelectionMode : ClassicMode + public class EditSelectionMode : BaseClassicMode { #region ================== Enums @@ -83,8 +83,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Variables - // Mode to return to - private EditMode basemode; + // Mode switching private bool modealreadyswitching = false; // Highlighted vertex @@ -133,7 +132,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Properties // Just keep the base mode button checked - public override string EditModeButtonName { get { return basemode.GetType().Name; } } + public override string EditModeButtonName { get { return General.Map.PreviousStableMode.Name; } } #endregion @@ -143,11 +142,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public EditSelectionMode() { // Initialize - basemode = General.Map.Mode; mode = ModifyMode.None; - - // TEST: - rotation = Angle2D.PI2 * 0;// 0.02f; } // Disposer @@ -163,461 +158,10 @@ namespace CodeImp.DoomBuilder.BuilderModes } } - #endregion - - #region ================== Events - - // Mode engages - public override void OnEngage() - { - base.OnEngage(); - - // Convert geometry selection - General.Map.Map.ClearAllMarks(); - General.Map.Map.MarkSelectedVertices(true, true); - General.Map.Map.MarkSelectedThings(true, true); - General.Map.Map.MarkSelectedLinedefs(true, true); - ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); - foreach(Vertex v in verts) v.Marked = true; - selectedvertices = General.Map.Map.GetMarkedVertices(true); - selectedthings = General.Map.Map.GetMarkedThings(true); - unselectedvertices = General.Map.Map.GetMarkedVertices(false); - - // Make sure everything is selected so that it turns up red - foreach(Vertex v in selectedvertices) v.Selected = true; - ICollection markedlines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); - foreach(Linedef l in markedlines) l.Selected = true; - unselectedlines = General.Map.Map.LinedefsFromMarkedVertices(true, false, false); - - // Array to keep original coordinates - vertexpos = new List(selectedvertices.Count); - thingpos = new List(selectedthings.Count); - - // A selection must be made! - if((selectedvertices.Count > 0) || (selectedthings.Count > 0)) - { - // Initialize offset and size - offset.x = float.MaxValue; - offset.y = float.MaxValue; - Vector2D right; - right.x = float.MinValue; - right.y = float.MinValue; - - foreach(Vertex v in selectedvertices) - { - // Find left-top and right-bottom - if(v.Position.x < offset.x) offset.x = v.Position.x; - if(v.Position.y < offset.y) offset.y = v.Position.y; - if(v.Position.x > right.x) right.x = v.Position.x; - if(v.Position.y > right.y) right.y = v.Position.y; - - // Keep original coordinates - vertexpos.Add(v.Position); - } - - foreach(Thing t in selectedthings) - { - // Find left-top and right-bottom - if(t.Position.x < offset.x) offset.x = t.Position.x; - if(t.Position.y < offset.y) offset.y = t.Position.y; - if(t.Position.x > right.x) right.x = t.Position.x; - if(t.Position.y > right.y) right.y = t.Position.y; - - // Keep original coordinates - thingpos.Add(t.Position); - } - - // Calculate size - size = right - offset; - - // If the width of a dimension is zero, add a little - if(Math.Abs(size.x) < 1.0f) - { - size.x += ZERO_SIZE_ADDITION; - offset.x -= ZERO_SIZE_ADDITION / 2; - } - - if(Math.Abs(size.y) < 1.0f) - { - size.y += ZERO_SIZE_ADDITION; - offset.y -= ZERO_SIZE_ADDITION / 2; - } - - basesize = size; - baseoffset = offset; - - // Set presentation - if(selectedthings.Count > 0) - renderer.SetPresentation(Presentation.Things); - else - renderer.SetPresentation(Presentation.Standard); - - // Update - UpdateRectangleComponents(); - } - else - { - General.Interface.DisplayWarning("Please make a selection first!"); - - // Cancel now - General.Map.CancelMode(); - } - } - - // Cancel mode - public override void OnCancel() - { - base.OnCancel(); - - // Reset geometry in original position - int index = 0; - foreach(Vertex v in selectedvertices) - v.Move(vertexpos[index++]); - - index = 0; - foreach(Thing t in selectedthings) - t.Move(thingpos[index++]); - - General.Map.Map.Update(true, true); - - // Return to original mode - Type mt = basemode.GetType(); - basemode = (EditMode)Activator.CreateInstance(mt); - General.Map.ChangeMode(basemode); - } - - // When accepted - public override void OnAccept() - { - base.OnAccept(); - - // Anything to do? - if((selectedthings.Count > 0) || (selectedvertices.Count > 0)) - { - Cursor.Current = Cursors.AppStarting; - - // Reset geometry in original position - int index = 0; - foreach(Vertex v in selectedvertices) - v.Move(vertexpos[index++]); - - index = 0; - foreach(Thing t in selectedthings) - t.Move(thingpos[index++]); - - // Make undo - General.Map.UndoRedo.CreateUndo("Edit selection", UndoGroup.None, 0); - - // Move geometry to new position - UpdateGeometry(); - General.Map.Map.Update(true, true); - - // Stitch geometry - if(snaptonearest) General.Map.Map.StitchGeometry(); - - // Snap to map format accuracy - General.Map.Map.SnapAllToAccuracy(); - - // Update cached values - General.Map.Map.Update(); - - // Done - selectedvertices = new List(); - selectedthings = new List(); - Cursor.Current = Cursors.Default; - General.Map.IsChanged = true; - } - - if(!modealreadyswitching) - { - // Return to original mode - Type mt = basemode.GetType(); - basemode = (EditMode)Activator.CreateInstance(mt); - General.Map.ChangeMode(basemode); - } - } - - // Mode disengages - public override void OnDisengage() - { - base.OnDisengage(); - - // When not cancelled manually, we assume it is accepted - if(!cancelled) - { - modealreadyswitching = true; - this.OnAccept(); - } - - // Hide highlight info - General.Interface.HideInfo(); - General.Interface.SetCursor(Cursors.Default); - } - - // This redraws the display - public override void OnRedrawDisplay() - { - UpdateRectangleComponents(); - - // Render lines - if(renderer.StartPlotter(true)) - { - renderer.PlotLinedefSet(General.Map.Map.Linedefs); - renderer.PlotVerticesSet(General.Map.Map.Vertices); - if(highlighted != null) renderer.PlotVertex(highlighted, ColorCollection.HIGHLIGHT); - renderer.Finish(); - } - - // Render things - if(renderer.StartThings(true)) - { - renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); - renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); - renderer.Finish(); - } - - // Render selection - if(renderer.StartOverlay(true)) - { - // Rectangle - PixelColor rectcolor = General.Colors.Highlight.WithAlpha(RECTANGLE_ALPHA); - renderer.RenderGeometry(cornerverts, null, true); - renderer.RenderLine(corners[0], corners[1], 4, rectcolor, true); - renderer.RenderLine(corners[1], corners[2], 4, rectcolor, true); - renderer.RenderLine(corners[2], corners[3], 4, rectcolor, true); - renderer.RenderLine(corners[3], corners[0], 4, rectcolor, true); - - // Extension line - if(extensionline.GetLengthSq() > 0.0f) - renderer.RenderLine(extensionline.v1, extensionline.v2, 1, General.Colors.Indication.WithAlpha(EXTENSION_LINE_ALPHA), true); - - // Grips - for(int i = 0; i < 4; i++) - { - renderer.RenderRectangleFilled(resizegrips[i], General.Colors.Background, true); - renderer.RenderRectangle(resizegrips[i], 2, General.Colors.Highlight, true); - renderer.RenderRectangleFilled(rotategrips[i], General.Colors.Background, true); - renderer.RenderRectangle(rotategrips[i], 2, General.Colors.Indication, true); - } - - renderer.Finish(); - } - - renderer.Present(); - } - - // Mouse moves - public override void OnMouseMove(MouseEventArgs e) - { - base.OnMouseMove(e); - - Update(); - } - - // Mouse leaves the display - public override void OnMouseLeave(EventArgs e) - { - base.OnMouseLeave(e); - - // Reset cursor - General.Interface.SetCursor(Cursors.Default); - } - - // When edit button is pressed - protected override void OnEdit() - { - base.OnEdit(); - OnSelect(); - } - - // When edit button is released - protected override void OnEndEdit() - { - base.OnEndEdit(); - OnEndSelect(); - } - - // When select button is pressed - protected override void OnSelect() - { - base.OnSelect(); - - // Used in many cases: - Vector2D center = offset + size * 0.5f; - Vector2D delta; - - // Check what grip the mouse is over - switch(CheckMouseGrip()) - { - // Drag main rectangle - case Grip.Main: - - // Find the original position of the highlighted vertex - if(highlighted != null) - { - int index = 0; - foreach(Vertex v in selectedvertices) - { - if(v == highlighted) highlightedpos = vertexpos[index]; - index++; - } - } - - dragoffset = mousemappos - offset; - mode = ModifyMode.Dragging; - break; - - // Resize - case Grip.SizeN: - - // The resize vector is a unit vector in the direction of the resize. - // We multiply this with the sign of the current size, because the - // corners may be reversed when the selection is flipped. - resizevector = corners[1] - corners[2]; - resizevector = resizevector.GetNormal() * Math.Sign(size.y); - - // The edgevector is a vector with length and direction of the edge perpendicular to the resizevector - edgevector = corners[1] - corners[0]; - - // Make the resize axis. This is a line with the length and direction - // of basesize used to calculate the resize percentage. - resizeaxis = new Line2D(corners[2], corners[2] + resizevector * basesize.y); - - // Original axis filter - resizefilter = new Vector2D(0.0f, 1.0f); - - // This is the corner that must stay in the same position - stickcorner = 2; - - Highlight(null); - mode = ModifyMode.Resizing; - break; - - // Resize - case Grip.SizeE: - // See description above - resizevector = corners[1] - corners[0]; - resizevector = resizevector.GetNormal() * Math.Sign(size.x); - edgevector = corners[1] - corners[2]; - resizeaxis = new Line2D(corners[0], corners[0] + resizevector * basesize.x); - resizefilter = new Vector2D(1.0f, 0.0f); - stickcorner = 0; - Highlight(null); - mode = ModifyMode.Resizing; - break; - - // Resize - case Grip.SizeS: - // See description above - resizevector = corners[2] - corners[1]; - resizevector = resizevector.GetNormal() * Math.Sign(size.y); - edgevector = corners[2] - corners[3]; - resizeaxis = new Line2D(corners[1], corners[1] + resizevector * basesize.y); - resizefilter = new Vector2D(0.0f, 1.0f); - stickcorner = 0; - Highlight(null); - mode = ModifyMode.Resizing; - break; - - // Resize - case Grip.SizeW: - // See description above - resizevector = corners[0] - corners[1]; - resizevector = resizevector.GetNormal() * Math.Sign(size.x); - edgevector = corners[0] - corners[3]; - resizeaxis = new Line2D(corners[1], corners[1] + resizevector * basesize.x); - resizefilter = new Vector2D(1.0f, 0.0f); - stickcorner = 1; - Highlight(null); - mode = ModifyMode.Resizing; - break; - - // Rotate - case Grip.RotateLB: - delta = corners[3] - center; - rotategripangle = delta.GetAngle() - rotation; - Highlight(null); - mode = ModifyMode.Rotating; - break; - - // Rotate - case Grip.RotateLT: - delta = corners[0] - center; - rotategripangle = delta.GetAngle() - rotation; - Highlight(null); - mode = ModifyMode.Rotating; - break; - - // Rotate - case Grip.RotateRB: - delta = corners[2] - center; - rotategripangle = delta.GetAngle() - rotation; - Highlight(null); - mode = ModifyMode.Rotating; - break; - - // Rotate - case Grip.RotateRT: - delta = corners[1] - center; - rotategripangle = delta.GetAngle() - rotation; - Highlight(null); - mode = ModifyMode.Rotating; - break; - - // Outside the selection? - default: - // Accept and be done with it - General.Map.AcceptMode(); - break; - } - } - - // When selected button is released - protected override void OnEndSelect() - { - base.OnEndSelect(); - - // Remove extension line - extensionline = new Line2D(); - - // No modifying mode - mode = ModifyMode.None; - - // Redraw - General.Interface.RedrawDisplay(); - } - - // When a key is released - public override void OnKeyUp(KeyEventArgs e) - { - base.OnKeyUp(e); - if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || - (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); - } - - // When a key is pressed - public override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || - (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); - } - - - #endregion #region ================== Methods - // This clears the selection - [BeginAction("clearselection", BaseAction = true)] - public void ClearSelection() - { - // Accept changes - General.Map.Map.ClearAllSelected(); - General.Map.AcceptMode(); - } - // This highlights a new vertex protected void Highlight(Vertex v) { @@ -1031,5 +575,456 @@ namespace CodeImp.DoomBuilder.BuilderModes } #endregion + + #region ================== Events + + // Mode engages + public override void OnEngage() + { + base.OnEngage(); + + // Convert geometry selection + General.Map.Map.ClearAllMarks(false); + General.Map.Map.MarkSelectedVertices(true, true); + General.Map.Map.MarkSelectedThings(true, true); + General.Map.Map.MarkSelectedLinedefs(true, true); + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(true); + foreach(Vertex v in verts) v.Marked = true; + selectedvertices = General.Map.Map.GetMarkedVertices(true); + selectedthings = General.Map.Map.GetMarkedThings(true); + unselectedvertices = General.Map.Map.GetMarkedVertices(false); + + // Make sure everything is selected so that it turns up red + foreach(Vertex v in selectedvertices) v.Selected = true; + ICollection markedlines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); + foreach(Linedef l in markedlines) l.Selected = true; + unselectedlines = General.Map.Map.LinedefsFromMarkedVertices(true, false, false); + + // Array to keep original coordinates + vertexpos = new List(selectedvertices.Count); + thingpos = new List(selectedthings.Count); + + // A selection must be made! + if((selectedvertices.Count > 0) || (selectedthings.Count > 0)) + { + // Initialize offset and size + offset.x = float.MaxValue; + offset.y = float.MaxValue; + Vector2D right; + right.x = float.MinValue; + right.y = float.MinValue; + + foreach(Vertex v in selectedvertices) + { + // Find left-top and right-bottom + if(v.Position.x < offset.x) offset.x = v.Position.x; + if(v.Position.y < offset.y) offset.y = v.Position.y; + if(v.Position.x > right.x) right.x = v.Position.x; + if(v.Position.y > right.y) right.y = v.Position.y; + + // Keep original coordinates + vertexpos.Add(v.Position); + } + + foreach(Thing t in selectedthings) + { + // Find left-top and right-bottom + if(t.Position.x < offset.x) offset.x = t.Position.x; + if(t.Position.y < offset.y) offset.y = t.Position.y; + if(t.Position.x > right.x) right.x = t.Position.x; + if(t.Position.y > right.y) right.y = t.Position.y; + + // Keep original coordinates + thingpos.Add(t.Position); + } + + // Calculate size + size = right - offset; + + // If the width of a dimension is zero, add a little + if(Math.Abs(size.x) < 1.0f) + { + size.x += ZERO_SIZE_ADDITION; + offset.x -= ZERO_SIZE_ADDITION / 2; + } + + if(Math.Abs(size.y) < 1.0f) + { + size.y += ZERO_SIZE_ADDITION; + offset.y -= ZERO_SIZE_ADDITION / 2; + } + + basesize = size; + baseoffset = offset; + + // Set presentation + if(selectedthings.Count > 0) + renderer.SetPresentation(Presentation.Things); + else + renderer.SetPresentation(Presentation.Standard); + + // Update + UpdateRectangleComponents(); + } + else + { + General.Interface.DisplayWarning("Please make a selection first!"); + + // Cancel now + General.Map.CancelMode(); + } + } + + // Cancel mode + public override void OnCancel() + { + base.OnCancel(); + + // Reset geometry in original position + int index = 0; + foreach(Vertex v in selectedvertices) + v.Move(vertexpos[index++]); + + index = 0; + foreach(Thing t in selectedthings) + t.Move(thingpos[index++]); + + General.Map.Map.Update(true, true); + + // Return to previous stable mode + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); + } + + // When accepted + public override void OnAccept() + { + base.OnAccept(); + + // Anything to do? + if((selectedthings.Count > 0) || (selectedvertices.Count > 0)) + { + Cursor.Current = Cursors.AppStarting; + + // Reset geometry in original position + int index = 0; + foreach(Vertex v in selectedvertices) + v.Move(vertexpos[index++]); + + index = 0; + foreach(Thing t in selectedthings) + t.Move(thingpos[index++]); + + // Make undo + General.Map.UndoRedo.CreateUndo("Edit selection", UndoGroup.None, 0); + + // Move geometry to new position + UpdateGeometry(); + General.Map.Map.Update(true, true); + + // Stitch geometry + if(snaptonearest) General.Map.Map.StitchGeometry(); + + // Snap to map format accuracy + General.Map.Map.SnapAllToAccuracy(); + + // Update cached values + General.Map.Map.Update(); + + // Done + selectedvertices = new List(); + selectedthings = new List(); + Cursor.Current = Cursors.Default; + General.Map.IsChanged = true; + } + + if(!modealreadyswitching) + { + // Return to previous stable mode + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); + } + } + + // Mode disengages + public override void OnDisengage() + { + base.OnDisengage(); + + // When not cancelled manually, we assume it is accepted + if(!cancelled) + { + modealreadyswitching = true; + this.OnAccept(); + } + + // Hide highlight info + General.Interface.HideInfo(); + General.Interface.SetCursor(Cursors.Default); + } + + // This redraws the display + public override void OnRedrawDisplay() + { + UpdateRectangleComponents(); + + // Render lines + if(renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + renderer.PlotVerticesSet(General.Map.Map.Vertices); + if(highlighted != null) renderer.PlotVertex(highlighted, ColorCollection.HIGHLIGHT); + renderer.Finish(); + } + + // Render things + if(renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_HIDDEN_ALPHA); + renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, 1.0f); + renderer.Finish(); + } + + // Render selection + if(renderer.StartOverlay(true)) + { + // Rectangle + PixelColor rectcolor = General.Colors.Highlight.WithAlpha(RECTANGLE_ALPHA); + renderer.RenderGeometry(cornerverts, null, true); + renderer.RenderLine(corners[0], corners[1], 4, rectcolor, true); + renderer.RenderLine(corners[1], corners[2], 4, rectcolor, true); + renderer.RenderLine(corners[2], corners[3], 4, rectcolor, true); + renderer.RenderLine(corners[3], corners[0], 4, rectcolor, true); + + // Extension line + if(extensionline.GetLengthSq() > 0.0f) + renderer.RenderLine(extensionline.v1, extensionline.v2, 1, General.Colors.Indication.WithAlpha(EXTENSION_LINE_ALPHA), true); + + // Grips + for(int i = 0; i < 4; i++) + { + renderer.RenderRectangleFilled(resizegrips[i], General.Colors.Background, true); + renderer.RenderRectangle(resizegrips[i], 2, General.Colors.Highlight, true); + renderer.RenderRectangleFilled(rotategrips[i], General.Colors.Background, true); + renderer.RenderRectangle(rotategrips[i], 2, General.Colors.Indication, true); + } + + renderer.Finish(); + } + + renderer.Present(); + } + + // Mouse moves + public override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + Update(); + } + + // Mouse leaves the display + public override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + // Reset cursor + General.Interface.SetCursor(Cursors.Default); + } + + // When edit button is pressed + protected override void OnEdit() + { + base.OnEdit(); + OnSelect(); + } + + // When edit button is released + protected override void OnEndEdit() + { + base.OnEndEdit(); + OnEndSelect(); + } + + // When select button is pressed + protected override void OnSelect() + { + base.OnSelect(); + + // Used in many cases: + Vector2D center = offset + size * 0.5f; + Vector2D delta; + + // Check what grip the mouse is over + switch(CheckMouseGrip()) + { + // Drag main rectangle + case Grip.Main: + + // Find the original position of the highlighted vertex + if(highlighted != null) + { + int index = 0; + foreach(Vertex v in selectedvertices) + { + if(v == highlighted) highlightedpos = vertexpos[index]; + index++; + } + } + + dragoffset = mousemappos - offset; + mode = ModifyMode.Dragging; + break; + + // Resize + case Grip.SizeN: + + // The resize vector is a unit vector in the direction of the resize. + // We multiply this with the sign of the current size, because the + // corners may be reversed when the selection is flipped. + resizevector = corners[1] - corners[2]; + resizevector = resizevector.GetNormal() * Math.Sign(size.y); + + // The edgevector is a vector with length and direction of the edge perpendicular to the resizevector + edgevector = corners[1] - corners[0]; + + // Make the resize axis. This is a line with the length and direction + // of basesize used to calculate the resize percentage. + resizeaxis = new Line2D(corners[2], corners[2] + resizevector * basesize.y); + + // Original axis filter + resizefilter = new Vector2D(0.0f, 1.0f); + + // This is the corner that must stay in the same position + stickcorner = 2; + + Highlight(null); + mode = ModifyMode.Resizing; + break; + + // Resize + case Grip.SizeE: + // See description above + resizevector = corners[1] - corners[0]; + resizevector = resizevector.GetNormal() * Math.Sign(size.x); + edgevector = corners[1] - corners[2]; + resizeaxis = new Line2D(corners[0], corners[0] + resizevector * basesize.x); + resizefilter = new Vector2D(1.0f, 0.0f); + stickcorner = 0; + Highlight(null); + mode = ModifyMode.Resizing; + break; + + // Resize + case Grip.SizeS: + // See description above + resizevector = corners[2] - corners[1]; + resizevector = resizevector.GetNormal() * Math.Sign(size.y); + edgevector = corners[2] - corners[3]; + resizeaxis = new Line2D(corners[1], corners[1] + resizevector * basesize.y); + resizefilter = new Vector2D(0.0f, 1.0f); + stickcorner = 0; + Highlight(null); + mode = ModifyMode.Resizing; + break; + + // Resize + case Grip.SizeW: + // See description above + resizevector = corners[0] - corners[1]; + resizevector = resizevector.GetNormal() * Math.Sign(size.x); + edgevector = corners[0] - corners[3]; + resizeaxis = new Line2D(corners[1], corners[1] + resizevector * basesize.x); + resizefilter = new Vector2D(1.0f, 0.0f); + stickcorner = 1; + Highlight(null); + mode = ModifyMode.Resizing; + break; + + // Rotate + case Grip.RotateLB: + delta = corners[3] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + break; + + // Rotate + case Grip.RotateLT: + delta = corners[0] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + break; + + // Rotate + case Grip.RotateRB: + delta = corners[2] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + break; + + // Rotate + case Grip.RotateRT: + delta = corners[1] - center; + rotategripangle = delta.GetAngle() - rotation; + Highlight(null); + mode = ModifyMode.Rotating; + break; + + // Outside the selection? + default: + // Accept and be done with it + General.Map.AcceptMode(); + break; + } + } + + // When selected button is released + protected override void OnEndSelect() + { + base.OnEndSelect(); + + // Remove extension line + extensionline = new Line2D(); + + // No modifying mode + mode = ModifyMode.None; + + // Redraw + General.Interface.RedrawDisplay(); + } + + // When a key is released + public override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + // When a key is pressed + public override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if((snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) || + (snaptonearest != (General.Interface.CtrlState ^ General.Interface.AutoMerge))) Update(); + } + + + + #endregion + + #region ================== Actions + + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() + { + // Accept changes + General.Map.Map.ClearAllSelected(); + General.Map.AcceptMode(); + } + + #endregion } } diff --git a/Source/BuilderModes/ClassicModes/FindReplaceMode.cs b/Source/BuilderModes/ClassicModes/FindReplaceMode.cs index 9d1d351f..1ced964d 100644 --- a/Source/BuilderModes/ClassicModes/FindReplaceMode.cs +++ b/Source/BuilderModes/ClassicModes/FindReplaceMode.cs @@ -39,8 +39,8 @@ namespace CodeImp.DoomBuilder.BuilderModes [EditMode(DisplayName = "Find & Replace", SwitchAction = "findmode", Volatile = true)] - - public sealed class FindReplaceMode : ClassicMode + + public sealed class FindReplaceMode : BaseClassicMode { #region ================== Constants @@ -48,41 +48,17 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Variables - // Mode to return to - private EditMode basemode; - #endregion #region ================== Properties - internal EditMode BaseMode { get { return basemode; } } - #endregion #region ================== Constructor / Disposer - // Constructor - public FindReplaceMode() - { - this.basemode = General.Map.Mode; - } - - // Disposer - public override void Dispose() - { - // Not already disposed? - if(!isdisposed) - { - // Clean up - - // Done - base.Dispose(); - } - } - #endregion - #region ================== Methods + #region ================== Events // Cancelled public override void OnCancel() @@ -91,7 +67,7 @@ namespace CodeImp.DoomBuilder.BuilderModes base.OnCancel(); // Return to base mode - General.Map.ChangeMode(basemode.GetType().Name); + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // Mode engages @@ -127,7 +103,7 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.IsChanged = true; // Return to base mode - General.Map.ChangeMode(basemode); + General.Map.ChangeMode(General.Map.PreviousStableMode.Name); } // Redrawing display diff --git a/Source/BuilderModes/ClassicModes/LinedefsMode.cs b/Source/BuilderModes/ClassicModes/LinedefsMode.cs index be6c258a..b83e2532 100644 --- a/Source/BuilderModes/ClassicModes/LinedefsMode.cs +++ b/Source/BuilderModes/ClassicModes/LinedefsMode.cs @@ -44,7 +44,7 @@ namespace CodeImp.DoomBuilder.BuilderModes ButtonImage = "LinesMode.png", // Image resource name for the button ButtonOrder = int.MinValue + 100)] // Position of the button (lower is more to the left) - public class LinedefsMode : ClassicMode + public class LinedefsMode : BaseClassicMode { #region ================== Constants @@ -70,28 +70,108 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Constructor / Disposer - // Constructor - public LinedefsMode() - { - } - - // Disposer - public override void Dispose() - { - // Not already disposed? - if(!isdisposed) - { - // Clean up - - // Dispose base - base.Dispose(); - } - } - #endregion #region ================== Methods + // This highlights a new item + protected void Highlight(Linedef l) + { + bool completeredraw = false; + LinedefActionInfo action = null; + + // Often we can get away by simply undrawing the previous + // highlight and drawing the new highlight. But if associations + // are or were drawn we need to redraw the entire display. + + // Previous association highlights something? + if((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; + + // Set highlight association + if(l != null) + highlightasso.Set(l.Tag, UniversalType.LinedefTag); + else + highlightasso.Set(0, 0); + + // New association highlights something? + if((l != null) && (l.Tag > 0)) completeredraw = true; + + if(l != null) + { + // Check if we can find the linedefs action + if((l.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(l.Action)) + action = General.Map.Config.LinedefActions[l.Action]; + } + + // Determine linedef associations + for(int i = 0; i < Linedef.NUM_ARGS; i++) + { + // Previous association highlights something? + if((association[i].type == UniversalType.SectorTag) || + (association[i].type == UniversalType.LinedefTag) || + (association[i].type == UniversalType.ThingTag)) completeredraw = true; + + // Make new association + if(action != null) + association[i].Set(l.Args[i], action.Args[i].Type); + else + association[i].Set(0, 0); + + // New association highlights something? + if((association[i].type == UniversalType.SectorTag) || + (association[i].type == UniversalType.LinedefTag) || + (association[i].type == UniversalType.ThingTag)) completeredraw = true; + } + + // If we're changing associations, then we + // need to redraw the entire display + if(completeredraw) + { + // Set new highlight and redraw completely + highlighted = l; + General.Interface.RedrawDisplay(); + } + else + { + // Update display + if(renderer.StartPlotter(false)) + { + // Undraw previous highlight + if((highlighted != null) && !highlighted.IsDisposed) + { + renderer.PlotLinedef(highlighted, renderer.DetermineLinedefColor(highlighted)); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + } + + // Set new highlight + highlighted = l; + + // Render highlighted item + if((highlighted != null) && !highlighted.IsDisposed) + { + renderer.PlotLinedef(highlighted, General.Colors.Highlight); + renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); + renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); + } + + // Done + renderer.Finish(); + renderer.Present(); + } + } + + // Show highlight info + if((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowLinedefInfo(highlighted); + else + General.Interface.HideInfo(); + } + + #endregion + + #region ================== Events + // Cancel mode public override void OnCancel() { @@ -108,7 +188,7 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.SetPresentation(Presentation.Standard); // Convert geometry selection to linedefs selection - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedVertices(true, true); ICollection lines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); foreach(Linedef l in lines) l.Selected = true; @@ -173,100 +253,6 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Present(); } - // This highlights a new item - protected void Highlight(Linedef l) - { - bool completeredraw = false; - LinedefActionInfo action = null; - - // Often we can get away by simply undrawing the previous - // highlight and drawing the new highlight. But if associations - // are or were drawn we need to redraw the entire display. - - // Previous association highlights something? - if((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; - - // Set highlight association - if(l != null) - highlightasso.Set(l.Tag, UniversalType.LinedefTag); - else - highlightasso.Set(0, 0); - - // New association highlights something? - if((l != null) && (l.Tag > 0)) completeredraw = true; - - if(l != null) - { - // Check if we can find the linedefs action - if((l.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(l.Action)) - action = General.Map.Config.LinedefActions[l.Action]; - } - - // Determine linedef associations - for(int i = 0; i < Linedef.NUM_ARGS; i++) - { - // Previous association highlights something? - if((association[i].type == UniversalType.SectorTag) || - (association[i].type == UniversalType.LinedefTag) || - (association[i].type == UniversalType.ThingTag)) completeredraw = true; - - // Make new association - if(action != null) - association[i].Set(l.Args[i], action.Args[i].Type); - else - association[i].Set(0, 0); - - // New association highlights something? - if((association[i].type == UniversalType.SectorTag) || - (association[i].type == UniversalType.LinedefTag) || - (association[i].type == UniversalType.ThingTag)) completeredraw = true; - } - - // If we're changing associations, then we - // need to redraw the entire display - if(completeredraw) - { - // Set new highlight and redraw completely - highlighted = l; - General.Interface.RedrawDisplay(); - } - else - { - // Update display - if(renderer.StartPlotter(false)) - { - // Undraw previous highlight - if((highlighted != null) && !highlighted.IsDisposed) - { - renderer.PlotLinedef(highlighted, renderer.DetermineLinedefColor(highlighted)); - renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); - renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); - } - - // Set new highlight - highlighted = l; - - // Render highlighted item - if((highlighted != null) && !highlighted.IsDisposed) - { - renderer.PlotLinedef(highlighted, General.Colors.Highlight); - renderer.PlotVertex(highlighted.Start, renderer.DetermineVertexColor(highlighted.Start)); - renderer.PlotVertex(highlighted.End, renderer.DetermineVertexColor(highlighted.End)); - } - - // Done - renderer.Finish(); - renderer.Present(); - } - } - - // Show highlight info - if((highlighted != null) && !highlighted.IsDisposed) - General.Interface.ShowLinedefInfo(highlighted); - else - General.Interface.HideInfo(); - } - // Selection protected override void OnSelect() { @@ -474,17 +460,31 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Present(); } } - + // When copying public override bool OnCopyBegin() { - return true; + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnCopyBegin(); } - + // When pasting public override bool OnPasteBegin() { - return true; + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnPasteBegin(); } #endregion diff --git a/Source/BuilderModes/ClassicModes/MakeSectorMode.cs b/Source/BuilderModes/ClassicModes/MakeSectorMode.cs index 22287639..6c96a6fb 100644 --- a/Source/BuilderModes/ClassicModes/MakeSectorMode.cs +++ b/Source/BuilderModes/ClassicModes/MakeSectorMode.cs @@ -43,7 +43,7 @@ namespace CodeImp.DoomBuilder.BuilderModes ButtonImage = "NewSector2.png", // Image resource name for the button ButtonOrder = int.MinValue + 202)] // Position of the button (lower is more to the left) - public class MakeSectorMode : ClassicMode + public class MakeSectorMode : BaseClassicMode { #region ================== Constants @@ -100,6 +100,111 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Methods + // This draws the geometry + private void DrawGeometry() + { + // Render lines and vertices + if(renderer.StartPlotter(true)) + { + renderer.PlotLinedefSet(General.Map.Map.Linedefs); + + // Render highlight + if(alllines != null) + { + foreach(Linedef l in alllines) renderer.PlotLinedef(l, General.Colors.Highlight); + } + + renderer.PlotVerticesSet(General.Map.Map.Vertices); + renderer.Finish(); + } + } + + // This draws the overlay + private void DrawOverlay() + { + // Redraw overlay + if(renderer.StartOverlay(true)) + { + if((flashpolygon != null) && (flashintensity > 0.0f)) + { + renderer.RenderGeometry(flashpolygon, null, true); + } + + renderer.Finish(); + } + } + + // This highlights a new region + protected void Highlight(bool buttonspressed) + { + LinedefSide newnearest; + + // Mouse inside? + if(mouseinside) + { + // Highlighting from a new sidedef? + Linedef nl = General.Map.Map.NearestLinedef(mousemappos); + float side = nl.SideOfLine(mousemappos); + newnearest = new LinedefSide(nl, (side <= 0.0f)); + if(newnearest != nearestside) + { + // Only change when buttons are not pressed + if(!buttonspressed || (editside == newnearest)) + { + // Find new sector + General.Interface.SetCursor(Cursors.AppStarting); + nearestside = newnearest; + allsides = SectorTools.FindPotentialSectorAt(mousemappos); + if(allsides != null) + { + alllines = new List(allsides.Count); + foreach(LinedefSide sd in allsides) alllines.Add(sd.Line); + } + else + { + alllines = null; + } + General.Interface.SetCursor(Cursors.Default); + } + else + { + // Don't highlight this one + nearestside = null; + allsides = null; + alllines = null; + } + + // Redraw overlay + DrawGeometry(); + renderer.Present(); + } + } + else + { + // No valid region + nearestside = null; + allsides = null; + alllines = null; + + // Redraw overlay + DrawGeometry(); + renderer.Present(); + } + } + + // Start select + protected override void OnSelect() + { + // Select pressed in this mode + selectpressed = true; + editside = nearestside; + base.OnEdit(); + } + + #endregion + + #region ================== Events + // Cancel mode public override void OnCancel() { @@ -171,107 +276,6 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Present(); } - // This draws the geometry - private void DrawGeometry() - { - // Render lines and vertices - if(renderer.StartPlotter(true)) - { - renderer.PlotLinedefSet(General.Map.Map.Linedefs); - - // Render highlight - if(alllines != null) - { - foreach(Linedef l in alllines) renderer.PlotLinedef(l, General.Colors.Highlight); - } - - renderer.PlotVerticesSet(General.Map.Map.Vertices); - renderer.Finish(); - } - } - - // This draws the overlay - private void DrawOverlay() - { - // Redraw overlay - if(renderer.StartOverlay(true)) - { - if((flashpolygon != null) && (flashintensity > 0.0f)) - { - renderer.RenderGeometry(flashpolygon, null, true); - } - - renderer.Finish(); - } - } - - // This highlights a new region - protected void Highlight(bool buttonspressed) - { - LinedefSide newnearest; - - // Mouse inside? - if(mouseinside) - { - // Highlighting from a new sidedef? - Linedef nl = General.Map.Map.NearestLinedef(mousemappos); - float side = nl.SideOfLine(mousemappos); - newnearest = new LinedefSide(nl, (side <= 0.0f)); - if(newnearest != nearestside) - { - // Only change when buttons are not pressed - if(!buttonspressed || (editside == newnearest)) - { - // Find new sector - General.Interface.SetCursor(Cursors.AppStarting); - nearestside = newnearest; - allsides = SectorTools.FindPotentialSectorAt(mousemappos); - if(allsides != null) - { - alllines = new List(allsides.Count); - foreach(LinedefSide sd in allsides) alllines.Add(sd.Line); - } - else - { - alllines = null; - } - General.Interface.SetCursor(Cursors.Default); - } - else - { - // Don't highlight this one - nearestside = null; - allsides = null; - alllines = null; - } - - // Redraw overlay - DrawGeometry(); - renderer.Present(); - } - } - else - { - // No valid region - nearestside = null; - allsides = null; - alllines = null; - - // Redraw overlay - DrawGeometry(); - renderer.Present(); - } - } - - // Start select - protected override void OnSelect() - { - // Select pressed in this mode - selectpressed = true; - editside = nearestside; - base.OnEdit(); - } - // Done selecting protected override void OnEndSelect() { diff --git a/Source/BuilderModes/ClassicModes/SectorsMode.cs b/Source/BuilderModes/ClassicModes/SectorsMode.cs index c3703bd5..d1d707cd 100644 --- a/Source/BuilderModes/ClassicModes/SectorsMode.cs +++ b/Source/BuilderModes/ClassicModes/SectorsMode.cs @@ -44,7 +44,7 @@ namespace CodeImp.DoomBuilder.BuilderModes ButtonImage = "SectorsMode.png", // Image resource name for the button ButtonOrder = int.MinValue + 200)] // Position of the button (lower is more to the left) - public class SectorsMode : ClassicMode + public class SectorsMode : BaseClassicMode { #region ================== Constants @@ -101,31 +101,105 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Methods - // This clears the selection - [BeginAction("clearselection", BaseAction = true)] - public void ClearSelection() + // Support function for joining and merging sectors + private void JoinMergeSectors(bool removelines) { - // Clear selection - General.Map.Map.ClearAllSelected(); + // Remove lines in betwen joining sectors? + if(removelines) + { + // Go for all selected linedefs + ICollection selectedlines = General.Map.Map.GetSelectedLinedefs(true); + foreach(Linedef ld in selectedlines) + { + // Front and back side? + if((ld.Front != null) && (ld.Back != null)) + { + // Both a selected sector, but not the same? + if(ld.Front.Sector.Selected && ld.Back.Sector.Selected && + (ld.Front.Sector != ld.Back.Sector)) + { + // Remove this line + ld.Dispose(); + } + } + } + } - // Redraw - General.Interface.RedrawDisplay(); - } - - // When undo is used - [EndAction("undo", BaseAction = true)] - public void Undo() - { - // Clear ordered selection - orderedselection.Clear(); + // Join all selected sectors with the first + for(int i = 1; i < orderedselection.Count; i++) + orderedselection[i].Join(orderedselection[0]); } - // When redo is used - [EndAction("redo", BaseAction = true)] - public void Redo() + // This highlights a new item + protected void Highlight(Sector s) { - // Clear ordered selection - orderedselection.Clear(); + bool completeredraw = false; + + // Often we can get away by simply undrawing the previous + // highlight and drawing the new highlight. But if associations + // are or were drawn we need to redraw the entire display. + + // Previous association highlights something? + if((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; + + // Set highlight association + if(s != null) + highlightasso.Set(s.Tag, UniversalType.SectorTag); + else + highlightasso.Set(0, 0); + + // New association highlights something? + if((s != null) && (s.Tag > 0)) completeredraw = true; + + // If we're changing associations, then we + // need to redraw the entire display + if(completeredraw) + { + // Set new highlight and redraw completely + highlighted = s; + General.Interface.RedrawDisplay(); + } + else + { + // Update display + if(renderer.StartPlotter(false)) + { + // Undraw previous highlight + if((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted); + + /* + // Undraw highlighted things + if(highlighted != null) + foreach(Thing t in highlighted.Things) + renderer.RenderThing(t, renderer.DetermineThingColor(t)); + */ + + // Set new highlight + highlighted = s; + + // Render highlighted item + if((highlighted != null) && !highlighted.IsDisposed) + renderer.PlotSector(highlighted, General.Colors.Highlight); + + /* + // Render highlighted things + if(highlighted != null) + foreach(Thing t in highlighted.Things) + renderer.RenderThing(t, General.Colors.Highlight); + */ + + // Done + renderer.Finish(); + renderer.Present(); + } + } + + // Show highlight info + if((highlighted != null) && !highlighted.IsDisposed) + General.Interface.ShowSectorInfo(highlighted); + else + General.Interface.HideInfo(); } // This selectes or deselects a sector @@ -161,6 +235,10 @@ namespace CodeImp.DoomBuilder.BuilderModes } } } + + #endregion + + #region ================== Events // Cancel mode public override void OnCancel() @@ -178,7 +256,7 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.SetPresentation(Presentation.Standard); // Convert geometry selection to sectors only - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedVertices(true, true); ICollection lines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); foreach(Linedef l in lines) l.Selected = true; @@ -257,78 +335,6 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Present(); } - // This highlights a new item - protected void Highlight(Sector s) - { - bool completeredraw = false; - - // Often we can get away by simply undrawing the previous - // highlight and drawing the new highlight. But if associations - // are or were drawn we need to redraw the entire display. - - // Previous association highlights something? - if((highlighted != null) && (highlighted.Tag > 0)) completeredraw = true; - - // Set highlight association - if(s != null) - highlightasso.Set(s.Tag, UniversalType.SectorTag); - else - highlightasso.Set(0, 0); - - // New association highlights something? - if((s != null) && (s.Tag > 0)) completeredraw = true; - - // If we're changing associations, then we - // need to redraw the entire display - if(completeredraw) - { - // Set new highlight and redraw completely - highlighted = s; - General.Interface.RedrawDisplay(); - } - else - { - // Update display - if(renderer.StartPlotter(false)) - { - // Undraw previous highlight - if((highlighted != null) && !highlighted.IsDisposed) - renderer.PlotSector(highlighted); - - /* - // Undraw highlighted things - if(highlighted != null) - foreach(Thing t in highlighted.Things) - renderer.RenderThing(t, renderer.DetermineThingColor(t)); - */ - - // Set new highlight - highlighted = s; - - // Render highlighted item - if((highlighted != null) && !highlighted.IsDisposed) - renderer.PlotSector(highlighted, General.Colors.Highlight); - - /* - // Render highlighted things - if(highlighted != null) - foreach(Thing t in highlighted.Things) - renderer.RenderThing(t, General.Colors.Highlight); - */ - - // Done - renderer.Finish(); - renderer.Present(); - } - } - - // Show highlight info - if((highlighted != null) && !highlighted.IsDisposed) - General.Interface.ShowSectorInfo(highlighted); - else - General.Interface.HideInfo(); - } - // Selection protected override void OnSelect() { @@ -588,6 +594,50 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Present(); } } + + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + SelectSector(highlighted, true); + } + + return base.OnCopyBegin(); + } + + // When pasting + public override bool OnPasteBegin() + { + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedSectors(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + SelectSector(highlighted, true); + } + + return base.OnPasteBegin(); + } + + // When undo is used + public override bool OnUndoBegin() + { + // Clear ordered selection + orderedselection.Clear(); + + return base.OnUndoBegin(); + } + + // When redo is used + public override bool OnRedoBegin() + { + // Clear ordered selection + orderedselection.Clear(); + + return base.OnRedoBegin(); + } #endregion @@ -721,35 +771,17 @@ namespace CodeImp.DoomBuilder.BuilderModes } } - // Support function for joining and merging sectors - private void JoinMergeSectors(bool removelines) + // This clears the selection + [BeginAction("clearselection", BaseAction = true)] + public void ClearSelection() { - // Remove lines in betwen joining sectors? - if(removelines) - { - // Go for all selected linedefs - ICollection selectedlines = General.Map.Map.GetSelectedLinedefs(true); - foreach(Linedef ld in selectedlines) - { - // Front and back side? - if((ld.Front != null) && (ld.Back != null)) - { - // Both a selected sector, but not the same? - if(ld.Front.Sector.Selected && ld.Back.Sector.Selected && - (ld.Front.Sector != ld.Back.Sector)) - { - // Remove this line - ld.Dispose(); - } - } - } - } - - // Join all selected sectors with the first - for(int i = 1; i < orderedselection.Count; i++) - orderedselection[i].Join(orderedselection[0]); - } + // Clear selection + General.Map.Map.ClearAllSelected(); + // Redraw + General.Interface.RedrawDisplay(); + } + #endregion } } diff --git a/Source/BuilderModes/ClassicModes/ThingsMode.cs b/Source/BuilderModes/ClassicModes/ThingsMode.cs index 7b335937..62faf46f 100644 --- a/Source/BuilderModes/ClassicModes/ThingsMode.cs +++ b/Source/BuilderModes/ClassicModes/ThingsMode.cs @@ -44,7 +44,7 @@ namespace CodeImp.DoomBuilder.BuilderModes ButtonImage = "ThingsMode.png", // Image resource name for the button ButtonOrder = int.MinValue + 300)] // Position of the button (lower is more to the left) - public class ThingsMode : ClassicMode + public class ThingsMode : BaseClassicMode { #region ================== Constants @@ -70,24 +70,6 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Constructor / Disposer - // Constructor - public ThingsMode() - { - } - - // Disposer - public override void Dispose() - { - // Not already disposed? - if(!isdisposed) - { - // Clean up - - // Dispose base - base.Dispose(); - } - } - #endregion #region ================== Methods @@ -108,7 +90,7 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.SetPresentation(Presentation.Things); // Convert geometry selection to linedefs selection - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedVertices(true, true); ICollection lines = General.Map.Map.LinedefsFromMarkedVertices(false, true, false); foreach(Linedef l in lines) l.Selected = true; @@ -456,6 +438,32 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Present(); } } + + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedThings(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnCopyBegin(); + } + + // When pasting + public override bool OnPasteBegin() + { + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedThings(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnPasteBegin(); + } #endregion diff --git a/Source/BuilderModes/ClassicModes/VerticesMode.cs b/Source/BuilderModes/ClassicModes/VerticesMode.cs index 48f84d99..070618f7 100644 --- a/Source/BuilderModes/ClassicModes/VerticesMode.cs +++ b/Source/BuilderModes/ClassicModes/VerticesMode.cs @@ -42,7 +42,7 @@ namespace CodeImp.DoomBuilder.BuilderModes ButtonImage = "VerticesMode.png", // Image resource name for the button ButtonOrder = int.MinValue + 0)] // Position of the button (lower is more to the left) - public class VerticesMode : ClassicMode + public class VerticesMode : BaseClassicMode { #region ================== Constants @@ -67,24 +67,6 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Constructor / Disposer - // Constructor - public VerticesMode() - { - } - - // Disposer - public override void Dispose() - { - // Not already disposed? - if(!isdisposed) - { - // Clean up - - // Dispose base - base.Dispose(); - } - } - #endregion #region ================== Methods @@ -107,7 +89,7 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.SetPresentation(Presentation.Standard); // Convert geometry selection to vertices only - General.Map.Map.ClearAllMarks(); + General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedLinedefs(true, true); General.Map.Map.MarkSelectedSectors(true, true); verts = General.Map.Map.GetVerticesFromLinesMarks(true); @@ -390,6 +372,32 @@ namespace CodeImp.DoomBuilder.BuilderModes } } + // When copying + public override bool OnCopyBegin() + { + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedVertices(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnCopyBegin(); + } + + // When pasting + public override bool OnPasteBegin() + { + // No selection made? But we have a highlight! + if((General.Map.Map.GetSelectedVertices(true).Count == 0) && (highlighted != null)) + { + // Make the highlight the selection + highlighted.Selected = true; + } + + return base.OnPasteBegin(); + } + #endregion #region ================== Actions @@ -463,7 +471,7 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Interface.RedrawDisplay(); } } - + #endregion } } diff --git a/Source/BuilderModes/General/BuilderPlug.cs b/Source/BuilderModes/General/BuilderPlug.cs index 26e2f38b..de2b381e 100644 --- a/Source/BuilderModes/General/BuilderPlug.cs +++ b/Source/BuilderModes/General/BuilderPlug.cs @@ -121,7 +121,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #endregion #region ================== Tools - + // This renders the associated sectors/linedefs with the indication color public void PlotAssociations(IRenderer2D renderer, Association asso) { @@ -143,6 +143,7 @@ namespace CodeImp.DoomBuilder.BuilderModes } } } + // This renders the associated things with the indication color public void RenderAssociations(IRenderer2D renderer, Association asso) @@ -159,6 +160,7 @@ namespace CodeImp.DoomBuilder.BuilderModes } } } + // This renders the associated sectors/linedefs with the indication color public void PlotReverseAssociations(IRenderer2D renderer, Association asso) @@ -181,6 +183,7 @@ namespace CodeImp.DoomBuilder.BuilderModes } } } + // This renders the associated things with the indication color public void RenderReverseAssociations(IRenderer2D renderer, Association asso) diff --git a/Source/BuilderModes/Interface/MenusForm.cs b/Source/BuilderModes/Interface/MenusForm.cs index 1082cd6e..9aeb91d6 100644 --- a/Source/BuilderModes/Interface/MenusForm.cs +++ b/Source/BuilderModes/Interface/MenusForm.cs @@ -117,16 +117,18 @@ namespace CodeImp.DoomBuilder.BuilderModes // This shows the menu for the current editing mode public void ShowEditingModeMenu(EditMode mode) { + Type sourcemode = typeof(object); + if(mode != null) sourcemode = mode.GetType(); + // When these modes are active, then test against the base mode they will return to - if(mode is DragGeometryMode) mode = (mode as DragGeometryMode).BaseMode; - if(mode is DragThingsMode) mode = (mode as DragThingsMode).BaseMode; - if(mode is DrawGeometryMode) mode = (mode as DrawGeometryMode).BaseMode; - if(mode is CurveLinedefsMode) mode = (mode as CurveLinedefsMode).BaseMode; + if((mode is DragGeometryMode) || (mode is DragThingsMode) || + (mode is DrawGeometryMode) || (mode is CurveLinedefsMode)) + sourcemode = General.Map.PreviousStableMode; // Final decision - if(mode is LinedefsMode) HideAllMenusExcept(linedefsmenu); - else if(mode is SectorsMode) HideAllMenusExcept(sectorsmenu); - else if(mode is ThingsMode) HideAllMenusExcept(thingsmenu); + if(sourcemode == typeof(LinedefsMode)) HideAllMenusExcept(linedefsmenu); + else if(sourcemode == typeof(SectorsMode)) HideAllMenusExcept(sectorsmenu); + else if(sourcemode == typeof(ThingsMode)) HideAllMenusExcept(thingsmenu); else HideAllMenus(); } diff --git a/Source/Config/GameConfiguration.cs b/Source/Config/GameConfiguration.cs index 1918959c..3e6fa3b3 100644 --- a/Source/Config/GameConfiguration.cs +++ b/Source/Config/GameConfiguration.cs @@ -43,6 +43,7 @@ namespace CodeImp.DoomBuilder.Config private Configuration cfg; // General settings + private string configname; private string enginename; private float defaulttexturescale; private float defaultflatscale; @@ -102,6 +103,7 @@ namespace CodeImp.DoomBuilder.Config #region ================== Properties // General settings + public string Name { get { return configname; } } public string EngineName { get { return enginename; } } public float DefaultTextureScale { get { return defaulttexturescale; } } public float DefaultFlatScale { get { return defaultflatscale; } } @@ -184,6 +186,7 @@ namespace CodeImp.DoomBuilder.Config this.skills = new List(); // Read general settings + configname = cfg.ReadSetting("game", ""); enginename = cfg.ReadSetting("engine", ""); defaulttexturescale = cfg.ReadSetting("defaulttexturescale", 1f); defaultflatscale = cfg.ReadSetting("defaultflatscale", 1f); diff --git a/Source/Editing/CopyPasteManager.cs b/Source/Editing/CopyPasteManager.cs index aa2df0f2..da03dbd7 100644 --- a/Source/Editing/CopyPasteManager.cs +++ b/Source/Editing/CopyPasteManager.cs @@ -96,22 +96,25 @@ namespace CodeImp.DoomBuilder.Editing // that need to be copied. if(General.Map.Mode.OnCopyBegin()) { - MapSet copyset = new MapSet(); - - // Copy all data over - copyset = General.Map.Map.Clone(); + // Get all marked elements + ICollection verts = General.Map.Map.GetMarkedVertices(true); + ICollection sides = General.Map.Map.GetMarkedSidedefs(true); + ICollection sectors = General.Map.Map.GetMarkedSectors(true); + ICollection lines = General.Map.Map.GetMarkedLinedefs(true); + ICollection things = General.Map.Map.GetMarkedThings(true); // Write data to stream MemoryStream memstream = new MemoryStream(); UniversalStreamWriter writer = new UniversalStreamWriter(); writer.RememberCustomTypes = false; - writer.Write(copyset, memstream, null); + writer.Write(verts, lines, sides, sectors, things, memstream, null); // Set on clipboard Clipboard.SetData(CLIPBOARD_DATA_FORMAT, memstream); // Done memstream.Dispose(); + General.Map.Mode.OnCopyEnd(); return true; } else @@ -124,21 +127,36 @@ namespace CodeImp.DoomBuilder.Editing // This performs the paste. Returns false when paste was cancelled. private bool DoPasteSelection() { - // Ask the editing mode to prepare selection for pasting. - if(General.Map.Mode.OnPasteBegin()) + // Anything to paste? + if(Clipboard.ContainsData(CLIPBOARD_DATA_FORMAT)) { - // TODO: Do the paste - - if(Clipboard.ContainsData(CLIPBOARD_DATA_FORMAT)) + // Ask the editing mode to prepare selection for pasting. + if(General.Map.Mode.OnPasteBegin()) { + // Read from clipboard Stream memstream = (Stream)Clipboard.GetData(CLIPBOARD_DATA_FORMAT); memstream.Seek(0, SeekOrigin.Begin); - StreamReader reader = new StreamReader(memstream, Encoding.ASCII); - //File.WriteAllText("C:\\Test.txt", reader.ReadToEnd()); - memstream.Dispose(); - } - return true; + // Mark all current geometry + General.Map.Map.ClearAllMarks(true); + + // Read data stream + UniversalStreamReader reader = new UniversalStreamReader(); + reader.Read(General.Map.Map, memstream); + + // The new geometry is not marked, so invert the marks to get it marked + General.Map.Map.InvertAllMarks(); + + // Done + memstream.Dispose(); + General.Map.Mode.OnPasteEnd(); + return true; + } + else + { + General.MessageBeep(MessageBeepType.Warning); + return false; + } } else { diff --git a/Source/Editing/EditMode.cs b/Source/Editing/EditMode.cs index 17c41e76..0b0e0a35 100644 --- a/Source/Editing/EditMode.cs +++ b/Source/Editing/EditMode.cs @@ -168,9 +168,20 @@ namespace CodeImp.DoomBuilder.Editing // The edit mode should mark all vertices, lines and sectors // that need to be copied. public virtual bool OnCopyBegin() { return false; } + + // Called when the marked geometry has been copied. + public virtual void OnCopyEnd() { } // Called before pasting. Return false when paste should be cancelled. - public virtual bool OnPasteBegin() { return false; } + public virtual bool OnPasteBegin() { return true; } + + // Called after new geometry has been pasted in. The new geometry is marked. + public virtual void OnPasteEnd() { } + + // Called when undo/redo is used + // Return false to cancel undo action + public virtual bool OnUndoBegin() { return true; } + public virtual bool OnRedoBegin() { return true; } // Interface events public virtual void OnMouseClick(MouseEventArgs e) { } diff --git a/Source/Editing/UndoManager.cs b/Source/Editing/UndoManager.cs index 07846d57..ea717404 100644 --- a/Source/Editing/UndoManager.cs +++ b/Source/Editing/UndoManager.cs @@ -218,42 +218,46 @@ namespace CodeImp.DoomBuilder.Editing Cursor oldcursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; - // Cancel volatile mode, if any - // This returns false when mode was not volatile - if(!General.CancelVolatileMode()) + // Call UndoBegin event + if(General.Map.Mode.OnUndoBegin()) { - // Anything to undo? - if(undos.Count > 0) + // Cancel volatile mode, if any + // This returns false when mode was not volatile + if(!General.CancelVolatileMode()) { - // Get undo snapshot - u = undos[0]; - undos.RemoveAt(0); + // Anything to undo? + if(undos.Count > 0) + { + // Get undo snapshot + u = undos[0]; + undos.RemoveAt(0); - General.WriteLogLine("Performing undo \"" + u.description + "\", Ticket ID " + u.ticketid + "..."); + General.WriteLogLine("Performing undo \"" + u.description + "\", Ticket ID " + u.ticketid + "..."); - // Make a snapshot for redo - r = new UndoSnapshot(u, General.Map.Map.Clone()); + // Make a snapshot for redo + r = new UndoSnapshot(u, General.Map.Map.Clone()); - // Put it on the stack - redos.Insert(0, r); - LimitUndoRedoLevel(redos); + // Put it on the stack + redos.Insert(0, r); + LimitUndoRedoLevel(redos); - // Reset grouping - lastgroup = UndoGroup.None; + // Reset grouping + lastgroup = UndoGroup.None; - // Remove selection - u.map.ClearAllMarks(); - u.map.ClearAllSelected(); + // Remove selection + u.map.ClearAllMarks(false); + u.map.ClearAllSelected(); - // Change map set - General.Map.ChangeMapSet(u.map); + // Change map set + General.Map.ChangeMapSet(u.map); - // Update - General.MainWindow.RedrawDisplay(); - General.MainWindow.UpdateInterface(); + // Update + General.MainWindow.RedrawDisplay(); + General.MainWindow.UpdateInterface(); + } } } - + Cursor.Current = oldcursor; } @@ -265,38 +269,42 @@ namespace CodeImp.DoomBuilder.Editing Cursor oldcursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; - // Cancel volatile mode, if any - General.CancelVolatileMode(); - - // Anything to redo? - if(redos.Count > 0) + // Call RedoBegin event + if(General.Map.Mode.OnRedoBegin()) { - // Get redo snapshot - r = redos[0]; - redos.RemoveAt(0); + // Cancel volatile mode, if any + General.CancelVolatileMode(); - General.WriteLogLine("Performing redo \"" + r.description + "\", Ticket ID " + r.ticketid + "..."); + // Anything to redo? + if(redos.Count > 0) + { + // Get redo snapshot + r = redos[0]; + redos.RemoveAt(0); - // Make a snapshot for undo - u = new UndoSnapshot(r, General.Map.Map.Clone()); + General.WriteLogLine("Performing redo \"" + r.description + "\", Ticket ID " + r.ticketid + "..."); - // Put it on the stack - undos.Insert(0, u); - LimitUndoRedoLevel(undos); - - // Reset grouping - lastgroup = UndoGroup.None; + // Make a snapshot for undo + u = new UndoSnapshot(r, General.Map.Map.Clone()); - // Remove selection - r.map.ClearAllMarks(); - r.map.ClearAllSelected(); + // Put it on the stack + undos.Insert(0, u); + LimitUndoRedoLevel(undos); - // Change map set - General.Map.ChangeMapSet(r.map); + // Reset grouping + lastgroup = UndoGroup.None; - // Update - General.MainWindow.RedrawDisplay(); - General.MainWindow.UpdateInterface(); + // Remove selection + r.map.ClearAllMarks(false); + r.map.ClearAllSelected(); + + // Change map set + General.Map.ChangeMapSet(r.map); + + // Update + General.MainWindow.RedrawDisplay(); + General.MainWindow.UpdateInterface(); + } } Cursor.Current = oldcursor; diff --git a/Source/General/MapManager.cs b/Source/General/MapManager.cs index 1069f7da..fcb9b594 100644 --- a/Source/General/MapManager.cs +++ b/Source/General/MapManager.cs @@ -76,6 +76,8 @@ namespace CodeImp.DoomBuilder private DataManager data; private EditMode mode; private EditMode newmode; + private Type prevmode; + private Type prevstablemode; private D3DDevice graphics; private Renderer2D renderer2d; private Renderer3D renderer3d; @@ -101,6 +103,8 @@ namespace CodeImp.DoomBuilder public MapSet Map { get { return map; } } public EditMode Mode { get { return mode; } } public EditMode NewMode { get { return newmode; } } + public Type PreviousMode { get { return prevmode; } } + public Type PreviousStableMode { get { return prevstablemode; } } public DataManager Data { get { return data; } } public bool IsChanged { get { return changed; } set { changed |= value; } } public bool IsDisposed { get { return isdisposed; } } @@ -862,7 +866,6 @@ namespace CodeImp.DoomBuilder public void ChangeMode(EditMode nextmode) { EditMode oldmode = mode; - newmode = nextmode; cancelmodechange = false; // Log info @@ -871,6 +874,19 @@ namespace CodeImp.DoomBuilder else General.WriteLogLine("Stopping edit mode..."); + // Remember previous mode + newmode = nextmode; + if(mode != null) + { + prevmode = mode.GetType(); + if(!mode.Attributes.Volatile) prevstablemode = prevmode; + } + else + { + prevmode = null; + prevstablemode = null; + } + // Let the plugins know beforehand General.Plugins.ModeChanges(oldmode, newmode); diff --git a/Source/IO/UniversalStreamWriter.cs b/Source/IO/UniversalStreamWriter.cs index c25b09cd..6d0029aa 100644 --- a/Source/IO/UniversalStreamWriter.cs +++ b/Source/IO/UniversalStreamWriter.cs @@ -107,6 +107,17 @@ namespace CodeImp.DoomBuilder.IO // This writes the structures to a stream // writenamespace may be null to omit writing the namespace to the stream public void Write(MapSet map, Stream stream, string writenamespace) + { + Write(map.Vertices, map.Linedefs, map.Sidedefs, map.Sectors, map.Things, stream, writenamespace); + } + + // This writes the structures to a stream + // NOTE: writenamespace may be null to omit writing the namespace to the stream. + // NOTE: The given structures must be complete, with the exception of the sidedefs. + // If there are missing sidedefs, their reference will be removed from the linedefs. + public void Write(ICollection vertices, ICollection linedefs, + ICollection sidedefs, ICollection sectors, + ICollection things, Stream stream, string writenamespace) { UniversalParser textmap = new UniversalParser(); @@ -118,24 +129,24 @@ namespace CodeImp.DoomBuilder.IO Dictionary sectorids = new Dictionary(); // Index the elements in the data structures - foreach(Vertex v in map.Vertices) vertexids.Add(v, vertexids.Count); - foreach(Sidedef sd in map.Sidedefs) sidedefids.Add(sd, sidedefids.Count); - foreach(Sector s in map.Sectors) sectorids.Add(s, sectorids.Count); + foreach(Vertex v in vertices) vertexids.Add(v, vertexids.Count); + foreach(Sidedef sd in sidedefs) sidedefids.Add(sd, sidedefids.Count); + foreach(Sector s in sectors) sectorids.Add(s, sectorids.Count); // If we write the custom field types again, then forget // all previous field types (this gets rid of unused field types) if(remembercustomtypes) General.Map.Options.ForgetUniversalFieldTypes(); // Write the data structures to textmap - WriteVertices(map.Vertices, textmap); - WriteLinedefs(map.Linedefs, textmap, sidedefids, vertexids); - WriteSidedefs(map.Sidedefs, textmap, sectorids); - WriteSectors(map.Sectors, textmap); - WriteThings(map.Things, textmap); - + WriteVertices(vertices, textmap); + WriteLinedefs(linedefs, textmap, sidedefids, vertexids); + WriteSidedefs(sidedefs, textmap, sectorids); + WriteSectors(sectors, textmap); + WriteThings(things, textmap); + // Get the textmap as string string textmapstr = textmap.OutputConfiguration(); - + // Write to stream StreamWriter writer = new StreamWriter(stream, Encoding.ASCII); writer.Write(textmapstr); @@ -172,8 +183,19 @@ namespace CodeImp.DoomBuilder.IO if(l.Tag != 0) coll.Add("id", l.Tag); coll.Add("v1", vertexids[l.Start]); coll.Add("v2", vertexids[l.End]); - if(l.Front != null) coll.Add("sidefront", sidedefids[l.Front]); else coll.Add("sidefront", -1); - if(l.Back != null) coll.Add("sideback", sidedefids[l.Back]); else coll.Add("sideback", -1); + + // Sidedef references + if((l.Front != null) && sidedefids.ContainsKey(l.Front)) + coll.Add("sidefront", sidedefids[l.Front]); + else + coll.Add("sidefront", -1); + + if((l.Back != null) && sidedefids.ContainsKey(l.Back)) + coll.Add("sideback", sidedefids[l.Back]); + else + coll.Add("sideback", -1); + + // Special if(l.Action != 0) coll.Add("special", l.Action); if(l.Args[0] != 0) coll.Add("arg0", l.Args[0]); if(l.Args[1] != 0) coll.Add("arg1", l.Args[1]); @@ -199,6 +221,8 @@ namespace CodeImp.DoomBuilder.IO // Go for all sidedefs foreach(Sidedef s in sidedefs) { + int sectorid = (s.Sector != null) ? sectorids[s.Sector] : -1; + // Make collection UniversalCollection coll = new UniversalCollection(); if(s.OffsetX != 0) coll.Add("offsetx", s.OffsetX); diff --git a/Source/Map/MapSet.cs b/Source/Map/MapSet.cs index 0959a87a..93335067 100644 --- a/Source/Map/MapSet.cs +++ b/Source/Map/MapSet.cs @@ -441,18 +441,51 @@ namespace CodeImp.DoomBuilder.Map return list; } + // This selects geometry based on the marking + public void SelectMarkedGeometry(bool mark, bool select) + { + SelectMarkedVertices(mark, select); + SelectMarkedLinedefs(mark, select); + SelectMarkedSectors(mark, select); + SelectMarkedThings(mark, select); + } + + // This selects geometry based on the marking + public void SelectMarkedVertices(bool mark, bool select) + { + foreach(Vertex v in vertices) if(v.Marked == mark) v.Selected = select; + } + + // This selects geometry based on the marking + public void SelectMarkedLinedefs(bool mark, bool select) + { + foreach(Linedef l in linedefs) if(l.Marked == mark) l.Selected = select; + } + + // This selects geometry based on the marking + public void SelectMarkedSectors(bool mark, bool select) + { + foreach(Sector s in sectors) if(s.Marked == mark) s.Selected = select; + } + + // This selects geometry based on the marking + public void SelectMarkedThings(bool mark, bool select) + { + foreach(Thing t in things) if(t.Marked == mark) t.Selected = select; + } + #endregion #region ================== Marking // This clears all marks - public void ClearAllMarks() + public void ClearAllMarks(bool mark) { - ClearMarkedVertices(false); - ClearMarkedThings(false); - ClearMarkedLinedefs(false); - ClearMarkedSectors(false); - ClearMarkedSidedefs(false); + ClearMarkedVertices(mark); + ClearMarkedThings(mark); + ClearMarkedLinedefs(mark); + ClearMarkedSectors(mark); + ClearMarkedSidedefs(mark); } // This clears marked vertices @@ -485,6 +518,46 @@ namespace CodeImp.DoomBuilder.Map foreach(Sector s in sectors) s.Marked = mark; } + // This inverts all marks + public void InvertAllMarks() + { + InvertMarkedVertices(); + InvertMarkedThings(); + InvertMarkedLinedefs(); + InvertMarkedSectors(); + InvertMarkedSidedefs(); + } + + // This inverts marked vertices + public void InvertMarkedVertices() + { + foreach(Vertex v in vertices) v.Marked = !v.Marked; + } + + // This inverts marked things + public void InvertMarkedThings() + { + foreach(Thing t in things) t.Marked = !t.Marked; + } + + // This inverts marked linedefs + public void InvertMarkedLinedefs() + { + foreach(Linedef l in linedefs) l.Marked = !l.Marked; + } + + // This inverts marked sidedefs + public void InvertMarkedSidedefs() + { + foreach(Sidedef s in sidedefs) s.Marked = !s.Marked; + } + + // This inverts marked sectors + public void InvertMarkedSectors() + { + foreach(Sector s in sectors) s.Marked = !s.Marked; + } + // Returns a collection of vertices that match a marked state public List GetMarkedVertices(bool mark) { @@ -509,6 +582,14 @@ namespace CodeImp.DoomBuilder.Map return list; } + // Returns a collection of sidedefs that match a marked state + public List GetMarkedSidedefs(bool mark) + { + List list = new List(sidedefs.Count >> 1); + foreach(Sidedef s in sidedefs) if(s.Marked == mark) list.Add(s); + return list; + } + // Returns a collection of sectors that match a marked state public List GetMarkedSectors(bool mark) { @@ -556,6 +637,17 @@ namespace CodeImp.DoomBuilder.Map } } + /// + /// This marks the sidedefs that make up the sectors with the matching mark + /// + public void MarkSidedefsFromSectors(bool matchmark, bool setmark) + { + foreach(Sidedef sd in sidedefs) + { + if(sd.Sector.Marked == matchmark) sd.Marked = setmark; + } + } + /// /// Returns a collection of vertices that match a marked state on the linedefs /// @@ -620,6 +712,49 @@ namespace CodeImp.DoomBuilder.Map } return list; } + + // This marks all selected geometry, including sidedefs from sectors + // Returns the number of selected elements + public void MarkAllSelectedGeometry(bool mark) + { + General.Map.Map.ClearAllMarks(!mark); + + // Direct vertices + General.Map.Map.MarkSelectedVertices(true, mark); + + // Direct linedefs + General.Map.Map.MarkSelectedLinedefs(true, mark); + + // Vertices from linedefs + ICollection verts = General.Map.Map.GetVerticesFromLinesMarks(mark); + foreach(Vertex v in verts) v.Marked = mark; + + // Linedefs from vertices + ICollection lines = General.Map.Map.LinedefsFromMarkedVertices(!mark, mark, !mark); + foreach(Linedef l in lines) l.Marked = mark; + + // Mark sectors from linedefs (note: this must be the first to mark + // sectors, because this clears the sector marks!) + General.Map.Map.ClearMarkedSectors(mark); + foreach(Linedef l in General.Map.Map.Linedefs) + { + if(!l.Selected) + { + if(l.Front != null) l.Front.Sector.Marked = !mark; + if(l.Back != null) l.Back.Sector.Marked = !mark; + } + } + + // Direct sectors + General.Map.Map.MarkSelectedSectors(true, mark); + + // Direct things + General.Map.Map.MarkSelectedThings(true, mark); + + // Sidedefs from linedefs + //General.Map.Map.MarkSidedefsFromLinedefs(true, mark); + General.Map.Map.MarkSidedefsFromSectors(true, mark); + } #endregion diff --git a/Source/Windows/MainForm.Designer.cs b/Source/Windows/MainForm.Designer.cs index e524dd01..33f01f98 100644 --- a/Source/Windows/MainForm.Designer.cs +++ b/Source/Windows/MainForm.Designer.cs @@ -43,6 +43,7 @@ namespace CodeImp.DoomBuilder.Windows System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; System.Windows.Forms.ToolStripSeparator toolStripSeparator2; System.Windows.Forms.ToolStripSeparator toolStripSeparator7; + System.Windows.Forms.ToolStripSeparator toolStripSeparator12; System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); this.itemeditmodesseperator = new System.Windows.Forms.ToolStripSeparator(); this.buttoneditmodesseperator = new System.Windows.Forms.ToolStripSeparator(); @@ -62,6 +63,10 @@ namespace CodeImp.DoomBuilder.Windows this.menuedit = new System.Windows.Forms.ToolStripMenuItem(); this.itemundo = new System.Windows.Forms.ToolStripMenuItem(); this.itemredo = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); + this.itemcut = new System.Windows.Forms.ToolStripMenuItem(); + this.itemcopy = new System.Windows.Forms.ToolStripMenuItem(); + this.itempaste = new System.Windows.Forms.ToolStripMenuItem(); this.itemsnaptogrid = new System.Windows.Forms.ToolStripMenuItem(); this.itemautomerge = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); @@ -96,6 +101,7 @@ namespace CodeImp.DoomBuilder.Windows this.statusbar = new System.Windows.Forms.StatusStrip(); this.statuslabel = new System.Windows.Forms.ToolStripStatusLabel(); this.warninglabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.configlabel = new System.Windows.Forms.ToolStripStatusLabel(); this.gridlabel = new System.Windows.Forms.ToolStripStatusLabel(); this.buttongrid = new System.Windows.Forms.ToolStripDropDownButton(); this.itemgrid1024 = new System.Windows.Forms.ToolStripMenuItem(); @@ -130,10 +136,6 @@ namespace CodeImp.DoomBuilder.Windows this.processor = new System.Windows.Forms.Timer(this.components); this.warningtimer = new System.Windows.Forms.Timer(this.components); this.warningflasher = new System.Windows.Forms.Timer(this.components); - this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); - this.itemcut = new System.Windows.Forms.ToolStripMenuItem(); - this.itemcopy = new System.Windows.Forms.ToolStripMenuItem(); - this.itempaste = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); @@ -148,6 +150,7 @@ namespace CodeImp.DoomBuilder.Windows toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); + toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); this.menumain.SuspendLayout(); this.toolbar.SuspendLayout(); this.statusbar.SuspendLayout(); @@ -230,6 +233,12 @@ namespace CodeImp.DoomBuilder.Windows toolStripSeparator7.Name = "toolStripSeparator7"; toolStripSeparator7.Size = new System.Drawing.Size(6, 25); // + // toolStripSeparator12 + // + toolStripSeparator12.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + toolStripSeparator12.Name = "toolStripSeparator12"; + toolStripSeparator12.Size = new System.Drawing.Size(6, 23); + // // itemeditmodesseperator // this.itemeditmodesseperator.Name = "itemeditmodesseperator"; @@ -401,6 +410,38 @@ namespace CodeImp.DoomBuilder.Windows this.itemredo.Text = "Redo"; this.itemredo.Click += new System.EventHandler(this.InvokeTaggedAction); // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(162, 6); + // + // itemcut + // + this.itemcut.Image = global::CodeImp.DoomBuilder.Properties.Resources.Cut; + this.itemcut.Name = "itemcut"; + this.itemcut.Size = new System.Drawing.Size(165, 22); + this.itemcut.Tag = "builder_cutselection"; + this.itemcut.Text = "Cut"; + this.itemcut.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itemcopy + // + this.itemcopy.Image = global::CodeImp.DoomBuilder.Properties.Resources.Copy; + this.itemcopy.Name = "itemcopy"; + this.itemcopy.Size = new System.Drawing.Size(165, 22); + this.itemcopy.Tag = "builder_copyselection"; + this.itemcopy.Text = "Copy"; + this.itemcopy.Click += new System.EventHandler(this.InvokeTaggedAction); + // + // itempaste + // + this.itempaste.Image = global::CodeImp.DoomBuilder.Properties.Resources.Paste; + this.itempaste.Name = "itempaste"; + this.itempaste.Size = new System.Drawing.Size(165, 22); + this.itempaste.Tag = "builder_pasteselection"; + this.itempaste.Text = "Paste"; + this.itempaste.Click += new System.EventHandler(this.InvokeTaggedAction); + // // itemsnaptogrid // this.itemsnaptogrid.Checked = true; @@ -732,6 +773,8 @@ namespace CodeImp.DoomBuilder.Windows this.statusbar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.statuslabel, this.warninglabel, + this.configlabel, + toolStripSeparator12, this.gridlabel, this.buttongrid, toolStripSeparator1, @@ -753,7 +796,7 @@ namespace CodeImp.DoomBuilder.Windows this.statuslabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; this.statuslabel.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.statuslabel.Name = "statuslabel"; - this.statuslabel.Size = new System.Drawing.Size(571, 18); + this.statuslabel.Size = new System.Drawing.Size(208, 18); this.statuslabel.Spring = true; this.statuslabel.Text = "Initializing user interface..."; this.statuslabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -770,12 +813,22 @@ namespace CodeImp.DoomBuilder.Windows this.warninglabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.warninglabel.Visible = false; // + // configlabel + // + this.configlabel.AutoSize = false; + this.configlabel.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.configlabel.Name = "configlabel"; + this.configlabel.Size = new System.Drawing.Size(250, 18); + this.configlabel.Text = "ZDoom (Doom in Hexen Format)"; + this.configlabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.configlabel.ToolTipText = "Current Game Configuration"; + // // gridlabel // this.gridlabel.AutoSize = false; this.gridlabel.AutoToolTip = true; this.gridlabel.Name = "gridlabel"; - this.gridlabel.Size = new System.Drawing.Size(128, 18); + this.gridlabel.Size = new System.Drawing.Size(62, 18); this.gridlabel.Text = "32 mp"; this.gridlabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; this.gridlabel.TextImageRelation = System.Windows.Forms.TextImageRelation.Overlay; @@ -1093,38 +1146,6 @@ namespace CodeImp.DoomBuilder.Windows // this.warningflasher.Tick += new System.EventHandler(this.warningflasher_Tick); // - // toolStripMenuItem7 - // - this.toolStripMenuItem7.Name = "toolStripMenuItem7"; - this.toolStripMenuItem7.Size = new System.Drawing.Size(162, 6); - // - // itemcut - // - this.itemcut.Image = global::CodeImp.DoomBuilder.Properties.Resources.Cut; - this.itemcut.Name = "itemcut"; - this.itemcut.Size = new System.Drawing.Size(165, 22); - this.itemcut.Tag = "builder_cutselection"; - this.itemcut.Text = "Cut"; - this.itemcut.Click += new System.EventHandler(this.InvokeTaggedAction); - // - // itemcopy - // - this.itemcopy.Image = global::CodeImp.DoomBuilder.Properties.Resources.Copy; - this.itemcopy.Name = "itemcopy"; - this.itemcopy.Size = new System.Drawing.Size(165, 22); - this.itemcopy.Tag = "builder_copyselection"; - this.itemcopy.Text = "Copy"; - this.itemcopy.Click += new System.EventHandler(this.InvokeTaggedAction); - // - // itempaste - // - this.itempaste.Image = global::CodeImp.DoomBuilder.Properties.Resources.Paste; - this.itempaste.Name = "itempaste"; - this.itempaste.Size = new System.Drawing.Size(165, 22); - this.itempaste.Tag = "builder_pasteselection"; - this.itempaste.Text = "Paste"; - this.itempaste.Click += new System.EventHandler(this.InvokeTaggedAction); - // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -1256,5 +1277,6 @@ namespace CodeImp.DoomBuilder.Windows private System.Windows.Forms.ToolStripMenuItem itemcut; private System.Windows.Forms.ToolStripMenuItem itemcopy; private System.Windows.Forms.ToolStripMenuItem itempaste; + private System.Windows.Forms.ToolStripStatusLabel configlabel; } } \ No newline at end of file diff --git a/Source/Windows/MainForm.cs b/Source/Windows/MainForm.cs index 439e4dc1..84454740 100644 --- a/Source/Windows/MainForm.cs +++ b/Source/Windows/MainForm.cs @@ -379,6 +379,7 @@ namespace CodeImp.DoomBuilder.Windows buttonzoom.Enabled = true; gridlabel.Enabled = true; buttongrid.Enabled = true; + configlabel.Text = General.Map.Config.Name; } else { @@ -392,6 +393,7 @@ namespace CodeImp.DoomBuilder.Windows buttonzoom.Enabled = false; gridlabel.Enabled = false; buttongrid.Enabled = false; + configlabel.Text = ""; } } diff --git a/Source/Windows/MainForm.resx b/Source/Windows/MainForm.resx index 7d6e8c57..b0de6af5 100644 --- a/Source/Windows/MainForm.resx +++ b/Source/Windows/MainForm.resx @@ -159,6 +159,9 @@ False + + False + 17, 17