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 = "Visual Mode",
SwitchAction = "gzdbvisualmode", // Action name used to switch to this mode
ButtonImage = "VisualMode.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
// 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;
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.Map.Config.VertexHeightSupport && 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 (BaseVisualSlope handle in kvp.Value)
if (handle.Selected) selectedobjects.Add(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>());
biwa
committed
if (!sidedefslopehandles.ContainsKey(sd.Sector))
sidedefslopehandles.Add(sd.Sector, new List<VisualSlope>());
biwa
committed
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
sidedefslopehandles[sd.Sector].Add(handle);
return handle;
}
internal VisualSlope CreateVisualSlopeHandle(SectorLevel level, Vertex v, Sector s, bool up)
{
VisualVertexSlope handle = new VisualVertexSlope(this, level, v, s, up);
/*
if (!allslopehandles.ContainsKey(level.sector))
allslopehandles.Add(level.sector, new List<VisualSlope>());
if (!vertexslopehandles.ContainsKey(level.sector))
vertexslopehandles.Add(level.sector, new List<VisualSlope>());
allslopehandles[level.sector].Add(handle);
vertexslopehandles[level.sector].Add(handle);
*/
if (!allslopehandles.ContainsKey(s))
allslopehandles.Add(s, new List<VisualSlope>());
if (!vertexslopehandles.ContainsKey(s))
vertexslopehandles.Add(s, new List<VisualSlope>());
allslopehandles[s].Add(handle);
vertexslopehandles[s].Add(handle);
// This creates a visual thing
protected override VisualThing CreateVisualThing(Thing t)
{
BaseVisualThing vt = new BaseVisualThing(this, t);
return vt.Setup() ? vt : null;
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
// 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);
VisualSlope pickedhandle = null;
// Should we update the info on panels?
bool updateinfo = (newtarget.picked != target.picked);
// Operating on slope handles is potentially expensive, so only do it it absolutely necessary (i.e. when a new slope handle was selected)
if (updateinfo)
{
if (target.picked is VisualSlope) // Old target
{
// Remove all smart pivot handles from being processed. There should only be exactly one, but better save than sorry
List<VisualSlope> sph = new List<VisualSlope>();
foreach (VisualSlope vs in usedslopehandles)
{
if(vs.SmartPivot && !(vs.Selected || vs.Pivot))
sph.Add(vs);
vs.SmartPivot = false;
}
foreach (VisualSlope vs in sph)
usedslopehandles.Remove(vs);
// Don't render old slope handle anymore
if (!((VisualSlope)target.picked).Selected && !((VisualSlope)target.picked).Pivot)
usedslopehandles.Remove((VisualSlope)target.picked);
usedslopehandles.Add((VisualSlope)newtarget.picked);
pickedhandle = ((VisualSlope)newtarget.picked);
}
}
// Apply new target
target = newtarget;
// Get the smart pivot handle for the targeted slope handle, so that it can be drawn. We have to do it after the current
// target is set because otherwise it might get wrong results if the old target was a floor/ceiling
if (pickedhandle != null)
{
VisualSlope handle = pickedhandle.GetSmartPivotHandle();
if (handle != null)
{
handle.SmartPivot = true;
usedslopehandles.Add(handle);
}
}
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))
biwa
committed
foreach (VisualSlope 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 && General.Map.Config.VertexHeightSupport)
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
}
}
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);
VisualSidedefSlope handle = (VisualSidedefSlope)((VisualSidedefSlope)HighlightedTarget).GetSmartPivotHandle();
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)((VisualSidedefSlope)HighlightedTarget).GetSmartPivotHandle();
handle = (VisualSidedefSlope)(handles[0].GetSmartPivotHandle());
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
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<Thing>> thingtags = new Dictionary<int, List<Thing>>();
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)
{
foreach(int tag in s.Tags)
if(tag == 0) continue;
if(!sectortags.ContainsKey(tag)) sectortags[tag] = new List<Sector>();
sectortags[tag].Add(s);
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
// ========== Thing vertex slope, vertices with UDMF vertex offsets ==========
if (s.Sidedefs.Count == 3)
{
if (General.Map.UDMF) GetSectorData(s).AddEffectVertexOffset(); //mxd
List<Thing> slopeceilingthings = new List<Thing>(3);
List<Thing> slopefloorthings = new List<Thing>(3);
foreach (Sidedef sd in s.Sidedefs)
{
Vertex v = sd.IsFront ? sd.Line.End : sd.Line.Start;
// Check if a thing is at this vertex
foreach (VisualBlockEntry block in blockmap.GetBlocks(v.Position))
{
foreach (Thing t in block.Things)
{
if ((Vector2D)t.Position == v.Position)
{
switch (t.Type)
{
case 1504: slopefloorthings.Add(t); break;
case 1505: slopeceilingthings.Add(t); break;
}
}
}
}
}
// Slope any floor vertices?
if (slopefloorthings.Count > 0)
{
SectorData sd = GetSectorData(s);
sd.AddEffectThingVertexSlope(slopefloorthings, true);
}
// Slope any ceiling vertices?
if (slopeceilingthings.Count > 0)
{
SectorData sd = GetSectorData(s);
sd.AddEffectThingVertexSlope(slopeceilingthings, false);
}
}
// Find interesting things (such as sector slopes)
// Pass one of slope things, and determine which one are for pass two
//TODO: unfuck this because UDB decided to overhaul this...
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
foreach (Thing t in General.Map.Map.Things)
{
// SRB2
if (t.Type == 750)
{
if (!thingtags.ContainsKey(t.Tag)) thingtags[t.Tag] = new List<Thing>();
thingtags[t.Tag].Add(t);
}
continue;
switch (t.Type)
{
// ========== Copy slope ==========
case 9511:
case 9510:
slopethingpass[1].Add(t);
break;
// ========== Thing line slope ==========
case 9501:
case 9500:
if (linetags.ContainsKey(t.Args[0]))
{
// Only slope each sector once, even when multiple lines of the same sector are tagged. See https://github.com/jewalky/UltimateDoomBuilder/issues/491
List<Sector> slopedsectors = new List<Sector>();
foreach (Linedef ld in linetags[t.Args[0]])
{
if (ld.Line.GetSideOfLine(t.Position) < 0.0f)
{
if (ld.Front != null && !slopedsectors.Contains(ld.Front.Sector))
{
GetSectorData(ld.Front.Sector).AddEffectThingLineSlope(t, ld.Front);
slopedsectors.Add(ld.Front.Sector);
}
}
else if (ld.Back != null && !slopedsectors.Contains(ld.Back.Sector))
{
GetSectorData(ld.Back.Sector).AddEffectThingLineSlope(t, ld.Back);
slopedsectors.Add(ld.Back.Sector);
}
}
}
break;
// ========== Thing slope ==========
case 9503:
case 9502:
t.DetermineSector(blockmap);
if (t.Sector != null)
{
SectorData sd = GetSectorData(t.Sector);
sd.AddEffectThingSlope(t);
}
break;
}
}
// 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;
case "srb2_vertexslope":
{
List<Thing> sourcethings = new List<Thing>();
if (!thingtags.ContainsKey(l.Args[1]) || thingtags[l.Args[1]].Count == 0)
break;
foreach (Thing thing in thingtags[l.Args[1]])
{
if (sourcethings.Contains(thing))
continue;
sourcethings.Add(thing);
break;
}
if (!thingtags.ContainsKey(l.Args[2]) || thingtags[l.Args[2]].Count == 0)
break;
foreach (Thing thing in thingtags[l.Args[2]])
{
if (sourcethings.Contains(thing))
continue;
sourcethings.Add(thing);
break;
}
if (!thingtags.ContainsKey(l.Args[3]) || thingtags[l.Args[3]].Count == 0)
break;
foreach (Thing thing in thingtags[l.Args[3]])
{
if (sourcethings.Contains(thing))
continue;
sourcethings.Add(thing);
break;
}
SectorData sd = GetSectorData((l.Args[0] < 2) ? l.Front.Sector : l.Back.Sector);
sd.AddEffectSRB2ThingVertexSlope(sourcethings, (l.Args[0] & 1) != 1);
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);
MaxED
committed
if(sectortags.ContainsKey(sectortag))
{
List<Sector> sectors = sectortags[sectortag];
foreach(Sector s in sectors)
{
SectorData sd = GetSectorData(s);
sd.AddEffect3DFloor(l);
}
}
break;
case "srb2_fofsolid":
case "srb2_fofwater":
case "srb2_fofsolidopaque":
case "srb2_fofcrumbling":
case "srb2_fofintangible":
case "srb2_fofbustable":
case "srb2_foflaser":
case "srb2_fofcustom":
if (l.Front != null && sectortags.ContainsKey(l.Args[0]))
List<Sector> sectors = sectortags[l.Args[0]];
foreach (Sector s in sectors)
SectorData sd = GetSectorData(s);
sd.AddEffect3DFloor(l);
}
}
break;
case "srb2_foflight":
case "srb2_foffog":
case "srb2_fofintangibleinvisible":
if (l.Front != null && sectortags.ContainsKey(l.Args[0]))
{
List<Sector> sectors = sectortags[l.Args[0]];
foreach (Sector s in sectors)
{
SectorData sd = GetSectorData(s);
//sd.AddEffectBrightnessLevel(l);
sd.AddEffect3DFloor(l);
MaxED
committed
break;
// ========== Transfer Brightness (50) (see http://zdoom.org/wiki/ExtraFloor_LightOnly) =========
case "extrafloor_lightonly":
MaxED
committed
if(l.Front != null && sectortags.ContainsKey(l.Args[0]))
MaxED
committed
List<Sector> sectors = sectortags[l.Args[0]];
foreach(Sector s in sectors)
{
SectorData sd = GetSectorData(s);
sd.AddEffectBrightnessLevel(l);
}
MaxED
committed
break;
// ========== mxd. Transfer Floor Brightness (210) (see http://www.zdoom.org/w/index.php?title=Transfer_FloorLight) =========
case "transfer_floorlight":
MaxED
committed
if(l.Front != null && sectortags.ContainsKey(l.Args[0]))
{
List<Sector> sectors = sectortags[l.Args[0]];
foreach(Sector s in sectors)
{
SectorData sd = GetSectorData(s);
sd.AddEffectTransferFloorBrightness(l);
}
}
break;
// ========== mxd. Transfer Ceiling Brightness (211) (see http://www.zdoom.org/w/index.php?title=Transfer_CeilingLight) =========
case "transfer_ceilinglight":
MaxED
committed
if(l.Front != null && sectortags.ContainsKey(l.Args[0]))
{
List<Sector> sectors = sectortags[l.Args[0]];
foreach(Sector s in sectors)
{
SectorData sd = GetSectorData(s);
sd.AddEffectTransferCeilingBrightness(l);
}
}
break;
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
// ========== mxd. BOOM: Set Tagged Floor Lighting to Lighting on 1st Sidedef's Sector (213) =========
case "boom_transfer_floorlight":
if(l.Front != null && sectortags.ContainsKey(l.Tag))
{
List<Sector> sectors = sectortags[l.Tag];
foreach(Sector s in sectors)
{
SectorData sd = GetSectorData(s);
sd.AddEffectTransferFloorBrightness(l);
}
}
break;
// ========== mxd. BOOM: Set Tagged Ceiling Lighting to Lighting on 1st Sidedef's Sector (261) =========
case "boom_transfer_ceilinglight":
if(l.Front != null && sectortags.ContainsKey(l.Tag))
{
List<Sector> sectors = sectortags[l.Tag];
foreach(Sector s in sectors)
{
SectorData sd = GetSectorData(s);
sd.AddEffectTransferCeilingBrightness(l);
}
}
break;
}
}
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
// Pass one for linedefs
foreach (Linedef l in slopelinedefpass[0])
{
//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())
{
// ========== Plane Align (181) (see http://zdoom.org/wiki/Plane_Align) ==========
case "plane_align":
if (((l.Args[0] == 1) || (l.Args[1] == 1)) && (l.Front != null))
{
SectorData sd = GetSectorData(l.Front.Sector);
sd.AddEffectLineSlope(l);
}
if (((l.Args[0] == 2) || (l.Args[1] == 2)) && (l.Back != null))
{
SectorData sd = GetSectorData(l.Back.Sector);
sd.AddEffectLineSlope(l);
}
break;
}
}
// Pass two of slope things
//TODO: unfuck this because UDB decided to overhaul this...
foreach (Thing t in slopethingpass[1])
{
switch (t.Type)
{
// ========== Copy slope ==========
case 9511:
case 9510:
t.DetermineSector(blockmap);
if (t.Sector != null)
SectorData sd = GetSectorData(t.Sector);
sd.AddEffectCopySlope(t);
}
}
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
// Pass two for linedefs
foreach (Linedef l in slopelinedefpass[1])
{
if (l.Action == 0 || !General.Map.Config.LinedefActions.ContainsKey(l.Action)) continue;
switch (General.Map.Config.LinedefActions[l.Action].Id.ToLowerInvariant())
{
// ========== Plane Copy (118) (mxd) (see http://zdoom.org/wiki/Plane_Copy) ==========
case "plane_copy":
{
//check the flags...
bool floorCopyToBack = false;
bool floorCopyToFront = false;
bool ceilingCopyToBack = false;
bool ceilingCopyToFront = false;
if (l.Args[4] > 0 && l.Args[4] != 3 && l.Args[4] != 12)
{
floorCopyToBack = (l.Args[4] & 1) == 1;
floorCopyToFront = (l.Args[4] & 2) == 2;
ceilingCopyToBack = (l.Args[4] & 4) == 4;
ceilingCopyToFront = (l.Args[4] & 8) == 8;
}
// Copy slope to front sector
if (l.Front != null)
{
if ((l.Args[0] > 0 || l.Args[1] > 0) || (l.Back != null && (floorCopyToFront || ceilingCopyToFront)))
{
SectorData sd = GetSectorData(l.Front.Sector);
sd.AddEffectPlaneClopySlope(l, true);
}
}
// Copy slope to back sector
if (l.Back != null)
{
if ((l.Args[2] > 0 || l.Args[3] > 0) || (l.Front != null && (floorCopyToBack || ceilingCopyToBack)))
{
SectorData sd = GetSectorData(l.Back.Sector);
sd.AddEffectPlaneClopySlope(l, false);
}
}
}
break;
}
}
// Visual slope handles
foreach (KeyValuePair<Sector, List<VisualSlope>> kvp in allslopehandles)
{
foreach (VisualSlope handle in kvp.Value)
biwa
committed
if (handle != null && handle.Selected)
if (handle is BaseVisualSlope)
RemoveSelectedObject((BaseVisualSlope)handle);
usedslopehandles.Clear();
biwa
committed
sidedefslopehandles.Clear();
vertexslopehandles.Clear();
BuildSlopeHandles(General.Map.Map.Sectors.ToList());
}
private void BuildSlopeHandles(List<Sector> sectors)
{
biwa
committed
if (!General.Map.UDMF)
return;
foreach (Sector s in sectors)
biwa
committed
if (s.IsDisposed)
continue;
SectorData sectordata = GetSectorData(s);
sectordata.Update();
// Clear old data
if (allslopehandles.ContainsKey(s)) allslopehandles.Remove(s);
if (sidedefslopehandles.ContainsKey(s)) sidedefslopehandles.Remove(s);
if (vertexslopehandles.ContainsKey(s)) vertexslopehandles.Remove(s);
// Create visual sidedef slope handles
foreach (Sidedef sidedef in s.Sidedefs)
biwa
committed
// Create handles for the regular floor and ceiling
CreateVisualSlopeHandle(sectordata.Floor, sidedef, true);
CreateVisualSlopeHandle(sectordata.Ceiling, sidedef, false);
// Create handles for 3D floors
if (sectordata.ExtraFloors.Count > 0)
{
biwa
committed
foreach (Effect3DFloor floor in sectordata.ExtraFloors)
{
CreateVisualSlopeHandle(floor.Floor, sidedef, false);
CreateVisualSlopeHandle(floor.Ceiling, sidedef, true);
}
}
biwa
committed
}
}
biwa
committed
// Create visual vertex slope handles
foreach(Vertex v in General.Map.Map.Vertices)
{
if (v.IsDisposed || v.Linedefs.Count == 0)
continue;
HashSet<Sector> vertexsectors = new HashSet<Sector>();
// Find all sectors that have lines connected to this vertex
foreach(Linedef ld in v.Linedefs)
{
if (ld.IsDisposed)
continue;
if (ld.Front != null && ld.Front.Sector != null && !ld.Front.Sector.IsDisposed) vertexsectors.Add(ld.Front.Sector);
if (ld.Back != null && ld.Back.Sector != null && !ld.Back.Sector.IsDisposed) vertexsectors.Add(ld.Back.Sector);
biwa
committed
}
biwa
committed
foreach(Sector s in vertexsectors)
{
SectorData sectordata = GetSectorData(s);
biwa
committed
// Create handles for the regular floor and ceiling
CreateVisualSlopeHandle(sectordata.Floor, v, s, true);
CreateVisualSlopeHandle(sectordata.Ceiling, v, s, false);
biwa
committed
// Create handles for 3D floors
if (sectordata.ExtraFloors.Count > 0)
biwa
committed
foreach (Effect3DFloor floor in sectordata.ExtraFloors)
biwa
committed
CreateVisualSlopeHandle(floor.Floor, v, s, false);
CreateVisualSlopeHandle(floor.Ceiling, v, s, true);
}
#endregion
#region ================== Events
// Help!
public override void OnHelp()
{
General.ShowHelp("e_visual.html");
}
codeimp
committed
// When entering this mode
public override void OnEngage()
{
//mxd
useSelectionFromClassicMode = BuilderPlug.Me.SyncSelection ? !General.Interface.ShiftState : General.Interface.ShiftState;
if(useSelectionFromClassicMode) UpdateSelectionInfo();
// Read settings
cameraflooroffset = General.Map.Config.ReadSetting("cameraflooroffset", cameraflooroffset);
cameraceilingoffset = General.Map.Config.ReadSetting("cameraceilingoffset", cameraceilingoffset);
//mxd. Update fog color (otherwise FogBoundaries won't be setup correctly)
foreach (Sector s in General.Map.Map.Sectors)
s.UpdateFogColor();
// biwa. We need a blockmap for the slope things. Can't wait until it's built in base.OnEngage
// This was the root cause for issue #160
FillBlockMap();
// (Re)create special effects
RebuildElementData();
// Objects are only selected when they are created, so for objects that are selected we have to make sure
// that they are created immediately. Otherwise the selection order will not be correct, or the objects
// will not be selected at all if they are out of the user's camera range when entering visual mode
// See https://github.com/jewalky/UltimateDoomBuilder/issues/938
if (useSelectionFromClassicMode)
{
foreach (Sector s in General.Map.Map.GetSelectedSectors(true))
{
BaseVisualSector bvs = CreateBaseVisualSector(s);
bvs.Ceiling.PerformAutoSelection();
bvs.Floor.PerformAutoSelection();
}
// Things are automatically selected on creation
foreach (Thing t in General.Map.Map.GetSelectedThings(true))
allthings[t] = CreateVisualThing(t);
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
// For linedefs it's a bit more complicated...
foreach (Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
{
foreach (Sidedef sd in new Sidedef[] { ld.Front, ld.Back })
{
if (sd != null)
{
if (!allsectors.ContainsKey(sd.Sector))
CreateBaseVisualSector(sd.Sector).Rebuild(); // We have to rebuild the sector so that potential 3D floors get created
VisualSidedefParts vsp = ((BaseVisualSector)allsectors[sd.Sector]).Sides[sd];
vsp.upper?.PerformAutoSelection();
vsp.middlesingle?.PerformAutoSelection();
vsp.middledouble?.PerformAutoSelection();
vsp.lower?.PerformAutoSelection();
if (vsp.middle3d != null)
foreach (VisualMiddle3D vm in vsp.middle3d)
vm.PerformAutoSelection();
if (vsp.middleback != null)
foreach (VisualMiddleBack vm in vsp.middleback)
vm.PerformAutoSelection();
}
}
}
}
//mxd. Update event lines
renderer.SetEventLines(LinksCollector.GetHelperShapes(General.Map.ThingsFilter.VisibleThings, blockmap));
ZZYZX
committed
// [ZZ] this enables calling of this object from the outside world. Only after properly initialized pls.
base.OnEngage();
}
codeimp
committed
// When returning to another mode
public override void OnDisengage()
{
base.OnDisengage();
if(BuilderPlug.Me.SyncSelection ? !General.Interface.ShiftState : General.Interface.ShiftState)
{
//clear previously selected stuff
General.Map.Map.ClearAllSelected();
//refill selection
List<int> selectedsectorindices = new List<int>();
List<int> selectedlineindices = new List<int>();
List<int> selectedvertexindices = new List<int>();
foreach(IVisualEventReceiver obj in selectedobjects)
{
if(obj is BaseVisualThing)
{
}
else if(obj is VisualFloor || obj is VisualCeiling)
{
VisualGeometry vg = (VisualGeometry)obj;
if(vg.Sector != null && vg.Sector.Sector != null && !selectedsectorindices.Contains(vg.Sector.Sector.Index))
{
selectedsectorindices.Add(vg.Sector.Sector.Index);
vg.Sector.Sector.Selected = true;
}
}
else if(obj is VisualLower || obj is VisualUpper || obj is VisualMiddleDouble
|| obj is VisualMiddleSingle || obj is VisualMiddle3D)
{
VisualGeometry vg = (VisualGeometry)obj;
if(vg.Sidedef != null && !selectedlineindices.Contains(vg.Sidedef.Line.Index))
{
selectedlineindices.Add(vg.Sidedef.Line.Index);
vg.Sidedef.Line.Selected = true;
}
else if(obj is VisualVertex)
{
VisualVertex v = (VisualVertex)obj;
if(!selectedvertexindices.Contains(v.Vertex.Index))
{
selectedvertexindices.Add(v.Vertex.Index);
v.Vertex.Selected = true;
}
}
codeimp
committed
General.Map.Map.Update();
}
public override void OnProcess(long deltatime)
long pickinterval = PICK_INTERVAL; // biwa
// Process things?
base.ProcessThings = (BuilderPlug.Me.ShowVisualThings != 0);
// Setup the move multiplier depending on gravity
biwa
committed
Vector3D movemultiplier = new Vector3D(1.0, 1.0, 1.0);
if(BuilderPlug.Me.UseGravity) movemultiplier.z = 0.0;
General.Map.VisualCamera.MoveMultiplier = movemultiplier;
// Apply gravity?
if(BuilderPlug.Me.UseGravity && (General.Map.VisualCamera.Sector != null))
{
SectorData sd = GetSectorData(General.Map.VisualCamera.Sector);
if(!sd.Updated) sd.Update();
// Camera below floor level?
Vector3D feetposition = General.Map.VisualCamera.Position;
SectorLevel floorlevel = sd.GetFloorBelow(feetposition) ?? sd.Floor;
double floorheight = floorlevel.plane.GetZ(General.Map.VisualCamera.Position);
biwa
committed
if(General.Map.VisualCamera.Position.z < (floorheight + cameraflooroffset + 0.1))
{
// Stay above floor
biwa
committed
gravity = new Vector3D(0.0, 0.0, 0.0);
General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x,
General.Map.VisualCamera.Position.y,
floorheight + cameraflooroffset);
}
else
{
// Fall down
MaxED
committed
gravity.z += GRAVITY * General.Map.VisualCamera.Gravity * deltatime;
biwa
committed
if(gravity.z > 3.0) gravity.z = 3.0;
// Test if we don't go through a floor
biwa
committed
if((General.Map.VisualCamera.Position.z + gravity.z) < (floorheight + cameraflooroffset + 0.1))
{
// Stay above floor
biwa
committed
gravity = new Vector3D(0.0, 0.0, 0.0);
General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x,
General.Map.VisualCamera.Position.y,
floorheight + cameraflooroffset);
}
else
{
// Apply gravity vector
General.Map.VisualCamera.Position += gravity;
}
// Camera above ceiling?
biwa
committed
feetposition = General.Map.VisualCamera.Position - new Vector3D(0, 0, cameraflooroffset - 7.0);
SectorLevel ceillevel = sd.GetCeilingAbove(feetposition) ?? sd.Ceiling;
double ceilheight = ceillevel.plane.GetZ(General.Map.VisualCamera.Position);
biwa
committed
if(General.Map.VisualCamera.Position.z > (ceilheight - cameraceilingoffset - 0.01))
{
// Stay below ceiling
General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x,
General.Map.VisualCamera.Position.y,
ceilheight - cameraceilingoffset);
}
}
else
{
biwa
committed
gravity = new Vector3D(0.0, 0.0, 0.0);
}
// Do processing
base.OnProcess(deltatime);
// Process visible geometry
foreach(IVisualEventReceiver g in visiblegeometry)
{
g.OnProcess(deltatime);
}
// biwa. Use a lower pick interval for paint selection, to make it more reliable
if (paintselectpressed)
pickinterval = PICK_INTERVAL_PAINT_SELECT;
// Time to pick a new target?
if(Clock.CurrentTime > (lastpicktime + pickinterval))
{
PickTargetUnlocked();
lastpicktime = Clock.CurrentTime;
}
// The mouse is always in motion
MouseEventArgs args = new MouseEventArgs(General.Interface.MouseButtons, 0, 0, 0, 0);
OnMouseMove(args);
}
MaxED
committed
//mxd
public override void OnClockReset()
{
base.OnClockReset();
lastpicktime = 0;
}
// This draws a frame
public override void OnRedrawDisplay()
{
renderer.SetClassicLightingColorMap(General.Map.Data.MainColorMap);
// Start drawing
if(renderer.Start())
{
// Use fog!
renderer.SetFogMode(true);
// Set target for highlighting
renderer.ShowSelection = General.Settings.GZOldHighlightMode || General.Settings.UseHighlight; //mxd
if(General.Settings.UseHighlight)
renderer.SetHighlightedObject(target.picked);
// Begin with geometry
renderer.StartGeometry();
// Render all visible sectors
foreach(VisualGeometry g in visiblegeometry)
renderer.AddSectorGeometry(g);
if(BuilderPlug.Me.ShowVisualThings != 0)
{
// Render things in cages?
renderer.DrawThingCages = ((BuilderPlug.Me.ShowVisualThings & 2) != 0);
// Render all visible things
foreach(VisualThing t in visiblethings)
renderer.AddThingGeometry(t);
}
if(General.Map.UDMF && General.Map.Config.VertexHeightSupport && General.Settings.GZShowVisualVertices && vertices.Count > 0)
MaxED
committed
{
List<VisualVertex> verts = new List<VisualVertex>();
foreach(KeyValuePair<Vertex, VisualVertexPair> pair in vertices)
verts.AddRange(pair.Value.Vertices);
renderer.SetVisualVertices(verts);
renderer.SetVisualSlopeHandles(usedslopehandles);
// Done rendering geometry
renderer.FinishGeometry();
// Render crosshair
renderer.RenderCrosshair();
// Present!
renderer.Finish();
}
}
// After resources were reloaded
protected override void ResourcesReloaded()
{
base.ResourcesReloaded();
RebuildElementData();
UpdateChangedObjects(); //mxd
PickTarget();
}
// This usually happens when geometry is changed by undo, redo, cut or paste actions
// and uses the marks to check what needs to be reloaded.
protected override void ResourcesReloadedPartial()
{
// Let the core do this (it will just dispose the sectors that were changed)
base.ResourcesReloadedPartial();
if (General.Map.UndoRedo.GeometryChanged)
{
// The base doesn't know anything about slobe handles, so we have to clear them up ourself
if (General.Map.UDMF)
{
List<Sector> removedsectors = new List<Sector>();
// Get the sectors that were disposed...
foreach(Sector s in allslopehandles.Keys)
{
if (s.IsDisposed)
removedsectors.Add(s);
}
// ... so that we can remove their slope handles
foreach(Sector s in removedsectors)
{
allslopehandles[s].Clear();
allslopehandles.Remove(s);
biwa
committed
sidedefslopehandles[s].Clear();
sidedefslopehandles.Remove(s);
vertexslopehandles[s].Clear();
vertexslopehandles.Remove(s);
}
// Rebuild slope handles for the changed sectors
BuildSlopeHandles(General.Map.Map.GetMarkedSectors(true));
}
bool sectorsmarked = false;
// Neighbour sectors must be updated as well
foreach (Sector s in General.Map.Map.Sectors)
{
if(s.Marked)
{
sectorsmarked = true;
foreach(Sidedef sd in s.Sidedefs)
{
sd.Marked = true;
if(sd.Other != null) sd.Other.Marked = true;
}
}
}
// Go for all sidedefs to update
foreach(Sidedef sd in General.Map.Map.Sidedefs)
{
if(sd.Marked && VisualSectorExists(sd.Sector))
BaseVisualSector vs = (BaseVisualSector)GetVisualSector(sd.Sector);
VisualSidedefParts parts = vs.GetSidedefParts(sd);
parts.SetupAllParts();
}
}
// Go for all sectors to update
foreach(Sector s in General.Map.Map.Sectors)
{
if(s.Marked)
SectorData sd = GetSectorDataEx(s);
if(sd != null)
sd.Reset(false); //mxd (changed Reset implementation)
// UpdateSectorGeometry for associated sectors (sd.UpdateAlso) as well!
foreach(KeyValuePair<Sector, bool> us in sd.UpdateAlso)
if(VisualSectorExists(us.Key))
{
BaseVisualSector vs = (BaseVisualSector)GetVisualSector(us.Key);
vs.UpdateSectorGeometry(us.Value);
}
}
}
// And update for this sector ofcourse
if(VisualSectorExists(s))
{
BaseVisualSector vs = (BaseVisualSector)GetVisualSector(s);
vs.UpdateSectorGeometry(false);
}
}
}
if(!sectorsmarked)
{
// No sectors or geometry changed. So we only have
// to update things when they have changed.
HashSet<Thing> toremove = new HashSet<Thing>(); //mxd
ZZYZX
committed
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
{
ZZYZX
committed
if((vt.Value != null) && vt.Key.Marked)
{
if(vt.Key.IsDisposed) toremove.Add(vt.Key); //mxd. Disposed things will cause problems
else ((BaseVisualThing)vt.Value).Rebuild();
}
}
//mxd. Remove disposed things
foreach(Thing t in toremove)
{
ZZYZX
committed
if(allthings[t] != null) allthings[t].Dispose();
allthings.Remove(t);
}
}
else
{
// Things depend on the sector they are in and because we can't
// easily determine which ones changed, we dispose all things
ZZYZX
committed
foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
if(vt.Value != null) vt.Value.Dispose();
// Apply new lists
allthings = new Dictionary<Thing, VisualThing>(allthings.Count);
}
// Clear visibility collections
visiblesectors.Clear();
visibleblocks.Clear();
visiblegeometry.Clear();
visiblethings.Clear();
// Make new blockmap
if(sectorsmarked || General.Map.UndoRedo.PopulationChanged || General.Map.IsChanged)
codeimp
committed
FillBlockMap();
RebuildElementData();
UpdateChangedObjects();
// Visibility culling (this re-creates the needed resources)
DoCulling();
}
// Determine what we're aiming at now
PickTarget();
}
// Mouse moves
public override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
IVisualEventReceiver o = GetTargetEventReceiver(true);
o.OnMouseMove(e);
//mxd. Show hints!
if(o.GetType() != lasthighlighttype)
{
if(General.Interface.ActiveDockerTabName == "Help")
{
if(o is BaseVisualGeometrySidedef)
{
General.Hints.ShowHints(this.GetType(), "sidedefs");
}
else if(o is BaseVisualGeometrySector)
{
General.Hints.ShowHints(this.GetType(), "sectors");
}
else if(o is BaseVisualThing)
{
General.Hints.ShowHints(this.GetType(), "things");
}
else if(o is BaseVisualVertex)
{
General.Hints.ShowHints(this.GetType(), "vertices");
}
else
{
General.Hints.ShowHints(this.GetType(), HintsManager.GENERAL);
}
}
lasthighlighttype = o.GetType();
}
// biwa
if (o is NullVisualEventReceiver)
highlighted = null;
else if (o is VisualGeometry)
highlighted = (VisualGeometry)o;
else if (o is VisualThing)
highlighted = (VisualThing)o;
// Undo performed
public override void OnUndoEnd()
{
base.OnUndoEnd();
//mxd. Effects may've become invalid
if(sectordata != null && sectordata.Count > 0) RebuildElementData();
foreach(VisualSector sector in visiblesectors)
MaxED
committed
{
BaseVisualSector vs = (BaseVisualSector)sector;
if(vs != null) vs.Rebuild();
codeimp
committed
RebuildSelectedObjectsList();
// We can't group with this undo level anymore
lastundogroup = UndoGroup.None;
}
codeimp
committed
// Redo performed
public override void OnRedoEnd()
{
base.OnRedoEnd();
if(sectordata != null && sectordata.Count > 0) RebuildElementData();
foreach(VisualSector sector in visiblesectors)
MaxED
committed
{
BaseVisualSector vs = (BaseVisualSector)sector;
if(vs != null) vs.Rebuild();
codeimp
committed
RebuildSelectedObjectsList();
}
MaxED
committed
public override void OnScriptRunEnd()
{
base.OnScriptRunEnd();
FillBlockMap();
// Effects may've become invalid
if (sectordata != null && sectordata.Count > 0) RebuildElementData();
// As well as geometry...
foreach (VisualSector sector in visiblesectors)
{
BaseVisualSector vs = (BaseVisualSector)sector;
if (vs != null) vs.Rebuild();
}
RebuildSelectedObjectsList();
}
MaxED
committed
//mxd
private void Interface_OnSectorEditFormValuesChanged(object sender, EventArgs e)
{
ZZYZX
committed
if(allsectors == null) return;
MaxED
committed
// Reset changed flags
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;
foreach(VisualCeiling vc in bvs.ExtraBackCeilings) vc.Changed = false;
MaxED
committed
bvs.Floor.Changed = false;
bvs.Ceiling.Changed = false;
}
UpdateChangedObjects();
ShowTargetInfo();
}
//mxd
private void Interface_OnThingEditFormValuesChanged(object sender, EventArgs e)
{
//update visual sectors, which are affected by certain things
List<Thing> things = GetSelectedThings();
MaxED
committed
foreach(Thing t in things)
{
if(thingdata.ContainsKey(t))
{
// Update what must be updated
ThingData td = GetThingData(t);
MaxED
committed
foreach(KeyValuePair<Sector, bool> s in td.UpdateAlso)
{
if(VisualSectorExists(s.Key))
{
BaseVisualSector vs = (BaseVisualSector)GetVisualSector(s.Key);
vs.UpdateSectorGeometry(s.Value);
}
}
}
}
UpdateChangedObjects();
ShowTargetInfo();
}
MaxED
committed
//mxd
private void Interface_OnEditFormValuesChanged(object sender, EventArgs e)
{
MaxED
committed
UpdateChangedObjects();
ShowTargetInfo();
}
private void Interface_OnUpdateChangedObjects(object sender, EventArgs e)
{
UpdateChangedObjects();
}
//mxd
private void SelectioninfoupdatetimerOnTick(object sender, EventArgs eventArgs)
{
selectioninfoupdatetimer.Stop();
UpdateSelectionInfo();
}
codeimp
committed
#region ================== Action Assist
// Because some actions can only be called on a single (the targeted) object because
// they show a dialog window or something, these functions help applying the result
// to all compatible selected objects.
// Apply texture offsets
public void ApplyTextureOffsetChange(int dx, int dy)
{
List<IVisualEventReceiver> objs = GetSelectedObjects(false, true, false, false, false);
//mxd. Because Upper/Middle/Lower textures offsets should be threated separately in UDMF
//MaxW. But they're not for Eternity, so this needs its own config setting
if(General.Map.UDMF && General.Map.Config.UseLocalSidedefTextureOffsets)
MaxED
committed
{
HashSet<BaseVisualGeometrySidedef> donesides = new HashSet<BaseVisualGeometrySidedef>();
foreach(IVisualEventReceiver i in objs)
{
BaseVisualGeometrySidedef vs = (BaseVisualGeometrySidedef)i; //mxd
if(!donesides.Contains(vs))
{
//mxd. added scaling by texture scale
if(vs.Texture.UsedInMap) //mxd. Otherwise it's MissingTexture3D and we probably don't want to drag that
vs.OnChangeTextureOffset((int)(dx / vs.Texture.Scale.x), (int)(dy / vs.Texture.Scale.y), false);
MaxED
committed
donesides.Add(vs);
}
}
}
else
{
HashSet<Sidedef> donesides = new HashSet<Sidedef>();
foreach(IVisualEventReceiver i in objs)
MaxED
committed
{
BaseVisualGeometrySidedef vs = (BaseVisualGeometrySidedef)i; //mxd
if(!donesides.Contains(vs.Sidedef))
MaxED
committed
//mxd. added scaling by texture scale
if(vs.Texture.UsedInMap) //mxd. Otherwise it's MissingTexture3D and we probably don't want to drag that
vs.OnChangeTextureOffset((int)(dx / vs.Texture.Scale.x), (int)(dy / vs.Texture.Scale.y), false);
donesides.Add(vs.Sidedef);
}
}
// Apply flat offsets
public void ApplyFlatOffsetChange(int dx, int dy)
{
HashSet<int> donesectors = new HashSet<int>();
List<IVisualEventReceiver> objs = GetSelectedObjects(true, false, false, false, false);
foreach(IVisualEventReceiver i in objs)
{
BaseVisualGeometrySector bvs = (BaseVisualGeometrySector)i;
if(bvs != null && !donesectors.Contains(bvs.Sector.Sector.Index))
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
//mxd. Sector surface belongs to 3d-floor?
if(bvs.Level.sector.Index != bvs.Sector.Sector.Index)
{
// Don't update control sector several times
if(!donesectors.Contains(bvs.Level.sector.Index))
{
// Update the offsets
bvs.OnChangeTextureOffset(dx, dy, false);
// Update control sector
SectorData sd = GetSectorData(bvs.Level.sector);
sd.Update();
BaseVisualSector vs = (BaseVisualSector)GetVisualSector(bvs.Level.sector);
vs.Rebuild();
// Add to collection
donesectors.Add(bvs.Level.sector.Index);
// Update 3d-floors
List<Sector> updatealso = new List<Sector>(sd.UpdateAlso.Keys);
foreach(Sector other in updatealso)
Loading
Loading full blame...