Skip to content
Snippets Groups Projects
BaseVisualMode.cs 77.5 KiB
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;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.Config;
MaxED's avatar
MaxED committed
using CodeImp.DoomBuilder.GZBuilder.Data;
MaxED's avatar
MaxED committed
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",

	public class BaseVisualMode : VisualMode
	{
		#region ================== Constants
		// Object picking
		private const float PICK_INTERVAL = 80.0f;
		private const float PICK_RANGE = 0.98f;

		// Gravity
		private const float GRAVITY = -0.06f;
		
		#endregion
		
		#region ================== Variables
		private float cameraflooroffset = 41f;		// same as in doom
		private float cameraceilingoffset = 10f;
		
		// Object picking
		private VisualPickResult target;
		private bool locktarget;
MaxED's avatar
MaxED committed
		private bool useSelectionFromClassicMode;//mxd
		public bool UseSelectionFromClassicMode { get { return useSelectionFromClassicMode; } }

		// This keeps extra element info
		private Dictionary<Sector, SectorData> sectordata;
MaxED's avatar
MaxED committed
		private Dictionary<Thing, ThingData> thingdata;
		private Dictionary<Vertex, VertexData> vertexdata; //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.
		private bool singleselection;
		// We keep these to determine if we need to make a new undo level
		private bool selectionchanged;
		private VisualActionResult actionresult;
		// List of selected objects when an action is performed
		private List<IVisualEventReceiver> selectedobjects;
MaxED's avatar
MaxED committed
        //mxd. Used in Cut/PasteSelection actions
        private List<ThingCopyData> copyBuffer;
MaxED's avatar
MaxED committed

		public static bool GZDoomRenderingEffects { get { return gzdoomRenderingEffects; } } //mxd
        private static bool gzdoomRenderingEffects = true; //mxd
MaxED's avatar
MaxED committed

		//mxd. Moved here from Tools
		private struct SidedefAlignJob
		{
			public Sidedef sidedef;
			public Sidedef controlSide; //mxd

			public float offsetx;

			// 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?
				if(target.picked is VisualGeometry)
				{
					VisualGeometry pickedgeo = (target.picked as VisualGeometry);

					if(pickedgeo.Sidedef != null)
						return pickedgeo.Sidedef;
					else if(pickedgeo.Sector != null)
						return pickedgeo.Sector;
					else
						return null;
				}
				// Thing picked?
				else if(target.picked is VisualThing)
				{
					VisualThing pickedthing = (target.picked as VisualThing);
					return pickedthing.Thing;
				}
				else
				{
					return null;
				}
			}
		}

