ZoneBuilder/Source/Plugins/BuilderModes/ClassicModes/VertexSlopeAssistMode.cs
MaxED 0000b7a02d Added, Draw Lines mode: additional guidelines and horizontal/vertical line lengths can now be displayed for currently drawn line using "Show guidelines" top menu button.
Changed, Draw Lines mode: line angles are now shown only when "Show guidelines" mode is enabled.
Fixed, Draw Lines mode: in some cases "Snap to cardinal directions" mode was snapping only to diagonals.
Fixed, Draw Lines mode: snap to geometry behaved incorrectly when "Snap to cardinal directions" mode was enabled.
Changed, Things mode: dynamic light shape is now drawn using highlight color when a dynamic light thing is highlighted.
Added more sanity checks to MODELDEFS parser.
2023-01-04 19:37:58 +01:00

1148 lines
48 KiB
C#

#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Windows;
using System.Drawing;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.BuilderModes.Interface;
#endregion
//JBR Vertex Slope Assistant mode
namespace CodeImp.DoomBuilder.BuilderModes
{
[EditMode(DisplayName = "Vertex Slope Assistant",
AllowCopyPaste = false,
Volatile = true)]
public sealed class VertexSlopeAssistMode : BaseClassicMode
{
#region ================== Constants
private const float LINE_THICKNESS = 1.2f;
private const float OVER_THICKNESS = 0.6f;
private const int MODE_SELECT = 0;
private const int MODE_ASSIGNGROUPS = 1;
private const int MODE_NEWPOINTS = 4; // This mode doesn't exist but tell which modes begin to set points
private const int MODE_NEWTAGGEDTRIANGLE = 4;
private const int MODE_NEWEMPTYTRIANGLE = 5;
private const int MODE_NEWGROUP = 6;
private const int MODE_MODGROUP = 7;
#endregion
#region ================== Variables
public int mode;
public bool createdUndo;
public TextLabel[] vertexnames;
public List<DrawnVertex> points;
public int expectedPoints;
// Groups selection
public List<int> overgroups;
public int overgroupoffset;
public int overTagID;
// Linedef stuff
public Linedef selectedLinedef;
public Linedef indexedLinedef;
public int indexedAction;
public List<int> indexedTags;
// Current mouse position
public Vector2D curmousepos;
public bool snaptogrid; // SHIFT to toggle
public bool snaptonearest; // CTRL to enable
public bool snaptocardinaldirection; //mxd. ALT-SHIFT to enable
#endregion
#region ================== Properties
// Just keep the base mode button checked
public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public VertexSlopeAssistMode(EditMode basemode)
{
vertexnames = new TextLabel[4];
for (int i = 0; i < 4; i++)
{
vertexnames[i] = new TextLabel();
vertexnames[i].TransformCoords = true;
vertexnames[i].Location = new Vector2D(0f, 0f);
vertexnames[i].AlignX = TextAlignmentX.Center;
vertexnames[i].AlignY = TextAlignmentY.Middle;
vertexnames[i].Color = General.Colors.Highlight.WithAlpha(255);
}
points = new List<DrawnVertex>();
overgroups = new List<int>();
overgroupoffset = 0;
curmousepos = new Vector2D();
overTagID = -1;
expectedPoints = 3;
selectedLinedef = null;
indexedLinedef = null;
indexedAction = 0;
indexedTags = new List<int>();
SetMode(MODE_SELECT);
}
// Disposer
public override void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Clean up
foreach (TextLabel tl in vertexnames) tl.Dispose();
// Done
base.Dispose();
}
}
#endregion
#region ================== Methods
public void CreateUndo()
{
if (!createdUndo)
{
General.Map.UndoRedo.CreateUndo("Vertex Slope Assistant");
createdUndo = true;
}
}
public void UpdateModeText()
{
if (mode == MODE_SELECT)
{
if (overTagID != -1)
BuilderPlug.Me.VertexSlopeAssistForm.ModeText = string.Format("Select/Edit group, Click to select tag {0}", overTagID);
else
BuilderPlug.Me.VertexSlopeAssistForm.ModeText = string.Format("Select/Edit group");
}
if (mode == MODE_ASSIGNGROUPS)
{
BuilderPlug.Me.VertexSlopeAssistForm.ModeText = string.Format("Double-Click on Group List to pick Vertex {0}", indexedTags.Count + 1);
}
if (mode == MODE_NEWTAGGEDTRIANGLE || mode == MODE_NEWEMPTYTRIANGLE)
{
BuilderPlug.Me.VertexSlopeAssistForm.ModeText = string.Format("Creating new triangle... Click to make point {0}", points.Count + 1);
}
if (mode == MODE_NEWGROUP)
{
BuilderPlug.Me.VertexSlopeAssistForm.ModeText = string.Format("Making new group... Click to make point {0}", points.Count + 1);
}
if (mode == MODE_MODGROUP)
{
BuilderPlug.Me.VertexSlopeAssistForm.ModeText = string.Format("Remaking group... Click to make point {0}", points.Count + 1);
}
}
public void SetMode(int newmode)
{
mode = newmode;
UpdateModeText();
points.Clear();
indexedTags.Clear();
selectedLinedef = null;
if (newmode != MODE_ASSIGNGROUPS) indexedLinedef = null;
}
public void CB_DrawNewTaggedTriangle(object sender, EventArgs eventArgs)
{
expectedPoints = 3;
SetMode(MODE_NEWTAGGEDTRIANGLE);
}
public void CB_DrawNewEmptyTriangle(object sender, EventArgs eventArgs)
{
expectedPoints = 3;
SetMode(MODE_NEWEMPTYTRIANGLE);
}
public void CB_TagGroup_Changed(object sender, EventArgs eventArgs)
{
if (mode != MODE_ASSIGNGROUPS)
SetMode(MODE_SELECT);
else
General.Interface.RedrawDisplay();
}
public void CB_TagGroup_Choosen(object sender, EventArgs eventArgs)
{
if (mode == MODE_ASSIGNGROUPS)
{
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
if (things != null && things.Count == 1)
{
DrawnVertex dv = new DrawnVertex();
dv.stitch = true;
dv.stitchline = true;
dv.pos = things[0].Position;
points.Add(dv);
indexedTags.Add(BuilderPlug.Me.VertexSlopeAssistForm.TagID);
if (indexedTags.Count >= 3)
{
if (indexedLinedef != null)
{
CreateUndo();
indexedLinedef.Tag = indexedTags[0];
indexedLinedef.SetFlag("8192", true);
if (indexedAction < 710) // 704, 705
{
indexedLinedef.Front.OffsetX = indexedTags[1];
indexedLinedef.Front.OffsetY = indexedTags[2];
}
else // 714, 715
{
indexedLinedef.Back.OffsetX = indexedTags[1];
indexedLinedef.Back.OffsetY = indexedTags[2];
}
indexedLinedef.Action = indexedAction;
General.Interface.DisplayStatus(StatusType.Action, string.Format("Action {0} and Tag {1} set to Linedef {2}.", indexedLinedef.Action, indexedLinedef.Tag, indexedLinedef.Index));
}
SetMode(MODE_SELECT);
}
}
else
{
General.Interface.DisplayStatus(StatusType.Warning, "Only single vertex slope groups can be assigned.");
}
UpdateModeText();
}
}
public void CB_TagGroup_NewTriangle(object sender, EventArgs eventArgs)
{
expectedPoints = 3;
SetMode(MODE_NEWGROUP);
}
public void CB_TagGroup_NewVertex(object sender, EventArgs eventArgs)
{
expectedPoints = 1;
SetMode(MODE_NEWGROUP);
}
public void CB_TagGroup_Modify(object sender, EventArgs eventArgs)
{
int vertices = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag();
if (vertices == 1)
expectedPoints = 1;
else
expectedPoints = 3;
SetMode(MODE_MODGROUP);
}
public void CB_TagGroup_ModifyAsTriangle(object sender, EventArgs eventArgs)
{
expectedPoints = 3;
SetMode(MODE_MODGROUP);
}
public void CB_TagGroup_ModifyAsVertex(object sender, EventArgs eventArgs)
{
expectedPoints = 1;
SetMode(MODE_MODGROUP);
}
public void CB_TagGroup_Tag(object sender, EventArgs eventArgs)
{
SetMode(MODE_SELECT);
int oldtag = BuilderPlug.Me.VertexSlopeAssistForm.TagID;
BuilderPlug.Me.VertexSlopeAssistForm.Hide();
int newtag = VertexSlopeAssistTagForm.RunDialog(BuilderPlug.Me.VertexSlopeAssistForm, oldtag);
BuilderPlug.Me.VertexSlopeAssistForm.Show();
BuilderPlug.Me.VertexSlopeAssistForm.Activate();
if (newtag != -1 && newtag != oldtag)
{
bool exists = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag(newtag) > 0;
if (exists)
{
General.Interface.DisplayStatus(StatusType.Warning, string.Format("Tag {0} already belongs to another group. Aborted retagging.", newtag));
}
else
{
CreateUndo();
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
if (things != null)
{
foreach (Thing t in things)
{
t.Rotate(newtag);
t.UpdateConfiguration();
}
}
General.Map.ThingsFilter.Update();
General.Map.Map.Update();
General.Map.IsChanged = true;
BuilderPlug.Me.VertexSlopeAssistForm.TagID = newtag;
BuilderPlug.Me.VertexSlopeAssistForm.CreateList();
BuilderPlug.Me.VertexSlopeAssistForm.UpdateForm();
}
}
}
public void CB_TagGroup_Delete(object sender, EventArgs eventArgs)
{
CreateUndo();
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
if (things != null)
{
for (int i = things.Count - 1; i >= 0; i--)
{
things[i].Dispose();
}
}
General.Map.ThingsFilter.Update();
General.Map.Map.Update();
General.Map.IsChanged = true;
BuilderPlug.Me.VertexSlopeAssistForm.CreateList();
BuilderPlug.Me.VertexSlopeAssistForm.UpdateForm();
SetMode(MODE_SELECT);
}
public void CB_ValueChanged(object sender, EventArgs eventArgs)
{
CreateUndo();
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
if (things != null)
{
CreateUndo();
if (things.Count >= 1)
{
things[0].Move(BuilderPlug.Me.VertexSlopeAssistForm.Vertex1);
if (BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ)
ChangeVSlopeFlags(things[0], (int)BuilderPlug.Me.VertexSlopeAssistForm.Vertex1.z);
things[0].Parameter = BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ ? 1 : 0;
things[0].UpdateConfiguration();
}
if (things.Count >= 2)
{
things[1].Move(BuilderPlug.Me.VertexSlopeAssistForm.Vertex2);
if (BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ)
ChangeVSlopeFlags(things[1], (int)BuilderPlug.Me.VertexSlopeAssistForm.Vertex2.z);
things[1].Parameter = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ ? 1 : 0;
things[1].UpdateConfiguration();
}
if (things.Count >= 3)
{
things[2].Move(BuilderPlug.Me.VertexSlopeAssistForm.Vertex3);
if (BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ)
ChangeVSlopeFlags(things[2], (int)BuilderPlug.Me.VertexSlopeAssistForm.Vertex3.z);
things[2].Parameter = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ ? 1 : 0;
things[2].UpdateConfiguration();
}
General.Map.ThingsFilter.Update();
General.Map.Map.Update();
General.Map.IsChanged = true;
BuilderPlug.Me.VertexSlopeAssistForm.UpdateForm();
SetMode(MODE_SELECT);
}
}
public void CB_Rotate_All_VSlopes(object sender, EventArgs eventArgs)
{
int vertices = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag();
if (vertices == 0 || vertices >= 3)
{
Vector3D v1 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex1;
Vector3D v2 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2;
Vector3D v3 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex1 = v2;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2 = v3;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3 = v1;
bool v1z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ;
bool v2z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ;
bool v3z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ = v2z;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ = v3z;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ = v1z;
CB_ValueChanged(sender, eventArgs);
}
}
public void CB_Flip_All_VSlopes(object sender, EventArgs eventArgs)
{
int vertices = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag();
if (vertices == 0 || vertices >= 3)
{
Vector3D v2 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2;
Vector3D v3 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2 = v3;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3 = v2;
bool v2z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ;
bool v3z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ = v3z;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ = v2z;
CB_ValueChanged(sender, eventArgs);
}
}
public void CB_Rotate_Height_VSlopes(object sender, EventArgs eventArgs)
{
int vertices = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag();
if (vertices == 0 || vertices >= 3)
{
Vector3D v1 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex1;
Vector3D v2 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2;
Vector3D v3 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex1 = new Vector3D(v1.x, v1.y, v2.z);
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2 = new Vector3D(v2.x, v2.y, v3.z);
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3 = new Vector3D(v3.x, v3.y, v1.z);
bool v1z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ;
bool v2z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ;
bool v3z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ = v2z;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ = v3z;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ = v1z;
CB_ValueChanged(sender, eventArgs);
}
}
public void CB_Flip_Height_VSlopes(object sender, EventArgs eventArgs)
{
int vertices = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag();
if (vertices == 0 || vertices >= 3)
{
Vector3D v2 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2;
Vector3D v3 = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2 = new Vector3D(v2.x, v2.y, v3.z);
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3 = new Vector3D(v3.x, v3.y, v2.z);
bool v2z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ;
bool v3z = BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ = v3z;
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ = v2z;
CB_ValueChanged(sender, eventArgs);
}
}
public void CB_Context_Set(object sender, int action)
{
if (selectedLinedef != null && action >= 0)
{
if (action != 0)
{
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
if (things != null)
{
if (things.Count == 3)
{
// Set tag and action
CreateUndo();
selectedLinedef.Tag = BuilderPlug.Me.VertexSlopeAssistForm.TagID;
selectedLinedef.SetFlag("8192", false);
selectedLinedef.Action = action;
General.Interface.DisplayStatus(StatusType.Action, string.Format("Action {0} and Tag {1} set to Linedef {2}.", selectedLinedef.Action, selectedLinedef.Tag, selectedLinedef.Index));
}
else if (things.Count == 1)
{
// Prepare for indexing groups
indexedLinedef = selectedLinedef;
indexedAction = action;
SetMode(MODE_ASSIGNGROUPS);
DrawnVertex dv = new DrawnVertex();
dv.stitch = true;
dv.stitchline = true;
dv.pos = things[0].Position;
points.Add(dv);
indexedTags.Add(BuilderPlug.Me.VertexSlopeAssistForm.TagID);
UpdateModeText();
}
}
}
else
{
// Clear tag and action
CreateUndo();
selectedLinedef.Tag = 0;
selectedLinedef.Action = 0;
General.Interface.DisplayStatus(StatusType.Action, string.Format("Cleared Action and Tag on Linedef {0}.", selectedLinedef.Index));
}
selectedLinedef = null;
General.Interface.RedrawDisplay();
}
if (action == -1)
{
// For some reason context menu emit a close before the option that user have choosen o_O
Timer delaytimer = new Timer();
delaytimer.Tick += (obj, e) =>
{
delaytimer.Stop();
selectedLinedef = null;
General.Interface.RedrawDisplay();
delaytimer.Dispose();
};
delaytimer.Interval = 20;
delaytimer.Start();
}
}
public void ChangeVSlopeFlags(Thing thing, int flags)
{
// SetFlagsValue doesn't set flags properly if thing's flags was properly initialized
thing.SetFlag("1", (flags & 1) == 1);
thing.SetFlag("2", (flags & 2) == 2);
thing.SetFlag("4", (flags & 4) == 4);
thing.SetFlag("8", (flags & 8) == 8);
thing.SetFlagsValue(flags);
}
public bool LineCollision(float p1x, float p1y, float p2x, float p2y, float px, float py, float epsilon)
{
float l1 = new Line2D(px, py, p1x, p1y).GetLength();
float l2 = new Line2D(px, py, p2x, p2y).GetLength();
float ln = new Line2D(p1x, p1y, p2x, p2y).GetLength();
return l1 + l2 <= ln + epsilon;
}
// http://www.jeffreythompson.org/collision-detection/tri-point.php
public bool TriangleCollision(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y, float px, float py)
{
// get the area of the triangle
float areaOrig = Math.Abs((p2x - p1x) * (p3y - p1y) - (p3x - p1x) * (p2y - p1y));
// get the area of 3 triangles made between the point and the corners of the triangle
float area1 = Math.Abs((p1x - px) * (p2y - py) - (p2x - px) * (p1y - py));
float area2 = Math.Abs((p2x - px) * (p3y - py) - (p3x - px) * (p2y - py));
float area3 = Math.Abs((p3x - px) * (p1y - py) - (p1x - px) * (p3y - py));
// if the sum of the three areas equals the original, we're inside the triangle!
return (area1 + area2 + area3 == areaOrig);
}
private List<Vector2D> GetAffineThingsPosition(bool insideaffine, int vertices)
{
List<Vector2D> affined = new List<Vector2D>();
// If no need of inside affine just return the rounded points
if (!insideaffine || vertices != 3)
{
for (int i = 0; i < vertices; i++)
{
affined.Add(points[i].pos.GetRounded());
}
return affined;
}
Vector2D[] corners = new Vector2D[] {
points[0].pos.GetRounded(),
points[1].pos.GetRounded(),
points[2].pos.GetRounded()
};
// Move vertex slopes away from edges or corners.
for (int i = 0; i < 3; i++)
{
Vector2D corner = new Vector2D(corners[i].x, corners[i].y);
// Find best cell... very slow but should get best result
Vector2D best = new Vector2D(corner);
float bestlen = 999f;
Vector2D point = new Vector2D();
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
// Point to test
point = corner + new Vector2D(x - 128, y - 128);
if (TriangleCollision(
corners[0].x, corners[0].y,
corners[1].x, corners[1].y,
corners[2].x, corners[2].y,
point.x, point.y))
{
// Good, we are inside the triangle atleast, now avoid the edges...
if (!LineCollision(
corners[0].x, corners[0].y,
corners[1].x, corners[1].y,
point.x, point.y, 0.001f) &&
!LineCollision(
corners[1].x, corners[1].y,
corners[2].x, corners[2].y,
point.x, point.y, 0.001f) &&
!LineCollision(
corners[2].x, corners[2].y,
corners[0].x, corners[0].y,
point.x, point.y, 0.001f))
{
// Cool, we test if this one is better then...
float len = (point - corner).GetLength();
if (len < bestlen)
{
best.x = point.x;
best.y = point.y;
bestlen = len;
}
}
}
}
}
affined.Add(best);
}
return affined;
}
private void ApplyVSlopeChanges()
{
float[] heights = {
BuilderPlug.Me.VertexSlopeAssistForm.Vertex1.z,
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2.z,
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3.z
};
bool[] absZs = {
BuilderPlug.Me.VertexSlopeAssistForm.Vertex1AbsZ,
BuilderPlug.Me.VertexSlopeAssistForm.Vertex2AbsZ,
BuilderPlug.Me.VertexSlopeAssistForm.Vertex3AbsZ
};
CreateUndo();
if (mode == MODE_NEWTAGGEDTRIANGLE)
{
List<Vector2D> newthings = GetAffineThingsPosition(BuilderPlug.Me.VertexSlopeAssistForm.AffineVSlopes, expectedPoints);
for (int i = 0; i < 3; i++)
{
Thing tp = General.Map.Map.CreateThing();
if (tp != null)
{
General.Settings.ApplyDefaultThingSettings(tp);
tp.SRB2Type = 750;
tp.Parameter = 0;
tp.Rotate(BuilderPlug.Me.VertexSlopeAssistForm.TagID);
tp.Move(newthings[i].x, newthings[i].y, heights[i]);
if (absZs[i])
ChangeVSlopeFlags(tp, (int)heights[i]);
tp.Parameter = absZs[i] ? 1 : 0;
tp.UpdateConfiguration();
}
}
DrawnVertex dv = new DrawnVertex();
dv.stitch = true;
dv.stitchline = true;
dv.pos = points[0].pos;
points.Add(dv);
if (!Tools.DrawLines(points, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate))
{
General.Map.UndoRedo.WithdrawUndo();
}
else
{
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy();
// Clear selection
General.Map.Map.ClearAllSelected();
// Update cached values
General.Map.Map.Update();
// Edit new sectors?
List<Sector> newsectors = General.Map.Map.GetMarkedSectors(true);
if (BuilderPlug.Me.EditNewSector && (newsectors.Count > 0))
General.Interface.ShowEditSectors(newsectors);
// Update the used textures
General.Map.Data.UpdateUsedTextures();
//mxd
General.Map.Renderer2D.UpdateExtraFloorFlag();
}
}
if (mode == MODE_NEWEMPTYTRIANGLE)
{
DrawnVertex dv = new DrawnVertex();
dv.stitch = true;
dv.stitchline = true;
dv.pos = points[0].pos;
points.Add(dv);
if (!Tools.DrawLines(points, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate))
{
General.Map.UndoRedo.WithdrawUndo();
}
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy();
// Clear selection
General.Map.Map.ClearAllSelected();
// Update cached values
General.Map.Map.Update();
// Edit new sectors?
List<Sector> newsectors = General.Map.Map.GetMarkedSectors(true);
if (BuilderPlug.Me.EditNewSector && (newsectors.Count > 0))
General.Interface.ShowEditSectors(newsectors);
// Update the used textures
General.Map.Data.UpdateUsedTextures();
//mxd
General.Map.Renderer2D.UpdateExtraFloorFlag();
}
if (mode == MODE_NEWGROUP)
{
List<Vector2D> newthings = GetAffineThingsPosition(BuilderPlug.Me.VertexSlopeAssistForm.AffineVSlopes, expectedPoints);
for (int i = 0; i < expectedPoints; i++)
{
Thing tp = General.Map.Map.CreateThing();
if (tp != null)
{
General.Settings.ApplyDefaultThingSettings(tp);
tp.SRB2Type = 750;
tp.Parameter = 0;
tp.Rotate(BuilderPlug.Me.VertexSlopeAssistForm.TagID);
tp.Move(newthings[i].x, newthings[i].y, heights[i]);
if (absZs[i])
ChangeVSlopeFlags(tp, (int)heights[i]);
tp.Parameter = absZs[i] ? 1 : 0;
tp.UpdateConfiguration();
}
}
}
if (mode == MODE_MODGROUP)
{
List<Vector2D> newthings = GetAffineThingsPosition(BuilderPlug.Me.VertexSlopeAssistForm.AffineVSlopes, expectedPoints);
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
for (int i = 0; i < expectedPoints; i++)
{
if (things != null && i < things.Count)
{
things[i].Move(newthings[i]);
}
else
{
// Ops! Missing vertex slopes!
Thing tp = General.Map.Map.CreateThing();
if (tp != null)
{
General.Settings.ApplyDefaultThingSettings(tp);
tp.SRB2Type = 750;
tp.Parameter = 0;
tp.Rotate(BuilderPlug.Me.VertexSlopeAssistForm.TagID);
tp.Move(newthings[i].x, newthings[i].y, heights[i]);
if (absZs[i])
ChangeVSlopeFlags(tp, (int)heights[i]);
tp.Parameter = absZs[i] ? 1 : 0;
tp.UpdateConfiguration();
}
}
}
// Dispose extra vertex slopes
for (int i = things.Count - 1; i >= expectedPoints; i--)
{
things[i].Dispose();
}
}
General.Map.ThingsFilter.Update();
General.Map.Map.Update();
General.Map.IsChanged = true;
SetMode(MODE_SELECT);
BuilderPlug.Me.VertexSlopeAssistForm.CreateList();
BuilderPlug.Me.VertexSlopeAssistForm.UpdateForm();
}
#endregion
#region ================== Events
public override void OnHelp()
{
General.ShowHelp("e_vertexslopeassist.html");
}
// Cancelled
public override void OnCancel()
{
// Cancel base class
base.OnCancel();
if (createdUndo)
{
General.Map.UndoRedo.WithdrawUndo();
}
// Return to base mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// Mode engages
public override void OnEngage()
{
base.OnEngage();
renderer.SetPresentation(Presentation.Standard);
createdUndo = false;
BuilderPlug.Me.VertexSlopeAssistForm.OnDrawNewTaggedTriangle += CB_DrawNewTaggedTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnDrawNewEmptyTriangle += CB_DrawNewEmptyTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupChanged += CB_TagGroup_Changed;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupChoosen += CB_TagGroup_Choosen;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupNewTriangle += CB_TagGroup_NewTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupNewVertex += CB_TagGroup_NewVertex;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupModify += CB_TagGroup_Modify;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupModifyAsTriangle += CB_TagGroup_ModifyAsTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupModifyAsVertex += CB_TagGroup_ModifyAsVertex;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupTag += CB_TagGroup_Tag;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupDelete += CB_TagGroup_Delete;
BuilderPlug.Me.VertexSlopeAssistForm.OnValueChanged += CB_ValueChanged;
BuilderPlug.Me.VertexSlopeAssistForm.OnRotateAll += CB_Rotate_All_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnFlipAll += CB_Flip_All_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnRotateHeight += CB_Rotate_Height_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnFlipHeight += CB_Flip_Height_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnContextSet += CB_Context_Set;
selectedLinedef = null;
// Show toolbox window
BuilderPlug.Me.VertexSlopeAssistForm.Show((Form)General.Interface);
}
// Disenagaging
public override void OnDisengage()
{
base.OnDisengage();
BuilderPlug.Me.VertexSlopeAssistForm.OnDrawNewTaggedTriangle -= CB_DrawNewTaggedTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnDrawNewEmptyTriangle -= CB_DrawNewEmptyTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupChanged -= CB_TagGroup_Changed;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupChoosen -= CB_TagGroup_Choosen;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupNewTriangle -= CB_TagGroup_NewTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupNewVertex -= CB_TagGroup_NewVertex;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupModify -= CB_TagGroup_Modify;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupModifyAsTriangle -= CB_TagGroup_ModifyAsTriangle;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupModifyAsVertex -= CB_TagGroup_ModifyAsVertex;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupTag -= CB_TagGroup_Tag;
BuilderPlug.Me.VertexSlopeAssistForm.OnGroupDelete -= CB_TagGroup_Delete;
BuilderPlug.Me.VertexSlopeAssistForm.OnValueChanged -= CB_ValueChanged;
BuilderPlug.Me.VertexSlopeAssistForm.OnRotateAll -= CB_Rotate_All_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnFlipAll -= CB_Flip_All_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnRotateHeight -= CB_Rotate_Height_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnFlipHeight -= CB_Flip_Height_VSlopes;
BuilderPlug.Me.VertexSlopeAssistForm.OnContextSet -= CB_Context_Set;
// Hide toolbox window
BuilderPlug.Me.VertexSlopeAssistForm.Hide();
}
// This applies the curves and returns to the base mode
public override void OnAccept()
{
// Return to base mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// Redrawing display
public override void OnRedrawDisplay()
{
renderer.RedrawSurface();
float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale;
snaptocardinaldirection = General.Interface.ShiftState && General.Interface.AltState; //mxd
snaptogrid = (snaptocardinaldirection || General.Interface.ShiftState ^ General.Interface.SnapToGrid);
snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge;
DrawnVertex curp = DrawGeometryMode.GetCurrentPosition(mousemappos, snaptonearest, snaptogrid, snaptocardinaldirection, false, renderer, points);
if (curp.pos.IsFinite()) curmousepos = curp.pos;
// Get selected tag things
List<Thing> things = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList();
bool tagvalid = (things != null && things.Count == 3);
PixelColor color = tagvalid ? General.Colors.Highlight : General.Colors.Selection;
// Render lines
if(renderer.StartPlotter(true))
{
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
renderer.PlotVerticesSet(General.Map.Map.Vertices);
renderer.Finish();
}
// Render things
if(renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.Map.Things, Presentation.THINGS_ALPHA);
renderer.RenderNiGHTSPath();
renderer.Finish();
}
// Render overlay
if(renderer.StartOverlay(true))
{
if (mode == MODE_SELECT && things != null)
{
if (things.Count >= 2)
{
for (int i = 1; i < things.Count; i++)
renderer.RenderLine(things[i - 1].Position, things[i].Position, LINE_THICKNESS, color, true);
renderer.RenderLine(things[things.Count - 1].Position, things[0].Position, LINE_THICKNESS, color, true);
if (tagvalid)
{
for (int i = 0; i < 3; i++)
{
Thing t = things[i];
TextLabel tl = vertexnames[i];
tl.Location = things[i].Position;
tl.Text = (i + 1).ToString();
renderer.RenderRectangleFilled(new RectangleF(t.Position.x - vsize, t.Position.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
renderer.RenderText(tl);
}
}
}
else if (things.Count == 1)
{
Thing t = things[0];
TextLabel tl = vertexnames[0];
tl.Location = things[0].Position;
tl.Text = "1";
renderer.RenderRectangleFilled(new RectangleF(t.Position.x - vsize, t.Position.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
renderer.RenderText(tl);
}
}
if (mode == MODE_ASSIGNGROUPS)
{
for (int i = 1; i < points.Count; i++)
renderer.RenderLine(points[i - 1].pos, points[i].pos, LINE_THICKNESS, color, true);
if (points.Count > 0) {
if (things != null && things.Count == 1)
{
renderer.RenderLine(points[points.Count - 1].pos, things[0].Position, LINE_THICKNESS, color, true);
renderer.RenderLine(things[0].Position, points[0].pos, LINE_THICKNESS, color, true);
renderer.RenderRectangleFilled(new RectangleF(things[0].Position.x - vsize, things[0].Position.y - vsize, vsize * 4.0f, vsize * 4.0f), color, true);
}
}
for (int i = 0; i < points.Count; i++)
{
if (i >= 3) break;
Vector2D v = points[i].pos;
TextLabel tl = vertexnames[i];
tl.Location = points[i].pos;
tl.Text = (i + 1).ToString();
renderer.RenderRectangleFilled(new RectangleF(v.x - vsize, v.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
renderer.RenderText(tl);
}
}
if (mode >= MODE_NEWPOINTS)
{
for (int i = 1; i < points.Count; i++)
renderer.RenderLine(points[i - 1].pos, points[i].pos, LINE_THICKNESS, color, true);
if (points.Count > 0)
{
renderer.RenderLine(points[points.Count - 1].pos, curmousepos, LINE_THICKNESS, color, true);
renderer.RenderLine(curmousepos, points[0].pos, LINE_THICKNESS, color, true);
}
for (int i = 0; i < points.Count; i++)
{
if (i >= 3) break;
Vector2D v = points[i].pos;
TextLabel tl = vertexnames[i];
tl.Location = points[i].pos;
tl.Text = (i + 1).ToString();
renderer.RenderRectangleFilled(new RectangleF(v.x - vsize, v.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
renderer.RenderText(tl);
}
renderer.RenderRectangleFilled(new RectangleF(curmousepos.x - vsize, curmousepos.y - vsize, vsize * 2.0f, vsize * 2.0f), General.Colors.InfoLine, true);
}
else
{
foreach (int tag in overgroups)
{
PixelColor overcolor = General.Colors.Indication.WithAlpha(128);
List<Thing> overthings = BuilderPlug.Me.VertexSlopeAssistForm.GetThingsList(tag);
if (overthings.Count >= 2)
{
for (int i = 1; i < overthings.Count; i++)
renderer.RenderLine(overthings[i - 1].Position, overthings[i].Position, OVER_THICKNESS, overcolor, true);
renderer.RenderLine(overthings[overthings.Count - 1].Position, overthings[0].Position, OVER_THICKNESS, overcolor, true);
}
else
{
renderer.RenderThing(overthings[0], overcolor, Presentation.THINGS_ALPHA);
renderer.RenderRectangleFilled(new RectangleF(overthings[0].Position.x - vsize, overthings[0].Position.y - vsize, vsize * 2.0f, vsize * 2.0f), overcolor, true);
}
}
}
if (selectedLinedef != null)
renderer.RenderLine(selectedLinedef.Start.Position, selectedLinedef.End.Position, LINE_THICKNESS, General.Colors.Linedefs, true);
if (indexedLinedef != null)
renderer.RenderLine(indexedLinedef.Start.Position, indexedLinedef.End.Position, LINE_THICKNESS, General.Colors.Linedefs, true);
renderer.Finish();
}
renderer.Present();
}
#endregion
#region ================== Events (Mouse & Keyboard)
// Mouse moving
public override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (panning) return; //mxd. Skip all this jazz while panning
if (mode < MODE_NEWPOINTS)
{
overgroups.Clear();
int newOverTagID = -1;
if (BuilderPlug.Me.VertexSlopeAssistForm.GetGroupsAt(mousemappos, ref overgroups, true, true) > 0)
{
newOverTagID = overgroups[overgroupoffset % overgroups.Count];
}
if (newOverTagID != overTagID)
{
overTagID = newOverTagID;
UpdateModeText();
}
}
General.Interface.RedrawDisplay();
}
// Mouse press
public override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (panning) return;
if (mode == MODE_SELECT)
{
if (e.Button == MouseButtons.Left)
{
overgroups.Clear();
if (BuilderPlug.Me.VertexSlopeAssistForm.GetGroupsAt(mousemappos, ref overgroups, true, true) > 0)
{
BuilderPlug.Me.VertexSlopeAssistForm.TagID = overgroups[overgroupoffset % overgroups.Count];
overgroupoffset++;
}
}
if (e.Button == MouseButtons.Right)
{
int vertices = BuilderPlug.Me.VertexSlopeAssistForm.VerticesInTag();
Linedef l = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.HighlightRange / renderer.Scale);
selectedLinedef = l;
if (l != null)
BuilderPlug.Me.VertexSlopeAssistForm.OpenContextLinedefMenu(Cursor.Position.X, Cursor.Position.Y, vertices == 1 || vertices == 3);
else
BuilderPlug.Me.VertexSlopeAssistForm.OpenContextGroupMenu(Cursor.Position.X, Cursor.Position.Y, vertices > 0);
General.Interface.RedrawDisplay();
}
UpdateModeText();
}
else if (mode == MODE_ASSIGNGROUPS)
{
if (e.Button == MouseButtons.Left)
{
overgroups.Clear();
if (BuilderPlug.Me.VertexSlopeAssistForm.GetGroupsAt(mousemappos, ref overgroups, true, false) > 0)
{
BuilderPlug.Me.VertexSlopeAssistForm.TagID = overgroups[overgroupoffset % overgroups.Count];
overgroupoffset++;
}
}
if (e.Button == MouseButtons.Right)
{
SetMode(MODE_SELECT);
}
}
else
{
if (e.Button == MouseButtons.Left)
{
DrawnVertex dv = new DrawnVertex();
dv.stitch = true;
dv.stitchline = true;
dv.pos = curmousepos;
points.Add(dv);
if (points.Count == expectedPoints) ApplyVSlopeChanges();
General.Interface.RedrawDisplay();
UpdateModeText();
}
if (e.Button == MouseButtons.Right)
{
SetMode(MODE_SELECT);
}
}
}
// 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)) ||
(snaptocardinaldirection != (General.Interface.AltState && General.Interface.ShiftState))) General.Interface.RedrawDisplay();
}
// 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)) ||
(snaptocardinaldirection != (General.Interface.AltState && General.Interface.ShiftState))) General.Interface.RedrawDisplay();
}
#endregion
#region ================== Actions
// Remove a point
[BeginAction("removepoint")]
public void RemovePoint()
{
if (points.Count > 0) points.RemoveAt(points.Count - 1);
if (indexedTags.Count > 0) indexedTags.RemoveAt(indexedTags.Count - 1);
UpdateModeText();
General.Interface.RedrawDisplay();
}
// Increase tag number
[BeginAction("increasebevel")]
public void IncreaseBevel()
{
if (BuilderPlug.Me.VertexSlopeAssistForm.TagID < General.Map.FormatInterface.MaxTag)
BuilderPlug.Me.VertexSlopeAssistForm.TagID++;
}
[BeginAction("increasesubdivlevel")]
public void IncreaseSubdivLevel()
{
IncreaseBevel();
}
// Decrease tag number
[BeginAction("decreasebevel")]
public void DecreaseBevel()
{
if (BuilderPlug.Me.VertexSlopeAssistForm.TagID > 0)
BuilderPlug.Me.VertexSlopeAssistForm.TagID--;
}
[BeginAction("decreasesubdivlevel")]
public void DecreaseSubdivLevel()
{
DecreaseBevel();
}
#endregion
}
}