- Progressive undo/redo system (much faster and up to 1000 undo/redo levels) Please note that several plugin functions and properties have changed.

- Some small optimizations in the classic 2D rendering (cached linedef flags, reduced some unneeded redrawing)
This commit is contained in:
codeimp 2009-06-11 21:21:20 +00:00
parent f66980b60f
commit 55e640b6e1
32 changed files with 1561 additions and 586 deletions

View file

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion> <ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</ProjectGuid> <ProjectGuid>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</ProjectGuid>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View file

@ -48,13 +48,14 @@ namespace CodeImp.DoomBuilder.Config
private Configuration cfg; private Configuration cfg;
// Cached variables // Cached variables
private int undolevels; //private int undolevels;
private bool blackbrowsers; private bool blackbrowsers;
private int visualfov; private int visualfov;
private float visualmousesensx; private float visualmousesensx;
private float visualmousesensy; private float visualmousesensy;
private int imagebrightness; private int imagebrightness;
private float doublesidedalpha; private float doublesidedalpha;
private byte doublesidedalphabyte;
private float backgroundalpha; private float backgroundalpha;
private bool qualitydisplay; private bool qualitydisplay;
private bool squarethings; private bool squarethings;
@ -92,11 +93,12 @@ namespace CodeImp.DoomBuilder.Config
#region ================== Properties #region ================== Properties
internal Configuration Config { get { return cfg; } } 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 bool BlackBrowsers { get { return blackbrowsers; } internal set { blackbrowsers = value; } }
public int VisualFOV { get { return visualfov; } internal set { visualfov = value; } } public int VisualFOV { get { return visualfov; } internal set { visualfov = value; } }
public int ImageBrightness { get { return imagebrightness; } internal set { imagebrightness = 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 BackgroundAlpha { get { return backgroundalpha; } internal set { backgroundalpha = value; } }
public float VisualMouseSensX { get { return visualmousesensx; } internal set { visualmousesensx = value; } } public float VisualMouseSensX { get { return visualmousesensx; } internal set { visualmousesensx = value; } }
public float VisualMouseSensY { get { return visualmousesensy; } internal set { visualmousesensy = value; } } public float VisualMouseSensY { get { return visualmousesensy; } internal set { visualmousesensy = value; } }
@ -153,12 +155,13 @@ namespace CodeImp.DoomBuilder.Config
{ {
// Read the cache variables // Read the cache variables
blackbrowsers = cfg.ReadSetting("blackbrowsers", false); blackbrowsers = cfg.ReadSetting("blackbrowsers", false);
undolevels = cfg.ReadSetting("undolevels", 20); //undolevels = cfg.ReadSetting("undolevels", 20);
visualfov = cfg.ReadSetting("visualfov", 80); visualfov = cfg.ReadSetting("visualfov", 80);
visualmousesensx = cfg.ReadSetting("visualmousesensx", 40f); visualmousesensx = cfg.ReadSetting("visualmousesensx", 40f);
visualmousesensy = cfg.ReadSetting("visualmousesensy", 40f); visualmousesensy = cfg.ReadSetting("visualmousesensy", 40f);
imagebrightness = cfg.ReadSetting("imagebrightness", 3); imagebrightness = cfg.ReadSetting("imagebrightness", 3);
doublesidedalpha = cfg.ReadSetting("doublesidedalpha", 0.4f); doublesidedalpha = cfg.ReadSetting("doublesidedalpha", 0.4f);
doublesidedalphabyte = (byte)(doublesidedalpha * 255f);
backgroundalpha = cfg.ReadSetting("backgroundalpha", 1.0f); backgroundalpha = cfg.ReadSetting("backgroundalpha", 1.0f);
qualitydisplay = cfg.ReadSetting("qualitydisplay", true); qualitydisplay = cfg.ReadSetting("qualitydisplay", true);
squarethings = cfg.ReadSetting("squarethings", false); squarethings = cfg.ReadSetting("squarethings", false);
@ -197,7 +200,7 @@ namespace CodeImp.DoomBuilder.Config
// Write the cache variables // Write the cache variables
cfg.WriteSetting("blackbrowsers", blackbrowsers); cfg.WriteSetting("blackbrowsers", blackbrowsers);
cfg.WriteSetting("undolevels", undolevels); //cfg.WriteSetting("undolevels", undolevels);
cfg.WriteSetting("visualfov", visualfov); cfg.WriteSetting("visualfov", visualfov);
cfg.WriteSetting("visualmousesensx", visualmousesensx); cfg.WriteSetting("visualmousesensx", visualmousesensx);
cfg.WriteSetting("visualmousesensy", visualmousesensy); cfg.WriteSetting("visualmousesensy", visualmousesensy);
@ -363,7 +366,7 @@ namespace CodeImp.DoomBuilder.Config
{ {
t.Type = defaultthingtype; t.Type = defaultthingtype;
t.Rotate(defaultthingangle); 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 // This attempts to find the default drawing settings

View file

@ -199,6 +199,8 @@ namespace CodeImp.DoomBuilder.Controls
// This applies the current fields to a UniFields object // This applies the current fields to a UniFields object
public void Apply(UniFields tofields) public void Apply(UniFields tofields)
{ {
tofields.BeforeFieldsChange();
// Go for all the fields // Go for all the fields
UniFields tempfields = new UniFields(tofields); UniFields tempfields = new UniFields(tofields);
foreach(KeyValuePair<string, UniValue> f in tempfields) foreach(KeyValuePair<string, UniValue> f in tempfields)

View file

@ -25,6 +25,7 @@ using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Plugins; using CodeImp.DoomBuilder.Plugins;
using CodeImp.DoomBuilder.Windows; using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO; using CodeImp.DoomBuilder.IO;
@ -41,6 +42,39 @@ namespace CodeImp.DoomBuilder.Editing
{ {
#region ================== Constants #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 #endregion
#region ================== Variables #region ================== Variables
@ -57,6 +91,17 @@ namespace CodeImp.DoomBuilder.Editing
// Unique tickets // Unique tickets
private int ticketid; 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 // Background thread
private volatile bool dobackgroundwork; private volatile bool dobackgroundwork;
private Thread backgroundthread; private Thread backgroundthread;
@ -68,10 +113,40 @@ namespace CodeImp.DoomBuilder.Editing
#region ================== Properties #region ================== Properties
public UndoSnapshot NextUndo { get { if(undos.Count > 0) return undos[0]; else return null; } } public UndoSnapshot NextUndo
public UndoSnapshot NextRedo { get { if(redos.Count > 0) return redos[0]; else return null; } } {
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; } } 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 #endregion
#region ================== Constructor / Disposer #region ================== Constructor / Disposer
@ -81,8 +156,8 @@ namespace CodeImp.DoomBuilder.Editing
{ {
// Initialize // Initialize
ticketid = 1; ticketid = 1;
undos = new List<UndoSnapshot>(General.Settings.UndoLevels + 1); undos = new List<UndoSnapshot>(MAX_UNDO_LEVELS + 1);
redos = new List<UndoSnapshot>(General.Settings.UndoLevels + 1); redos = new List<UndoSnapshot>(MAX_UNDO_LEVELS + 1);
// Bind any methods // Bind any methods
General.Actions.BindMethods(this); General.Actions.BindMethods(this);
@ -154,7 +229,7 @@ namespace CodeImp.DoomBuilder.Editing
UndoSnapshot u; UndoSnapshot u;
// Too many? // Too many?
if(list.Count > General.Settings.UndoLevels) if(list.Count > MAX_UNDO_LEVELS)
{ {
// Remove one and dispose map // Remove one and dispose map
u = list[list.Count - 1]; u = list[list.Count - 1];
@ -175,23 +250,23 @@ namespace CodeImp.DoomBuilder.Editing
dobackgroundwork = false; dobackgroundwork = false;
int undolevel = 0; int undolevel = 0;
UndoSnapshot snapshot; UndoSnapshot us;
while(true) while(true)
{ {
// Get the next snapshot or leave // Get the next snapshot or leave
lock(undos) lock(undos)
{ {
if(undolevel < undos.Count) if(undolevel < undos.Count)
snapshot = undos[undolevel]; us = undos[undolevel];
else else
break; break;
} }
// Write to file or load from file, if needed // Write to file or load from file, if needed
if(snapshot.StoreOnDisk && !snapshot.IsOnDisk) if(us.StoreOnDisk && !us.IsOnDisk)
snapshot.WriteToFile(); us.WriteToFile();
else if(!snapshot.StoreOnDisk && snapshot.IsOnDisk) else if(!us.StoreOnDisk && us.IsOnDisk)
snapshot.RestoreFromFile(); us.RestoreFromFile();
// Next // Next
undolevel++; undolevel++;
@ -204,16 +279,16 @@ namespace CodeImp.DoomBuilder.Editing
lock(redos) lock(redos)
{ {
if(redolevel < redos.Count) if(redolevel < redos.Count)
snapshot = redos[redolevel]; us = redos[redolevel];
else else
break; break;
} }
// Write to file or load from file, if needed // Write to file or load from file, if needed
if(snapshot.StoreOnDisk && !snapshot.IsOnDisk) if(us.StoreOnDisk && !us.IsOnDisk)
snapshot.WriteToFile(); us.WriteToFile();
else if(!snapshot.StoreOnDisk && snapshot.IsOnDisk) else if(!us.StoreOnDisk && us.IsOnDisk)
snapshot.RestoreFromFile(); us.RestoreFromFile();
// Next // Next
redolevel++; 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 #endregion
#region ================== Public Methods #region ================== Public Methods
@ -272,14 +463,15 @@ namespace CodeImp.DoomBuilder.Editing
(groupid == 0) || (lastgroupid == 0) || (groupid != lastgroupid) || (groupid == 0) || (lastgroupid == 0) || (groupid != lastgroupid) ||
(grouptag != lastgrouptag)) (grouptag != lastgrouptag))
{ {
FinishRecording();
// Next ticket id // Next ticket id
if(++ticketid == int.MaxValue) ticketid = 1; if(++ticketid == int.MaxValue) ticketid = 1;
General.WriteLogLine("Creating undo snapshot \"" + description + "\", Source " + groupsourcename + ", Group " + groupid + ", Tag " + grouptag + ", Ticket ID " + ticketid + "..."); General.WriteLogLine("Creating undo snapshot \"" + description + "\", Source " + groupsourcename + ", Group " + groupid + ", Tag " + grouptag + ", Ticket ID " + ticketid + "...");
// Make a snapshot if((snapshot != null) && !isundosnapshot)
u = new UndoSnapshot(description, General.Map.Map.Serialize(), ticketid); {
lock(undos) lock(undos)
{ {
// The current top of the stack can now be written to disk // The current top of the stack can now be written to disk
@ -287,9 +479,13 @@ namespace CodeImp.DoomBuilder.Editing
if(undos.Count > 0) undos[0].StoreOnDisk = true; if(undos.Count > 0) undos[0].StoreOnDisk = true;
// Put it on the stack // Put it on the stack
undos.Insert(0, u); undos.Insert(0, snapshot);
LimitUndoRedoLevel(undos); LimitUndoRedoLevel(undos);
} }
}
StartRecording(description);
isundosnapshot = false;
// Clear all redos // Clear all redos
ClearRedos(); ClearRedos();
@ -326,15 +522,17 @@ namespace CodeImp.DoomBuilder.Editing
{ {
General.WriteLogLine("Withdrawing undo snapshot \"" + undos[0].Description + "\", Ticket ID " + ticket + "..."); General.WriteLogLine("Withdrawing undo snapshot \"" + undos[0].Description + "\", Ticket ID " + ticket + "...");
lock(undos) if(snapshot != null)
{ {
// Remove the last made undo // Just trash this recording
undos[0].Dispose(); // You must call CreateUndo first before making any more changes
undos.RemoveAt(0); FinishRecording();
isundosnapshot = false;
// Make the current top of the stack load into memory snapshot = null;
// because it just became the next immediate undo level }
if(undos.Count > 0) undos[0].StoreOnDisk = false; else
{
throw new Exception("No undo is recording that can be withdrawn");
} }
// Update // Update
@ -348,12 +546,12 @@ namespace CodeImp.DoomBuilder.Editing
[BeginAction("undo")] [BeginAction("undo")]
public void PerformUndo() public void PerformUndo()
{ {
UndoSnapshot u, r; UndoSnapshot u = null;
Cursor oldcursor = Cursor.Current; Cursor oldcursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor; Cursor.Current = Cursors.WaitCursor;
// Anything to undo? // Anything to undo?
if(undos.Count > 0) if((undos.Count > 0) || ((snapshot != null) && !isundosnapshot))
{ {
// Let the plugins know // Let the plugins know
if(General.Plugins.OnUndoBegin()) if(General.Plugins.OnUndoBegin())
@ -364,6 +562,37 @@ namespace CodeImp.DoomBuilder.Editing
// Cancel volatile mode, if any // Cancel volatile mode, if any
// This returns false when mode was not volatile // This returns false when mode was not volatile
if(!General.CancelVolatileMode()) if(!General.CancelVolatileMode())
{
FinishRecording();
if(isundosnapshot)
{
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) lock(undos)
{ {
@ -375,44 +604,39 @@ namespace CodeImp.DoomBuilder.Editing
// because it just became the next immediate undo level // because it just became the next immediate undo level
if(undos.Count > 0) undos[0].StoreOnDisk = false; if(undos.Count > 0) undos[0].StoreOnDisk = false;
} }
}
General.WriteLogLine("Performing undo \"" + u.Description + "\", Ticket ID " + u.TicketID + "..."); General.WriteLogLine("Performing undo \"" + u.Description + "\", Ticket ID " + u.TicketID + "...");
General.Interface.DisplayStatus(StatusType.Action, u.Description + " undone."); General.Interface.DisplayStatus(StatusType.Action, u.Description + " undone.");
// Make a snapshot for redo // Make a snapshot for redo
r = new UndoSnapshot(u, General.Map.Map.Serialize()); StartRecording(u.Description);
isundosnapshot = true;
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);
}
// Reset grouping // Reset grouping
lastgroupplugin = null; lastgroupplugin = null;
// Change map set // Play back the stream in reverse
MemoryStream data = u.GetMapData(); MemoryStream data = u.GetStream();
General.Map.ChangeMapSet(new MapSet(data)); PlaybackStream(data);
data.Dispose(); data.Dispose();
// Remove selection // Remove selection
General.Map.Map.ClearAllMarks(false);
General.Map.Map.ClearAllSelected(); 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 // Done
General.Editing.Mode.OnUndoEnd(); General.Editing.Mode.OnUndoEnd();
General.Plugins.OnUndoEnd(); General.Plugins.OnUndoEnd();
// Update // Update interface
dobackgroundwork = true; dobackgroundwork = true;
General.Map.Data.UpdateUsedTextures();
General.MainWindow.RedrawDisplay();
General.MainWindow.UpdateInterface(); General.MainWindow.UpdateInterface();
} }
} }
@ -426,12 +650,12 @@ namespace CodeImp.DoomBuilder.Editing
[BeginAction("redo")] [BeginAction("redo")]
public void PerformRedo() public void PerformRedo()
{ {
UndoSnapshot u, r; UndoSnapshot r = null;
Cursor oldcursor = Cursor.Current; Cursor oldcursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor; Cursor.Current = Cursors.WaitCursor;
// Anything to redo? // Anything to redo?
if(redos.Count > 0) if((redos.Count > 0) || ((snapshot != null) && isundosnapshot))
{ {
// Let the plugins know // Let the plugins know
if(General.Plugins.OnRedoBegin()) if(General.Plugins.OnRedoBegin())
@ -442,6 +666,37 @@ namespace CodeImp.DoomBuilder.Editing
// Cancel volatile mode, if any // Cancel volatile mode, if any
General.CancelVolatileMode(); General.CancelVolatileMode();
FinishRecording();
if(isundosnapshot)
{
// 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) lock(redos)
{ {
// Get redo snapshot // Get redo snapshot
@ -452,44 +707,38 @@ namespace CodeImp.DoomBuilder.Editing
// because it just became the next immediate undo level // because it just became the next immediate undo level
if(redos.Count > 0) redos[0].StoreOnDisk = false; if(redos.Count > 0) redos[0].StoreOnDisk = false;
} }
}
General.WriteLogLine("Performing redo \"" + r.Description + "\", Ticket ID " + r.TicketID + "..."); General.WriteLogLine("Performing redo \"" + r.Description + "\", Ticket ID " + r.TicketID + "...");
General.Interface.DisplayStatus(StatusType.Action, r.Description + " redone."); General.Interface.DisplayStatus(StatusType.Action, r.Description + " redone.");
// Make a snapshot for undo StartRecording(r.Description);
u = new UndoSnapshot(r, General.Map.Map.Serialize()); isundosnapshot = false;
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);
}
// Reset grouping // Reset grouping
lastgroupplugin = null; lastgroupplugin = null;
// Change map set // Play back the stream in reverse
MemoryStream data = r.GetMapData(); MemoryStream data = r.GetStream();
General.Map.ChangeMapSet(new MapSet(data)); PlaybackStream(data);
data.Dispose(); data.Dispose();
// Remove selection // Remove selection
General.Map.Map.ClearAllMarks(false);
General.Map.Map.ClearAllSelected(); 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 // Done
General.Editing.Mode.OnRedoEnd(); General.Editing.Mode.OnRedoEnd();
General.Plugins.OnRedoEnd(); General.Plugins.OnRedoEnd();
// Update // Update interface
dobackgroundwork = true; dobackgroundwork = true;
General.Map.Data.UpdateUsedTextures();
General.MainWindow.RedrawDisplay();
General.MainWindow.UpdateInterface(); General.MainWindow.UpdateInterface();
} }
} }
@ -499,5 +748,428 @@ namespace CodeImp.DoomBuilder.Editing
} }
#endregion #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
} }
} }

View file

@ -40,7 +40,7 @@ namespace CodeImp.DoomBuilder.Editing
{ {
#region ================== Variables #region ================== Variables
private MemoryStream mapdata; private MemoryStream recstream;
private string filename; private string filename;
private string description; private string description;
private int ticketid; // For safe withdrawing private int ticketid; // For safe withdrawing
@ -62,22 +62,22 @@ namespace CodeImp.DoomBuilder.Editing
#region ================== Constructor / Disposer #region ================== Constructor / Disposer
// Constructor // 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.ticketid = ticketid;
this.description = description; this.description = description;
this.mapdata = mapdata; this.recstream = recstream;
this.filename = null; this.filename = null;
} }
// Constructor // 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.ticketid = info.ticketid;
this.description = info.description; this.description = info.description;
this.mapdata = mapdata; this.recstream = recstream;
this.filename = null; this.filename = null;
} }
@ -87,8 +87,8 @@ namespace CodeImp.DoomBuilder.Editing
lock(this) lock(this)
{ {
isdisposed = true; isdisposed = true;
if(mapdata != null) mapdata.Dispose(); if(recstream != null) recstream.Dispose();
mapdata = null; recstream = null;
if(isondisk) File.Delete(filename); if(isondisk) File.Delete(filename);
isondisk = false; isondisk = false;
} }
@ -99,15 +99,15 @@ namespace CodeImp.DoomBuilder.Editing
#region ================== Methods #region ================== Methods
// This returns the map data // This returns the map data
internal MemoryStream GetMapData() internal MemoryStream GetStream()
{ {
lock(this) lock(this)
{ {
// Restore into memory if needed // Restore into memory if needed
if(isondisk) RestoreFromFile(); if(isondisk) RestoreFromFile();
// Return a copy of the buffer // Return the buffer
return new MemoryStream(mapdata.ToArray()); return recstream;
} }
} }
@ -121,9 +121,9 @@ namespace CodeImp.DoomBuilder.Editing
isondisk = true; isondisk = true;
// Compress data // Compress data
mapdata.Seek(0, SeekOrigin.Begin); recstream.Seek(0, SeekOrigin.Begin);
MemoryStream outstream = new MemoryStream((int)mapdata.Length); MemoryStream outstream = new MemoryStream((int)recstream.Length);
BZip2.Compress(mapdata, outstream, 300000); BZip2.Compress(recstream, outstream, 300000);
// Make temporary file // Make temporary file
filename = General.MakeTempFilename(General.Map.TempPath, "snapshot"); filename = General.MakeTempFilename(General.Map.TempPath, "snapshot");
@ -132,8 +132,8 @@ namespace CodeImp.DoomBuilder.Editing
File.WriteAllBytes(filename, outstream.ToArray()); File.WriteAllBytes(filename, outstream.ToArray());
// Remove data from memory // Remove data from memory
mapdata.Dispose(); recstream.Dispose();
mapdata = null; recstream = null;
outstream.Dispose(); outstream.Dispose();
} }
} }
@ -154,7 +154,7 @@ namespace CodeImp.DoomBuilder.Editing
MemoryStream outstream = new MemoryStream((int)instream.Length * 4); MemoryStream outstream = new MemoryStream((int)instream.Length * 4);
instream.Seek(0, SeekOrigin.Begin); instream.Seek(0, SeekOrigin.Begin);
BZip2.Decompress(instream, outstream); BZip2.Decompress(instream, outstream);
mapdata = new MemoryStream(outstream.ToArray()); recstream = new MemoryStream(outstream.ToArray());
// Clean up // Clean up
instream.Dispose(); instream.Dispose();

View file

@ -348,6 +348,9 @@ namespace CodeImp.DoomBuilder
General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "..."); General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "...");
io = MapSetIO.Create(config.FormatInterface, tempwad, this); io = MapSetIO.Create(config.FormatInterface, tempwad, this);
General.WriteLogLine("Reading map data structures from file..."); General.WriteLogLine("Reading map data structures from file...");
#if DEBUG
map = io.Read(map, TEMP_MAP_HEADER);
#else
try { map = io.Read(map, TEMP_MAP_HEADER); } try { map = io.Read(map, TEMP_MAP_HEADER); }
catch(Exception e) catch(Exception e)
{ {
@ -355,6 +358,7 @@ namespace CodeImp.DoomBuilder
General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK); General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK);
return false; return false;
} }
#endif
map.EndAddRemove(); map.EndAddRemove();
// Load data manager // Load data manager
@ -446,6 +450,8 @@ namespace CodeImp.DoomBuilder
{ {
if(t.Type == config.Start3DModeThingType) 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); List<string> flagkeys = new List<string>(t.Flags.Keys);
foreach(string k in flagkeys) t.Flags[k] = false; foreach(string k in flagkeys) t.Flags[k] = false;
} }

