diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index 72797df08ca2ca1a0bef61d8b0dd3781f8099d9c..0983fdadf84ec894171b9d6cdc80a3053d91f8e5 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -3,7 +3,7 @@ <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProductVersion>8.0.50727</ProductVersion> + <ProductVersion>9.0.30729</ProductVersion> <SchemaVersion>2.0</SchemaVersion> <ProjectGuid>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</ProjectGuid> <OutputType>WinExe</OutputType> diff --git a/Source/Core/Config/ProgramConfiguration.cs b/Source/Core/Config/ProgramConfiguration.cs index 341665743a637b8af41055450a2f2ed6e3a96300..bff438246c716214044a3da048ca0ef914ddf8d6 100644 --- a/Source/Core/Config/ProgramConfiguration.cs +++ b/Source/Core/Config/ProgramConfiguration.cs @@ -48,13 +48,14 @@ namespace CodeImp.DoomBuilder.Config private Configuration cfg; // Cached variables - private int undolevels; + //private int undolevels; private bool blackbrowsers; private int visualfov; private float visualmousesensx; private float visualmousesensy; private int imagebrightness; private float doublesidedalpha; + private byte doublesidedalphabyte; private float backgroundalpha; private bool qualitydisplay; private bool squarethings; @@ -92,11 +93,12 @@ namespace CodeImp.DoomBuilder.Config #region ================== Properties internal Configuration Config { get { return cfg; } } - public int UndoLevels { get { return undolevels; } internal set { undolevels = value; } } + //public int UndoLevels { get { return undolevels; } internal set { undolevels = value; } } public bool BlackBrowsers { get { return blackbrowsers; } internal set { blackbrowsers = value; } } public int VisualFOV { get { return visualfov; } internal set { visualfov = value; } } public int ImageBrightness { get { return imagebrightness; } internal set { imagebrightness = value; } } - public float DoubleSidedAlpha { get { return doublesidedalpha; } internal set { doublesidedalpha = value; } } + public float DoubleSidedAlpha { get { return doublesidedalpha; } internal set { doublesidedalpha = value; doublesidedalphabyte = (byte)(doublesidedalpha * 255f); } } + public byte DoubleSidedAlphaByte { get { return doublesidedalphabyte; } } public float BackgroundAlpha { get { return backgroundalpha; } internal set { backgroundalpha = value; } } public float VisualMouseSensX { get { return visualmousesensx; } internal set { visualmousesensx = value; } } public float VisualMouseSensY { get { return visualmousesensy; } internal set { visualmousesensy = value; } } @@ -153,12 +155,13 @@ namespace CodeImp.DoomBuilder.Config { // Read the cache variables blackbrowsers = cfg.ReadSetting("blackbrowsers", false); - undolevels = cfg.ReadSetting("undolevels", 20); + //undolevels = cfg.ReadSetting("undolevels", 20); visualfov = cfg.ReadSetting("visualfov", 80); visualmousesensx = cfg.ReadSetting("visualmousesensx", 40f); visualmousesensy = cfg.ReadSetting("visualmousesensy", 40f); imagebrightness = cfg.ReadSetting("imagebrightness", 3); doublesidedalpha = cfg.ReadSetting("doublesidedalpha", 0.4f); + doublesidedalphabyte = (byte)(doublesidedalpha * 255f); backgroundalpha = cfg.ReadSetting("backgroundalpha", 1.0f); qualitydisplay = cfg.ReadSetting("qualitydisplay", true); squarethings = cfg.ReadSetting("squarethings", false); @@ -197,7 +200,7 @@ namespace CodeImp.DoomBuilder.Config // Write the cache variables cfg.WriteSetting("blackbrowsers", blackbrowsers); - cfg.WriteSetting("undolevels", undolevels); + //cfg.WriteSetting("undolevels", undolevels); cfg.WriteSetting("visualfov", visualfov); cfg.WriteSetting("visualmousesensx", visualmousesensx); cfg.WriteSetting("visualmousesensy", visualmousesensy); @@ -363,7 +366,7 @@ namespace CodeImp.DoomBuilder.Config { t.Type = defaultthingtype; t.Rotate(defaultthingangle); - foreach(string f in defaultthingflags) t.Flags[f] = true; + foreach(string f in defaultthingflags) t.SetFlag(f, true); } // This attempts to find the default drawing settings diff --git a/Source/Core/Controls/FieldsEditorControl.cs b/Source/Core/Controls/FieldsEditorControl.cs index 8794c14a6d21b3cf7730c2f9ba605e1835b6ba9c..9e0d9f0edb16505c2c391f4925fc38d4eb33d539 100644 --- a/Source/Core/Controls/FieldsEditorControl.cs +++ b/Source/Core/Controls/FieldsEditorControl.cs @@ -199,6 +199,8 @@ namespace CodeImp.DoomBuilder.Controls // This applies the current fields to a UniFields object public void Apply(UniFields tofields) { + tofields.BeforeFieldsChange(); + // Go for all the fields UniFields tempfields = new UniFields(tofields); foreach(KeyValuePair<string, UniValue> f in tempfields) diff --git a/Source/Core/Editing/UndoManager.cs b/Source/Core/Editing/UndoManager.cs index 391453779991d33d530f70868fb4bb7234744496..63780d9a24fef77f7311a845bafd37353527a32b 100644 --- a/Source/Core/Editing/UndoManager.cs +++ b/Source/Core/Editing/UndoManager.cs @@ -25,6 +25,7 @@ using System.Threading; using System.Windows.Forms; using System.IO; using System.Reflection; +using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Plugins; using CodeImp.DoomBuilder.Windows; using CodeImp.DoomBuilder.IO; @@ -41,6 +42,39 @@ namespace CodeImp.DoomBuilder.Editing { #region ================== Constants + // Maximum undo/redo levels + private const int MAX_UNDO_LEVELS = 1000; + + // Default stream capacity + private const int STREAM_CAPACITY = 1000; + + // Stream codes + // "Prp" stands for property changes (uses the ReadWrite functions) + // "Ref" stands for reference changes + private enum StreamCodes : byte + { + AddVertex, + RemVertex, + PrpVertex, + AddLinedef, + RemLinedef, + PrpLinedef, + RefLinedefStart, + RefLinedefEnd, + RefLinedefFront, + RefLinedefBack, + AddSidedef, + RemSidedef, + PrpSidedef, + RefSidedefSector, + AddSector, + RemSector, + PrpSector, + AddThing, + RemThing, + PrpThing, + } + #endregion #region ================== Variables @@ -57,6 +91,17 @@ namespace CodeImp.DoomBuilder.Editing // Unique tickets private int ticketid; + // Writing stream + private UndoSnapshot snapshot; + private bool isundosnapshot; + private MemoryStream stream; + private SerializerStream ss; + private int commandswritten; + private long prevstreamlength; + private bool ignorepropchanges; + private bool isrecordingcommand; + private MapElement propsrecorded; + // Background thread private volatile bool dobackgroundwork; private Thread backgroundthread; @@ -68,10 +113,40 @@ namespace CodeImp.DoomBuilder.Editing #region ================== Properties - public UndoSnapshot NextUndo { get { if(undos.Count > 0) return undos[0]; else return null; } } - public UndoSnapshot NextRedo { get { if(redos.Count > 0) return redos[0]; else return null; } } + public UndoSnapshot NextUndo + { + get + { + if(!isundosnapshot && (snapshot != null)) + return snapshot; + else if(undos.Count > 0) + return undos[0]; + else + return null; + } + } + + public UndoSnapshot NextRedo + { + get + { + if(isundosnapshot && (snapshot != null)) + return snapshot; + else if(redos.Count > 0) + return redos[0]; + else + return null; + } + } + public bool IsDisposed { get { return isdisposed; } } - + + /// <summary> + /// This can be used to ignore insignificant element property changes. Any property changes + /// that are made while this is set to True will not be undoable. Use with great care! + /// </summary> + public bool IgnorePropChanges { get { return ignorepropchanges; } set { ignorepropchanges = value; } } + #endregion #region ================== Constructor / Disposer @@ -81,9 +156,9 @@ namespace CodeImp.DoomBuilder.Editing { // Initialize ticketid = 1; - undos = new List<UndoSnapshot>(General.Settings.UndoLevels + 1); - redos = new List<UndoSnapshot>(General.Settings.UndoLevels + 1); - + undos = new List<UndoSnapshot>(MAX_UNDO_LEVELS + 1); + redos = new List<UndoSnapshot>(MAX_UNDO_LEVELS + 1); + // Bind any methods General.Actions.BindMethods(this); @@ -154,7 +229,7 @@ namespace CodeImp.DoomBuilder.Editing UndoSnapshot u; // Too many? - if(list.Count > General.Settings.UndoLevels) + if(list.Count > MAX_UNDO_LEVELS) { // Remove one and dispose map u = list[list.Count - 1]; @@ -175,23 +250,23 @@ namespace CodeImp.DoomBuilder.Editing dobackgroundwork = false; int undolevel = 0; - UndoSnapshot snapshot; + UndoSnapshot us; while(true) { // Get the next snapshot or leave lock(undos) { if(undolevel < undos.Count) - snapshot = undos[undolevel]; + us = undos[undolevel]; else break; } // Write to file or load from file, if needed - if(snapshot.StoreOnDisk && !snapshot.IsOnDisk) - snapshot.WriteToFile(); - else if(!snapshot.StoreOnDisk && snapshot.IsOnDisk) - snapshot.RestoreFromFile(); + if(us.StoreOnDisk && !us.IsOnDisk) + us.WriteToFile(); + else if(!us.StoreOnDisk && us.IsOnDisk) + us.RestoreFromFile(); // Next undolevel++; @@ -204,16 +279,16 @@ namespace CodeImp.DoomBuilder.Editing lock(redos) { if(redolevel < redos.Count) - snapshot = redos[redolevel]; + us = redos[redolevel]; else break; } // Write to file or load from file, if needed - if(snapshot.StoreOnDisk && !snapshot.IsOnDisk) - snapshot.WriteToFile(); - else if(!snapshot.StoreOnDisk && snapshot.IsOnDisk) - snapshot.RestoreFromFile(); + if(us.StoreOnDisk && !us.IsOnDisk) + us.WriteToFile(); + else if(!us.StoreOnDisk && us.IsOnDisk) + us.RestoreFromFile(); // Next redolevel++; @@ -225,6 +300,122 @@ namespace CodeImp.DoomBuilder.Editing } } + // This starts a new recording + private void StartRecording(string description) + { + stream = new MemoryStream(STREAM_CAPACITY); + ss = new SerializerStream(stream); + ss.Begin(); + commandswritten = 0; + propsrecorded = null; + snapshot = new UndoSnapshot(description, stream, ticketid); + } + + // This finishes recording + private void FinishRecording() + { + // End current recording + if(stream != null) + { + propsrecorded = null; + ss.wInt(commandswritten); + ss.End(); + } + } + + // This begins writing to the record stream + private bool BeginRecordData(StreamCodes code) + { + if(ss == null) return false; + isrecordingcommand = true; + prevstreamlength = stream.Length; + ss.wByte((byte)code); + return true; + } + + // This ends writing to the record stream + private void EndRecordData() + { + // We write the difference in bytes to the stream so that + // the stream can be read from the end backwards + int delta = (int)(stream.Length - prevstreamlength); + ss.wInt(delta); + commandswritten++; + isrecordingcommand = false; + } + + // This outputs record info, if desired + private void LogRecordInfo(string info) + { + #if DEBUG + //General.WriteLogLine(info); + #endif + } + + // This plays back a stream in reverse + private void PlaybackStream(MemoryStream pstream) + { + General.Map.Map.AutoRemove = false; + + General.Map.Map.ClearAllMarks(false); + + pstream.Seek(0, SeekOrigin.Begin); + DeserializerStream ds = new DeserializerStream(pstream); + ds.Begin(); + + if(pstream.Length > 4) + { + // Start at the end + pstream.Seek(ds.EndPosition - 4, SeekOrigin.Begin); + int numcmds; ds.rInt(out numcmds); + pstream.Seek(-8, SeekOrigin.Current); + while(numcmds > 0) + { + // Go back up the stream to the beginning of the prev command + int len; ds.rInt(out len); + pstream.Seek(-(len + 4), SeekOrigin.Current); + + // Play back the command + long beginpos = pstream.Position; + byte cmd; ds.rByte(out cmd); + switch((StreamCodes)cmd) + { + case StreamCodes.AddVertex: PlayAddVertex(ds); break; + case StreamCodes.RemVertex: PlayRemVertex(ds); break; + case StreamCodes.PrpVertex: PlayPrpVertex(ds); break; + case StreamCodes.AddLinedef: PlayAddLinedef(ds); break; + case StreamCodes.RemLinedef: PlayRemLinedef(ds); break; + case StreamCodes.PrpLinedef: PlayPrpLinedef(ds); break; + case StreamCodes.RefLinedefStart: PlayRefLinedefStart(ds); break; + case StreamCodes.RefLinedefEnd: PlayRefLinedefEnd(ds); break; + case StreamCodes.RefLinedefFront: PlayRefLinedefFront(ds); break; + case StreamCodes.RefLinedefBack: PlayRefLinedefBack(ds); break; + case StreamCodes.AddSidedef: PlayAddSidedef(ds); break; + case StreamCodes.RemSidedef: PlayRemSidedef(ds); break; + case StreamCodes.PrpSidedef: PlayPrpSidedef(ds); break; + case StreamCodes.RefSidedefSector: PlayRefSidedefSector(ds); break; + case StreamCodes.AddSector: PlayAddSector(ds); break; + case StreamCodes.RemSector: PlayRemSector(ds); break; + case StreamCodes.PrpSector: PlayPrpSector(ds); break; + case StreamCodes.AddThing: PlayAddThing(ds); break; + case StreamCodes.RemThing: PlayRemThing(ds); break; + case StreamCodes.PrpThing: PlayPrpThing(ds); break; + } + + // Sanity check + if((beginpos + len) != pstream.Position) + throw new Exception("The last command did not read the same amount of data that was written for this command!"); + + // Go back for next command + pstream.Seek(-(len + 4), SeekOrigin.Current); + + numcmds--; + } + } + + General.Map.Map.AutoRemove = true; + } + #endregion #region ================== Public Methods @@ -272,25 +463,30 @@ namespace CodeImp.DoomBuilder.Editing (groupid == 0) || (lastgroupid == 0) || (groupid != lastgroupid) || (grouptag != lastgrouptag)) { + FinishRecording(); + // Next ticket id if(++ticketid == int.MaxValue) ticketid = 1; - + General.WriteLogLine("Creating undo snapshot \"" + description + "\", Source " + groupsourcename + ", Group " + groupid + ", Tag " + grouptag + ", Ticket ID " + ticketid + "..."); - - // Make a snapshot - u = new UndoSnapshot(description, General.Map.Map.Serialize(), ticketid); - - lock(undos) + + if((snapshot != null) && !isundosnapshot) { - // The current top of the stack can now be written to disk - // because it is no longer the next immediate undo level - if(undos.Count > 0) undos[0].StoreOnDisk = true; - - // Put it on the stack - undos.Insert(0, u); - LimitUndoRedoLevel(undos); + lock(undos) + { + // The current top of the stack can now be written to disk + // because it is no longer the next immediate undo level + if(undos.Count > 0) undos[0].StoreOnDisk = true; + + // Put it on the stack + undos.Insert(0, snapshot); + LimitUndoRedoLevel(undos); + } } - + + StartRecording(description); + isundosnapshot = false; + // Clear all redos ClearRedos(); @@ -325,16 +521,18 @@ namespace CodeImp.DoomBuilder.Editing if(ticket == undos[0].TicketID) { General.WriteLogLine("Withdrawing undo snapshot \"" + undos[0].Description + "\", Ticket ID " + ticket + "..."); - - lock(undos) + + if(snapshot != null) { - // Remove the last made undo - undos[0].Dispose(); - undos.RemoveAt(0); - - // Make the current top of the stack load into memory - // because it just became the next immediate undo level - if(undos.Count > 0) undos[0].StoreOnDisk = false; + // Just trash this recording + // You must call CreateUndo first before making any more changes + FinishRecording(); + isundosnapshot = false; + snapshot = null; + } + else + { + throw new Exception("No undo is recording that can be withdrawn"); } // Update @@ -348,12 +546,12 @@ namespace CodeImp.DoomBuilder.Editing [BeginAction("undo")] public void PerformUndo() { - UndoSnapshot u, r; + UndoSnapshot u = null; Cursor oldcursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; // Anything to undo? - if(undos.Count > 0) + if((undos.Count > 0) || ((snapshot != null) && !isundosnapshot)) { // Let the plugins know if(General.Plugins.OnUndoBegin()) @@ -365,54 +563,80 @@ namespace CodeImp.DoomBuilder.Editing // This returns false when mode was not volatile if(!General.CancelVolatileMode()) { - lock(undos) + FinishRecording(); + + if(isundosnapshot) { - // Get undo snapshot - u = undos[0]; - undos.RemoveAt(0); - - // Make the current top of the stack load into memory - // because it just became the next immediate undo level - if(undos.Count > 0) undos[0].StoreOnDisk = false; + if(snapshot != null) + { + // This snapshot was made by a previous call to this + // function and should go on the redo list + lock(redos) + { + // The current top of the stack can now be written to disk + // because it is no longer the next immediate redo level + if(redos.Count > 0) redos[0].StoreOnDisk = true; + + // Put it on the stack + redos.Insert(0, snapshot); + LimitUndoRedoLevel(redos); + } + } } - + else + { + // The snapshot can be undone immediately and it will + // be recorded for the redo list + if(snapshot != null) + u = snapshot; + } + + // No immediate snapshot to undo? Then get the next one from the stack + if(u == null) + { + lock(undos) + { + // Get undo snapshot + u = undos[0]; + undos.RemoveAt(0); + + // Make the current top of the stack load into memory + // because it just became the next immediate undo level + if(undos.Count > 0) undos[0].StoreOnDisk = false; + } + } + General.WriteLogLine("Performing undo \"" + u.Description + "\", Ticket ID " + u.TicketID + "..."); General.Interface.DisplayStatus(StatusType.Action, u.Description + " undone."); - + // Make a snapshot for redo - r = new UndoSnapshot(u, General.Map.Map.Serialize()); - - lock(redos) - { - // The current top of the stack can now be written to disk - // because it is no longer the next immediate undo level - if(redos.Count > 0) redos[0].StoreOnDisk = true; - - // Put it on the stack - redos.Insert(0, r); - LimitUndoRedoLevel(redos); - } + StartRecording(u.Description); + isundosnapshot = true; // Reset grouping lastgroupplugin = null; - // Change map set - MemoryStream data = u.GetMapData(); - General.Map.ChangeMapSet(new MapSet(data)); + // Play back the stream in reverse + MemoryStream data = u.GetStream(); + PlaybackStream(data); data.Dispose(); // Remove selection - General.Map.Map.ClearAllMarks(false); General.Map.Map.ClearAllSelected(); - + + // Update map + General.Map.Map.Update(); + foreach(Thing t in General.Map.Map.Things) if(t.Marked) t.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + General.Map.Data.UpdateUsedTextures(); + General.MainWindow.RedrawDisplay(); + // Done General.Editing.Mode.OnUndoEnd(); General.Plugins.OnUndoEnd(); - // Update + // Update interface dobackgroundwork = true; - General.Map.Data.UpdateUsedTextures(); - General.MainWindow.RedrawDisplay(); General.MainWindow.UpdateInterface(); } } @@ -426,12 +650,12 @@ namespace CodeImp.DoomBuilder.Editing [BeginAction("redo")] public void PerformRedo() { - UndoSnapshot u, r; + UndoSnapshot r = null; Cursor oldcursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; // Anything to redo? - if(redos.Count > 0) + if((redos.Count > 0) || ((snapshot != null) && isundosnapshot)) { // Let the plugins know if(General.Plugins.OnRedoBegin()) @@ -442,54 +666,79 @@ namespace CodeImp.DoomBuilder.Editing // Cancel volatile mode, if any General.CancelVolatileMode(); - lock(redos) + FinishRecording(); + + if(isundosnapshot) { - // Get redo snapshot - r = redos[0]; - redos.RemoveAt(0); - - // Make the current top of the stack load into memory - // because it just became the next immediate undo level - if(redos.Count > 0) redos[0].StoreOnDisk = false; + // This snapshot was started by PerformUndo, which means + // it can directly be used to redo to previous undo + if(snapshot != null) + r = snapshot; + } + else + { + if(snapshot != null) + { + // This snapshot was made by a previous call to this + // function and should go on the undo list + lock(undos) + { + // The current top of the stack can now be written to disk + // because it is no longer the next immediate undo level + if(undos.Count > 0) undos[0].StoreOnDisk = true; + + // Put it on the stack + undos.Insert(0, snapshot); + LimitUndoRedoLevel(undos); + } + } + } + + // No immediate snapshot to redo? Then get the next one from the stack + if(r == null) + { + lock(redos) + { + // Get redo snapshot + r = redos[0]; + redos.RemoveAt(0); + + // Make the current top of the stack load into memory + // because it just became the next immediate undo level + if(redos.Count > 0) redos[0].StoreOnDisk = false; + } } General.WriteLogLine("Performing redo \"" + r.Description + "\", Ticket ID " + r.TicketID + "..."); General.Interface.DisplayStatus(StatusType.Action, r.Description + " redone."); - // Make a snapshot for undo - u = new UndoSnapshot(r, General.Map.Map.Serialize()); - - lock(undos) - { - // The current top of the stack can now be written to disk - // because it is no longer the next immediate undo level - if(undos.Count > 0) undos[0].StoreOnDisk = true; - - // Put it on the stack - undos.Insert(0, u); - LimitUndoRedoLevel(undos); - } + StartRecording(r.Description); + isundosnapshot = false; // Reset grouping lastgroupplugin = null; - // Change map set - MemoryStream data = r.GetMapData(); - General.Map.ChangeMapSet(new MapSet(data)); + // Play back the stream in reverse + MemoryStream data = r.GetStream(); + PlaybackStream(data); data.Dispose(); // Remove selection - General.Map.Map.ClearAllMarks(false); General.Map.Map.ClearAllSelected(); + // Update map + General.Map.Map.Update(); + foreach(Thing t in General.Map.Map.Things) if(t.Marked) t.UpdateConfiguration(); + General.Map.ThingsFilter.Update(); + General.Map.Data.UpdateUsedTextures(); + General.MainWindow.RedrawDisplay(); + // Done General.Editing.Mode.OnRedoEnd(); General.Plugins.OnRedoEnd(); - // Update + // Update interface dobackgroundwork = true; - General.Map.Data.UpdateUsedTextures(); - General.MainWindow.RedrawDisplay(); General.MainWindow.UpdateInterface(); } } @@ -499,5 +748,428 @@ namespace CodeImp.DoomBuilder.Editing } #endregion + + #region ================== Record and Playback + + internal void RecAddVertex(Vertex v) + { + if(!BeginRecordData(StreamCodes.AddVertex)) return; + ss.wInt(v.Index); + EndRecordData(); + + LogRecordInfo("REC: Adding vertex " + v.Index + " at " + v.Position); + propsrecorded = null; + } + + internal void PlayAddVertex(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Removing vertex " + index); + Vertex v = General.Map.Map.GetVertexByIndex(index); + v.Dispose(); + } + + internal void RecRemVertex(Vertex v) + { + if(!BeginRecordData(StreamCodes.RemVertex)) return; + ss.wInt(v.Index); + ss.wVector2D(v.Position); + v.ReadWrite(ss); + EndRecordData(); + + LogRecordInfo("REC: Removing vertex " + v.Index + " (at " + v.Position + ")"); + propsrecorded = null; + } + + internal void PlayRemVertex(DeserializerStream ds) + { + int index; ds.rInt(out index); + Vector2D pos; ds.rVector2D(out pos); + LogRecordInfo("PLY: Adding vertex " + index + " at " + pos); + Vertex v = General.Map.Map.CreateVertex(index, pos); + v.ReadWrite(ds); + v.Marked = true; + } + + internal void RecPrpVertex(Vertex v) + { + if(!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(v, propsrecorded)) + { + if(!BeginRecordData(StreamCodes.PrpVertex)) return; + ss.wInt(v.Index); + v.ReadWrite(ss); + EndRecordData(); + propsrecorded = v; + } + } + + internal void PlayPrpVertex(DeserializerStream ds) + { + int index; ds.rInt(out index); + Vertex v = General.Map.Map.GetVertexByIndex(index); + v.ReadWrite(ds); + v.Marked = true; + } + + internal void RecAddLinedef(Linedef l) + { + if(!BeginRecordData(StreamCodes.AddLinedef)) return; + ss.wInt(l.Index); + EndRecordData(); + + LogRecordInfo("REC: Adding linedef " + l.Index + " from " + ((l.Start != null) ? l.Start.Index : -1) + " to " + ((l.End != null) ? l.End.Index : -1)); + propsrecorded = null; + } + + internal void PlayAddLinedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Removing linedef " + index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + l.Dispose(); + } + + internal void RecRemLinedef(Linedef l) + { + if(!BeginRecordData(StreamCodes.RemLinedef)) return; + ss.wInt(l.Index); + ss.wInt(l.Start.Index); + ss.wInt(l.End.Index); + l.ReadWrite(ss); + EndRecordData(); + + LogRecordInfo("REC: Removing linedef " + l.Index + " (from " + ((l.Start != null) ? l.Start.Index : -1) + " to " + ((l.End != null) ? l.End.Index : -1) + ")"); + propsrecorded = null; + } + + internal void PlayRemLinedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + int sindex; ds.rInt(out sindex); + int eindex; ds.rInt(out eindex); + LogRecordInfo("PLY: Adding linedef " + index + " from " + sindex + " to " + eindex); + Vertex vs = General.Map.Map.GetVertexByIndex(sindex); + Vertex ve = General.Map.Map.GetVertexByIndex(eindex); + Linedef l = General.Map.Map.CreateLinedef(index, vs, ve); + l.ReadWrite(ds); + l.Marked = true; + } + + internal void RecPrpLinedef(Linedef l) + { + if(!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(l, propsrecorded)) + { + if(!BeginRecordData(StreamCodes.PrpLinedef)) return; + ss.wInt(l.Index); + l.ReadWrite(ss); + EndRecordData(); + propsrecorded = l; + } + } + + internal void PlayPrpLinedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + l.ReadWrite(ds); + l.Marked = true; + } + + internal void RecRefLinedefStart(Linedef l) + { + if(!BeginRecordData(StreamCodes.RefLinedefStart)) return; + ss.wInt(l.Index); + if(l.Start != null) ss.wInt(l.Start.Index); else ss.wInt(-1); + EndRecordData(); + + LogRecordInfo("REC: Setting linedef " + l.Index + " start vertex " + ((l.Start != null) ? l.Start.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefStart(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int vindex; ds.rInt(out vindex); + LogRecordInfo("PLY: Setting linedef " + index + " start vertex " + vindex); + Vertex v = (vindex >= 0) ? General.Map.Map.GetVertexByIndex(vindex) : null; + l.SetStartVertex(v); + l.Marked = true; + } + + internal void RecRefLinedefEnd(Linedef l) + { + if(!BeginRecordData(StreamCodes.RefLinedefEnd)) return; + ss.wInt(l.Index); + if(l.End != null) ss.wInt(l.End.Index); else ss.wInt(-1); + EndRecordData(); + + LogRecordInfo("REC: Setting linedef " + l.Index + " end vertex " + ((l.End != null) ? l.End.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefEnd(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int vindex; ds.rInt(out vindex); + LogRecordInfo("PLY: Setting linedef " + index + " end vertex " + vindex); + Vertex v = (vindex >= 0) ? General.Map.Map.GetVertexByIndex(vindex) : null; + l.SetEndVertex(v); + l.Marked = true; + } + + internal void RecRefLinedefFront(Linedef l) + { + if(!BeginRecordData(StreamCodes.RefLinedefFront)) return; + ss.wInt(l.Index); + if(l.Front != null) ss.wInt(l.Front.Index); else ss.wInt(-1); + EndRecordData(); + + LogRecordInfo("REC: Setting linedef " + l.Index + " front sidedef " + ((l.Front != null) ? l.Front.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefFront(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int sindex; ds.rInt(out sindex); + LogRecordInfo("PLY: Setting linedef " + index + " front sidedef " + sindex); + Sidedef sd = (sindex >= 0) ? General.Map.Map.GetSidedefByIndex(sindex) : null; + l.AttachFront(sd); + l.Marked = true; + sd.Marked = true; + } + + internal void RecRefLinedefBack(Linedef l) + { + if(!BeginRecordData(StreamCodes.RefLinedefBack)) return; + ss.wInt(l.Index); + if(l.Back != null) ss.wInt(l.Back.Index); else ss.wInt(-1); + EndRecordData(); + + LogRecordInfo("REC: Setting linedef " + l.Index + " back sidedef " + ((l.Back != null) ? l.Back.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefLinedefBack(DeserializerStream ds) + { + int index; ds.rInt(out index); + Linedef l = General.Map.Map.GetLinedefByIndex(index); + int sindex; ds.rInt(out sindex); + LogRecordInfo("PLY: Setting linedef " + index + " back sidedef " + sindex); + Sidedef sd = (sindex >= 0) ? General.Map.Map.GetSidedefByIndex(sindex) : null; + l.AttachBack(sd); + l.Marked = true; + sd.Marked = true; + } + + internal void RecAddSidedef(Sidedef s) + { + if(!BeginRecordData(StreamCodes.AddSidedef)) return; + ss.wInt(s.Index); + EndRecordData(); + + LogRecordInfo("REC: Adding sidedef " + s.Index + " to linedef " + s.Line.Index + (s.IsFront ? " front" : " back") + " and sector " + s.Sector.Index); + propsrecorded = null; + } + + internal void PlayAddSidedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Removing sidedef " + index); + Sidedef s = General.Map.Map.GetSidedefByIndex(index); + s.Dispose(); + } + + internal void RecRemSidedef(Sidedef s) + { + if(!BeginRecordData(StreamCodes.RemSidedef)) return; + ss.wInt(s.Index); + ss.wInt(s.Line.Index); + ss.wBool(s.IsFront); + ss.wInt(s.Sector.Index); + s.ReadWrite(ss); + EndRecordData(); + + LogRecordInfo("REC: Removing sidedef " + s.Index + " (from linedef " + s.Line.Index + (s.IsFront ? " front" : " back") + " and sector " + s.Sector.Index + ")"); + propsrecorded = null; + } + + internal void PlayRemSidedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + int dindex; ds.rInt(out dindex); + bool front; ds.rBool(out front); + int sindex; ds.rInt(out sindex); + LogRecordInfo("PLY: Adding sidedef " + index + " to linedef " + dindex + (front ? " front" : " back") + " and sector " + sindex); + Linedef l = General.Map.Map.GetLinedefByIndex(dindex); + Sector s = General.Map.Map.GetSectorByIndex(sindex); + Sidedef sd = General.Map.Map.CreateSidedef(index, l, front, s); + sd.ReadWrite(ds); + sd.Marked = true; + } + + internal void RecPrpSidedef(Sidedef s) + { + if(!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(s, propsrecorded)) + { + if(!BeginRecordData(StreamCodes.PrpSidedef)) return; + ss.wInt(s.Index); + s.ReadWrite(ss); + EndRecordData(); + propsrecorded = s; + } + } + + internal void PlayPrpSidedef(DeserializerStream ds) + { + int index; ds.rInt(out index); + Sidedef s = General.Map.Map.GetSidedefByIndex(index); + s.ReadWrite(ds); + s.Marked = true; + } + + internal void RecRefSidedefSector(Sidedef s) + { + if(!BeginRecordData(StreamCodes.RefSidedefSector)) return; + ss.wInt(s.Index); + if(s.Sector != null) ss.wInt(s.Sector.Index); else ss.wInt(-1); + EndRecordData(); + + LogRecordInfo("REC: Setting sidedef " + s.Index + " sector " + ((s.Sector != null) ? s.Sector.Index : -1)); + propsrecorded = null; + } + + internal void PlayRefSidedefSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + Sidedef sd = General.Map.Map.GetSidedefByIndex(index); + int sindex; ds.rInt(out sindex); + LogRecordInfo("PLY: Setting sidedef " + index + " sector " + sindex); + Sector sc = (sindex >= 0) ? General.Map.Map.GetSectorByIndex(sindex) : null; + sd.SetSector(sc); + sd.Marked = true; + sc.Marked = true; + } + + internal void RecAddSector(Sector s) + { + if(!BeginRecordData(StreamCodes.AddSector)) return; + ss.wInt(s.Index); + EndRecordData(); + + LogRecordInfo("REC: Adding sector " + s.Index); + propsrecorded = null; + } + + internal void PlayAddSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Removing sector " + index); + Sector s = General.Map.Map.GetSectorByIndex(index); + s.Dispose(); + } + + internal void RecRemSector(Sector s) + { + if(!BeginRecordData(StreamCodes.RemSector)) return; + ss.wInt(s.Index); + s.ReadWrite(ss); + EndRecordData(); + + LogRecordInfo("REC: Removing sector " + s.Index); + propsrecorded = null; + } + + internal void PlayRemSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Adding sector " + index); + Sector s = General.Map.Map.CreateSector(index); + s.ReadWrite(ds); + s.Marked = true; + } + + internal void RecPrpSector(Sector s) + { + if(!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(s, propsrecorded)) + { + if(!BeginRecordData(StreamCodes.PrpSector)) return; + ss.wInt(s.Index); + s.ReadWrite(ss); + EndRecordData(); + propsrecorded = s; + } + } + + internal void PlayPrpSector(DeserializerStream ds) + { + int index; ds.rInt(out index); + Sector s = General.Map.Map.GetSectorByIndex(index); + s.ReadWrite(ds); + s.Marked = true; + } + + internal void RecAddThing(Thing t) + { + if(!BeginRecordData(StreamCodes.AddThing)) return; + ss.wInt(t.Index); + EndRecordData(); + + LogRecordInfo("REC: Adding thing " + t.Index); + propsrecorded = null; + } + + internal void PlayAddThing(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Removing thing " + index); + Thing t = General.Map.Map.GetThingByIndex(index); + t.Dispose(); + } + + internal void RecRemThing(Thing t) + { + if(!BeginRecordData(StreamCodes.RemThing)) return; + ss.wInt(t.Index); + t.ReadWrite(ss); + EndRecordData(); + + LogRecordInfo("REC: Removing thing " + t.Index); + propsrecorded = null; + } + + internal void PlayRemThing(DeserializerStream ds) + { + int index; ds.rInt(out index); + LogRecordInfo("PLY: Adding thing " + index); + Thing t = General.Map.Map.CreateThing(index); + t.ReadWrite(ds); + t.Marked = true; + } + + internal void RecPrpThing(Thing t) + { + if(!ignorepropchanges && !isrecordingcommand && !object.ReferenceEquals(t, propsrecorded)) + { + if(!BeginRecordData(StreamCodes.PrpThing)) return; + ss.wInt(t.Index); + t.ReadWrite(ss); + EndRecordData(); + propsrecorded = t; + } + } + + internal void PlayPrpThing(DeserializerStream ds) + { + int index; ds.rInt(out index); + Thing t = General.Map.Map.GetThingByIndex(index); + t.ReadWrite(ds); + t.Marked = true; + } + + #endregion } } diff --git a/Source/Core/Editing/UndoSnapshot.cs b/Source/Core/Editing/UndoSnapshot.cs index 641afe8a1444a6f07401a188c868aafbdc0a6a24..add013941f0c483f64be1ef3ecc21465e19a8f19 100644 --- a/Source/Core/Editing/UndoSnapshot.cs +++ b/Source/Core/Editing/UndoSnapshot.cs @@ -40,7 +40,7 @@ namespace CodeImp.DoomBuilder.Editing { #region ================== Variables - private MemoryStream mapdata; + private MemoryStream recstream; private string filename; private string description; private int ticketid; // For safe withdrawing @@ -62,22 +62,22 @@ namespace CodeImp.DoomBuilder.Editing #region ================== Constructor / Disposer // Constructor - internal UndoSnapshot(string description, MemoryStream mapdata, int ticketid) + internal UndoSnapshot(string description, MemoryStream recstream, int ticketid) { - if(mapdata == null) General.Fail("Argument cannot be null!"); + if(recstream == null) General.Fail("Argument cannot be null!"); this.ticketid = ticketid; this.description = description; - this.mapdata = mapdata; + this.recstream = recstream; this.filename = null; } // Constructor - internal UndoSnapshot(UndoSnapshot info, MemoryStream mapdata) + internal UndoSnapshot(UndoSnapshot info, MemoryStream recstream) { - if(mapdata == null) General.Fail("Argument cannot be null!"); + if(recstream == null) General.Fail("Argument cannot be null!"); this.ticketid = info.ticketid; this.description = info.description; - this.mapdata = mapdata; + this.recstream = recstream; this.filename = null; } @@ -87,8 +87,8 @@ namespace CodeImp.DoomBuilder.Editing lock(this) { isdisposed = true; - if(mapdata != null) mapdata.Dispose(); - mapdata = null; + if(recstream != null) recstream.Dispose(); + recstream = null; if(isondisk) File.Delete(filename); isondisk = false; } @@ -99,15 +99,15 @@ namespace CodeImp.DoomBuilder.Editing #region ================== Methods // This returns the map data - internal MemoryStream GetMapData() + internal MemoryStream GetStream() { lock(this) { // Restore into memory if needed if(isondisk) RestoreFromFile(); - // Return a copy of the buffer - return new MemoryStream(mapdata.ToArray()); + // Return the buffer + return recstream; } } @@ -121,9 +121,9 @@ namespace CodeImp.DoomBuilder.Editing isondisk = true; // Compress data - mapdata.Seek(0, SeekOrigin.Begin); - MemoryStream outstream = new MemoryStream((int)mapdata.Length); - BZip2.Compress(mapdata, outstream, 300000); + recstream.Seek(0, SeekOrigin.Begin); + MemoryStream outstream = new MemoryStream((int)recstream.Length); + BZip2.Compress(recstream, outstream, 300000); // Make temporary file filename = General.MakeTempFilename(General.Map.TempPath, "snapshot"); @@ -132,8 +132,8 @@ namespace CodeImp.DoomBuilder.Editing File.WriteAllBytes(filename, outstream.ToArray()); // Remove data from memory - mapdata.Dispose(); - mapdata = null; + recstream.Dispose(); + recstream = null; outstream.Dispose(); } } @@ -154,7 +154,7 @@ namespace CodeImp.DoomBuilder.Editing MemoryStream outstream = new MemoryStream((int)instream.Length * 4); instream.Seek(0, SeekOrigin.Begin); BZip2.Decompress(instream, outstream); - mapdata = new MemoryStream(outstream.ToArray()); + recstream = new MemoryStream(outstream.ToArray()); // Clean up instream.Dispose(); diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs index 06ab56e41a16513c1f172ddbfe6c24429077f0fc..f30fdfc17c099f445864fc1c4d4986852e18a971 100644 --- a/Source/Core/General/MapManager.cs +++ b/Source/Core/General/MapManager.cs @@ -348,13 +348,17 @@ namespace CodeImp.DoomBuilder General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "..."); io = MapSetIO.Create(config.FormatInterface, tempwad, this); General.WriteLogLine("Reading map data structures from file..."); - try { map = io.Read(map, TEMP_MAP_HEADER); } - catch(Exception e) - { - General.ErrorLogger.Add(ErrorType.Error, "Unable to read the map data structures with the specified configuration. " + e.GetType().Name + ": " + e.Message); - General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK); - return false; - } + #if DEBUG + map = io.Read(map, TEMP_MAP_HEADER); + #else + try { map = io.Read(map, TEMP_MAP_HEADER); } + catch(Exception e) + { + General.ErrorLogger.Add(ErrorType.Error, "Unable to read the map data structures with the specified configuration. " + e.GetType().Name + ": " + e.Message); + General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK); + return false; + } + #endif map.EndAddRemove(); // Load data manager @@ -446,6 +450,8 @@ namespace CodeImp.DoomBuilder { if(t.Type == config.Start3DModeThingType) { + // We're not using SetFlag here, this doesn't have to be undone. + // Please note that this is totally exceptional! List<string> flagkeys = new List<string>(t.Flags.Keys); foreach(string k in flagkeys) t.Flags[k] = false; } diff --git a/Source/Core/Geometry/Tools.cs b/Source/Core/Geometry/Tools.cs index 0cac495772e51d558bfaa4b2a816230fe749ba14..985c7c8bcf3261ae504e5073ec99eff4ea12ca1a 100644 --- a/Source/Core/Geometry/Tools.cs +++ b/Source/Core/Geometry/Tools.cs @@ -566,14 +566,14 @@ namespace CodeImp.DoomBuilder.Geometry { // Create sidedef is needed and ensure it points to the new sector if(ls.Line.Front == null) General.Map.Map.CreateSidedef(ls.Line, true, newsector); - if(ls.Line.Front.Sector != newsector) ls.Line.Front.ChangeSector(newsector); + if(ls.Line.Front.Sector != newsector) ls.Line.Front.SetSector(newsector); ApplyDefaultsToSidedef(ls.Line.Front, sourceside); } else { // Create sidedef is needed and ensure it points to the new sector if(ls.Line.Back == null) General.Map.Map.CreateSidedef(ls.Line, false, newsector); - if(ls.Line.Back.Sector != newsector) ls.Line.Back.ChangeSector(newsector); + if(ls.Line.Back.Sector != newsector) ls.Line.Back.SetSector(newsector); ApplyDefaultsToSidedef(ls.Line.Back, sourceside); } @@ -622,7 +622,7 @@ namespace CodeImp.DoomBuilder.Geometry else { // Link to the new sector - ls.Line.Front.ChangeSector(original.Sector); + ls.Line.Front.SetSector(original.Sector); } } else @@ -641,7 +641,7 @@ namespace CodeImp.DoomBuilder.Geometry else { // Link to the new sector - ls.Line.Back.ChangeSector(original.Sector); + ls.Line.Back.SetSector(original.Sector); } } } diff --git a/Source/Core/Geometry/Triangulation.cs b/Source/Core/Geometry/Triangulation.cs index 499e2624752053cbdce3c457795cf2750792343d..44c1dab441ae8c96b60f7e3998fb7b67f1d9350a 100644 --- a/Source/Core/Geometry/Triangulation.cs +++ b/Source/Core/Geometry/Triangulation.cs @@ -101,6 +101,9 @@ namespace CodeImp.DoomBuilder.Geometry // Constructor public Triangulation() { + islandvertices = Array.AsReadOnly<int>(new int[0]); + vertices = Array.AsReadOnly<Vector2D>(new Vector2D[0]); + sidedefs = Array.AsReadOnly<Sidedef>(new Sidedef[0]); } // This performs the triangulation diff --git a/Source/Core/IO/DeserializerStream.cs b/Source/Core/IO/DeserializerStream.cs index 019ba949a21953dce550bdbc292e470a14c0375d..6fd35f13cf66b229587b2ca63659e7c8c0c5914c 100644 --- a/Source/Core/IO/DeserializerStream.cs +++ b/Source/Core/IO/DeserializerStream.cs @@ -42,6 +42,7 @@ namespace CodeImp.DoomBuilder.IO private Stream stream; private BinaryReader reader; private string[] stringstable; + private int stringtablepos; #endregion @@ -49,6 +50,8 @@ namespace CodeImp.DoomBuilder.IO public bool IsWriting { get { return false; } } + public int EndPosition { get { return stringtablepos; } } + #endregion #region ================== Constructor / Destructor @@ -69,8 +72,8 @@ namespace CodeImp.DoomBuilder.IO public void Begin() { // First 4 bytes are reserved for the offset of the strings table - int offset = reader.ReadInt32(); - stream.Seek(offset, SeekOrigin.Begin); + stringtablepos = reader.ReadInt32(); + stream.Seek(stringtablepos, SeekOrigin.Begin); // Read the strings List<string> strings = new List<string>(); diff --git a/Source/Core/IO/UniversalStreamReader.cs b/Source/Core/IO/UniversalStreamReader.cs index da22483f5dc76068a13f01821f603262d7b47bd8..638feffac7526a61a0b53c9ef14ab2decb83d3a9 100644 --- a/Source/Core/IO/UniversalStreamReader.cs +++ b/Source/Core/IO/UniversalStreamReader.cs @@ -375,6 +375,8 @@ namespace CodeImp.DoomBuilder.IO // This reads custom fields from a collection and adds them to a map element private void ReadCustomFields(UniversalCollection collection, MapElement element, string elementname) { + element.Fields.BeforeFieldsChange(); + // Go for all the elements in the collection foreach(UniversalEntry e in collection) { diff --git a/Source/Core/Map/Linedef.cs b/Source/Core/Map/Linedef.cs index 923e208533a0ed395c8c57a44315826332e46527..67f25ffe16b8c539ea99acba54374c3ca512721b 100644 --- a/Source/Core/Map/Linedef.cs +++ b/Source/Core/Map/Linedef.cs @@ -67,6 +67,8 @@ namespace CodeImp.DoomBuilder.Map private float lengthinv; private float angle; private RectangleF rect; + private bool blocksoundflag; + private bool impassableflag; // Properties private Dictionary<string, bool> flags; @@ -89,10 +91,10 @@ namespace CodeImp.DoomBuilder.Map public Sidedef Front { get { return front; } } public Sidedef Back { get { return back; } } public Line2D Line { get { return new Line2D(start.Position, end.Position); } } - public Dictionary<string, bool> Flags { get { return flags; } } - public int Action { get { return action; } set { action = value; } } - public int Activate { get { return activate; } set { activate = value; } } - public int Tag { get { return tag; } set { tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } + internal Dictionary<string, bool> Flags { get { return flags; } } + public int Action { get { return action; } set { BeforePropsChange(); action = value; } } + public int Activate { get { return activate; } set { BeforePropsChange(); activate = value; } } + public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } public float LengthSq { get { return lengthsq; } } public float Length { get { return length; } } public float LengthInv { get { return lengthinv; } } @@ -102,7 +104,9 @@ namespace CodeImp.DoomBuilder.Map public int[] Args { get { return args; } } internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } } internal bool FrontInterior { get { return frontinterior; } set { frontinterior = value; } } - + internal bool ImpassableFlag { get { return impassableflag; } } + internal bool BlockSoundFlag { get { return blocksoundflag; } } + #endregion #region ================== Constructor / Disposer @@ -113,41 +117,23 @@ namespace CodeImp.DoomBuilder.Map // Initialize this.map = map; this.listindex = listindex; - this.start = start; - this.end = end; this.updateneeded = true; this.args = new int[NUM_ARGS]; this.flags = new Dictionary<string, bool>(); // Attach to vertices - startvertexlistitem = start.AttachLinedef(this); - endvertexlistitem = end.AttachLinedef(this); - - // We have no destructor - GC.SuppressFinalize(this); - } - - // Constructor - internal Linedef(MapSet map, int listindex, Vertex start, Vertex end, IReadWriteStream stream) - { - // Initialize - this.map = map; - this.listindex = listindex; this.start = start; + this.startvertexlistitem = start.AttachLinedefP(this); this.end = end; - this.updateneeded = true; - this.args = new int[NUM_ARGS]; - - // Attach to vertices - startvertexlistitem = start.AttachLinedef(this); - endvertexlistitem = end.AttachLinedef(this); - - ReadWrite(stream); + this.endvertexlistitem = end.AttachLinedefP(this); + + if(map == General.Map.Map) + General.Map.UndoRedo.RecAddLinedef(this); // We have no destructor GC.SuppressFinalize(this); } - + // Disposer public override void Dispose() { @@ -157,18 +143,23 @@ namespace CodeImp.DoomBuilder.Map // Already set isdisposed so that changes can be prohibited isdisposed = true; + // Dispose sidedefs + if((front != null) && map.AutoRemove) front.Dispose(); else AttachFrontP(null); + if((back != null) && map.AutoRemove) back.Dispose(); else AttachBackP(null); + + if(map == General.Map.Map) + General.Map.UndoRedo.RecRemLinedef(this); + // Remove from main list map.RemoveLinedef(listindex); - + // Detach from vertices - start.DetachLinedef(startvertexlistitem); - end.DetachLinedef(endvertexlistitem); + if(startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem); startvertexlistitem = null; + start = null; + if(endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem); endvertexlistitem = null; - - // Dispose sidedefs - if((front != null) && map.AutoRemove) front.Dispose(); - if((back != null) && map.AutoRemove) back.Dispose(); + end = null; // Clean up start = null; @@ -185,10 +176,19 @@ namespace CodeImp.DoomBuilder.Map #endregion #region ================== Management - - // Serialize / deserialize + + // Call this before changing properties + protected override void BeforePropsChange() + { + if(map == General.Map.Map) + General.Map.UndoRedo.RecPrpLinedef(this); + } + + // Serialize / deserialize (passive: doesn't record) internal void ReadWrite(IReadWriteStream s) { + if(!s.IsWriting) BeforePropsChange(); + base.ReadWrite(s); if(s.IsWriting) @@ -223,32 +223,46 @@ namespace CodeImp.DoomBuilder.Map // This sets new start vertex public void SetStartVertex(Vertex v) { + if(map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefStart(this); + // Change start - if(startvertexlistitem != null) start.DetachLinedef(startvertexlistitem); + if(startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem); startvertexlistitem = null; start = v; - if(start != null) startvertexlistitem = start.AttachLinedef(this); + if(start != null) startvertexlistitem = start.AttachLinedefP(this); this.updateneeded = true; } // This sets new end vertex public void SetEndVertex(Vertex v) { + if(map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefEnd(this); + // Change end - if(endvertexlistitem != null) end.DetachLinedef(endvertexlistitem); + if(endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem); endvertexlistitem = null; end = v; - if(end != null) endvertexlistitem = end.AttachLinedef(this); + if(end != null) endvertexlistitem = end.AttachLinedefP(this); this.updateneeded = true; } // This detaches a vertex - internal void DetachVertex(Vertex v) + internal void DetachVertexP(Vertex v) { if(v == start) - SetStartVertex(null); + { + if(startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem); + startvertexlistitem = null; + start = null; + } else if(v == end) - SetEndVertex(null); + { + if(endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem); + endvertexlistitem = null; + end = null; + } else throw new Exception("Specified Vertex is not attached to this Linedef."); } @@ -256,6 +270,8 @@ namespace CodeImp.DoomBuilder.Map // This copies all properties to another line new public void CopyPropertiesTo(Linedef l) { + l.BeforePropsChange(); + // Copy properties l.action = action; l.args = (int[])args.Clone(); @@ -263,42 +279,57 @@ namespace CodeImp.DoomBuilder.Map l.tag = tag; l.updateneeded = true; l.activate = activate; + l.impassableflag = impassableflag; + l.blocksoundflag = blocksoundflag; base.CopyPropertiesTo(l); } // This attaches a sidedef on the front - public void AttachFront(Sidedef s) + internal void AttachFront(Sidedef s) { - // No sidedef here yet? - if(front == null) - { - // Attach and recalculate - front = s; - updateneeded = true; - } - else throw new Exception("Linedef already has a front Sidedef."); + if(map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefFront(this); + + // Attach and recalculate + AttachFrontP(s); + } + + // Passive version, does not record the change + internal void AttachFrontP(Sidedef s) + { + // Attach and recalculate + front = s; + if(front != null) front.SetLinedefP(this); + updateneeded = true; } // This attaches a sidedef on the back - public void AttachBack(Sidedef s) + internal void AttachBack(Sidedef s) { - // No sidedef here yet? - if(back == null) - { - // Attach and recalculate - back = s; - updateneeded = true; - } - else throw new Exception("Linedef already has a back Sidedef."); + if(map == General.Map.Map) + General.Map.UndoRedo.RecRefLinedefBack(this); + + // Attach and recalculate + AttachBackP(s); + } + + // Passive version, does not record the change + internal void AttachBackP(Sidedef s) + { + // Attach and recalculate + back = s; + if(back != null) back.SetLinedefP(this); + updateneeded = true; } // This detaches a sidedef from the front - public void DetachSidedef(Sidedef s) + internal void DetachSidedefP(Sidedef s) { // Sidedef is on the front? if(front == s) { // Remove sidedef reference + if(front != null) front.SetLinedefP(null); front = null; updateneeded = true; } @@ -306,10 +337,11 @@ namespace CodeImp.DoomBuilder.Map else if(back == s) { // Remove sidedef reference + if(back != null) back.SetLinedefP(null); back = null; updateneeded = true; } - else throw new Exception("Specified Sidedef is not attached to this Linedef."); + //else throw new Exception("Specified Sidedef is not attached to this Linedef."); } // This updates the line when changes have been made @@ -333,6 +365,10 @@ namespace CodeImp.DoomBuilder.Map float b = Math.Max(start.Position.y, end.Position.y); rect = new RectangleF(l, t, r - l, b - t); + // Cached flags + blocksoundflag = IsFlagSet(General.Map.Config.SoundLinedefFlag); + impassableflag = IsFlagSet(General.Map.Config.ImpassableFlag); + // Updated updateneeded = false; } @@ -478,20 +514,45 @@ namespace CodeImp.DoomBuilder.Map else return false; } + + // This sets a flag + public void SetFlag(string flagname, bool value) + { + if(!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) + { + BeforePropsChange(); + + flags[flagname] = value; + + // Cached flags + if(flagname == General.Map.Config.SoundLinedefFlag) blocksoundflag = value; + if(flagname == General.Map.Config.ImpassableFlag) impassableflag = value; + } + } + + // This returns a copy of the flags dictionary + public Dictionary<string, bool> GetFlags() + { + return new Dictionary<string, bool>(flags); + } + + // This clears all flags + public void ClearFlags() + { + flags.Clear(); + blocksoundflag = false; + impassableflag = false; + } // This flips the linedef's vertex attachments public void FlipVertices() { // Flip vertices - Vertex v = start; - start = end; - end = v; - - // Flip tickets accordingly - LinkedListNode<Linedef> vn = startvertexlistitem; - startvertexlistitem = endvertexlistitem; - endvertexlistitem = vn; - + Vertex oldstart = start; + Vertex oldend = end; + SetStartVertex(oldend); + SetEndVertex(oldstart); + // For drawing, the interior now lies on the other side frontinterior = !frontinterior; @@ -504,9 +565,10 @@ namespace CodeImp.DoomBuilder.Map public void FlipSidedefs() { // Flip sidedefs - Sidedef sd = front; - front = back; - back = sd; + Sidedef oldfront = front; + Sidedef oldback = back; + AttachFront(oldback); + AttachBack(oldfront); General.Map.IsChanged = true; } @@ -544,14 +606,14 @@ namespace CodeImp.DoomBuilder.Map if((front != null) && (back != null)) { // Apply or remove flags for doublesided line - flags[General.Map.Config.SingleSidedFlag] = false; - flags[General.Map.Config.DoubleSidedFlag] = true; + SetFlag(General.Map.Config.SingleSidedFlag, false); + SetFlag(General.Map.Config.DoubleSidedFlag, true); } else { // Apply or remove flags for singlesided line - flags[General.Map.Config.SingleSidedFlag] = true; - flags[General.Map.Config.DoubleSidedFlag] = false; + SetFlag(General.Map.Config.SingleSidedFlag, true); + SetFlag(General.Map.Config.DoubleSidedFlag, false); } General.Map.IsChanged = true; @@ -952,6 +1014,8 @@ namespace CodeImp.DoomBuilder.Map // This updates all properties public void Update(Dictionary<string, bool> flags, int activate, int tag, int action, int[] args) { + BeforePropsChange(); + // Apply changes this.flags = new Dictionary<string, bool>(flags); this.tag = tag; diff --git a/Source/Core/Map/MapElement.cs b/Source/Core/Map/MapElement.cs index b8b5365b431ff1f3abbd79a9c797de65a0554c17..810d2142fde4b0c4e6990182d72f436b842e7910 100644 --- a/Source/Core/Map/MapElement.cs +++ b/Source/Core/Map/MapElement.cs @@ -68,13 +68,14 @@ namespace CodeImp.DoomBuilder.Map internal MapElement() { // Initialize - fields = new UniFields(); + fields = new UniFields(this); } // Disposer public virtual void Dispose() { // Clean up + fields.Owner = null; fields = null; // Done @@ -101,7 +102,7 @@ namespace CodeImp.DoomBuilder.Map } else { - fields = new UniFields(c); + fields = new UniFields(this, c); for(int i = 0; i < c; i++) { string t; s.rString(out t); @@ -114,7 +115,16 @@ namespace CodeImp.DoomBuilder.Map // This copies properties to any other element public void CopyPropertiesTo(MapElement element) { - element.fields = new UniFields(this.fields); + element.fields = new UniFields(this, this.fields); + } + + // This must implement the call to the undo system to record the change of properties + protected abstract void BeforePropsChange(); + + // This is called before the custom fields change + internal void BeforeFieldsChange() + { + BeforePropsChange(); } #endregion diff --git a/Source/Core/Map/MapSet.cs b/Source/Core/Map/MapSet.cs index f4aee588e925e787ba9b3b6b110df462391ed1a8..fe21e07cd0b558794217a0691c2349f8d70157df 100644 --- a/Source/Core/Map/MapSet.cs +++ b/Source/Core/Map/MapSet.cs @@ -204,7 +204,6 @@ namespace CodeImp.DoomBuilder.Map { // Already set isdisposed so that changes can be prohibited isdisposed = true; - autoremove = false; BeginAddRemove(); // Dispose all things @@ -435,6 +434,7 @@ namespace CodeImp.DoomBuilder.Map { virtualsector = newset.CreateSector(); l.Front.Sector.CopyPropertiesTo(virtualsector); + virtualsector.Fields.BeforeFieldsChange(); virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue); } @@ -463,6 +463,7 @@ namespace CodeImp.DoomBuilder.Map { virtualsector = newset.CreateSector(); l.Back.Sector.CopyPropertiesTo(virtualsector); + virtualsector.Fields.BeforeFieldsChange(); virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue); } @@ -509,24 +510,6 @@ namespace CodeImp.DoomBuilder.Map return v; } - // This creates a new vertex from a stream - private Vertex CreateVertex(IReadWriteStream stream) - { - // Make the vertex - Vertex v = new Vertex(this, numvertices, stream); - AddItem(v, ref vertices, numvertices, ref numvertices); - return v; - } - - // This creates a new vertex from a stream - private Vertex CreateVertex(int index, IReadWriteStream stream) - { - // Make the vertex - Vertex v = new Vertex(this, index, stream); - AddItem(v, ref vertices, index, ref numvertices); - return v; - } - /// <summary>This creates a new linedef and returns it.</summary> public Linedef CreateLinedef(Vertex start, Vertex end) { @@ -545,24 +528,6 @@ namespace CodeImp.DoomBuilder.Map return l; } - // This creates a new linedef from a stream - private Linedef CreateLinedef(Vertex start, Vertex end, IReadWriteStream stream) - { - // Make the linedef - Linedef l = new Linedef(this, numlinedefs, start, end, stream); - AddItem(l, ref linedefs, numlinedefs, ref numlinedefs); - return l; - } - - // This creates a new linedef from a stream - private Linedef CreateLinedef(int index, Vertex start, Vertex end, IReadWriteStream stream) - { - // Make the linedef - Linedef l = new Linedef(this, index, start, end, stream); - AddItem(l, ref linedefs, index, ref numlinedefs); - return l; - } - /// <summary>This creates a new sidedef and returns it.</summary> public Sidedef CreateSidedef(Linedef l, bool front, Sector s) { @@ -581,24 +546,6 @@ namespace CodeImp.DoomBuilder.Map return sd; } - // This creates a new sidedef from a stream - private Sidedef CreateSidedef(Linedef l, bool front, Sector s, IReadWriteStream stream) - { - // Make the sidedef - Sidedef sd = new Sidedef(this, numsidedefs, l, front, s, stream); - AddItem(sd, ref sidedefs, numsidedefs, ref numsidedefs); - return sd; - } - - // This creates a new sidedef from a stream - private Sidedef CreateSidedef(int index, Linedef l, bool front, Sector s, IReadWriteStream stream) - { - // Make the sidedef - Sidedef sd = new Sidedef(this, index, l, front, s, stream); - AddItem(sd, ref sidedefs, index, ref numsidedefs); - return sd; - } - /// <summary>This creates a new sector and returns it.</summary> public Sector CreateSector() { @@ -637,15 +584,6 @@ namespace CodeImp.DoomBuilder.Map return s; } - // This creates a new sector from a stream - private Sector CreateSector(IReadWriteStream stream) - { - // Make the sector - Sector s = new Sector(this, numsectors, stream); - AddItem(s, ref sectors, numsectors, ref numsectors); - return s; - } - /// <summary>This creates a new thing and returns it.</summary> public Thing CreateThing() { @@ -919,7 +857,8 @@ namespace CodeImp.DoomBuilder.Map // Go for all vertices for(int i = 0; i < c; i++) { - array[i] = CreateVertex(stream); + array[i] = CreateVertex(new Vector2D()); + array[i].ReadWrite(stream); } return array; @@ -941,7 +880,8 @@ namespace CodeImp.DoomBuilder.Map stream.rInt(out end); - array[i] = CreateLinedef(verticesarray[start], verticesarray[end], stream); + array[i] = CreateLinedef(verticesarray[start], verticesarray[end]); + array[i].ReadWrite(stream); } return array; @@ -964,7 +904,8 @@ namespace CodeImp.DoomBuilder.Map stream.rBool(out front); - CreateSidedef(linedefsarray[lineindex], front, sectorsarray[sectorindex], stream); + Sidedef sd = CreateSidedef(linedefsarray[lineindex], front, sectorsarray[sectorindex]); + sd.ReadWrite(stream); } } @@ -978,7 +919,8 @@ namespace CodeImp.DoomBuilder.Map // Go for all sectors for(int i = 0; i < c; i++) { - array[i] = CreateSector(stream); + array[i] = CreateSector(); + array[i].ReadWrite(stream); } return array; @@ -2677,14 +2619,14 @@ namespace CodeImp.DoomBuilder.Map { // Replace with stored sidedef bool isfront = snsd.IsFront; - snsd.Line.DetachSidedef(snsd); + snsd.Line.DetachSidedefP(snsd); if(isfront) snsd.Line.AttachFront(stored); else snsd.Line.AttachBack(stored); // Remove the sidedef - snsd.ChangeSector(null); + snsd.SetSector(null); RemoveSidedef(sn); } else diff --git a/Source/Core/Map/Sector.cs b/Source/Core/Map/Sector.cs index b1425a1372b1ba010f0b03496fb56b07834de7d2..cf58f329f174d6edd923ffbf3f2a6527626c64af 100644 --- a/Source/Core/Map/Sector.cs +++ b/Source/Core/Map/Sector.cs @@ -86,15 +86,15 @@ namespace CodeImp.DoomBuilder.Map /// An unique index that does not change when other sectors are removed. /// </summary> public int FixedIndex { get { return fixedindex; } } - public int FloorHeight { get { return floorheight; } set { floorheight = value; } } - public int CeilHeight { get { return ceilheight; } set { ceilheight = value; } } + public int FloorHeight { get { return floorheight; } set { BeforePropsChange(); floorheight = value; } } + public int CeilHeight { get { return ceilheight; } set { BeforePropsChange(); ceilheight = value; } } public string FloorTexture { get { return floortexname; } } public string CeilTexture { get { return ceiltexname; } } public long LongFloorTexture { get { return longfloortexname; } } public long LongCeilTexture { get { return longceiltexname; } } - public int Effect { get { return effect; } set { effect = value; } } - public int Tag { get { return tag; } set { tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } - public int Brightness { get { return brightness; } set { brightness = value; updateneeded = true; } } + public int Effect { get { return effect; } set { BeforePropsChange(); effect = value; } } + public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } + public int Brightness { get { return brightness; } set { BeforePropsChange(); brightness = value; updateneeded = true; } } public bool UpdateNeeded { get { return updateneeded; } set { updateneeded |= value; triangulationneeded |= value; } } public RectangleF BBox { get { return bbox; } } internal Sector Clone { get { return clone; } set { clone = value; } } @@ -122,21 +122,8 @@ namespace CodeImp.DoomBuilder.Map this.triangulationneeded = true; this.surfaceentry = new SurfaceEntry(-1, -1, -1); - // We have no destructor - GC.SuppressFinalize(this); - } - - // Constructor - internal Sector(MapSet map, int listindex, IReadWriteStream stream) - { - // Initialize - this.map = map; - this.listindex = listindex; - this.sidedefs = new LinkedList<Sidedef>(); - this.triangulationneeded = true; - this.surfaceentry = new SurfaceEntry(-1, -1, -1); - - ReadWrite(stream); + if(map == General.Map.Map) + General.Map.UndoRedo.RecAddSector(this); // We have no destructor GC.SuppressFinalize(this); @@ -150,10 +137,13 @@ namespace CodeImp.DoomBuilder.Map { // Already set isdisposed so that changes can be prohibited isdisposed = true; + + if(map == General.Map.Map) + General.Map.UndoRedo.RecRemSector(this); // Remove from main list map.RemoveSector(listindex); - + // Register the index as free map.AddSectorIndexHole(fixedindex); @@ -161,14 +151,16 @@ namespace CodeImp.DoomBuilder.Map // because a sidedef cannot exist without reference to its sector. if(map.AutoRemove) foreach(Sidedef sd in sidedefs) sd.Dispose(); - + else + foreach(Sidedef sd in sidedefs) sd.SetSectorP(null); + // Free surface entry General.Map.CRenderer2D.Surfaces.FreeSurfaces(surfaceentry); - + // Clean up sidedefs = null; map = null; - + // Dispose base base.Dispose(); } @@ -178,49 +170,30 @@ namespace CodeImp.DoomBuilder.Map #region ================== Management - // Serialize / deserialize + // Call this before changing properties + protected override void BeforePropsChange() + { + if(map == General.Map.Map) + General.Map.UndoRedo.RecPrpSector(this); + } + + // Serialize / deserialize (passive: this doesn't record) internal void ReadWrite(IReadWriteStream s) { - base.ReadWrite(s); + if(!s.IsWriting) BeforePropsChange(); + base.ReadWrite(s); + s.rwInt(ref fixedindex); s.rwInt(ref floorheight); s.rwInt(ref ceilheight); s.rwString(ref floortexname); s.rwString(ref ceiltexname); - //s.rwLong(ref longfloortexname); - //s.rwLong(ref longceiltexname); + s.rwLong(ref longfloortexname); + s.rwLong(ref longceiltexname); s.rwInt(ref effect); s.rwInt(ref tag); s.rwInt(ref brightness); - - // Use a new triangulator when reading from stream - if(!s.IsWriting && (triangles == null)) triangles = new Triangulation(); - triangles.ReadWrite(s); - - if(s.IsWriting) - { - s.wInt(labels.Count); - for(int i = 0; i < labels.Count; i++) - { - s.wVector2D(labels[i].position); - s.wFloat(labels[i].radius); - } - } - else - { - longfloortexname = Lump.MakeLongName(floortexname); - longceiltexname = Lump.MakeLongName(ceiltexname); - - int c; s.rInt(out c); - LabelPositionInfo[] labelsarray = new LabelPositionInfo[c]; - for(int i = 0; i < c; i++) - { - s.rVector2D(out labelsarray[i].position); - s.rFloat(out labelsarray[i].radius); - } - labels = Array.AsReadOnly<LabelPositionInfo>(labelsarray); - } } // After deserialization @@ -237,6 +210,8 @@ namespace CodeImp.DoomBuilder.Map // This copies all properties to another sector public void CopyPropertiesTo(Sector s) { + s.BeforePropsChange(); + // Copy properties s.ceilheight = ceilheight; s.ceiltexname = ceiltexname; @@ -252,7 +227,7 @@ namespace CodeImp.DoomBuilder.Map } // This attaches a sidedef and returns the listitem - public LinkedListNode<Sidedef> AttachSidedef(Sidedef sd) + internal LinkedListNode<Sidedef> AttachSidedefP(Sidedef sd) { updateneeded = true; triangulationneeded = true; @@ -260,7 +235,7 @@ namespace CodeImp.DoomBuilder.Map } // This detaches a sidedef - public void DetachSidedef(LinkedListNode<Sidedef> l) + internal void DetachSidedefP(LinkedListNode<Sidedef> l) { // Not disposing? if(!isdisposed) @@ -357,6 +332,8 @@ namespace CodeImp.DoomBuilder.Map // This updates the floor surface public void UpdateFloorSurface() { + if(flatvertices == null) return; + // Create floor vertices FlatVertex[] floorvertices = new FlatVertex[flatvertices.Length]; flatvertices.CopyTo(floorvertices, 0); @@ -372,6 +349,8 @@ namespace CodeImp.DoomBuilder.Map // This updates the ceiling surface public void UpdateCeilingSurface() { + if(flatvertices == null) return; + // Create ceiling vertices FlatVertex[] ceilvertices = new FlatVertex[flatvertices.Length]; flatvertices.CopyTo(ceilvertices, 0); @@ -484,7 +463,7 @@ namespace CodeImp.DoomBuilder.Map // Change secter reference on my sidedefs // This automatically disposes this sector while(sidedefs != null) - sidedefs.First.Value.ChangeSector(other); + sidedefs.First.Value.SetSector(other); } else { @@ -509,6 +488,8 @@ namespace CodeImp.DoomBuilder.Map // This updates all properties public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, int tag, int brightness) { + BeforePropsChange(); + // Apply changes this.floorheight = hfloor; this.ceilheight = hceil; @@ -523,6 +504,8 @@ namespace CodeImp.DoomBuilder.Map // This sets texture public void SetFloorTexture(string name) { + BeforePropsChange(); + floortexname = name; longfloortexname = Lump.MakeLongName(name); updateneeded = true; @@ -532,6 +515,8 @@ namespace CodeImp.DoomBuilder.Map // This sets texture public void SetCeilTexture(string name) { + BeforePropsChange(); + ceiltexname = name; longceiltexname = Lump.MakeLongName(name); updateneeded = true; diff --git a/Source/Core/Map/Sidedef.cs b/Source/Core/Map/Sidedef.cs index 941429485f64211d4d41478e5da6a9b90ea5ae56..d970bdab5a9b1004e145283512e98fdca52d45de 100644 --- a/Source/Core/Map/Sidedef.cs +++ b/Source/Core/Map/Sidedef.cs @@ -67,13 +67,13 @@ namespace CodeImp.DoomBuilder.Map #region ================== Properties public MapSet Map { get { return map; } } - public bool IsFront { get { return (this == linedef.Front); } } + public bool IsFront { get { return (linedef != null) ? (this == linedef.Front) : false; } } public Linedef Line { get { return linedef; } } public Sidedef Other { get { if(this == linedef.Front) return linedef.Back; else return linedef.Front; } } public Sector Sector { get { return sector; } } public float Angle { get { if(IsFront) return linedef.Angle; else return Angle2D.Normalized(linedef.Angle + Angle2D.PI); } } - public int OffsetX { get { return offsetx; } set { offsetx = value; } } - public int OffsetY { get { return offsety; } set { offsety = value; } } + public int OffsetX { get { return offsetx; } set { BeforePropsChange(); offsetx = value; } } + public int OffsetY { get { return offsety; } set { BeforePropsChange(); offsety = value; } } public string HighTexture { get { return texnamehigh; } } public string MiddleTexture { get { return texnamemid; } } public string LowTexture { get { return texnamelow; } } @@ -92,8 +92,6 @@ namespace CodeImp.DoomBuilder.Map // Initialize this.map = map; this.listindex = listindex; - this.linedef = l; - this.sector = s; this.texnamehigh = "-"; this.texnamemid = "-"; this.texnamelow = "-"; @@ -101,33 +99,22 @@ namespace CodeImp.DoomBuilder.Map this.longtexnamemid = MapSet.EmptyLongName; this.longtexnamelow = MapSet.EmptyLongName; - // Attach to the linedef - if(front) l.AttachFront(this); else l.AttachBack(this); - - // Attach to sector - sectorlistitem = s.AttachSidedef(this); - - // We have no destructor - GC.SuppressFinalize(this); - } - - // Constructor - internal Sidedef(MapSet map, int listindex, Linedef l, bool front, Sector s, IReadWriteStream stream) - { - // Initialize - this.map = map; - this.listindex = listindex; + // Attach linedef this.linedef = l; - this.sector = s; - - // Attach to the linedef - if(front) l.AttachFront(this); else l.AttachBack(this); - - // Attach to sector - sectorlistitem = s.AttachSidedef(this); - - ReadWrite(stream); - + if(l != null) + { + if(front) + l.AttachFrontP(this); + else + l.AttachBackP(this); + } + + // Attach sector + SetSectorP(s); + + if(map == General.Map.Map) + General.Map.UndoRedo.RecAddSidedef(this); + // We have no destructor GC.SuppressFinalize(this); } @@ -141,15 +128,18 @@ namespace CodeImp.DoomBuilder.Map // Already set isdisposed so that changes can be prohibited isdisposed = true; + if(map == General.Map.Map) + General.Map.UndoRedo.RecRemSidedef(this); + // Remove from main list map.RemoveSidedef(listindex); // Detach from linedef - linedef.DetachSidedef(this); + if(linedef != null) linedef.DetachSidedefP(this); // Detach from sector - sector.DetachSidedef(sectorlistitem); - + SetSectorP(null); + // Clean up sectorlistitem = null; linedef = null; @@ -164,31 +154,36 @@ namespace CodeImp.DoomBuilder.Map #endregion #region ================== Management - - // Serialize / deserialize + + // Call this before changing properties + protected override void BeforePropsChange() + { + if(map == General.Map.Map) + General.Map.UndoRedo.RecPrpSidedef(this); + } + + // Serialize / deserialize (passive: this doesn't record) internal void ReadWrite(IReadWriteStream s) { - base.ReadWrite(s); + if(!s.IsWriting) BeforePropsChange(); + base.ReadWrite(s); + s.rwInt(ref offsetx); s.rwInt(ref offsety); s.rwString(ref texnamehigh); s.rwString(ref texnamemid); s.rwString(ref texnamelow); - //s.rwLong(ref longtexnamehigh); - //s.rwLong(ref longtexnamemid); - //s.rwLong(ref longtexnamelow); - if(!s.IsWriting) - { - longtexnamehigh = Lump.MakeLongName(texnamehigh); - longtexnamemid = Lump.MakeLongName(texnamemid); - longtexnamelow = Lump.MakeLongName(texnamelow); - } + s.rwLong(ref longtexnamehigh); + s.rwLong(ref longtexnamemid); + s.rwLong(ref longtexnamelow); } // This copies all properties to another sidedef public void CopyPropertiesTo(Sidedef s) { + s.BeforePropsChange(); + // Copy properties s.offsetx = offsetx; s.offsety = offsety; @@ -201,71 +196,49 @@ namespace CodeImp.DoomBuilder.Map base.CopyPropertiesTo(s); } - // This creates a checksum from the sidedef properties - // Used for faster sidedefs compression - public uint GetChecksum() - { - CRC crc = new CRC(); - crc.Add(sector.FixedIndex); - crc.Add(offsetx); - crc.Add(offsety); - crc.Add(longtexnamehigh); - crc.Add(longtexnamelow); - crc.Add(longtexnamemid); - return (uint)(crc.Value & 0x00000000FFFFFFFF); - } - - // This copies textures to another sidedef - // And possibly also the offsets - public void AddTexturesTo(Sidedef s) + // This changes sector + public void SetSector(Sector newsector) { - int copyoffsets = 0; - - // s cannot be null - if(s == null) return; + if(map == General.Map.Map) + General.Map.UndoRedo.RecRefSidedefSector(this); - // Upper texture set? - if((texnamehigh.Length > 0) && (texnamehigh[0] != '-')) - { - // Copy upper texture - s.texnamehigh = texnamehigh; - s.longtexnamehigh = longtexnamehigh; - - // Counts as a half coice for copying offsets - copyoffsets += 1; - } + // Change sector + SetSectorP(newsector); + } - // Middle texture set? - if((texnamemid.Length > 0) && (texnamemid[0] != '-')) - { - // Copy middle texture - s.texnamemid = texnamemid; - s.longtexnamemid = longtexnamemid; + internal void SetSectorP(Sector newsector) + { + // Detach from sector + if(sector != null) sector.DetachSidedefP(sectorlistitem); - // Counts for copying offsets - copyoffsets += 2; - } + // Change sector + sector = newsector; - // Lower texture set? - if((texnamelow.Length > 0) && (texnamelow[0] != '-')) - { - // Copy middle texture - s.texnamelow = texnamelow; - s.longtexnamelow = longtexnamelow; + // Attach to sector + if(sector != null) + sectorlistitem = sector.AttachSidedefP(this); - // Counts as a half coice for copying offsets - copyoffsets += 1; - } + General.Map.IsChanged = true; + } - // Copy offsets also? - if(copyoffsets >= 2) + // This sets the linedef + public void SetLinedef(Linedef ld, bool front) + { + if(linedef != null) linedef.DetachSidedefP(this); + + if(ld != null) { - // Copy offsets - s.offsetx = offsetx; - s.offsety = offsety; + if(front) + ld.AttachFront(this); + else + ld.AttachBack(this); } + } - General.Map.IsChanged = true; + // This sets the linedef (passive: this doesn't tell the linedef and doesn't record) + internal void SetLinedefP(Linedef ld) + { + linedef = ld; } #endregion @@ -281,6 +254,8 @@ namespace CodeImp.DoomBuilder.Map // This removes textures that are not required public void RemoveUnneededTextures(bool removemiddle, bool force) { + BeforePropsChange(); + // The middle texture can be removed regardless of any sector tag or linedef action if(!MiddleRequired() && removemiddle) { @@ -347,6 +322,75 @@ namespace CodeImp.DoomBuilder.Map return false; } } + + // This creates a checksum from the sidedef properties + // Used for faster sidedefs compression + public uint GetChecksum() + { + CRC crc = new CRC(); + crc.Add(sector.FixedIndex); + crc.Add(offsetx); + crc.Add(offsety); + crc.Add(longtexnamehigh); + crc.Add(longtexnamelow); + crc.Add(longtexnamemid); + return (uint)(crc.Value & 0x00000000FFFFFFFF); + } + + // This copies textures to another sidedef + // And possibly also the offsets + public void AddTexturesTo(Sidedef s) + { + int copyoffsets = 0; + + // s cannot be null + if(s == null) return; + + s.BeforePropsChange(); + + // Upper texture set? + if((texnamehigh.Length > 0) && (texnamehigh[0] != '-')) + { + // Copy upper texture + s.texnamehigh = texnamehigh; + s.longtexnamehigh = longtexnamehigh; + + // Counts as a half coice for copying offsets + copyoffsets += 1; + } + + // Middle texture set? + if((texnamemid.Length > 0) && (texnamemid[0] != '-')) + { + // Copy middle texture + s.texnamemid = texnamemid; + s.longtexnamemid = longtexnamemid; + + // Counts for copying offsets + copyoffsets += 2; + } + + // Lower texture set? + if((texnamelow.Length > 0) && (texnamelow[0] != '-')) + { + // Copy middle texture + s.texnamelow = texnamelow; + s.longtexnamelow = longtexnamelow; + + // Counts as a half coice for copying offsets + copyoffsets += 1; + } + + // Copy offsets also? + if(copyoffsets >= 2) + { + // Copy offsets + s.offsetx = offsetx; + s.offsety = offsety; + } + + General.Map.IsChanged = true; + } #endregion @@ -355,6 +399,8 @@ namespace CodeImp.DoomBuilder.Map // This updates all properties public void Update(int offsetx, int offsety, string thigh, string tmid, string tlow) { + BeforePropsChange(); + // Apply changes this.offsetx = offsetx; this.offsety = offsety; @@ -366,6 +412,8 @@ namespace CodeImp.DoomBuilder.Map // This sets texture public void SetTextureHigh(string name) { + BeforePropsChange(); + texnamehigh = name; longtexnamehigh = Lump.MakeLongName(name); General.Map.IsChanged = true; @@ -374,6 +422,8 @@ namespace CodeImp.DoomBuilder.Map // This sets texture public void SetTextureMid(string name) { + BeforePropsChange(); + texnamemid = name; longtexnamemid = Lump.MakeLongName(name); General.Map.IsChanged = true; @@ -382,26 +432,12 @@ namespace CodeImp.DoomBuilder.Map // This sets texture public void SetTextureLow(string name) { + BeforePropsChange(); + texnamelow = name; longtexnamelow = Lump.MakeLongName(name); General.Map.IsChanged = true; } - - // This changes sector - public void ChangeSector(Sector newsector) - { - // Detach from sector - sector.DetachSidedef(sectorlistitem); - - // Change sector - sector = newsector; - - // Attach to sector - if(sector != null) - sectorlistitem = sector.AttachSidedef(this); - - General.Map.IsChanged = true; - } #endregion } diff --git a/Source/Core/Map/Thing.cs b/Source/Core/Map/Thing.cs index 4fdfaeba9affc311ff3673f06df7670450bcde3b..c7b06a55d5901d66deb4450244b2d2db5663f01b 100644 --- a/Source/Core/Map/Thing.cs +++ b/Source/Core/Map/Thing.cs @@ -71,18 +71,18 @@ namespace CodeImp.DoomBuilder.Map #region ================== Properties public MapSet Map { get { return map; } } - public int Type { get { return type; } set { type = value; } } + public int Type { get { return type; } set { BeforePropsChange(); type = value; } } public Vector3D Position { get { return pos; } } public float Angle { get { return angle; } } public int AngleDeg { get { return (int)Angle2D.RadToDeg(angle); } } - public Dictionary<string, bool> Flags { get { return flags; } } - public int Action { get { return action; } set { action = value; } } + internal Dictionary<string, bool> Flags { get { return flags; } } + public int Action { get { return action; } set { BeforePropsChange(); action = value; } } public int[] Args { get { return args; } } public float Size { get { return size; } } public float IconOffset { get { return iconoffset; } } public PixelColor Color { get { return color; } } public bool FixedSize { get { return fixedsize; } } - public int Tag { get { return tag; } set { tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } + public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } } public Sector Sector { get { return sector; } } #endregion @@ -98,6 +98,9 @@ namespace CodeImp.DoomBuilder.Map this.flags = new Dictionary<string, bool>(); this.args = new int[NUM_ARGS]; + if(map == General.Map.Map) + General.Map.UndoRedo.RecAddThing(this); + // We have no destructor GC.SuppressFinalize(this); } @@ -111,6 +114,9 @@ namespace CodeImp.DoomBuilder.Map // Already set isdisposed so that changes can be prohibited isdisposed = true; + if(map == General.Map.Map) + General.Map.UndoRedo.RecRemThing(this); + // Remove from main list map.RemoveThing(listindex); @@ -129,10 +135,19 @@ namespace CodeImp.DoomBuilder.Map #endregion #region ================== Management - + + // Call this before changing properties + protected override void BeforePropsChange() + { + if(map == General.Map.Map) + General.Map.UndoRedo.RecPrpThing(this); + } + // Serialize / deserialize internal void ReadWrite(IReadWriteStream s) { + if(!s.IsWriting) BeforePropsChange(); + base.ReadWrite(s); if(s.IsWriting) @@ -169,6 +184,8 @@ namespace CodeImp.DoomBuilder.Map // This copies all properties to another thing public void CopyPropertiesTo(Thing t) { + t.BeforePropsChange(); + // Copy properties t.type = type; t.angle = angle; @@ -325,6 +342,8 @@ namespace CodeImp.DoomBuilder.Map // NOTE: This does not update sector! (call DetermineSector) public void Move(Vector3D newpos) { + BeforePropsChange(); + // Change position this.pos = newpos; General.Map.IsChanged = true; @@ -334,6 +353,8 @@ namespace CodeImp.DoomBuilder.Map // NOTE: This does not update sector! (call DetermineSector) public void Move(Vector2D newpos) { + BeforePropsChange(); + // Change position this.pos = new Vector3D(newpos.x, newpos.y, pos.z); General.Map.IsChanged = true; @@ -343,6 +364,8 @@ namespace CodeImp.DoomBuilder.Map // NOTE: This does not update sector! (call DetermineSector) public void Move(float x, float y, float zoffset) { + BeforePropsChange(); + // Change position this.pos = new Vector3D(x, y, zoffset); General.Map.IsChanged = true; @@ -351,6 +374,8 @@ namespace CodeImp.DoomBuilder.Map // This rotates the thing public void Rotate(float newangle) { + BeforePropsChange(); + // Change angle this.angle = newangle; General.Map.IsChanged = true; @@ -403,7 +428,41 @@ namespace CodeImp.DoomBuilder.Map #endregion #region ================== Methods + + // This checks and returns a flag without creating it + public bool IsFlagSet(string flagname) + { + if(flags.ContainsKey(flagname)) + return flags[flagname]; + else + return false; + } + + // This sets a flag + public void SetFlag(string flagname, bool value) + { + if(!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value)) + { + BeforePropsChange(); + flags[flagname] = value; + } + } + + // This returns a copy of the flags dictionary + public Dictionary<string, bool> GetFlags() + { + return new Dictionary<string,bool>(flags); + } + + // This clears all flags + public void ClearFlags() + { + BeforePropsChange(); + + flags.Clear(); + } + // This snaps the vertex to the grid public void SnapToGrid() { diff --git a/Source/Core/Map/UniFields.cs b/Source/Core/Map/UniFields.cs index 6976ccc44ddb49e2d43f02699d4a3c95967c4918..c20f28f6ee1431813206a72e2536cab22406ac48 100644 --- a/Source/Core/Map/UniFields.cs +++ b/Source/Core/Map/UniFields.cs @@ -15,6 +15,10 @@ namespace CodeImp.DoomBuilder.Map /// </summary> public class UniFields : SortedList<string, UniValue> { + // Owner of this list + protected MapElement owner; + public MapElement Owner { get { return owner; } internal set { owner = value; } } + // New constructor ///<summary></summary> public UniFields() : base(2) @@ -32,5 +36,33 @@ namespace CodeImp.DoomBuilder.Map public UniFields(UniFields copyfrom) : base(copyfrom) { } + + // New constructor + ///<summary></summary> + public UniFields(MapElement owner) : base(2) + { + this.owner = owner; + } + + // New constructor + ///<summary></summary> + public UniFields(MapElement owner, int capacity) : base(capacity) + { + this.owner = owner; + } + + // Copy constructor + ///<summary></summary> + public UniFields(MapElement owner, UniFields copyfrom) : base(copyfrom) + { + this.owner = owner; + } + + /// <summary>Call this before making changes to the fields, or they may not be updated correctly with undo/redo!</summary> + public void BeforeFieldsChange() + { + if(owner != null) + owner.BeforeFieldsChange(); + } } } diff --git a/Source/Core/Map/Vertex.cs b/Source/Core/Map/Vertex.cs index e5d876c5df4e510c96f9f7fdf78ac5ebf89a82c6..0e22428742bf1c9b2c979074ad386e28ecd39dc9 100644 --- a/Source/Core/Map/Vertex.cs +++ b/Source/Core/Map/Vertex.cs @@ -81,20 +81,9 @@ namespace CodeImp.DoomBuilder.Map this.listindex = listindex; this.pos = pos; - // We have no destructor - GC.SuppressFinalize(this); - } - - // Constructor - internal Vertex(MapSet map, int listindex, IReadWriteStream stream) - { - // Initialize - this.map = map; - this.linedefs = new LinkedList<Linedef>(); - this.listindex = listindex; - - ReadWrite(stream); - + if(map == General.Map.Map) + General.Map.UndoRedo.RecAddVertex(this); + // We have no destructor GC.SuppressFinalize(this); } @@ -107,6 +96,9 @@ namespace CodeImp.DoomBuilder.Map { // Already set isdisposed so that changes can be prohibited isdisposed = true; + + if(map == General.Map.Map) + General.Map.UndoRedo.RecRemVertex(this); // Remove from main list map.RemoveVertex(listindex); @@ -120,7 +112,7 @@ namespace CodeImp.DoomBuilder.Map else { // Detach from linedefs - foreach(Linedef ld in linedefs) ld.DetachVertex(this); + foreach(Linedef ld in linedefs) ld.DetachVertexP(this); } // Clean up @@ -136,11 +128,21 @@ namespace CodeImp.DoomBuilder.Map #region ================== Management + // Call this before changing properties + protected override void BeforePropsChange() + { + if(map == General.Map.Map) + General.Map.UndoRedo.RecPrpVertex(this); + } + // This attaches a linedef and returns the listitem - public LinkedListNode<Linedef> AttachLinedef(Linedef l) { return linedefs.AddLast(l); } + internal LinkedListNode<Linedef> AttachLinedefP(Linedef l) + { + return linedefs.AddLast(l); + } // This detaches a linedef - public void DetachLinedef(LinkedListNode<Linedef> l) + internal void DetachLinedefP(LinkedListNode<Linedef> l) { // Not disposing? if(!isdisposed) @@ -160,9 +162,17 @@ namespace CodeImp.DoomBuilder.Map // Serialize / deserialize internal void ReadWrite(IReadWriteStream s) { + if(!s.IsWriting) BeforePropsChange(); + base.ReadWrite(s); s.rwVector2D(ref pos); + + if(s.IsWriting) + { + // Let all lines know they need an update + foreach(Linedef l in linedefs) l.NeedUpdate(); + } } // Selected @@ -187,6 +197,8 @@ namespace CodeImp.DoomBuilder.Map // This copies all properties to another thing public void CopyPropertiesTo(Vertex v) { + v.BeforePropsChange(); + // Copy properties v.pos = pos; base.CopyPropertiesTo(v); @@ -213,6 +225,8 @@ namespace CodeImp.DoomBuilder.Map // Do we actually move? if(newpos != pos) { + BeforePropsChange(); + // Change position pos = newpos; diff --git a/Source/Core/Rendering/Renderer2D.cs b/Source/Core/Rendering/Renderer2D.cs index 8e2b641a38797eb79650de217b95cf8ac3e4078a..0d0de346915455f17a8b1a27c5a0d480047ec523 100644 --- a/Source/Core/Rendering/Renderer2D.cs +++ b/Source/Core/Rendering/Renderer2D.cs @@ -575,22 +575,20 @@ namespace CodeImp.DoomBuilder.Rendering // This returns the color for a linedef public PixelColor DetermineLinedefColor(Linedef l) { - // Impassable lines - if(l.IsFlagSet(General.Map.Config.ImpassableFlag)) + if(l.Selected) + return General.Colors.Selection; + else if(l.ImpassableFlag) { - // Determine color - if(l.Selected) return General.Colors.Selection; - else if(l.Action != 0) return General.Colors.Actions; + // Impassable lines + if(l.Action != 0) return General.Colors.Actions; else return General.Colors.Linedefs; } else { - // Determine color - byte a = (byte)(General.Settings.DoubleSidedAlpha * 255.0f); - if(l.Selected) return General.Colors.Selection; - else if(l.Action != 0) return General.Colors.Actions.WithAlpha(a); - else if(l.IsFlagSet(General.Map.Config.SoundLinedefFlag)) return General.Colors.Sounds.WithAlpha(a); - else return General.Colors.Linedefs.WithAlpha(a); + // Passable lines + if(l.Action != 0) return General.Colors.Actions.WithAlpha(General.Settings.DoubleSidedAlphaByte); + else if(l.BlockSoundFlag) return General.Colors.Sounds.WithAlpha(General.Settings.DoubleSidedAlphaByte); + else return General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte); } } diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs index 064afd52663afc8f5e02240da4ab42e338cd359c..431b487099c5992ae2d7328a756cd8eb53628c17 100644 --- a/Source/Core/VisualModes/VisualMode.cs +++ b/Source/Core/VisualModes/VisualMode.cs @@ -191,8 +191,7 @@ namespace CodeImp.DoomBuilder.VisualModes public override void OnUndoEnd() { base.OnUndoEnd(); - ResourcesReloaded(); - General.Map.Data.UpdateUsedTextures(); + ResourcesReloadedPartial(); renderer.SetCrosshairBusy(false); } @@ -206,8 +205,7 @@ namespace CodeImp.DoomBuilder.VisualModes public override void OnRedoEnd() { base.OnRedoEnd(); - ResourcesReloaded(); - General.Map.Data.UpdateUsedTextures(); + ResourcesReloadedPartial(); renderer.SetCrosshairBusy(false); } @@ -616,8 +614,7 @@ namespace CodeImp.DoomBuilder.VisualModes #region ================== Processing /// <summary> - /// This disposes all resources and rebuilds the ones needed. - /// This usually happens when geometry is changed by undo, redo, cut or paste actions. + /// This disposes all resources. Needed geometry will be rebuild automatically. /// </summary> protected virtual void ResourcesReloaded() { @@ -647,7 +644,92 @@ namespace CodeImp.DoomBuilder.VisualModes // Visibility culling (this re-creates the needed resources) DoCulling(); } - + + /// <summary> + /// This disposes orphaned resources and resources on changed geometry. + /// 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. + /// </summary> + protected virtual void ResourcesReloadedPartial() + { + Dictionary<Sector, VisualSector> newsectors = new Dictionary<Sector,VisualSector>(allsectors.Count); + int counter = 0; + + // Neighbour sectors must be updated as well + foreach(Sector s in General.Map.Map.Sectors) + { + if(s.Marked) + { + foreach(Sidedef sd in s.Sidedefs) + if(sd.Other != null) sd.Other.Marked = true; + } + } + + // Go for all sidedefs to mark sectors that need updating + foreach(Sidedef sd in General.Map.Map.Sidedefs) + if(sd.Marked) sd.Sector.Marked = true; + + // Go for all vertices to mark linedefs that need updating + foreach(Vertex v in General.Map.Map.Vertices) + { + if(v.Marked) + { + foreach(Linedef ld in v.Linedefs) + ld.Marked = true; + } + } + + // Go for all linedefs to mark sectors that need updating + foreach(Linedef ld in General.Map.Map.Linedefs) + { + if(ld.Marked) + { + if(ld.Front != null) ld.Front.Sector.Marked = true; + if(ld.Back != null) ld.Back.Sector.Marked = true; + } + } + + // Dispose if source was disposed or marked + foreach(KeyValuePair<Sector, VisualSector> vs in allsectors) + { + if(vs.Key.IsDisposed || vs.Key.Marked) + { + vs.Value.Dispose(); + counter++; + } + else + newsectors.Add(vs.Key, vs.Value); + } + + General.WriteLogLine("VisualSectors disposed: " + counter); + + // Things depend on the sector they are in and because we can't + // easily determine which ones changed, we dispose all things + foreach(KeyValuePair<Thing, VisualThing> vt in allthings) + vt.Value.Dispose(); + + // Apply new lists + allsectors = newsectors; + allthings = new Dictionary<Thing, VisualThing>(allthings.Count); + + // Clear visibility collections + visiblesectors.Clear(); + visibleblocks.Clear(); + visiblegeometry.Clear(); + visiblethings.Clear(); + + // Make new blockmap + if(blockmap != null) + { + blockmap.Dispose(); + blockmap = new VisualBlockMap(); + FillBlockMap(); + } + + // Visibility culling (this re-creates the needed resources) + DoCulling(); + } + /// <summary> /// Implement this to create an instance of your VisualSector implementation. /// </summary> diff --git a/Source/Core/Windows/LinedefEditForm.cs b/Source/Core/Windows/LinedefEditForm.cs index 91e5ec30a1c55592518a8f5886c04d6ccfe931c7..ff13dfbbd396c76a1f02079aaa43d233071866ad 100644 --- a/Source/Core/Windows/LinedefEditForm.cs +++ b/Source/Core/Windows/LinedefEditForm.cs @@ -362,8 +362,8 @@ namespace CodeImp.DoomBuilder.Windows // Apply all flags foreach(CheckBox c in flags.Checkboxes) { - if(c.CheckState == CheckState.Checked) l.Flags[c.Tag.ToString()] = true; - else if(c.CheckState == CheckState.Unchecked) l.Flags[c.Tag.ToString()] = false; + if(c.CheckState == CheckState.Checked) l.SetFlag(c.Tag.ToString(), true); + else if(c.CheckState == CheckState.Unchecked) l.SetFlag(c.Tag.ToString(), false); } // Apply chosen activation flag @@ -374,8 +374,8 @@ namespace CodeImp.DoomBuilder.Windows foreach(CheckBox c in udmfactivates.Checkboxes) { LinedefActivateInfo ai = (c.Tag as LinedefActivateInfo); - if(c.CheckState == CheckState.Checked) l.Flags[ai.Key] = true; - else if(c.CheckState == CheckState.Unchecked) l.Flags[ai.Key] = false; + if(c.CheckState == CheckState.Checked) l.SetFlag(ai.Key, true); + else if(c.CheckState == CheckState.Unchecked) l.SetFlag(ai.Key, false); } // Action/tags @@ -415,7 +415,7 @@ namespace CodeImp.DoomBuilder.Windows if(l.Front == null) General.Map.Map.CreateSidedef(l, true, s); // Change sector? - if(l.Front.Sector != s) l.Front.ChangeSector(s); + if(l.Front.Sector != s) l.Front.SetSector(s); // Apply settings l.Front.OffsetX = General.Clamp(frontoffsetx.GetResult(l.Front.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); @@ -442,7 +442,7 @@ namespace CodeImp.DoomBuilder.Windows if(l.Back == null) General.Map.Map.CreateSidedef(l, false, s); // Change sector? - if(l.Back.Sector != s) l.Back.ChangeSector(s); + if(l.Back.Sector != s) l.Back.SetSector(s); // Apply settings l.Back.OffsetX = General.Clamp(backoffsetx.GetResult(l.Back.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); diff --git a/Source/Core/Windows/ThingEditForm.cs b/Source/Core/Windows/ThingEditForm.cs index 36bf1b88c7281dee48227ff73b1436ea530b8ee8..984300b02f63522ce36df11a3963c3535d6a756e 100644 --- a/Source/Core/Windows/ThingEditForm.cs +++ b/Source/Core/Windows/ThingEditForm.cs @@ -291,8 +291,8 @@ namespace CodeImp.DoomBuilder.Windows // Apply all flags foreach(CheckBox c in flags.Checkboxes) { - if(c.CheckState == CheckState.Checked) t.Flags[c.Tag.ToString()] = true; - else if(c.CheckState == CheckState.Unchecked) t.Flags[c.Tag.ToString()] = false; + if(c.CheckState == CheckState.Checked) t.SetFlag(c.Tag.ToString(), true); + else if(c.CheckState == CheckState.Unchecked) t.SetFlag(c.Tag.ToString(), false); } // Action/tags diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs index 597ff183d2f7b663d501a83dc1f63e7e4bdc453e..89ab53487bc1800e33ad1ff43e19e87ce347f5af 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/DragGeometryMode.cs @@ -115,6 +115,10 @@ namespace CodeImp.DoomBuilder.BuilderModes this.dragstartmappos = dragstartmappos; Cursor.Current = Cursors.AppStarting; + + // We don't want to record this for undoing while we move the geometry around. + // This will be set back to normal when we're done. + General.Map.UndoRedo.IgnorePropChanges = true; // Make list of selected vertices selectedverts = General.Map.Map.GetMarkedVertices(true); @@ -272,6 +276,9 @@ namespace CodeImp.DoomBuilder.BuilderModes { // Move geometry back to original position MoveGeometryRelative(new Vector2D(0f, 0f), false, false); + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; // If only a single vertex was selected, deselect it now if(selectedverts.Count == 1) General.Map.Map.ClearSelectedVertices(); @@ -308,6 +315,9 @@ namespace CodeImp.DoomBuilder.BuilderModes // Move geometry back to original position MoveGeometryRelative(new Vector2D(0f, 0f), false, false); + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + // Make undo for the dragging General.Map.UndoRedo.CreateUndo("Drag geometry"); @@ -364,10 +374,17 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.Map.Update(true, false); // Redraw - General.Interface.RedrawDisplay(); + UpdateRedraw(); + renderer.Present(); + //General.Interface.RedrawDisplay(); } } + // This redraws only the required things + protected virtual void UpdateRedraw() + { + } + // When edit button is released protected override void OnEditEnd() { diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs index 81f808ffbae9ac5a9f47c34768f77281d144293b..232cfc2f24c3d22d71a8e01feeeb72e985edec97 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/DragLinedefsMode.cs @@ -136,6 +136,24 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.RedrawSurface(); + UpdateRedraw(); + + if(viewchanged) + { + // Start rendering things + if(renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This redraws only the required things + protected override void UpdateRedraw() + { // Start rendering structure if(renderer.StartPlotter(true)) { @@ -148,21 +166,11 @@ namespace CodeImp.DoomBuilder.BuilderModes // This is important to know, because this item is used // for snapping to the grid and snapping to nearest items renderer.PlotVertex(dragitem, ColorCollection.HIGHLIGHT); - + // Done renderer.Finish(); } - if(viewchanged) - { - // Start rendering things - if(renderer.StartThings(true)) - { - renderer.RenderThingSet(General.Map.Map.Things, 1.0f); - renderer.Finish(); - } - } - // Redraw overlay if(renderer.StartOverlay(true)) { @@ -172,8 +180,6 @@ namespace CodeImp.DoomBuilder.BuilderModes } renderer.Finish(); } - - renderer.Present(); } #endregion diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs index 3a04fd179fbeff1fe3163b0859237130ce869fcf..980f2f6c1011e61b4bc7a77c0d473edd13a9ef8c 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/DragSectorsMode.cs @@ -141,6 +141,24 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.RedrawSurface(); + UpdateRedraw(); + + // Redraw things when view changed + if(viewchanged) + { + if(renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This redraws only the required things + protected override void UpdateRedraw() + { // Start rendering if(renderer.StartPlotter(true)) { @@ -159,16 +177,6 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Finish(); } - // Redraw things when view changed - if(viewchanged) - { - if(renderer.StartThings(true)) - { - renderer.RenderThingSet(General.Map.Map.Things, 1.0f); - renderer.Finish(); - } - } - // Redraw overlay if(renderer.StartOverlay(true)) { @@ -178,8 +186,6 @@ namespace CodeImp.DoomBuilder.BuilderModes } renderer.Finish(); } - - renderer.Present(); } #endregion diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs index 49e4a187558193de9ddc3e4b4fa1520daca74e28..3ae54dd306a6b9cbd58ccf3830cabc4a2eb16fa3 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/DragThingsMode.cs @@ -232,6 +232,15 @@ namespace CodeImp.DoomBuilder.BuilderModes } } + // Render things + UpdateRedraw(); + + renderer.Present(); + } + + // This redraws only changed things + private void UpdateRedraw() + { // Render things if(renderer.StartThings(true)) { @@ -248,8 +257,6 @@ namespace CodeImp.DoomBuilder.BuilderModes // Done renderer.Finish(); } - - renderer.Present(); } // Cancelled @@ -346,7 +353,9 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.Map.Update(); // Redraw - General.Interface.RedrawDisplay(); + UpdateRedraw(); + renderer.Present(); + //General.Interface.RedrawDisplay(); } } diff --git a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs index 78234183ab153aea61b138d7b3459bb38ba91bea..94f637a4f08e3ba3435809b498c4a29f8a095fb6 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/DragVerticesMode.cs @@ -123,6 +123,24 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.RedrawSurface(); + UpdateRedraw(); + + // Redraw things when view changed + if(viewchanged) + { + if(renderer.StartThings(true)) + { + renderer.RenderThingSet(General.Map.Map.Things, 1.0f); + renderer.Finish(); + } + } + + renderer.Present(); + } + + // This redraws only the required things + protected override void UpdateRedraw() + { // Start rendering if(renderer.StartPlotter(true)) { @@ -140,16 +158,6 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.Finish(); } - // Redraw things when view changed - if(viewchanged) - { - if(renderer.StartThings(true)) - { - renderer.RenderThingSet(General.Map.Map.Things, 1.0f); - renderer.Finish(); - } - } - // Redraw overlay if(renderer.StartOverlay(true)) { @@ -159,8 +167,6 @@ namespace CodeImp.DoomBuilder.BuilderModes } renderer.Finish(); } - - renderer.Present(); } #endregion diff --git a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs index d8dad863ebae7dcf6b6b4b736f491178745b29de..4d07cfff9952568b72422872b4da29cfc31b1d7d 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs @@ -613,7 +613,11 @@ namespace CodeImp.DoomBuilder.BuilderModes // Add toolbar buttons General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionH); General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionV); - + + // We don't want to record this for undoing while we move the geometry around. + // This will be set back to normal when we're done. + General.Map.UndoRedo.IgnorePropChanges = true; + // Convert geometry selection General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkSelectedVertices(true, true); @@ -751,6 +755,9 @@ namespace CodeImp.DoomBuilder.BuilderModes // Paste operation? if(pasting) { + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + // Remove the geometry int index = 0; foreach(Vertex v in selectedvertices) @@ -773,6 +780,9 @@ namespace CodeImp.DoomBuilder.BuilderModes t.Rotate(thingangle[index]); t.Move(thingpos[index++]); } + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; } General.Map.Map.Update(true, true); @@ -810,7 +820,10 @@ namespace CodeImp.DoomBuilder.BuilderModes // Make undo General.Map.UndoRedo.CreateUndo("Edit selection"); } - + + // Resume normal undo/redo recording + General.Map.UndoRedo.IgnorePropChanges = false; + // Mark selected geometry General.Map.Map.ClearAllMarks(false); General.Map.Map.MarkAllSelectedGeometry(true, true, true, true, false); @@ -863,7 +876,7 @@ namespace CodeImp.DoomBuilder.BuilderModes if(joinsidedef != null) { // Join! - s.ChangeSector(joinsidedef.Sector); + s.SetSector(joinsidedef.Sector); s.Marked = false; joined = true; diff --git a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs index 5bef100e56477a3290793997bd609f1a5d240059..8450b6d3a63bd41b4f93d61b76bbee151d41147f 100644 --- a/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/SectorsMode.cs @@ -912,8 +912,8 @@ namespace CodeImp.DoomBuilder.BuilderModes sd.SetTextureLow("-"); // Set upper/lower unpegged flags - sd.Line.Flags[General.Map.Config.UpperUnpeggedFlag] = false; - sd.Line.Flags[General.Map.Config.LowerUnpeggedFlag] = true; + sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false); + sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true); } else { @@ -923,8 +923,8 @@ namespace CodeImp.DoomBuilder.BuilderModes if(doortex.Length > 0) sd.Other.SetTextureHigh(doortex); // Set upper/lower unpegged flags - sd.Line.Flags[General.Map.Config.UpperUnpeggedFlag] = false; - sd.Line.Flags[General.Map.Config.LowerUnpeggedFlag] = false; + sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false); + sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, false); // Get door linedef type from config sd.Line.Action = General.Map.Config.MakeDoorAction; diff --git a/Source/Plugins/BuilderModes/General/CopyStructures.cs b/Source/Plugins/BuilderModes/General/CopyStructures.cs index 868b1706d9765dead819f555c6204d3eb21e0192..781fa7dd8ecd37f875a4b8551a54ed4aace658d2 100644 --- a/Source/Plugins/BuilderModes/General/CopyStructures.cs +++ b/Source/Plugins/BuilderModes/General/CopyStructures.cs @@ -113,9 +113,9 @@ namespace CodeImp.DoomBuilder.BuilderModes { type = t.Type; angle = t.Angle; - flags = new Dictionary<string, bool>(t.Flags); + flags = t.GetFlags(); tag = t.Tag; - action = t.Action; + action = t.Action; args = (int[])(t.Args.Clone()); } @@ -123,9 +123,9 @@ namespace CodeImp.DoomBuilder.BuilderModes { t.Type = type; t.Rotate(angle); - t.Flags.Clear(); + t.ClearFlags(); foreach(KeyValuePair<string, bool> f in flags) - t.Flags.Add(f.Key, f.Value); + t.SetFlag(f.Key, f.Value); t.Tag = tag; t.Action = action; for(int i = 0; i < t.Args.Length; i++) diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs index 982383db958a10c6ad871ee5033c5ea440254f1b..c21b9c64526a1ecdb009eb487ca597df1755e977 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs @@ -220,8 +220,7 @@ namespace CodeImp.DoomBuilder.BuilderModes // Toggle upper-unpegged public virtual void OnToggleUpperUnpegged() { - if(this.Sidedef.Line.Flags.ContainsKey(General.Map.Config.UpperUnpeggedFlag) && - this.Sidedef.Line.Flags[General.Map.Config.UpperUnpeggedFlag]) + if(this.Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag)) { // Remove flag mode.ApplyUpperUnpegged(false); @@ -236,8 +235,7 @@ namespace CodeImp.DoomBuilder.BuilderModes // Toggle lower-unpegged public virtual void OnToggleLowerUnpegged() { - if(this.Sidedef.Line.Flags.ContainsKey(General.Map.Config.LowerUnpeggedFlag) && - this.Sidedef.Line.Flags[General.Map.Config.LowerUnpeggedFlag]) + if(this.Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag)) { // Remove flag mode.ApplyLowerUnpegged(false); @@ -258,14 +256,14 @@ namespace CodeImp.DoomBuilder.BuilderModes // Remove flag mode.CreateUndo("Remove upper-unpegged setting"); mode.SetActionResult("Removed upper-unpegged setting."); - this.Sidedef.Line.Flags[General.Map.Config.UpperUnpeggedFlag] = false; + this.Sidedef.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false); } else { // Add flag mode.CreateUndo("Set upper-unpegged setting"); mode.SetActionResult("Set upper-unpegged setting."); - this.Sidedef.Line.Flags[General.Map.Config.UpperUnpeggedFlag] = true; + this.Sidedef.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, true); } // Update sidedef geometry @@ -290,14 +288,14 @@ namespace CodeImp.DoomBuilder.BuilderModes // Remove flag mode.CreateUndo("Remove lower-unpegged setting"); mode.SetActionResult("Removed lower-unpegged setting."); - this.Sidedef.Line.Flags[General.Map.Config.LowerUnpeggedFlag] = false; + this.Sidedef.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, false); } else { // Add flag mode.CreateUndo("Set lower-unpegged setting"); mode.SetActionResult("Set lower-unpegged setting."); - this.Sidedef.Line.Flags[General.Map.Config.LowerUnpeggedFlag] = true; + this.Sidedef.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true); } // Update sidedef geometry diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs index 65df0a2039abebb04cdfe338018d98947a223613..a988fa807cdfbe1e44c73b57cc8df28e02955979 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs @@ -512,6 +512,13 @@ namespace CodeImp.DoomBuilder.BuilderModes PickTarget(); } + // After resources were partially reloaded + protected override void ResourcesReloadedPartial() + { + base.ResourcesReloadedPartial(); + PickTarget(); + } + // Mouse moves public override void OnMouseMove(MouseEventArgs e) {