Newer
Older
#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.Generic;
using System.Windows.Forms;
using CodeImp.DoomBuilder.BuilderModes.Interface;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.Data;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
[EditMode(DisplayName = "GZDB Visual Mode",
SwitchAction = "gzdbvisualmode", // Action name used to switch to this mode
ButtonImage = "VisualModeGZ.png", // Image resource name for the button
ButtonOrder = 1, // Position of the button (lower is more to the left)
ButtonGroup = "001_visual",
UseByDefault = true)]
public class BaseVisualMode : VisualMode
{
#region ================== Constants
// Object picking
private const long PICK_INTERVAL = 80;
private const long PICK_INTERVAL_PAINT_SELECT = 10; // biwa
private const float PICK_RANGE = 0.98f;
// Gravity
private const float GRAVITY = -0.06f;
#endregion
#region ================== Variables
// Gravity
private Vector3D gravity;
biwa
committed
private double cameraflooroffset = 41.0; // same as in doom
private double cameraceilingoffset = 10.0;
// Object picking
private VisualPickResult target;
private long lastpicktime;
private readonly Timer selectioninfoupdatetimer; //mxd
// This keeps extra element info
private Dictionary<Sector, SectorData> sectordata;
private Dictionary<Thing, ThingData> thingdata;
private Dictionary<Vertex, VertexData> vertexdata; //mxd
//private Dictionary<Thing, EffectDynamicLight> lightdata; //mxd
// This is true when a selection was made because the action is performed
// on an object that was not selected. In this case the previous selection
// is cleared and the targeted object is temporarely selected to perform
// the action on. After the action is completed, the object is deselected.
// We keep these to determine if we need to make a new undo level
private bool selectionchanged;
private int lastundogroup;
private VisualActionResult actionresult;
private bool undocreated;
// List of selected objects when an action is performed
private List<IVisualEventReceiver> selectedobjects;
//mxd. Used in Cut/PasteSelection actions
private readonly List<ThingCopyData> copybuffer;
private Type lasthighlighttype;
// biwa. Info for paint selection
protected bool paintselectpressed;
protected Type paintselecttype = null;
protected IVisualPickable highlighted; // biwa
//mxd. Moved here from Tools
private struct SidedefAlignJob
{
public Sidedef sidedef;
public double offsetx;
public double scaleX; //mxd
public double scaleY; //mxd
private Sidedef controlside; //mxd
public Sidedef controlSide
{
get
{
return controlside;
}
set
{
controlside = value;
ceilingheight = (controlside.Index != sidedef.Index && controlside.Line.Args[1] == 0 ? controlside.Sector.FloorHeight : controlside.Sector.CeilHeight);
}
}
private int ceilingheight; //mxd
public int ceilingHeight { get { return ceilingheight; } } //mxd
// When this is true, the previous sidedef was on the left of
// this one and the texture X offset of this sidedef can be set
// directly. When this is false, the length of this sidedef
// must be subtracted from the X offset first.
public bool forward;
}
#endregion
#region ================== Properties
public override object HighlightedObject
{
get
{
// Geometry picked?
VisualGeometry vg = target.picked as VisualGeometry;
if(vg != null)
if(vg.Sidedef != null) return vg.Sidedef;
if(vg.Sector != null) return vg.Sector;
}
// Thing picked?
VisualThing vt = target.picked as VisualThing;
if(vt != null) return vt.Thing;
public object HighlightedTarget { get { return target.picked; } } //mxd
public bool UseSelectionFromClassicMode { get { return useSelectionFromClassicMode; } } //mxd
new public IRenderer3D Renderer { get { return renderer; } }
public bool IsSingleSelection { get { return singleselection; } }
public bool SelectionChanged { get { return selectionchanged; } set { selectionchanged |= value; } }
public bool PaintSelectPressed { get { return paintselectpressed; } } // biwa
public Type PaintSelectType { get { return paintselecttype; } set { paintselecttype = value; } } // biwa
public IVisualPickable Highlighted { get { return highlighted; } } // biwa
#endregion
#region ================== Constructor / Disposer
// Constructor
public BaseVisualMode()
{
// Initialize
this.gravity = new Vector3D(0.0f, 0.0f, 0.0f);
codeimp
committed
this.selectedobjects = new List<IVisualEventReceiver>();
this.copybuffer = new List<ThingCopyData>();
this.selectioninfoupdatetimer = new Timer();
selectioninfoupdatetimer.Interval = 100;
selectioninfoupdatetimer.Tick += SelectioninfoupdatetimerOnTick;
// We have no destructor
GC.SuppressFinalize(this);
}
// Disposer
public override void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Clean up
selectioninfoupdatetimer.Dispose(); //mxd
// Done
base.Dispose();
}
}
#endregion
#region ================== Methods
codeimp
committed
// This calculates brightness level
codeimp
committed
internal int CalculateBrightness(int level)
codeimp
committed
{
codeimp
committed
return renderer.CalculateBrightness(level);
codeimp
committed
}
MaxED
committed
//mxd. This calculates brightness level with doom-style shading
internal int CalculateBrightness(int level, Sidedef sd)
{
return renderer.CalculateBrightness(level, sd);
}
codeimp
committed
codeimp
committed
// This adds a selected object
internal void AddSelectedObject(IVisualEventReceiver obj)
{
selectedobjects.Add(obj);
selectionchanged = true;
selectioninfoupdatetimer.Start(); //mxd
codeimp
committed
}
// This removes a selected object
internal void RemoveSelectedObject(IVisualEventReceiver obj)
{
selectedobjects.Remove(obj);
selectionchanged = true;
selectioninfoupdatetimer.Start(); //mxd
codeimp
committed
}
// This is called before an action is performed
public void PreAction(int multiselectionundogroup)
actionresult = new VisualActionResult();
PickTargetUnlocked();
// If the action is not performed on a selected object, clear the
// current selection and make a temporary selection for the target.
if ((target.picked != null) && !target.picked.Selected && (BuilderPlug.Me.VisualModeClearSelection || (selectedobjects.Count == 0)))
// Single object, no selection
singleselection = true;
// Only clear the selection if anything is selected, since it can be very time consuming on huge maps
if(BuilderPlug.Me.VisualModeClearSelection && selectedobjects.Count > 0)
ClearSelection();
undocreated = false;
singleselection = false;
// Check if we should make a new undo level
// We don't want to do this if this is the same action with the same
// selection and the action wants to group the undo levels
if((lastundogroup != multiselectionundogroup) || (lastundogroup == UndoGroup.None) ||
(multiselectionundogroup == UndoGroup.None) || selectionchanged)
{
// We want to create a new undo level, but not just yet
lastundogroup = multiselectionundogroup;
undocreated = false;
}
else
// We don't want to make a new undo level (changes will be combined)
undocreated = true;
}
codeimp
committed
// Called before an action is performed. This does not make an undo level
private void PreActionNoChange()
{
actionresult = new VisualActionResult();
singleselection = false;
undocreated = false;
// This is called after an action is performed
if(!string.IsNullOrEmpty(actionresult.displaystatus))
General.Interface.DisplayStatus(StatusType.Action, actionresult.displaystatus);
ZZYZX
committed
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
ZZYZX
committed
BaseVisualSector bvs = (BaseVisualSector)vs.Value;
foreach(VisualFloor vf in bvs.ExtraFloors) vf.Changed = false;
foreach(VisualCeiling vc in bvs.ExtraCeilings) vc.Changed = false;
foreach(VisualFloor vf in bvs.ExtraBackFloors) vf.Changed = false; //mxd
foreach(VisualCeiling vc in bvs.ExtraBackCeilings) vc.Changed = false; //mxd
bvs.Floor.Changed = false;
bvs.Ceiling.Changed = false;
}
// Only clear the selection if anything is selected, since it can be very time consuming on huge maps
if (singleselection && selectedobjects.Count > 0) ClearSelection();
UpdateChangedObjects();
ShowTargetInfo();
}
// This sets the result for an action
public void SetActionResult(VisualActionResult result)
{
actionresult = result;
}
// This sets the result for an action
public void SetActionResult(string displaystatus)
{
actionresult = new VisualActionResult {displaystatus = displaystatus};
}
// This creates an undo, when only a single selection is made
// When a multi-selection is made, the undo is created by the PreAction function
public int CreateUndo(string description, int group, int grouptag)
if(!undocreated)
{
undocreated = true;
if(singleselection)
return General.Map.UndoRedo.CreateUndo(description, this, group, grouptag);
return General.Map.UndoRedo.CreateUndo(description, this, UndoGroup.None, 0);
}
// This creates an undo, when only a single selection is made
// When a multi-selection is made, the undo is created by the PreAction function
public int CreateUndo(string description)
return CreateUndo(description, UndoGroup.None, 0);
}
// This makes a list of the selected object
codeimp
committed
private void RebuildSelectedObjectsList()
{
// Make list of selected objects
selectedobjects = new List<IVisualEventReceiver>();
ZZYZX
committed
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
ZZYZX
committed
if(vs.Value != null)
ZZYZX
committed
BaseVisualSector bvs = (BaseVisualSector)vs.Value;
if((bvs.Floor != null) && bvs.Floor.Selected) selectedobjects.Add(bvs.Floor);
if((bvs.Ceiling != null) && bvs.Ceiling.Selected) selectedobjects.Add(bvs.Ceiling);
// Also check extra floors
if (bvs.ExtraFloors.Count > 0)
foreach (VisualFloor vf in bvs.ExtraFloors)
if (vf.Selected) selectedobjects.Add(vf);
if (bvs.ExtraBackFloors.Count > 0)
foreach (VisualFloor vf in bvs.ExtraBackFloors)
if (vf.Selected) selectedobjects.Add(vf);
// Also check extra ceilings
if (bvs.ExtraCeilings.Count > 0)
foreach (VisualCeiling vc in bvs.ExtraCeilings)
if (vc.Selected) selectedobjects.Add(vc);
if (bvs.ExtraBackCeilings.Count > 0)
foreach (VisualCeiling vc in bvs.ExtraBackCeilings)
if (vc.Selected) selectedobjects.Add(vc);
foreach (Sidedef sd in vs.Key.Sidedefs)
ZZYZX
committed
List<VisualGeometry> sidedefgeos = bvs.GetSidedefGeometry(sd);
foreach(VisualGeometry sdg in sidedefgeos)
{
if(sdg.Selected) selectedobjects.Add((IVisualEventReceiver)sdg);
}
}
}
}
ZZYZX
committed
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
ZZYZX
committed
if(vt.Value != null)
{
BaseVisualThing bvt = (BaseVisualThing)vt.Value;
if(bvt.Selected) selectedobjects.Add(bvt);
}
if(General.Map.UDMF && General.Settings.GZShowVisualVertices)
{
foreach(KeyValuePair<Vertex, VisualVertexPair> pair in vertices)
{
if(pair.Value.CeilingVertex.Selected)
selectedobjects.Add((BaseVisualVertex)pair.Value.CeilingVertex);
if(pair.Value.FloorVertex.Selected)
selectedobjects.Add((BaseVisualVertex)pair.Value.FloorVertex);
if (General.Map.UDMF)
{
foreach (KeyValuePair<Sector, List<VisualSlope>> kvp in allslopehandles)
{
foreach (VisualSlope handle in kvp.Value)
if (handle.Selected) selectedobjects.Add((VisualSidedefSlope)handle);
}
}
//mxd
UpdateSelectionInfo();
//mxd. Need this to apply changes to 3d-floor even if control sector doesn't exist as BaseVisualSector
internal BaseVisualSector CreateBaseVisualSector(Sector s)
{
BaseVisualSector vs = new BaseVisualSector(this, s);
ZZYZX
committed
allsectors.Add(s, vs);
return vs;
}
// This creates a visual sector
protected override VisualSector CreateVisualSector(Sector s)
{
BaseVisualSector vs = new BaseVisualSector(this, s);
ZZYZX
committed
allsectors.Add(s, vs); //mxd
internal VisualSlope CreateVisualSlopeHandle(SectorLevel level, Sidedef sd, bool up)
{
VisualSidedefSlope handle = new VisualSidedefSlope(this, level, sd, up);
if (!allslopehandles.ContainsKey(sd.Sector))
allslopehandles.Add(sd.Sector, new List<VisualSlope>());
allslopehandles[sd.Sector].Add(handle);
return handle;
}
// This creates a visual thing
protected override VisualThing CreateVisualThing(Thing t)
{
BaseVisualThing vt = new BaseVisualThing(this, t);
return vt.Setup() ? vt : null;
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
// This locks the target so that it isn't changed until unlocked
public void LockTarget()
{
locktarget = true;
}
// This unlocks the target so that is changes to the aimed geometry again
public void UnlockTarget()
{
locktarget = false;
}
// This picks a new target, if not locked
private void PickTargetUnlocked()
{
if(!locktarget) PickTarget();
}
// This picks a new target
private void PickTarget()
{
// Find the object we are aiming at
Vector3D start = General.Map.VisualCamera.Position;
Vector3D delta = General.Map.VisualCamera.Target - General.Map.VisualCamera.Position;
delta = delta.GetFixedLength(General.Settings.ViewDistance * PICK_RANGE);
VisualPickResult newtarget = PickObject(start, start + delta);
// Should we update the info on panels?
bool updateinfo = (newtarget.picked != target.picked);
if (updateinfo)
{
if (newtarget.picked is VisualSidedefSlope)
{
// Get the smart pivot handle for the targeted slope handle, so that it can be drawn
VisualSidedefSlope handle = VisualSidedefSlope.GetSmartPivotHandle((VisualSidedefSlope)newtarget.picked, this);
if (handle != null)
handle.SmartPivot = true;
}
else if(target.picked is VisualSidedefSlope)
{
// Clear smart pivot handles, otherwise it will keep being displayed
foreach (KeyValuePair<Sector, List<VisualSlope>> kvp in allslopehandles)
foreach (VisualSidedefSlope checkhandle in kvp.Value)
checkhandle.SmartPivot = false;
}
}
// Apply new target
target = newtarget;
// Show target info
if (updateinfo)
ShowTargetInfo();
}
// This shows the picked target information
public void ShowTargetInfo()
{
// Any result?
if(target.picked != null)
{
// Geometry picked?
if(target.picked is VisualGeometry)
{
VisualGeometry pickedgeo = (VisualGeometry)target.picked;
// Sidedef?
if(pickedgeo is BaseVisualGeometrySidedef)
{
BaseVisualGeometrySidedef pickedsidedef = (BaseVisualGeometrySidedef)pickedgeo;
General.Interface.ShowLinedefInfo(pickedsidedef.GetControlLinedef(), pickedsidedef.Sidedef); //mxd
}
// Sector?
else if(pickedgeo is BaseVisualGeometrySector)
{
BaseVisualGeometrySector pickedsector = (BaseVisualGeometrySector)pickedgeo;
bool isceiling = (pickedsector is VisualCeiling); //mxd
General.Interface.ShowSectorInfo(pickedsector.Level.sector, isceiling, !isceiling);
General.Interface.HideInfo();
// Thing picked?
{
VisualThing pickedthing = (VisualThing)target.picked;
General.Interface.ShowThingInfo(pickedthing.Thing);
//mxd. Vertex picked?
else if(target.picked is VisualVertex)
VisualVertex pickedvert = (VisualVertex)target.picked;
}
}
else
{
General.Interface.HideInfo();
}
}
// This updates the VisualSectors and VisualThings that have their Changed property set
private void UpdateChangedObjects()
ZZYZX
committed
foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
ZZYZX
committed
if(vs.Value != null)
{
BaseVisualSector bvs = (BaseVisualSector)vs.Value;
if(bvs.Changed)
{
bvs.Rebuild();
// Also update slope handles
if (allslopehandles.ContainsKey(vs.Key))
foreach (VisualSidedefSlope handle in allslopehandles[vs.Key])
ZZYZX
committed
}
ZZYZX
committed
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
ZZYZX
committed
if(vt.Value != null)
{
BaseVisualThing bvt = (BaseVisualThing)vt.Value;
if(bvt.Changed) bvt.Rebuild();
}
if(General.Map.UDMF)
{
foreach(KeyValuePair<Vertex, VisualVertexPair> pair in vertices)
pair.Value.Update();
}
//mxd. Update event lines (still better than updating them on every frame redraw)
renderer.SetEventLines(LinksCollector.GetHelperShapes(General.Map.ThingsFilter.VisibleThings, blockmap));
MaxED
committed
protected override void MoveSelectedThings(Vector2D direction, bool absoluteposition)
MaxED
committed
List<VisualThing> visualthings = GetSelectedVisualThings(true);
if(visualthings.Count == 0) return;
PreAction(UndoGroup.ThingMove);
MaxED
committed
Vector3D[] coords = new Vector3D[visualthings.Count];
for(int i = 0; i < visualthings.Count; i++)
coords[i] = visualthings[i].Thing.Position;
//move things...
MaxED
committed
Vector3D[] translatedcoords = TranslateCoordinates(coords, direction, absoluteposition);
for(int i = 0; i < visualthings.Count; i++)
BaseVisualThing t = (BaseVisualThing)visualthings[i];
MaxED
committed
t.OnMove(translatedcoords[i]);
MaxED
committed
// Things may've changed sectors...
FillBlockMap();
PostAction();
}
MaxED
committed
private static Vector3D[] TranslateCoordinates(Vector3D[] coordinates, Vector2D direction, bool absolutePosition)
if(coordinates.Length == 0) return null;
direction.x = Math.Round(direction.x);
direction.y = Math.Round(direction.y);
Vector3D[] translatedCoords = new Vector3D[coordinates.Length];
//move things...
if(!absolutePosition) //...relatively (that's easy)
MaxED
committed
{
int camAngle = (int)Math.Round(Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY));
int sector = General.ClampAngle(camAngle - 45) / 90;
direction = direction.GetRotated(sector * Angle2D.PIHALF);
for(int i = 0; i < coordinates.Length; i++)
translatedCoords[i] = coordinates[i] + new Vector3D(direction);
return translatedCoords;
}
//...to specified location preserving relative positioning (that's harder)
if(coordinates.Length == 1) //just move it there
MaxED
committed
{
translatedCoords[0] = new Vector3D(direction.x, direction.y, coordinates[0].z);
return translatedCoords;
}
//we need some reference
double minX = coordinates[0].x;
double maxX = minX;
double minY = coordinates[0].y;
double maxY = minY;
//get bounding coordinates for selected things
for(int i = 1; i < coordinates.Length; i++)
MaxED
committed
{
if(coordinates[i].x < minX)
minX = coordinates[i].x;
else if(coordinates[i].x > maxX)
maxX = coordinates[i].x;
if(coordinates[i].y < minY)
minY = coordinates[i].y;
else if(coordinates[i].y > maxY)
maxY = coordinates[i].y;
}
Vector2D selectionCenter = new Vector2D(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2);
for(int i = 0; i < coordinates.Length; i++)
translatedCoords[i] = new Vector3D(Math.Round(direction.x - (selectionCenter.x - coordinates[i].x)), Math.Round(direction.y - (selectionCenter.y - coordinates[i].y)), Math.Round(coordinates[i].z));
return translatedCoords;
}
public override void UpdateSelectionInfo()
// Collect info
int numWalls = 0;
int numFloors = 0;
int numCeilings = 0;
int numThings = 0;
int numVerts = 0;
foreach(IVisualEventReceiver obj in selectedobjects)
{
if(!obj.Selected) continue;
if(obj is BaseVisualThing) numThings++;
else if(obj is BaseVisualVertex) numVerts++;
else if(obj is VisualCeiling) numCeilings++;
else if(obj is VisualFloor) numFloors++;
else if(obj is VisualMiddleSingle || obj is VisualMiddleDouble || obj is VisualLower || obj is VisualUpper || obj is VisualMiddle3D || obj is VisualMiddleBack)
numWalls++;
}
List<string> results = new List<string>();
if(numWalls > 0) results.Add(numWalls + (numWalls > 1 ? " sidedefs" : " sidedef"));
if(numFloors > 0) results.Add(numFloors + (numFloors > 1 ? " floors" : " floor"));
if(numCeilings > 0) results.Add(numCeilings + (numCeilings > 1 ? " ceilings" : " ceiling"));
if(numThings > 0) results.Add(numThings + (numThings > 1 ? " things" : " thing"));
if(numVerts > 0) results.Add(numVerts + (numVerts > 1 ? " vertices" : " vertex"));
// Display results
string result = string.Empty;
if(results.Count > 0)
result = string.Join(", ", results.ToArray());
int pos = result.LastIndexOf(",", StringComparison.Ordinal);
if(pos != -1) result = result.Remove(pos, 1).Insert(pos, " and");
result += " selected.";
General.Interface.DisplayStatus(StatusType.Selection, result);
MaxED
committed
//mxd
MaxED
committed
internal void StartRealtimeInterfaceUpdate(SelectionType selectiontype)
{
switch(selectiontype)
MaxED
committed
{
case SelectionType.All:
case SelectionType.Linedefs:
case SelectionType.Sectors:
General.Interface.OnEditFormValuesChanged += Interface_OnSectorEditFormValuesChanged;
break;
case SelectionType.Things:
General.Interface.OnEditFormValuesChanged += Interface_OnThingEditFormValuesChanged;
break;
default:
General.Interface.OnEditFormValuesChanged += Interface_OnEditFormValuesChanged;
break;
MaxED
committed
}
}
//mxd
MaxED
committed
internal void StopRealtimeInterfaceUpdate(SelectionType selectiontype)
{
switch(selectiontype)
MaxED
committed
{
case SelectionType.All:
case SelectionType.Linedefs:
case SelectionType.Sectors:
General.Interface.OnEditFormValuesChanged -= Interface_OnSectorEditFormValuesChanged;
break;
case SelectionType.Things:
General.Interface.OnEditFormValuesChanged -= Interface_OnThingEditFormValuesChanged;
break;
default:
General.Interface.OnEditFormValuesChanged -= Interface_OnEditFormValuesChanged;
break;
MaxED
committed
}
}
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
private List<VisualSidedefSlope> GetSlopeHandlePair()
{
List<VisualSidedefSlope> handles = GetSelectedSlopeHandles();
// No handles selected, try to slope between highlighted handle and it smart pivot
if (handles.Count == 0 && HighlightedTarget is VisualSidedefSlope)
{
VisualSidedefSlope handle = VisualSidedefSlope.GetSmartPivotHandle((VisualSidedefSlope)HighlightedTarget, this);
if (handle == null)
{
General.Interface.DisplayStatus(StatusType.Warning, "Couldn't find a smart pivot handle.");
return handles;
}
handles.Add((VisualSidedefSlope)HighlightedTarget);
handles.Add(handle);
}
// One handle selected, try to slope between it and the highlighted handle or the selected one's smart pivot
else if (handles.Count == 1)
{
if (HighlightedTarget == handles[0] || !(HighlightedTarget is VisualSidedefSlope))
{
VisualSidedefSlope handle;
if (HighlightedTarget is VisualSidedefSlope)
handle = VisualSidedefSlope.GetSmartPivotHandle((VisualSidedefSlope)HighlightedTarget, this);
else
handle = VisualSidedefSlope.GetSmartPivotHandle(handles[0], this);
if (handle == null)
{
General.Interface.DisplayStatus(StatusType.Warning, "Couldn't find a smart pivot handle.");
return handles;
}
handles.Add(handle);
}
else
{
handles.Add((VisualSidedefSlope)HighlightedTarget);
}
}
// Return if more than two handles are selected
else if (handles.Count > 2)
{
General.Interface.DisplayStatus(StatusType.Warning, "Too many slope handles selected.");
return handles;
}
// Everything else
else if (handles.Count != 2)
{
General.Interface.DisplayStatus(StatusType.Warning, "No slope handles selected or highlighted.");
return handles;
}
return handles;
}
#region ================== Extended Methods
// This requests a sector's extra data
internal SectorData GetSectorData(Sector s)
{
// Make fresh sector data when it doesn't exist yet
if(!sectordata.ContainsKey(s))
sectordata[s] = new SectorData(this, s);
return sectordata[s];
}
//mxd. This requests a sector's extra data or null if given sector doesn't have it
internal SectorData GetSectorDataEx(Sector s)
{
return (sectordata.ContainsKey(s) ? sectordata[s] : null);
}
// This requests a things's extra data
internal ThingData GetThingData(Thing t)
{
// Make fresh sector data when it doesn't exist yet
if(!thingdata.ContainsKey(t))
thingdata[t] = new ThingData(this, t);
return thingdata[t];
}
MaxED
committed
internal VertexData GetVertexData(Vertex v)
{
if(!vertexdata.ContainsKey(v))
vertexdata[v] = new VertexData(this, v);
return vertexdata[v];
}
MaxED
committed
internal BaseVisualVertex GetVisualVertex(Vertex v, bool floor)
{
if(!vertices.ContainsKey(v))
vertices.Add(v, new VisualVertexPair(new BaseVisualVertex(this, v, false), new BaseVisualVertex(this, v, true)));
return (floor ? (BaseVisualVertex)vertices[v].FloorVertex : (BaseVisualVertex)vertices[v].CeilingVertex);
}
MaxED
committed
internal void UpdateVertexHandle(Vertex v)
{
vertices.Add(v, new VisualVertexPair(new BaseVisualVertex(this, v, false), new BaseVisualVertex(this, v, true)));
MaxED
committed
vertices[v].Changed = true;
// This rebuilds the sector data
// This requires that the blockmap is up-to-date!
internal void RebuildElementData()
{
HashSet<Sector> effectsectors = null; //mxd
List<Linedef>[] slopelinedefpass = new List<Linedef>[] { new List<Linedef>(), new List<Linedef>() };
List<Thing>[] slopethingpass = new List<Thing>[] { new List<Thing>(), new List<Thing>() };
if (!General.Settings.EnhancedRenderingEffects) //mxd
MaxED
committed
{
// Store all sectors with effects
MaxED
committed
if(sectordata != null && sectordata.Count > 0)
effectsectors = new HashSet<Sector>(sectordata.Keys);
// Remove all vertex handles from selection
MaxED
committed
if(vertices != null && vertices.Count > 0)
{
for (int i = 0; i < selectedobjects.Count; i++)
{
if (selectedobjects[i] is BaseVisualVertex)
{
RemoveSelectedObject(selectedobjects[i]);
i--;
}
}
Dictionary<int, List<Sector>> sectortags = new Dictionary<int, List<Sector>>();
Dictionary<int, List<Linedef>> linetags = new Dictionary<int, List<Linedef>>();
sectordata = new Dictionary<Sector, SectorData>(General.Map.Map.Sectors.Count);
thingdata = new Dictionary<Thing, ThingData>(General.Map.Map.Things.Count);
//mxd. Rebuild all sectors with effects
if(effectsectors != null)
MaxED
committed
{
foreach(Sector s in effectsectors)
MaxED
committed
{
if(!VisualSectorExists(s)) continue;
// The visual sector associated is now outdated
BaseVisualSector vs = (BaseVisualSector)GetVisualSector(s);
vs.UpdateSectorGeometry(true);
}
}
MaxED
committed
if(General.Map.UDMF)
{
vertexdata = new Dictionary<Vertex, VertexData>(General.Map.Map.Vertices.Count); //mxd
vertices.Clear();
}
if(!General.Settings.EnhancedRenderingEffects) return; //mxd
// Find all sector who's tag is not 0 and hash them so that we can find them quickly
foreach(Sector s in General.Map.Map.Sectors)
{
if(tag == 0) continue;
if(!sectortags.ContainsKey(tag)) sectortags[tag] = new List<Sector>();
sectortags[tag].Add(s);
}
}
// Find interesting linedefs (such as line slopes)
// This also determines which slope lines belong to pass one and pass two. See https://zdoom.org/wiki/Slope
foreach (Linedef l in General.Map.Map.Linedefs)
// Builds a cache of linedef ids/tags. Used for slope things. Use linedef tags in UDMF
if(General.Map.UDMF)
{
foreach(int tag in l.Tags)
{
if (!linetags.ContainsKey(tag)) linetags[tag] = new List<Linedef>();
linetags[tag].Add(l);
}
}
//mxd. Rewritten to use action ID instead of number
if (l.Action == 0 || !General.Map.Config.LinedefActions.ContainsKey(l.Action)) continue;
switch(General.Map.Config.LinedefActions[l.Action].Id.ToLowerInvariant())
// ========== Line Set Identification (121) (see https://zdoom.org/wiki/Line_SetIdentification) ==========
// Builds a cache of linedef ids/tags. Used for slope things. Only used for Hexen format
case "line_setidentification":
int tag = l.Args[0] + l.Args[4] * 256;
if (!linetags.ContainsKey(tag)) linetags[tag] = new List<Linedef>();
linetags[tag].Add(l);
break;
// ========== Plane Align (181) (see http://zdoom.org/wiki/Plane_Align) ==========
case "plane_align":
slopelinedefpass[0].Add(l);
MaxED
committed
break;
// ========== Plane Copy (118) (mxd) (see http://zdoom.org/wiki/Plane_Copy) ==========
case "plane_copy":
slopelinedefpass[1].Add(l);
MaxED
committed
break;
// ========== Sector 3D floor (160) (see http://zdoom.org/wiki/Sector_Set3dFloor) ==========
case "sector_set3dfloor":
MaxED
committed
if(l.Front != null)
MaxED
committed
//mxd. Added hi-tag/line ID check
int sectortag = (General.Map.UDMF || (l.Args[1] & (int)Effect3DFloor.FloorTypes.HiTagIsLineID) != 0) ? l.Args[0] : l.Args[0] + (l.Args[4] << 8);