View file

@ -566,14 +566,14 @@ namespace CodeImp.DoomBuilder.Geometry
{ {
// Create sidedef is needed and ensure it points to the new sector // 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 == 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); ApplyDefaultsToSidedef(ls.Line.Front, sourceside);
} }
else else
{ {
// Create sidedef is needed and ensure it points to the new sector // 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 == 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); ApplyDefaultsToSidedef(ls.Line.Back, sourceside);
} }
@ -622,7 +622,7 @@ namespace CodeImp.DoomBuilder.Geometry
else else
{ {
// Link to the new sector // Link to the new sector
ls.Line.Front.ChangeSector(original.Sector); ls.Line.Front.SetSector(original.Sector);
} }
} }
else else
@ -641,7 +641,7 @@ namespace CodeImp.DoomBuilder.Geometry
else else
{ {
// Link to the new sector // Link to the new sector
ls.Line.Back.ChangeSector(original.Sector); ls.Line.Back.SetSector(original.Sector);
} }
} }
} }

View file

@ -101,6 +101,9 @@ namespace CodeImp.DoomBuilder.Geometry
// Constructor // Constructor
public Triangulation() 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 // This performs the triangulation

View file

@ -42,6 +42,7 @@ namespace CodeImp.DoomBuilder.IO
private Stream stream; private Stream stream;
private BinaryReader reader; private BinaryReader reader;
private string[] stringstable; private string[] stringstable;
private int stringtablepos;
#endregion #endregion
@ -49,6 +50,8 @@ namespace CodeImp.DoomBuilder.IO
public bool IsWriting { get { return false; } } public bool IsWriting { get { return false; } }
public int EndPosition { get { return stringtablepos; } }
#endregion #endregion
#region ================== Constructor / Destructor #region ================== Constructor / Destructor
@ -69,8 +72,8 @@ namespace CodeImp.DoomBuilder.IO
public void Begin() public void Begin()
{ {
// First 4 bytes are reserved for the offset of the strings table // First 4 bytes are reserved for the offset of the strings table
int offset = reader.ReadInt32(); stringtablepos = reader.ReadInt32();
stream.Seek(offset, SeekOrigin.Begin); stream.Seek(stringtablepos, SeekOrigin.Begin);
// Read the strings // Read the strings
List<string> strings = new List<string>(); List<string> strings = new List<string>();

View file

@ -375,6 +375,8 @@ namespace CodeImp.DoomBuilder.IO
// This reads custom fields from a collection and adds them to a map element // This reads custom fields from a collection and adds them to a map element
private void ReadCustomFields(UniversalCollection collection, MapElement element, string elementname) private void ReadCustomFields(UniversalCollection collection, MapElement element, string elementname)
{ {
element.Fields.BeforeFieldsChange();
// Go for all the elements in the collection // Go for all the elements in the collection
foreach(UniversalEntry e in collection) foreach(UniversalEntry e in collection)
{ {

View file

@ -67,6 +67,8 @@ namespace CodeImp.DoomBuilder.Map
private float lengthinv; private float lengthinv;
private float angle; private float angle;
private RectangleF rect; private RectangleF rect;
private bool blocksoundflag;
private bool impassableflag;
// Properties // Properties
private Dictionary<string, bool> flags; private Dictionary<string, bool> flags;
@ -89,10 +91,10 @@ namespace CodeImp.DoomBuilder.Map
public Sidedef Front { get { return front; } } public Sidedef Front { get { return front; } }
public Sidedef Back { get { return back; } } public Sidedef Back { get { return back; } }
public Line2D Line { get { return new Line2D(start.Position, end.Position); } } public Line2D Line { get { return new Line2D(start.Position, end.Position); } }
public Dictionary<string, bool> Flags { get { return flags; } } internal Dictionary<string, bool> Flags { get { return flags; } }
public int Action { get { return action; } set { action = value; } } public int Action { get { return action; } set { BeforePropsChange(); action = value; } }
public int Activate { get { return activate; } set { activate = value; } } public int Activate { get { return activate; } set { BeforePropsChange(); 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"); } } 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 LengthSq { get { return lengthsq; } }
public float Length { get { return length; } } public float Length { get { return length; } }
public float LengthInv { get { return lengthinv; } } public float LengthInv { get { return lengthinv; } }
@ -102,6 +104,8 @@ namespace CodeImp.DoomBuilder.Map
public int[] Args { get { return args; } } public int[] Args { get { return args; } }
internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } } internal int SerializedIndex { get { return serializedindex; } set { serializedindex = value; } }
internal bool FrontInterior { get { return frontinterior; } set { frontinterior = value; } } internal bool FrontInterior { get { return frontinterior; } set { frontinterior = value; } }
internal bool ImpassableFlag { get { return impassableflag; } }
internal bool BlockSoundFlag { get { return blocksoundflag; } }
#endregion #endregion
@ -113,36 +117,18 @@ namespace CodeImp.DoomBuilder.Map
// Initialize // Initialize
this.map = map; this.map = map;
this.listindex = listindex; this.listindex = listindex;
this.start = start;
this.end = end;
this.updateneeded = true; this.updateneeded = true;
this.args = new int[NUM_ARGS]; this.args = new int[NUM_ARGS];
this.flags = new Dictionary<string, bool>(); this.flags = new Dictionary<string, bool>();
// Attach to vertices // 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.start = start;
this.startvertexlistitem = start.AttachLinedefP(this);
this.end = end; this.end = end;
this.updateneeded = true; this.endvertexlistitem = end.AttachLinedefP(this);
this.args = new int[NUM_ARGS];
// Attach to vertices if(map == General.Map.Map)
startvertexlistitem = start.AttachLinedef(this); General.Map.UndoRedo.RecAddLinedef(this);
endvertexlistitem = end.AttachLinedef(this);
ReadWrite(stream);
// We have no destructor // We have no destructor
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
@ -157,18 +143,23 @@ namespace CodeImp.DoomBuilder.Map
// Already set isdisposed so that changes can be prohibited // Already set isdisposed so that changes can be prohibited
isdisposed = true; 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 // Remove from main list
map.RemoveLinedef(listindex); map.RemoveLinedef(listindex);
// Detach from vertices // Detach from vertices
start.DetachLinedef(startvertexlistitem); if(startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem);
end.DetachLinedef(endvertexlistitem);
startvertexlistitem = null; startvertexlistitem = null;
start = null;
if(endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem);
endvertexlistitem = null; endvertexlistitem = null;
end = null;
// Dispose sidedefs
if((front != null) && map.AutoRemove) front.Dispose();
if((back != null) && map.AutoRemove) back.Dispose();
// Clean up // Clean up
start = null; start = null;
@ -186,9 +177,18 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Management #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) internal void ReadWrite(IReadWriteStream s)
{ {
if(!s.IsWriting) BeforePropsChange();
base.ReadWrite(s); base.ReadWrite(s);
if(s.IsWriting) if(s.IsWriting)
@ -223,32 +223,46 @@ namespace CodeImp.DoomBuilder.Map
// This sets new start vertex // This sets new start vertex
public void SetStartVertex(Vertex v) public void SetStartVertex(Vertex v)
{ {
if(map == General.Map.Map)
General.Map.UndoRedo.RecRefLinedefStart(this);
// Change start // Change start
if(startvertexlistitem != null) start.DetachLinedef(startvertexlistitem); if(startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem);
startvertexlistitem = null; startvertexlistitem = null;
start = v; start = v;
if(start != null) startvertexlistitem = start.AttachLinedef(this); if(start != null) startvertexlistitem = start.AttachLinedefP(this);
this.updateneeded = true; this.updateneeded = true;
} }
// This sets new end vertex // This sets new end vertex
public void SetEndVertex(Vertex v) public void SetEndVertex(Vertex v)
{ {
if(map == General.Map.Map)
General.Map.UndoRedo.RecRefLinedefEnd(this);
// Change end // Change end
if(endvertexlistitem != null) end.DetachLinedef(endvertexlistitem); if(endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem);
endvertexlistitem = null; endvertexlistitem = null;
end = v; end = v;
if(end != null) endvertexlistitem = end.AttachLinedef(this); if(end != null) endvertexlistitem = end.AttachLinedefP(this);
this.updateneeded = true; this.updateneeded = true;
} }
// This detaches a vertex // This detaches a vertex
internal void DetachVertex(Vertex v) internal void DetachVertexP(Vertex v)
{ {
if(v == start) if(v == start)
SetStartVertex(null); {
if(startvertexlistitem != null) start.DetachLinedefP(startvertexlistitem);
startvertexlistitem = null;
start = null;
}
else if(v == end) else if(v == end)
SetEndVertex(null); {
if(endvertexlistitem != null) end.DetachLinedefP(endvertexlistitem);
endvertexlistitem = null;
end = null;
}
else else
throw new Exception("Specified Vertex is not attached to this Linedef."); 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 // This copies all properties to another line
new public void CopyPropertiesTo(Linedef l) new public void CopyPropertiesTo(Linedef l)
{ {
l.BeforePropsChange();
// Copy properties // Copy properties
l.action = action; l.action = action;
l.args = (int[])args.Clone(); l.args = (int[])args.Clone();
@ -263,42 +279,57 @@ namespace CodeImp.DoomBuilder.Map
l.tag = tag; l.tag = tag;
l.updateneeded = true; l.updateneeded = true;
l.activate = activate; l.activate = activate;
l.impassableflag = impassableflag;
l.blocksoundflag = blocksoundflag;
base.CopyPropertiesTo(l); base.CopyPropertiesTo(l);
} }
// This attaches a sidedef on the front // This attaches a sidedef on the front
public void AttachFront(Sidedef s) internal void AttachFront(Sidedef s)
{ {
// No sidedef here yet? if(map == General.Map.Map)
if(front == null) 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 // Attach and recalculate
front = s; front = s;
if(front != null) front.SetLinedefP(this);
updateneeded = true; updateneeded = true;
} }
else throw new Exception("Linedef already has a front Sidedef.");
}
// This attaches a sidedef on the back // This attaches a sidedef on the back
public void AttachBack(Sidedef s) internal void AttachBack(Sidedef s)
{ {
// No sidedef here yet? if(map == General.Map.Map)
if(back == null) 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 // Attach and recalculate
back = s; back = s;
if(back != null) back.SetLinedefP(this);
updateneeded = true; updateneeded = true;
} }
else throw new Exception("Linedef already has a back Sidedef.");
}
// This detaches a sidedef from the front // This detaches a sidedef from the front
public void DetachSidedef(Sidedef s) internal void DetachSidedefP(Sidedef s)
{ {
// Sidedef is on the front? // Sidedef is on the front?
if(front == s) if(front == s)
{ {
// Remove sidedef reference // Remove sidedef reference
if(front != null) front.SetLinedefP(null);
front = null; front = null;
updateneeded = true; updateneeded = true;
} }
@ -306,10 +337,11 @@ namespace CodeImp.DoomBuilder.Map
else if(back == s) else if(back == s)
{ {
// Remove sidedef reference // Remove sidedef reference
if(back != null) back.SetLinedefP(null);
back = null; back = null;
updateneeded = true; 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 // 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); float b = Math.Max(start.Position.y, end.Position.y);
rect = new RectangleF(l, t, r - l, b - t); rect = new RectangleF(l, t, r - l, b - t);
// Cached flags
blocksoundflag = IsFlagSet(General.Map.Config.SoundLinedefFlag);
impassableflag = IsFlagSet(General.Map.Config.ImpassableFlag);
// Updated // Updated
updateneeded = false; updateneeded = false;
} }
@ -479,18 +515,43 @@ namespace CodeImp.DoomBuilder.Map
return false; 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 // This flips the linedef's vertex attachments
public void FlipVertices() public void FlipVertices()
{ {
// Flip vertices // Flip vertices
Vertex v = start; Vertex oldstart = start;
start = end; Vertex oldend = end;
end = v; SetStartVertex(oldend);
SetEndVertex(oldstart);
// Flip tickets accordingly
LinkedListNode<Linedef> vn = startvertexlistitem;
startvertexlistitem = endvertexlistitem;
endvertexlistitem = vn;
// For drawing, the interior now lies on the other side // For drawing, the interior now lies on the other side
frontinterior = !frontinterior; frontinterior = !frontinterior;
@ -504,9 +565,10 @@ namespace CodeImp.DoomBuilder.Map
public void FlipSidedefs() public void FlipSidedefs()
{ {
// Flip sidedefs // Flip sidedefs
Sidedef sd = front; Sidedef oldfront = front;
front = back; Sidedef oldback = back;
back = sd; AttachFront(oldback);
AttachBack(oldfront);
General.Map.IsChanged = true; General.Map.IsChanged = true;
} }
@ -544,14 +606,14 @@ namespace CodeImp.DoomBuilder.Map
if((front != null) && (back != null)) if((front != null) && (back != null))
{ {
// Apply or remove flags for doublesided line // Apply or remove flags for doublesided line
flags[General.Map.Config.SingleSidedFlag] = false; SetFlag(General.Map.Config.SingleSidedFlag, false);
flags[General.Map.Config.DoubleSidedFlag] = true; SetFlag(General.Map.Config.DoubleSidedFlag, true);
} }
else else
{ {
// Apply or remove flags for singlesided line // Apply or remove flags for singlesided line
flags[General.Map.Config.SingleSidedFlag] = true; SetFlag(General.Map.Config.SingleSidedFlag, true);
flags[General.Map.Config.DoubleSidedFlag] = false; SetFlag(General.Map.Config.DoubleSidedFlag, false);
} }
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -952,6 +1014,8 @@ namespace CodeImp.DoomBuilder.Map
// This updates all properties // This updates all properties
public void Update(Dictionary<string, bool> flags, int activate, int tag, int action, int[] args) public void Update(Dictionary<string, bool> flags, int activate, int tag, int action, int[] args)
{ {
BeforePropsChange();
// Apply changes // Apply changes
this.flags = new Dictionary<string, bool>(flags); this.flags = new Dictionary<string, bool>(flags);
this.tag = tag; this.tag = tag;

View file

@ -68,13 +68,14 @@ namespace CodeImp.DoomBuilder.Map
internal MapElement() internal MapElement()
{ {
// Initialize // Initialize
fields = new UniFields(); fields = new UniFields(this);
} }
// Disposer // Disposer
public virtual void Dispose() public virtual void Dispose()
{ {
// Clean up // Clean up
fields.Owner = null;
fields = null; fields = null;
// Done // Done
@ -101,7 +102,7 @@ namespace CodeImp.DoomBuilder.Map
} }
else else
{ {
fields = new UniFields(c); fields = new UniFields(this, c);
for(int i = 0; i < c; i++) for(int i = 0; i < c; i++)
{ {
string t; s.rString(out t); string t; s.rString(out t);
@ -114,7 +115,16 @@ namespace CodeImp.DoomBuilder.Map
// This copies properties to any other element // This copies properties to any other element
public void CopyPropertiesTo(MapElement 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 #endregion

View file

@ -204,7 +204,6 @@ namespace CodeImp.DoomBuilder.Map
{ {
// Already set isdisposed so that changes can be prohibited // Already set isdisposed so that changes can be prohibited
isdisposed = true; isdisposed = true;
autoremove = false;
BeginAddRemove(); BeginAddRemove();
// Dispose all things // Dispose all things
@ -435,6 +434,7 @@ namespace CodeImp.DoomBuilder.Map
{ {
virtualsector = newset.CreateSector(); virtualsector = newset.CreateSector();
l.Front.Sector.CopyPropertiesTo(virtualsector); l.Front.Sector.CopyPropertiesTo(virtualsector);
virtualsector.Fields.BeforeFieldsChange();
virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue); virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue);
} }
@ -463,6 +463,7 @@ namespace CodeImp.DoomBuilder.Map
{ {
virtualsector = newset.CreateSector(); virtualsector = newset.CreateSector();
l.Back.Sector.CopyPropertiesTo(virtualsector); l.Back.Sector.CopyPropertiesTo(virtualsector);
virtualsector.Fields.BeforeFieldsChange();
virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue); virtualsector.Fields[VIRTUAL_SECTOR_FIELD] = new UniValue(virtualsectorvalue);
} }
@ -509,24 +510,6 @@ namespace CodeImp.DoomBuilder.Map
return v; 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> /// <summary>This creates a new linedef and returns it.</summary>
public Linedef CreateLinedef(Vertex start, Vertex end) public Linedef CreateLinedef(Vertex start, Vertex end)
{ {
@ -545,24 +528,6 @@ namespace CodeImp.DoomBuilder.Map
return l; 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> /// <summary>This creates a new sidedef and returns it.</summary>
public Sidedef CreateSidedef(Linedef l, bool front, Sector s) public Sidedef CreateSidedef(Linedef l, bool front, Sector s)
{ {
@ -581,24 +546,6 @@ namespace CodeImp.DoomBuilder.Map
return sd; 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> /// <summary>This creates a new sector and returns it.</summary>
public Sector CreateSector() public Sector CreateSector()
{ {
@ -637,15 +584,6 @@ namespace CodeImp.DoomBuilder.Map
return s; 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> /// <summary>This creates a new thing and returns it.</summary>
public Thing CreateThing() public Thing CreateThing()
{ {
@ -919,7 +857,8 @@ namespace CodeImp.DoomBuilder.Map
// Go for all vertices // Go for all vertices
for(int i = 0; i < c; i++) for(int i = 0; i < c; i++)
{ {
array[i] = CreateVertex(stream); array[i] = CreateVertex(new Vector2D());
array[i].ReadWrite(stream);
} }
return array; return array;
@ -941,7 +880,8 @@ namespace CodeImp.DoomBuilder.Map
stream.rInt(out end); 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; return array;
@ -964,7 +904,8 @@ namespace CodeImp.DoomBuilder.Map
stream.rBool(out front); 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 // Go for all sectors
for(int i = 0; i < c; i++) for(int i = 0; i < c; i++)
{ {
array[i] = CreateSector(stream); array[i] = CreateSector();
array[i].ReadWrite(stream);
} }
return array; return array;
@ -2677,14 +2619,14 @@ namespace CodeImp.DoomBuilder.Map
{ {
// Replace with stored sidedef // Replace with stored sidedef
bool isfront = snsd.IsFront; bool isfront = snsd.IsFront;
snsd.Line.DetachSidedef(snsd); snsd.Line.DetachSidedefP(snsd);
if(isfront) if(isfront)
snsd.Line.AttachFront(stored); snsd.Line.AttachFront(stored);
else else
snsd.Line.AttachBack(stored); snsd.Line.AttachBack(stored);
// Remove the sidedef // Remove the sidedef
snsd.ChangeSector(null); snsd.SetSector(null);
RemoveSidedef(sn); RemoveSidedef(sn);
} }
else else

View file

@ -86,15 +86,15 @@ namespace CodeImp.DoomBuilder.Map
/// An unique index that does not change when other sectors are removed. /// An unique index that does not change when other sectors are removed.
/// </summary> /// </summary>
public int FixedIndex { get { return fixedindex; } } public int FixedIndex { get { return fixedindex; } }
public int FloorHeight { get { return floorheight; } set { floorheight = value; } } public int FloorHeight { get { return floorheight; } set { BeforePropsChange(); floorheight = value; } }
public int CeilHeight { get { return ceilheight; } set { ceilheight = value; } } public int CeilHeight { get { return ceilheight; } set { BeforePropsChange(); ceilheight = value; } }
public string FloorTexture { get { return floortexname; } } public string FloorTexture { get { return floortexname; } }
public string CeilTexture { get { return ceiltexname; } } public string CeilTexture { get { return ceiltexname; } }
public long LongFloorTexture { get { return longfloortexname; } } public long LongFloorTexture { get { return longfloortexname; } }
public long LongCeilTexture { get { return longceiltexname; } } public long LongCeilTexture { get { return longceiltexname; } }
public int Effect { get { return effect; } set { effect = value; } } public int Effect { get { return effect; } set { BeforePropsChange(); 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 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 { brightness = value; updateneeded = true; } } public int Brightness { get { return brightness; } set { BeforePropsChange(); brightness = value; updateneeded = true; } }
public bool UpdateNeeded { get { return updateneeded; } set { updateneeded |= value; triangulationneeded |= value; } } public bool UpdateNeeded { get { return updateneeded; } set { updateneeded |= value; triangulationneeded |= value; } }
public RectangleF BBox { get { return bbox; } } public RectangleF BBox { get { return bbox; } }
internal Sector Clone { get { return clone; } set { clone = value; } } internal Sector Clone { get { return clone; } set { clone = value; } }
@ -122,21 +122,8 @@ namespace CodeImp.DoomBuilder.Map
this.triangulationneeded = true; this.triangulationneeded = true;
this.surfaceentry = new SurfaceEntry(-1, -1, -1); this.surfaceentry = new SurfaceEntry(-1, -1, -1);
// We have no destructor if(map == General.Map.Map)
GC.SuppressFinalize(this); General.Map.UndoRedo.RecAddSector(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);
// We have no destructor // We have no destructor
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
@ -151,6 +138,9 @@ namespace CodeImp.DoomBuilder.Map
// Already set isdisposed so that changes can be prohibited // Already set isdisposed so that changes can be prohibited
isdisposed = true; isdisposed = true;
if(map == General.Map.Map)
General.Map.UndoRedo.RecRemSector(this);
// Remove from main list // Remove from main list
map.RemoveSector(listindex); map.RemoveSector(listindex);
@ -161,6 +151,8 @@ namespace CodeImp.DoomBuilder.Map
// because a sidedef cannot exist without reference to its sector. // because a sidedef cannot exist without reference to its sector.
if(map.AutoRemove) if(map.AutoRemove)
foreach(Sidedef sd in sidedefs) sd.Dispose(); foreach(Sidedef sd in sidedefs) sd.Dispose();
else
foreach(Sidedef sd in sidedefs) sd.SetSectorP(null);
// Free surface entry // Free surface entry
General.Map.CRenderer2D.Surfaces.FreeSurfaces(surfaceentry); General.Map.CRenderer2D.Surfaces.FreeSurfaces(surfaceentry);
@ -178,9 +170,18 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Management #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) internal void ReadWrite(IReadWriteStream s)
{ {
if(!s.IsWriting) BeforePropsChange();
base.ReadWrite(s); base.ReadWrite(s);
s.rwInt(ref fixedindex); s.rwInt(ref fixedindex);
@ -188,39 +189,11 @@ namespace CodeImp.DoomBuilder.Map
s.rwInt(ref ceilheight); s.rwInt(ref ceilheight);
s.rwString(ref floortexname); s.rwString(ref floortexname);
s.rwString(ref ceiltexname); s.rwString(ref ceiltexname);
//s.rwLong(ref longfloortexname); s.rwLong(ref longfloortexname);
//s.rwLong(ref longceiltexname); s.rwLong(ref longceiltexname);
s.rwInt(ref effect); s.rwInt(ref effect);
s.rwInt(ref tag); s.rwInt(ref tag);
s.rwInt(ref brightness); 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 // After deserialization
@ -237,6 +210,8 @@ namespace CodeImp.DoomBuilder.Map
// This copies all properties to another sector // This copies all properties to another sector
public void CopyPropertiesTo(Sector s) public void CopyPropertiesTo(Sector s)
{ {
s.BeforePropsChange();
// Copy properties // Copy properties
s.ceilheight = ceilheight; s.ceilheight = ceilheight;
s.ceiltexname = ceiltexname; s.ceiltexname = ceiltexname;
@ -252,7 +227,7 @@ namespace CodeImp.DoomBuilder.Map
} }
// This attaches a sidedef and returns the listitem // This attaches a sidedef and returns the listitem
public LinkedListNode<Sidedef> AttachSidedef(Sidedef sd) internal LinkedListNode<Sidedef> AttachSidedefP(Sidedef sd)
{ {
updateneeded = true; updateneeded = true;
triangulationneeded = true; triangulationneeded = true;
@ -260,7 +235,7 @@ namespace CodeImp.DoomBuilder.Map
} }
// This detaches a sidedef // This detaches a sidedef
public void DetachSidedef(LinkedListNode<Sidedef> l) internal void DetachSidedefP(LinkedListNode<Sidedef> l)
{ {
// Not disposing? // Not disposing?
if(!isdisposed) if(!isdisposed)
@ -357,6 +332,8 @@ namespace CodeImp.DoomBuilder.Map
// This updates the floor surface // This updates the floor surface
public void UpdateFloorSurface() public void UpdateFloorSurface()
{ {
if(flatvertices == null) return;
// Create floor vertices // Create floor vertices
FlatVertex[] floorvertices = new FlatVertex[flatvertices.Length]; FlatVertex[] floorvertices = new FlatVertex[flatvertices.Length];
flatvertices.CopyTo(floorvertices, 0); flatvertices.CopyTo(floorvertices, 0);
@ -372,6 +349,8 @@ namespace CodeImp.DoomBuilder.Map
// This updates the ceiling surface // This updates the ceiling surface
public void UpdateCeilingSurface() public void UpdateCeilingSurface()
{ {
if(flatvertices == null) return;
// Create ceiling vertices // Create ceiling vertices
FlatVertex[] ceilvertices = new FlatVertex[flatvertices.Length]; FlatVertex[] ceilvertices = new FlatVertex[flatvertices.Length];
flatvertices.CopyTo(ceilvertices, 0); flatvertices.CopyTo(ceilvertices, 0);
@ -484,7 +463,7 @@ namespace CodeImp.DoomBuilder.Map
// Change secter reference on my sidedefs // Change secter reference on my sidedefs
// This automatically disposes this sector // This automatically disposes this sector
while(sidedefs != null) while(sidedefs != null)
sidedefs.First.Value.ChangeSector(other); sidedefs.First.Value.SetSector(other);
} }
else else
{ {
@ -509,6 +488,8 @@ namespace CodeImp.DoomBuilder.Map
// This updates all properties // This updates all properties
public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, int tag, int brightness) public void Update(int hfloor, int hceil, string tfloor, string tceil, int effect, int tag, int brightness)
{ {
BeforePropsChange();
// Apply changes // Apply changes
this.floorheight = hfloor; this.floorheight = hfloor;
this.ceilheight = hceil; this.ceilheight = hceil;
@ -523,6 +504,8 @@ namespace CodeImp.DoomBuilder.Map
// This sets texture // This sets texture
public void SetFloorTexture(string name) public void SetFloorTexture(string name)
{ {
BeforePropsChange();
floortexname = name; floortexname = name;
longfloortexname = Lump.MakeLongName(name); longfloortexname = Lump.MakeLongName(name);
updateneeded = true; updateneeded = true;
@ -532,6 +515,8 @@ namespace CodeImp.DoomBuilder.Map
// This sets texture // This sets texture
public void SetCeilTexture(string name) public void SetCeilTexture(string name)
{ {
BeforePropsChange();
ceiltexname = name; ceiltexname = name;
longceiltexname = Lump.MakeLongName(name); longceiltexname = Lump.MakeLongName(name);
updateneeded = true; updateneeded = true;

View file

@ -67,13 +67,13 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Properties #region ================== Properties
public MapSet Map { get { return map; } } 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 Linedef Line { get { return linedef; } }
public Sidedef Other { get { if(this == linedef.Front) return linedef.Back; else return linedef.Front; } } public Sidedef Other { get { if(this == linedef.Front) return linedef.Back; else return linedef.Front; } }
public Sector Sector { get { return sector; } } public Sector Sector { get { return sector; } }
public float Angle { get { if(IsFront) return linedef.Angle; else return Angle2D.Normalized(linedef.Angle + Angle2D.PI); } } 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 OffsetX { get { return offsetx; } set { BeforePropsChange(); offsetx = value; } }
public int OffsetY { get { return offsety; } set { offsety = value; } } public int OffsetY { get { return offsety; } set { BeforePropsChange(); offsety = value; } }
public string HighTexture { get { return texnamehigh; } } public string HighTexture { get { return texnamehigh; } }
public string MiddleTexture { get { return texnamemid; } } public string MiddleTexture { get { return texnamemid; } }
public string LowTexture { get { return texnamelow; } } public string LowTexture { get { return texnamelow; } }
@ -92,8 +92,6 @@ namespace CodeImp.DoomBuilder.Map
// Initialize // Initialize
this.map = map; this.map = map;
this.listindex = listindex; this.listindex = listindex;
this.linedef = l;
this.sector = s;
this.texnamehigh = "-"; this.texnamehigh = "-";
this.texnamemid = "-"; this.texnamemid = "-";
this.texnamelow = "-"; this.texnamelow = "-";
@ -101,32 +99,21 @@ namespace CodeImp.DoomBuilder.Map
this.longtexnamemid = MapSet.EmptyLongName; this.longtexnamemid = MapSet.EmptyLongName;
this.longtexnamelow = MapSet.EmptyLongName; this.longtexnamelow = MapSet.EmptyLongName;
// Attach to the linedef // Attach linedef
if(front) l.AttachFront(this); else l.AttachBack(this); this.linedef = l;
if(l != null)
// Attach to sector {
sectorlistitem = s.AttachSidedef(this); if(front)
l.AttachFrontP(this);
// We have no destructor else
GC.SuppressFinalize(this); l.AttachBackP(this);
} }
// Constructor // Attach sector
internal Sidedef(MapSet map, int listindex, Linedef l, bool front, Sector s, IReadWriteStream stream) SetSectorP(s);
{
// Initialize
this.map = map;
this.listindex = listindex;
this.linedef = l;
this.sector = s;
// Attach to the linedef if(map == General.Map.Map)
if(front) l.AttachFront(this); else l.AttachBack(this); General.Map.UndoRedo.RecAddSidedef(this);
// Attach to sector
sectorlistitem = s.AttachSidedef(this);
ReadWrite(stream);
// We have no destructor // We have no destructor
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
@ -141,14 +128,17 @@ namespace CodeImp.DoomBuilder.Map
// Already set isdisposed so that changes can be prohibited // Already set isdisposed so that changes can be prohibited
isdisposed = true; isdisposed = true;
if(map == General.Map.Map)
General.Map.UndoRedo.RecRemSidedef(this);
// Remove from main list // Remove from main list
map.RemoveSidedef(listindex); map.RemoveSidedef(listindex);
// Detach from linedef // Detach from linedef
linedef.DetachSidedef(this); if(linedef != null) linedef.DetachSidedefP(this);
// Detach from sector // Detach from sector
sector.DetachSidedef(sectorlistitem); SetSectorP(null);
// Clean up // Clean up
sectorlistitem = null; sectorlistitem = null;
@ -165,9 +155,18 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Management #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) internal void ReadWrite(IReadWriteStream s)
{ {
if(!s.IsWriting) BeforePropsChange();
base.ReadWrite(s); base.ReadWrite(s);
s.rwInt(ref offsetx); s.rwInt(ref offsetx);
@ -175,20 +174,16 @@ namespace CodeImp.DoomBuilder.Map
s.rwString(ref texnamehigh); s.rwString(ref texnamehigh);
s.rwString(ref texnamemid); s.rwString(ref texnamemid);
s.rwString(ref texnamelow); s.rwString(ref texnamelow);
//s.rwLong(ref longtexnamehigh); s.rwLong(ref longtexnamehigh);
//s.rwLong(ref longtexnamemid); s.rwLong(ref longtexnamemid);
//s.rwLong(ref longtexnamelow); s.rwLong(ref longtexnamelow);
if(!s.IsWriting)
{
longtexnamehigh = Lump.MakeLongName(texnamehigh);
longtexnamemid = Lump.MakeLongName(texnamemid);
longtexnamelow = Lump.MakeLongName(texnamelow);
}
} }
// This copies all properties to another sidedef // This copies all properties to another sidedef
public void CopyPropertiesTo(Sidedef s) public void CopyPropertiesTo(Sidedef s)
{ {
s.BeforePropsChange();
// Copy properties // Copy properties
s.offsetx = offsetx; s.offsetx = offsetx;
s.offsety = offsety; s.offsety = offsety;
@ -201,73 +196,51 @@ namespace CodeImp.DoomBuilder.Map
base.CopyPropertiesTo(s); base.CopyPropertiesTo(s);
} }
// This creates a checksum from the sidedef properties // This changes sector
// Used for faster sidedefs compression public void SetSector(Sector newsector)
public uint GetChecksum()
{ {
CRC crc = new CRC(); if(map == General.Map.Map)
crc.Add(sector.FixedIndex); General.Map.UndoRedo.RecRefSidedefSector(this);
crc.Add(offsetx);
crc.Add(offsety); // Change sector
crc.Add(longtexnamehigh); SetSectorP(newsector);
crc.Add(longtexnamelow);
crc.Add(longtexnamemid);
return (uint)(crc.Value & 0x00000000FFFFFFFF);
} }
// This copies textures to another sidedef internal void SetSectorP(Sector newsector)
// And possibly also the offsets
public void AddTexturesTo(Sidedef s)
{ {
int copyoffsets = 0; // Detach from sector
if(sector != null) sector.DetachSidedefP(sectorlistitem);
// s cannot be null // Change sector
if(s == null) return; sector = newsector;
// Upper texture set? // Attach to sector
if((texnamehigh.Length > 0) && (texnamehigh[0] != '-')) if(sector != null)
{ sectorlistitem = sector.AttachSidedefP(this);
// 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; General.Map.IsChanged = true;
} }
// This sets the linedef
public void SetLinedef(Linedef ld, bool front)
{
if(linedef != null) linedef.DetachSidedefP(this);
if(ld != null)
{
if(front)
ld.AttachFront(this);
else
ld.AttachBack(this);
}
}
// This sets the linedef (passive: this doesn't tell the linedef and doesn't record)
internal void SetLinedefP(Linedef ld)
{
linedef = ld;
}
#endregion #endregion
#region ================== Methods #region ================== Methods
@ -281,6 +254,8 @@ namespace CodeImp.DoomBuilder.Map
// This removes textures that are not required // This removes textures that are not required
public void RemoveUnneededTextures(bool removemiddle, bool force) public void RemoveUnneededTextures(bool removemiddle, bool force)
{ {
BeforePropsChange();
// The middle texture can be removed regardless of any sector tag or linedef action // The middle texture can be removed regardless of any sector tag or linedef action
if(!MiddleRequired() && removemiddle) if(!MiddleRequired() && removemiddle)
{ {
@ -348,6 +323,75 @@ namespace CodeImp.DoomBuilder.Map
} }
} }
// 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 #endregion
#region ================== Changes #region ================== Changes
@ -355,6 +399,8 @@ namespace CodeImp.DoomBuilder.Map
// This updates all properties // This updates all properties
public void Update(int offsetx, int offsety, string thigh, string tmid, string tlow) public void Update(int offsetx, int offsety, string thigh, string tmid, string tlow)
{ {
BeforePropsChange();
// Apply changes // Apply changes
this.offsetx = offsetx; this.offsetx = offsetx;
this.offsety = offsety; this.offsety = offsety;
@ -366,6 +412,8 @@ namespace CodeImp.DoomBuilder.Map
// This sets texture // This sets texture
public void SetTextureHigh(string name) public void SetTextureHigh(string name)
{ {
BeforePropsChange();
texnamehigh = name; texnamehigh = name;
longtexnamehigh = Lump.MakeLongName(name); longtexnamehigh = Lump.MakeLongName(name);
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -374,6 +422,8 @@ namespace CodeImp.DoomBuilder.Map
// This sets texture // This sets texture
public void SetTextureMid(string name) public void SetTextureMid(string name)
{ {
BeforePropsChange();
texnamemid = name; texnamemid = name;
longtexnamemid = Lump.MakeLongName(name); longtexnamemid = Lump.MakeLongName(name);
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -382,27 +432,13 @@ namespace CodeImp.DoomBuilder.Map
// This sets texture // This sets texture
public void SetTextureLow(string name) public void SetTextureLow(string name)
{ {
BeforePropsChange();
texnamelow = name; texnamelow = name;
longtexnamelow = Lump.MakeLongName(name); longtexnamelow = Lump.MakeLongName(name);
General.Map.IsChanged = true; 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 #endregion
} }
} }

View file

@ -71,18 +71,18 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Properties #region ================== Properties
public MapSet Map { get { return map; } } 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 Vector3D Position { get { return pos; } }
public float Angle { get { return angle; } } public float Angle { get { return angle; } }
public int AngleDeg { get { return (int)Angle2D.RadToDeg(angle); } } public int AngleDeg { get { return (int)Angle2D.RadToDeg(angle); } }
public Dictionary<string, bool> Flags { get { return flags; } } internal Dictionary<string, bool> Flags { get { return flags; } }
public int Action { get { return action; } set { action = value; } } public int Action { get { return action; } set { BeforePropsChange(); action = value; } }
public int[] Args { get { return args; } } public int[] Args { get { return args; } }
public float Size { get { return size; } } public float Size { get { return size; } }
public float IconOffset { get { return iconoffset; } } public float IconOffset { get { return iconoffset; } }
public PixelColor Color { get { return color; } } public PixelColor Color { get { return color; } }
public bool FixedSize { get { return fixedsize; } } 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; } } public Sector Sector { get { return sector; } }
#endregion #endregion
@ -98,6 +98,9 @@ namespace CodeImp.DoomBuilder.Map
this.flags = new Dictionary<string, bool>(); this.flags = new Dictionary<string, bool>();
this.args = new int[NUM_ARGS]; this.args = new int[NUM_ARGS];
if(map == General.Map.Map)
General.Map.UndoRedo.RecAddThing(this);
// We have no destructor // We have no destructor
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@ -111,6 +114,9 @@ namespace CodeImp.DoomBuilder.Map
// Already set isdisposed so that changes can be prohibited // Already set isdisposed so that changes can be prohibited
isdisposed = true; isdisposed = true;
if(map == General.Map.Map)
General.Map.UndoRedo.RecRemThing(this);
// Remove from main list // Remove from main list
map.RemoveThing(listindex); map.RemoveThing(listindex);
@ -130,9 +136,18 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Management #region ================== Management
// Call this before changing properties
protected override void BeforePropsChange()
{
if(map == General.Map.Map)
General.Map.UndoRedo.RecPrpThing(this);
}
// Serialize / deserialize // Serialize / deserialize
internal void ReadWrite(IReadWriteStream s) internal void ReadWrite(IReadWriteStream s)
{ {
if(!s.IsWriting) BeforePropsChange();
base.ReadWrite(s); base.ReadWrite(s);
if(s.IsWriting) if(s.IsWriting)
@ -169,6 +184,8 @@ namespace CodeImp.DoomBuilder.Map
// This copies all properties to another thing // This copies all properties to another thing
public void CopyPropertiesTo(Thing t) public void CopyPropertiesTo(Thing t)
{ {
t.BeforePropsChange();
// Copy properties // Copy properties
t.type = type; t.type = type;
t.angle = angle; t.angle = angle;
@ -325,6 +342,8 @@ namespace CodeImp.DoomBuilder.Map
// NOTE: This does not update sector! (call DetermineSector) // NOTE: This does not update sector! (call DetermineSector)
public void Move(Vector3D newpos) public void Move(Vector3D newpos)
{ {
BeforePropsChange();
// Change position // Change position
this.pos = newpos; this.pos = newpos;
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -334,6 +353,8 @@ namespace CodeImp.DoomBuilder.Map
// NOTE: This does not update sector! (call DetermineSector) // NOTE: This does not update sector! (call DetermineSector)
public void Move(Vector2D newpos) public void Move(Vector2D newpos)
{ {
BeforePropsChange();
// Change position // Change position
this.pos = new Vector3D(newpos.x, newpos.y, pos.z); this.pos = new Vector3D(newpos.x, newpos.y, pos.z);
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -343,6 +364,8 @@ namespace CodeImp.DoomBuilder.Map
// NOTE: This does not update sector! (call DetermineSector) // NOTE: This does not update sector! (call DetermineSector)
public void Move(float x, float y, float zoffset) public void Move(float x, float y, float zoffset)
{ {
BeforePropsChange();
// Change position // Change position
this.pos = new Vector3D(x, y, zoffset); this.pos = new Vector3D(x, y, zoffset);
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -351,6 +374,8 @@ namespace CodeImp.DoomBuilder.Map
// This rotates the thing // This rotates the thing
public void Rotate(float newangle) public void Rotate(float newangle)
{ {
BeforePropsChange();
// Change angle // Change angle
this.angle = newangle; this.angle = newangle;
General.Map.IsChanged = true; General.Map.IsChanged = true;
@ -404,6 +429,40 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Methods #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 // This snaps the vertex to the grid
public void SnapToGrid() public void SnapToGrid()
{ {

View file

@ -15,6 +15,10 @@ namespace CodeImp.DoomBuilder.Map
/// </summary> /// </summary>
public class UniFields : SortedList<string, UniValue> 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 // New constructor
///<summary></summary> ///<summary></summary>
public UniFields() : base(2) public UniFields() : base(2)
@ -32,5 +36,33 @@ namespace CodeImp.DoomBuilder.Map
public UniFields(UniFields copyfrom) : base(copyfrom) 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();
}
} }
} }

View file

@ -81,19 +81,8 @@ namespace CodeImp.DoomBuilder.Map
this.listindex = listindex; this.listindex = listindex;
this.pos = pos; this.pos = pos;
// We have no destructor if(map == General.Map.Map)
GC.SuppressFinalize(this); General.Map.UndoRedo.RecAddVertex(this);
}
// Constructor
internal Vertex(MapSet map, int listindex, IReadWriteStream stream)
{
// Initialize
this.map = map;
this.linedefs = new LinkedList<Linedef>();
this.listindex = listindex;
ReadWrite(stream);
// We have no destructor // We have no destructor
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
@ -108,6 +97,9 @@ namespace CodeImp.DoomBuilder.Map
// Already set isdisposed so that changes can be prohibited // Already set isdisposed so that changes can be prohibited
isdisposed = true; isdisposed = true;
if(map == General.Map.Map)
General.Map.UndoRedo.RecRemVertex(this);
// Remove from main list // Remove from main list
map.RemoveVertex(listindex); map.RemoveVertex(listindex);
@ -120,7 +112,7 @@ namespace CodeImp.DoomBuilder.Map
else else
{ {
// Detach from linedefs // Detach from linedefs
foreach(Linedef ld in linedefs) ld.DetachVertex(this); foreach(Linedef ld in linedefs) ld.DetachVertexP(this);
} }
// Clean up // Clean up
@ -136,11 +128,21 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Management #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 // 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 // This detaches a linedef
public void DetachLinedef(LinkedListNode<Linedef> l) internal void DetachLinedefP(LinkedListNode<Linedef> l)
{ {
// Not disposing? // Not disposing?
if(!isdisposed) if(!isdisposed)
@ -160,9 +162,17 @@ namespace CodeImp.DoomBuilder.Map
// Serialize / deserialize // Serialize / deserialize
internal void ReadWrite(IReadWriteStream s) internal void ReadWrite(IReadWriteStream s)
{ {
if(!s.IsWriting) BeforePropsChange();
base.ReadWrite(s); base.ReadWrite(s);
s.rwVector2D(ref pos); s.rwVector2D(ref pos);
if(s.IsWriting)
{
// Let all lines know they need an update
foreach(Linedef l in linedefs) l.NeedUpdate();
}
} }
// Selected // Selected
@ -187,6 +197,8 @@ namespace CodeImp.DoomBuilder.Map
// This copies all properties to another thing // This copies all properties to another thing
public void CopyPropertiesTo(Vertex v) public void CopyPropertiesTo(Vertex v)
{ {
v.BeforePropsChange();
// Copy properties // Copy properties
v.pos = pos; v.pos = pos;
base.CopyPropertiesTo(v); base.CopyPropertiesTo(v);
@ -213,6 +225,8 @@ namespace CodeImp.DoomBuilder.Map
// Do we actually move? // Do we actually move?
if(newpos != pos) if(newpos != pos)
{ {
BeforePropsChange();
// Change position // Change position
pos = newpos; pos = newpos;

View file

@ -575,22 +575,20 @@ namespace CodeImp.DoomBuilder.Rendering
// This returns the color for a linedef // This returns the color for a linedef
public PixelColor DetermineLinedefColor(Linedef l) public PixelColor DetermineLinedefColor(Linedef l)
{ {
// Impassable lines if(l.Selected)
if(l.IsFlagSet(General.Map.Config.ImpassableFlag)) return General.Colors.Selection;
else if(l.ImpassableFlag)
{ {
// Determine color // Impassable lines
if(l.Selected) return General.Colors.Selection; if(l.Action != 0) return General.Colors.Actions;
else if(l.Action != 0) return General.Colors.Actions;
else return General.Colors.Linedefs; else return General.Colors.Linedefs;
} }
else else
{ {
// Determine color // Passable lines
byte a = (byte)(General.Settings.DoubleSidedAlpha * 255.0f); if(l.Action != 0) return General.Colors.Actions.WithAlpha(General.Settings.DoubleSidedAlphaByte);
if(l.Selected) return General.Colors.Selection; else if(l.BlockSoundFlag) return General.Colors.Sounds.WithAlpha(General.Settings.DoubleSidedAlphaByte);
else if(l.Action != 0) return General.Colors.Actions.WithAlpha(a); else return General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte);
else if(l.IsFlagSet(General.Map.Config.SoundLinedefFlag)) return General.Colors.Sounds.WithAlpha(a);
else return General.Colors.Linedefs.WithAlpha(a);
} }
} }

View file

@ -191,8 +191,7 @@ namespace CodeImp.DoomBuilder.VisualModes
public override void OnUndoEnd() public override void OnUndoEnd()
{ {
base.OnUndoEnd(); base.OnUndoEnd();
ResourcesReloaded(); ResourcesReloadedPartial();
General.Map.Data.UpdateUsedTextures();
renderer.SetCrosshairBusy(false); renderer.SetCrosshairBusy(false);
} }
@ -206,8 +205,7 @@ namespace CodeImp.DoomBuilder.VisualModes
public override void OnRedoEnd() public override void OnRedoEnd()
{ {
base.OnRedoEnd(); base.OnRedoEnd();
ResourcesReloaded(); ResourcesReloadedPartial();
General.Map.Data.UpdateUsedTextures();
renderer.SetCrosshairBusy(false); renderer.SetCrosshairBusy(false);
} }
@ -616,8 +614,7 @@ namespace CodeImp.DoomBuilder.VisualModes
#region ================== Processing #region ================== Processing
/// <summary> /// <summary>
/// This disposes all resources and rebuilds the ones needed. /// This disposes all resources. Needed geometry will be rebuild automatically.
/// This usually happens when geometry is changed by undo, redo, cut or paste actions.
/// </summary> /// </summary>
protected virtual void ResourcesReloaded() protected virtual void ResourcesReloaded()
{ {
@ -648,6 +645,91 @@ namespace CodeImp.DoomBuilder.VisualModes
DoCulling(); 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> /// <summary>
/// Implement this to create an instance of your VisualSector implementation. /// Implement this to create an instance of your VisualSector implementation.
/// </summary> /// </summary>

View file

@ -362,8 +362,8 @@ namespace CodeImp.DoomBuilder.Windows
// Apply all flags // Apply all flags
foreach(CheckBox c in flags.Checkboxes) foreach(CheckBox c in flags.Checkboxes)
{ {
if(c.CheckState == CheckState.Checked) l.Flags[c.Tag.ToString()] = true; if(c.CheckState == CheckState.Checked) l.SetFlag(c.Tag.ToString(), true);
else if(c.CheckState == CheckState.Unchecked) l.Flags[c.Tag.ToString()] = false; else if(c.CheckState == CheckState.Unchecked) l.SetFlag(c.Tag.ToString(), false);
} }
// Apply chosen activation flag // Apply chosen activation flag
@ -374,8 +374,8 @@ namespace CodeImp.DoomBuilder.Windows
foreach(CheckBox c in udmfactivates.Checkboxes) foreach(CheckBox c in udmfactivates.Checkboxes)
{ {
LinedefActivateInfo ai = (c.Tag as LinedefActivateInfo); LinedefActivateInfo ai = (c.Tag as LinedefActivateInfo);
if(c.CheckState == CheckState.Checked) l.Flags[ai.Key] = true; if(c.CheckState == CheckState.Checked) l.SetFlag(ai.Key, true);
else if(c.CheckState == CheckState.Unchecked) l.Flags[ai.Key] = false; else if(c.CheckState == CheckState.Unchecked) l.SetFlag(ai.Key, false);
} }
// Action/tags // Action/tags
@ -415,7 +415,7 @@ namespace CodeImp.DoomBuilder.Windows
if(l.Front == null) General.Map.Map.CreateSidedef(l, true, s); if(l.Front == null) General.Map.Map.CreateSidedef(l, true, s);
// Change sector? // Change sector?
if(l.Front.Sector != s) l.Front.ChangeSector(s); if(l.Front.Sector != s) l.Front.SetSector(s);
// Apply settings // Apply settings
l.Front.OffsetX = General.Clamp(frontoffsetx.GetResult(l.Front.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); 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); if(l.Back == null) General.Map.Map.CreateSidedef(l, false, s);
// Change sector? // Change sector?
if(l.Back.Sector != s) l.Back.ChangeSector(s); if(l.Back.Sector != s) l.Back.SetSector(s);
// Apply settings // Apply settings
l.Back.OffsetX = General.Clamp(backoffsetx.GetResult(l.Back.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset); l.Back.OffsetX = General.Clamp(backoffsetx.GetResult(l.Back.OffsetX), General.Map.FormatInterface.MinTextureOffset, General.Map.FormatInterface.MaxTextureOffset);

View file

@ -291,8 +291,8 @@ namespace CodeImp.DoomBuilder.Windows
// Apply all flags // Apply all flags
foreach(CheckBox c in flags.Checkboxes) foreach(CheckBox c in flags.Checkboxes)
{ {
if(c.CheckState == CheckState.Checked) t.Flags[c.Tag.ToString()] = true; if(c.CheckState == CheckState.Checked) t.SetFlag(c.Tag.ToString(), true);
else if(c.CheckState == CheckState.Unchecked) t.Flags[c.Tag.ToString()] = false; else if(c.CheckState == CheckState.Unchecked) t.SetFlag(c.Tag.ToString(), false);
} }
// Action/tags // Action/tags

View file

@ -116,6 +116,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
Cursor.Current = Cursors.AppStarting; 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 // Make list of selected vertices
selectedverts = General.Map.Map.GetMarkedVertices(true); selectedverts = General.Map.Map.GetMarkedVertices(true);
@ -273,6 +277,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Move geometry back to original position // Move geometry back to original position
MoveGeometryRelative(new Vector2D(0f, 0f), false, false); 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 only a single vertex was selected, deselect it now
if(selectedverts.Count == 1) General.Map.Map.ClearSelectedVertices(); if(selectedverts.Count == 1) General.Map.Map.ClearSelectedVertices();
@ -308,6 +315,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Move geometry back to original position // Move geometry back to original position
MoveGeometryRelative(new Vector2D(0f, 0f), false, false); MoveGeometryRelative(new Vector2D(0f, 0f), false, false);
// Resume normal undo/redo recording
General.Map.UndoRedo.IgnorePropChanges = false;
// Make undo for the dragging // Make undo for the dragging
General.Map.UndoRedo.CreateUndo("Drag geometry"); General.Map.UndoRedo.CreateUndo("Drag geometry");
@ -364,10 +374,17 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.Map.Update(true, false); General.Map.Map.Update(true, false);
// Redraw // 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 // When edit button is released
protected override void OnEditEnd() protected override void OnEditEnd()
{ {

View file

@ -136,6 +136,24 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.RedrawSurface(); 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 // Start rendering structure
if(renderer.StartPlotter(true)) if(renderer.StartPlotter(true))
{ {
@ -153,16 +171,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.Finish(); renderer.Finish();
} }
if(viewchanged)
{
// Start rendering things
if(renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.Map.Things, 1.0f);
renderer.Finish();
}
}
// Redraw overlay // Redraw overlay
if(renderer.StartOverlay(true)) if(renderer.StartOverlay(true))
{ {
@ -172,8 +180,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
} }
renderer.Finish(); renderer.Finish();
} }
renderer.Present();
} }
#endregion #endregion

View file

@ -141,6 +141,24 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.RedrawSurface(); 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 // Start rendering
if(renderer.StartPlotter(true)) if(renderer.StartPlotter(true))
{ {
@ -159,16 +177,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.Finish(); 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 // Redraw overlay
if(renderer.StartOverlay(true)) if(renderer.StartOverlay(true))
{ {
@ -178,8 +186,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
} }
renderer.Finish(); renderer.Finish();
} }
renderer.Present();
} }
#endregion #endregion

View file

@ -232,6 +232,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
} }
} }
// Render things
UpdateRedraw();
renderer.Present();
}
// This redraws only changed things
private void UpdateRedraw()
{
// Render things // Render things
if(renderer.StartThings(true)) if(renderer.StartThings(true))
{ {
@ -248,8 +257,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Done // Done
renderer.Finish(); renderer.Finish();
} }
renderer.Present();
} }
// Cancelled // Cancelled
@ -346,7 +353,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.Map.Update(); General.Map.Map.Update();
// Redraw // Redraw
General.Interface.RedrawDisplay(); UpdateRedraw();
renderer.Present();
//General.Interface.RedrawDisplay();
} }
} }

View file

@ -123,6 +123,24 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.RedrawSurface(); 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 // Start rendering
if(renderer.StartPlotter(true)) if(renderer.StartPlotter(true))
{ {
@ -140,16 +158,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.Finish(); 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 // Redraw overlay
if(renderer.StartOverlay(true)) if(renderer.StartOverlay(true))
{ {
@ -159,8 +167,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
} }
renderer.Finish(); renderer.Finish();
} }
renderer.Present();
} }
#endregion #endregion

View file

@ -614,6 +614,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionH); General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionH);
General.Interface.AddButton(BuilderPlug.Me.MenusForm.FlipSelectionV); 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 // Convert geometry selection
General.Map.Map.ClearAllMarks(false); General.Map.Map.ClearAllMarks(false);
General.Map.Map.MarkSelectedVertices(true, true); General.Map.Map.MarkSelectedVertices(true, true);
@ -751,6 +755,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Paste operation? // Paste operation?
if(pasting) if(pasting)
{ {
// Resume normal undo/redo recording
General.Map.UndoRedo.IgnorePropChanges = false;
// Remove the geometry // Remove the geometry
int index = 0; int index = 0;
foreach(Vertex v in selectedvertices) foreach(Vertex v in selectedvertices)
@ -773,6 +780,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
t.Rotate(thingangle[index]); t.Rotate(thingangle[index]);
t.Move(thingpos[index++]); t.Move(thingpos[index++]);
} }
// Resume normal undo/redo recording
General.Map.UndoRedo.IgnorePropChanges = false;
} }
General.Map.Map.Update(true, true); General.Map.Map.Update(true, true);
@ -811,6 +821,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.UndoRedo.CreateUndo("Edit selection"); General.Map.UndoRedo.CreateUndo("Edit selection");
} }
// Resume normal undo/redo recording
General.Map.UndoRedo.IgnorePropChanges = false;
// Mark selected geometry // Mark selected geometry
General.Map.Map.ClearAllMarks(false); General.Map.Map.ClearAllMarks(false);
General.Map.Map.MarkAllSelectedGeometry(true, true, true, true, false); General.Map.Map.MarkAllSelectedGeometry(true, true, true, true, false);
@ -863,7 +876,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(joinsidedef != null) if(joinsidedef != null)
{ {
// Join! // Join!
s.ChangeSector(joinsidedef.Sector); s.SetSector(joinsidedef.Sector);
s.Marked = false; s.Marked = false;
joined = true; joined = true;

View file

@ -912,8 +912,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
sd.SetTextureLow("-"); sd.SetTextureLow("-");
// Set upper/lower unpegged flags // Set upper/lower unpegged flags
sd.Line.Flags[General.Map.Config.UpperUnpeggedFlag] = false; sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false);
sd.Line.Flags[General.Map.Config.LowerUnpeggedFlag] = true; sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true);
} }
else else
{ {
@ -923,8 +923,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(doortex.Length > 0) sd.Other.SetTextureHigh(doortex); if(doortex.Length > 0) sd.Other.SetTextureHigh(doortex);
// Set upper/lower unpegged flags // Set upper/lower unpegged flags
sd.Line.Flags[General.Map.Config.UpperUnpeggedFlag] = false; sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, false);
sd.Line.Flags[General.Map.Config.LowerUnpeggedFlag] = false; sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, false);
// Get door linedef type from config // Get door linedef type from config
sd.Line.Action = General.Map.Config.MakeDoorAction; sd.Line.Action = General.Map.Config.MakeDoorAction;

View file

@ -113,7 +113,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{ {
type = t.Type; type = t.Type;
angle = t.Angle; angle = t.Angle;
flags = new Dictionary<string, bool>(t.Flags); flags = t.GetFlags();
tag = t.Tag; tag = t.Tag;
action = t.Action; action = t.Action;
args = (int[])(t.Args.Clone()); args = (int[])(t.Args.Clone());
@ -123,9 +123,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
{ {
t.Type = type; t.Type = type;
t.Rotate(angle); t.Rotate(angle);
t.Flags.Clear(); t.ClearFlags();
foreach(KeyValuePair<string, bool> f in flags) foreach(KeyValuePair<string, bool> f in flags)
t.Flags.Add(f.Key, f.Value); t.SetFlag(f.Key, f.Value);
t.Tag = tag; t.Tag = tag;
t.Action = action; t.Action = action;
for(int i = 0; i < t.Args.Length; i++) for(int i = 0; i < t.Args.Length; i++)

View file

@ -220,8 +220,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Toggle upper-unpegged // Toggle upper-unpegged
public virtual void OnToggleUpperUnpegged() public virtual void OnToggleUpperUnpegged()
{ {
if(this.Sidedef.Line.Flags.ContainsKey(General.Map.Config.UpperUnpeggedFlag) && if(this.Sidedef.Line.IsFlagSet(General.Map.Config.UpperUnpeggedFlag))
this.Sidedef.Line.Flags[General.Map.Config.UpperUnpeggedFlag])
{ {
// Remove flag // Remove flag
mode.ApplyUpperUnpegged(false); mode.ApplyUpperUnpegged(false);
@ -236,8 +235,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Toggle lower-unpegged // Toggle lower-unpegged
public virtual void OnToggleLowerUnpegged() public virtual void OnToggleLowerUnpegged()
{ {
if(this.Sidedef.Line.Flags.ContainsKey(General.Map.Config.LowerUnpeggedFlag) && if(this.Sidedef.Line.IsFlagSet(General.Map.Config.LowerUnpeggedFlag))
this.Sidedef.Line.Flags[General.Map.Config.LowerUnpeggedFlag])
{ {
// Remove flag // Remove flag
mode.ApplyLowerUnpegged(false); mode.ApplyLowerUnpegged(false);
@ -258,14 +256,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Remove flag // Remove flag
mode.CreateUndo("Remove upper-unpegged setting"); mode.CreateUndo("Remove upper-unpegged setting");
mode.SetActionResult("Removed 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 else
{ {
// Add flag // Add flag
mode.CreateUndo("Set upper-unpegged setting"); mode.CreateUndo("Set upper-unpegged setting");
mode.SetActionResult("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 // Update sidedef geometry
@ -290,14 +288,14 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Remove flag // Remove flag
mode.CreateUndo("Remove lower-unpegged setting"); mode.CreateUndo("Remove lower-unpegged setting");
mode.SetActionResult("Removed 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 else
{ {
// Add flag // Add flag
mode.CreateUndo("Set lower-unpegged setting"); mode.CreateUndo("Set lower-unpegged setting");
mode.SetActionResult("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 // Update sidedef geometry

View file

@ -512,6 +512,13 @@ namespace CodeImp.DoomBuilder.BuilderModes
PickTarget(); PickTarget();
} }
// After resources were partially reloaded
protected override void ResourcesReloadedPartial()
{
base.ResourcesReloadedPartial();
PickTarget();
}
// Mouse moves // Mouse moves
public override void OnMouseMove(MouseEventArgs e) public override void OnMouseMove(MouseEventArgs e)
{ {