MaxED's avatar
MaxED committed
		new public IRenderer3D Renderer { get { return renderer; } }
		
		public bool IsSingleSelection { get { return singleselection; } }
		public bool SelectionChanged { get { return selectionchanged; } set { selectionchanged |= value; } }
		#endregion
		
		#region ================== Constructor / Disposer

		// Constructor
		public BaseVisualMode()
		{
			// Initialize
			this.gravity = new Vector3D(0.0f, 0.0f, 0.0f);
			this.selectedobjects = new List<IVisualEventReceiver>();
MaxED's avatar
MaxED committed
            //mxd
            this.copyBuffer = new List<ThingCopyData>();
			
			// We have no destructor
			GC.SuppressFinalize(this);
		}

		// Disposer
		public override void Dispose()
		{
			// Not already disposed?
			if(!isdisposed)
			{
				// Clean up
				
				// Done
				base.Dispose();
			}
		}

		#endregion
		
		#region ================== Methods
		internal int CalculateBrightness(int level)
			return renderer.CalculateBrightness(level);

        //mxd. This calculates brightness level with doom-style shading
        internal int CalculateBrightness(int level, Sidedef sd) {
            return renderer.CalculateBrightness(level, sd);
        }
		// This adds a selected object
		internal void AddSelectedObject(IVisualEventReceiver obj)
		{
			selectedobjects.Add(obj);
			selectionchanged = true;
		}
		
		// This removes a selected object
		internal void RemoveSelectedObject(IVisualEventReceiver obj)
		{
			selectedobjects.Remove(obj);
			selectionchanged = true;
		}
		
		// 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;
				// 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;
					// We don't want to make a new undo level (changes will be combined)
					undocreated = true;
		// 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
		private void PostAction()
			if(!string.IsNullOrEmpty(actionresult.displaystatus))
				General.Interface.DisplayStatus(StatusType.Action, actionresult.displaystatus);

			// Reset changed flags
			foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
			{
				BaseVisualSector bvs = (vs.Value as BaseVisualSector);
				foreach(VisualFloor vf in bvs.ExtraFloors) vf.Changed = false;
				foreach(VisualCeiling vc in bvs.ExtraCeilings) vc.Changed = false;
MaxED's avatar
MaxED committed
				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;
			}
			selectionchanged = false;
			
			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();
			actionresult.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
		{
			// Make list of selected objects
			selectedobjects = new List<IVisualEventReceiver>();
			foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
			{
					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);
					foreach(Sidedef sd in vs.Key.Sidedefs)
						List<VisualGeometry> sidedefgeos = bvs.GetSidedefGeometry(sd);
						foreach(VisualGeometry sdg in sidedefgeos)
						{
							if(sdg.Selected) selectedobjects.Add((sdg as IVisualEventReceiver));
						}
					}
				}
			}

			foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
			{
				if(vt.Value != null)
				{
					BaseVisualThing bvt = (BaseVisualThing)vt.Value;
					if(bvt.Selected) selectedobjects.Add(bvt);
				}
MaxED's avatar
MaxED committed

			//mxd
			if(General.Map.UDMF && General.Settings.GZShowVisualVertices) {
				foreach(KeyValuePair<Vertex, VisualVertexPair> pair in vertices) {
					if(pair.Value.Vertex1.Selected)
						selectedobjects.Add((BaseVisualVertex)pair.Value.Vertex1);
					if(pair.Value.Vertex2.Selected)
						selectedobjects.Add((BaseVisualVertex)pair.Value.Vertex2);
				}
			}
		//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);
			if(vs != null) allsectors.Add(s, vs);
			return vs;
		}

		// This creates a visual sector
		protected override VisualSector CreateVisualSector(Sector s)
		{
			BaseVisualSector vs = new BaseVisualSector(this, s);
			return vs;
		}
		
		// This creates a visual thing
		protected override VisualThing CreateVisualThing(Thing t)
		{
			BaseVisualThing vt = new BaseVisualThing(this, t);
			return vt.Setup() ? vt : null;
		// 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);
			
			// 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 = (target.picked as VisualGeometry);
					
					// Sidedef?
					if(pickedgeo is BaseVisualGeometrySidedef)
					{
						BaseVisualGeometrySidedef pickedsidedef = (pickedgeo as BaseVisualGeometrySidedef);
MaxED's avatar
MaxED committed
						General.Interface.ShowLinedefInfo(pickedsidedef.GetControlLinedef()); //mxd
					}
					// Sector?
					else if(pickedgeo is BaseVisualGeometrySector)
					{
						BaseVisualGeometrySector pickedsector = (pickedgeo as BaseVisualGeometrySector);
						General.Interface.ShowSectorInfo(pickedsector.Level.sector);
					}
						General.Interface.HideInfo();
MaxED's avatar
MaxED committed
				} 
				else if(target.picked is VisualThing) 
				{ // Thing picked?
					VisualThing pickedthing = (target.picked as VisualThing);
					General.Interface.ShowThingInfo(pickedthing.Thing);
MaxED's avatar
MaxED committed
				} 
				else if(target.picked is VisualVertex) //mxd
				{
					VisualVertex pickedvert = (target.picked as VisualVertex);
					General.Interface.ShowVertexInfo(pickedvert.Vertex);
		// This updates the VisualSectors and VisualThings that have their Changed property set
        private void UpdateChangedObjects()
		{
			foreach(KeyValuePair<Sector, VisualSector> vs in allsectors)
			{
				if(vs.Value != null)
				{
					BaseVisualSector bvs = (BaseVisualSector)vs.Value;
					if(bvs.Changed) bvs.Rebuild();
				}
			}

			foreach(KeyValuePair<Thing, VisualThing> vt in allthings)
			{
				if(vt.Value != null)
				{
					BaseVisualThing bvt = (BaseVisualThing)vt.Value;
					if(bvt.Changed) bvt.Rebuild();
				}
MaxED's avatar
MaxED committed

			//mxd
			if(General.Map.UDMF) {
				foreach(KeyValuePair<Vertex, VisualVertexPair> pair in vertices)
					pair.Value.Update();
			}
MaxED's avatar
MaxED committed
        protected override void moveSelectedThings(Vector2D direction, bool absolutePosition) {
MaxED's avatar
MaxED committed
            List<VisualThing> visualThings = GetSelectedVisualThings(true);
MaxED's avatar
MaxED committed
            if (visualThings.Count == 0) {
                General.Interface.DisplayStatus(StatusType.Warning, "Select some Things first!");
                return;
            }

MaxED's avatar
MaxED committed
            PreAction(UndoGroup.ThingMove);
MaxED's avatar
MaxED committed

            Vector3D[] coords = new Vector3D[visualThings.Count];
            for (int i = 0; i < visualThings.Count; i++)
                coords[i] = visualThings[i].Thing.Position;

            //move things...
            Vector3D[] translatedCoords = translateCoordinates(coords, direction, absolutePosition);
            for (int i = 0; i < visualThings.Count; i++) {
                BaseVisualThing t = visualThings[i] as BaseVisualThing;
                t.OnMove(translatedCoords[i]);
            }

            PostAction();
        }

        //mxd
        private Vector3D[] translateCoordinates(Vector3D[] coordinates, Vector2D direction, bool absolutePosition) {
            if (coordinates.Length == 0) return null;

            direction.x = (float)Math.Round(direction.x);
            direction.y = (float)Math.Round(direction.y);

            Vector3D[] translatedCoords = new Vector3D[coordinates.Length];

MaxED's avatar
MaxED committed
            //move things...
            if (!absolutePosition) { //...relatively (that's easy)
                int camAngle = (int)Math.Round(General.Map.VisualCamera.AngleXY * 180 / Math.PI);
                int sector = (int)(General.ClampAngle(camAngle - 45f) / 90f);
                direction = direction.GetRotated((float)(sector * Math.PI / 2f));

MaxED's avatar
MaxED committed
                for (int i = 0; i < coordinates.Length; i++)
                    translatedCoords[i] = coordinates[i] + new Vector3D(direction);
MaxED's avatar
MaxED committed

MaxED's avatar
MaxED committed
                return translatedCoords;
            }
MaxED's avatar
MaxED committed

MaxED's avatar
MaxED committed
            //...to specified location preserving relative positioning (that's harder)
            if (coordinates.Length == 1) {//just move it there
                translatedCoords[0] = new Vector3D(direction.x, direction.y, coordinates[0].z);
                return translatedCoords;
            }
MaxED's avatar
MaxED committed

MaxED's avatar
MaxED committed
            //we need some reference
            float minX = coordinates[0].x;
            float maxX = minX;
            float minY = coordinates[0].y;
            float maxY = minY;

            //get bounding coordinates for selected things
            for (int i = 1; i < coordinates.Length; i++) {
                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;
MaxED's avatar
MaxED committed

            Vector2D selectionCenter = new Vector2D(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2);

            //move them
            for (int i = 0; i < coordinates.Length; i++)
                translatedCoords[i] = new Vector3D((float)Math.Round(direction.x - (selectionCenter.x - coordinates[i].x)), (float)Math.Round(direction.y - (selectionCenter.y - coordinates[i].y)), (float)Math.Round(coordinates[i].z));

            return translatedCoords;

		#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];
		}
		// 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's avatar
MaxED committed

		//mxd
		internal VertexData GetVertexData(Vertex v) {
			if(!vertexdata.ContainsKey(v))
				vertexdata[v] = new VertexData(this, v);
			return vertexdata[v];
		}

		//mxd
		internal void UpdateVertexHandle(Vertex v) {
			if(!vertices.ContainsKey(v))
				vertices.Add(v, new VisualVertexPair(new BaseVisualVertex(this, v, true), new BaseVisualVertex(this, v, false)));
			else
MaxED's avatar
MaxED committed
		}
		// This rebuilds the sector data
		// This requires that the blockmap is up-to-date!
		internal void RebuildElementData()
		{
MaxED's avatar
MaxED committed
			//mxd
            if (!gzdoomRenderingEffects) {
				if(sectordata != null && sectordata.Count > 0) {
					//rebuild sectors with effects
					foreach(KeyValuePair<Sector, SectorData> group in sectordata)
						group.Value.Reset();
				}

				//remove all vertex handles from selection
				if(vertices != null && vertices.Count > 0) {
					foreach(IVisualEventReceiver i in selectedobjects){
						if(i is BaseVisualVertex) RemoveSelectedObject(i);
					}
				}
            }

            Dictionary<int, List<Sector>> sectortags = new Dictionary<int, List<Sector>>();
            sectordata = new Dictionary<Sector, SectorData>(General.Map.Map.Sectors.Count);
            thingdata = new Dictionary<Thing, ThingData>(General.Map.Map.Things.Count);

MaxED's avatar
MaxED committed
			if(General.Map.UDMF) {
				vertexdata = new Dictionary<Vertex, VertexData>(General.Map.Map.Vertices.Count); //mxd
				vertices.Clear();
			}

            if (!gzdoomRenderingEffects) return; //mxd
			
			// Find all sector who's tag is not 0 and hash them so that we can find them quicly
			foreach(Sector s in General.Map.Map.Sectors)
			{
				if(s.Tag != 0)
				{
					if(!sectortags.ContainsKey(s.Tag)) sectortags[s.Tag] = new List<Sector>();
					sectortags[s.Tag].Add(s);
				}
			}

			// Find sectors with 3 vertices, because they can be sloped
			foreach(Sector s in General.Map.Map.Sectors)
			{
MaxED's avatar
MaxED committed
				// ========== Thing vertex slope, vertices with UDMF vertex offsets ==========
MaxED's avatar
MaxED committed
					if(General.Map.UDMF) //mxd
						GetSectorData(s).AddEffectVertexOffset();

					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
						VisualBlockEntry b = blockmap.GetBlock(blockmap.GetBlockCoordinates(v.Position));
						foreach(Thing t in b.Things) {
							if((Vector2D)t.Position == v.Position) {
								if(t.Type == 1504)
									slopefloorthings.Add(t);
								else if(t.Type == 1505)
									slopeceilingthings.Add(t);
							}
						}
					}

					// 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 linedefs (such as line slopes)
			foreach(Linedef l in General.Map.Map.Linedefs)
			{
				// ========== Plane Align (see http://zdoom.org/wiki/Plane_Align) ==========
				if(l.Action == 181)
				{
					// Slope front
					if(((l.Args[0] == 1) || (l.Args[1] == 1)) && (l.Front != null))
					{
						SectorData sd = GetSectorData(l.Front.Sector);
						sd.AddEffectLineSlope(l);
					}
					
					// Slope back
					if(((l.Args[0] == 2) || (l.Args[1] == 2)) && (l.Back != null))
					{
						SectorData sd = GetSectorData(l.Back.Sector);
						sd.AddEffectLineSlope(l);
					}
				}
				// ========== Sector 3D floor (see http://zdoom.org/wiki/Sector_Set3dFloor) ==========
				else if((l.Action == 160) && (l.Front != null))
				{
					int sectortag = l.Args[0] + (l.Args[4] << 8);
					if(sectortags.ContainsKey(sectortag))
					{
						List<Sector> sectors = sectortags[sectortag];
						foreach(Sector s in sectors)
						{
							SectorData sd = GetSectorData(s);
							sd.AddEffect3DFloor(l);
						}
					}
				}
				// ========== Transfer Brightness (see http://zdoom.org/wiki/ExtraFloor_LightOnly) =========
				else if((l.Action == 50) && (l.Front != null))
				{
					if(sectortags.ContainsKey(l.Args[0]))
					{
						List<Sector> sectors = sectortags[l.Args[0]];
						foreach(Sector s in sectors)
						{
							SectorData sd = GetSectorData(s);
							sd.AddEffectBrightnessLevel(l);
						}
					}
				}
			}

			// Find interesting things (such as sector slopes)
			foreach(Thing t in General.Map.Map.Things)
			{
				// ========== Copy slope ==========
				if((t.Type == 9510) || (t.Type == 9511))
				{
					t.DetermineSector(blockmap);
					if(t.Sector != null)
					{
						SectorData sd = GetSectorData(t.Sector);
						sd.AddEffectCopySlope(t);
					}
				}
				// ========== Thing line slope ==========
				else if((t.Type == 9500) || (t.Type == 9501))
				{
					t.DetermineSector(blockmap);
					if(t.Sector != null)
					{
						SectorData sd = GetSectorData(t.Sector);
						sd.AddEffectThingLineSlope(t);
					}
				}
			}
		}
		
		#endregion

		#region ================== Events

		// Help!
		public override void OnHelp()
		{
			General.ShowHelp("e_visual.html");
		}
		// When entering this mode
		public override void OnEngage()
		{
			base.OnEngage();
MaxED's avatar
MaxED committed

			//mxd
			useSelectionFromClassicMode = BuilderPlug.Me.SyncSelection ? !General.Interface.ShiftState : General.Interface.ShiftState;
			
			// Read settings
			cameraflooroffset = General.Map.Config.ReadSetting("cameraflooroffset", cameraflooroffset);
			cameraceilingoffset = General.Map.Config.ReadSetting("cameraceilingoffset", cameraceilingoffset);
		// When returning to another mode
		public override void OnDisengage()
		{
			base.OnDisengage();
MaxED's avatar
MaxED committed

			//mxd
			if(BuilderPlug.Me.SyncSelection ? !General.Interface.ShiftState : General.Interface.ShiftState) {
				//clear previously selected stuff
				General.Map.Map.ClearAllSelected();
				
				//refill selection
				List<Sector> selectedSectors = new List<Sector>();
				List<Linedef> selectedLines = new List<Linedef>();
				List<Vertex> selectedVertices = new List<Vertex>();

				foreach(IVisualEventReceiver obj in selectedobjects) {
					if(obj is BaseVisualThing) {
						((BaseVisualThing)obj).Thing.Selected = true;
					} else if(obj is VisualFloor || obj is VisualCeiling) {
						VisualGeometry vg = (VisualGeometry)obj;

						if(vg.Sector != null && vg.Sector.Sector != null && !selectedSectors.Contains(vg.Sector.Sector)) {
							selectedSectors.Add(vg.Sector.Sector);

							foreach(Sidedef s in vg.Sector.Sector.Sidedefs){
								if(!selectedLines.Contains(s.Line))
									selectedLines.Add(s.Line);
							}
						}
					} 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 && !selectedLines.Contains(vg.Sidedef.Line))
							selectedLines.Add(vg.Sidedef.Line);
					}
				}

				foreach(Sector s in selectedSectors) 
					s.Selected = true;

				foreach(Linedef l in selectedLines) {
					l.Selected = true;

					if(!selectedVertices.Contains(l.Start))
						selectedVertices.Add(l.Start);
					if(!selectedVertices.Contains(l.End))
						selectedVertices.Add(l.End);
				}

				foreach(Vertex v in selectedVertices)
					v.Selected = true;
			}

MaxED's avatar
MaxED committed
            copyBuffer.Clear(); //mxd
		public override void OnProcess(float deltatime)
		{
			// Process things?
			base.ProcessThings = (BuilderPlug.Me.ShowVisualThings != 0);
			
			// Setup the move multiplier depending on gravity
			Vector3D movemultiplier = new Vector3D(1.0f, 1.0f, 1.0f);
			if(BuilderPlug.Me.UseGravity) movemultiplier.z = 0.0f;
			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.LightLevels[0];
				float floorheight = floorlevel.plane.GetZ(General.Map.VisualCamera.Position);
				if(General.Map.VisualCamera.Position.z < (floorheight + cameraflooroffset + 0.1f))
				{
					// Stay above floor
					gravity = new Vector3D(0.0f, 0.0f, 0.0f);
					General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x,
																	 General.Map.VisualCamera.Position.y,
																	 floorheight + cameraflooroffset);
					gravity.z += GRAVITY * deltatime;
					if(gravity.z > 3.0f) gravity.z = 3.0f;

					// Test if we don't go through a floor
					if((General.Map.VisualCamera.Position.z + gravity.z) < (floorheight + cameraflooroffset + 0.1f))
					{
						// Stay above floor
						gravity = new Vector3D(0.0f, 0.0f, 0.0f);
						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?
				feetposition = General.Map.VisualCamera.Position - new Vector3D(0, 0, cameraflooroffset - 7.0f);
				SectorLevel ceillevel = sd.GetCeilingAbove(feetposition) ?? sd.LightLevels[sd.LightLevels.Count - 1];
				float ceilheight = ceillevel.plane.GetZ(General.Map.VisualCamera.Position);
				if(General.Map.VisualCamera.Position.z > (ceilheight - cameraceilingoffset - 0.01f))
				{
					// Stay below ceiling
					General.Map.VisualCamera.Position = new Vector3D(General.Map.VisualCamera.Position.x,
																	 General.Map.VisualCamera.Position.y,
																	 ceilheight - cameraceilingoffset);
				}
			}
			else
			{
				gravity = new Vector3D(0.0f, 0.0f, 0.0f);
			}
			
			// Do processing
			base.OnProcess(deltatime);

			// Process visible geometry
			foreach(IVisualEventReceiver g in visiblegeometry)
			{
				g.OnProcess(deltatime);
			}
			
			// Time to pick a new target?
MaxED's avatar
MaxED committed
			if(General.Clock.CurrentTime > (lastpicktime + PICK_INTERVAL))
			{
				PickTargetUnlocked();
MaxED's avatar
MaxED committed
				lastpicktime = General.Clock.CurrentTime;
			}
			
			// The mouse is always in motion
			MouseEventArgs args = new MouseEventArgs(General.Interface.MouseButtons, 0, 0, 0, 0);
			OnMouseMove(args);
		}
		
		// This draws a frame
		public override void OnRedrawDisplay()
		{
			// Start drawing
			if(renderer.Start())
			{
				// Use fog!
				renderer.SetFogMode(true);

				// Set target for highlighting
                renderer.ShowSelection = General.Settings.GZOldHighlightMode || BuilderPlug.Me.UseHighlight; //mxd
				if(BuilderPlug.Me.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);
				}
MaxED's avatar
MaxED committed

				//mxd
				if(General.Map.UDMF && General.Settings.GZShowVisualVertices && vertices.Count > 0) {
					List<VisualVertex> verts = new List<VisualVertex>();

					foreach(KeyValuePair<Vertex, VisualVertexPair> pair in vertices)
						verts.AddRange(pair.Value.Vertices);

					renderer.AddVisualVertices(verts.ToArray());
				}
				
				// Done rendering geometry
				renderer.FinishGeometry();
				
				// Render crosshair
				renderer.RenderCrosshair();
				
				// Present!
				renderer.Finish();
			}
		}