UltimateZoneBuilder/Source/Core/General/MapManager.cs
MaxED 8599f18f4c "Reset" button was missing from "Draw Grid" settings.
Buttons order was messed up in editing modes toolbar after performing "Reload Resources" action.
Fixed editing modes toolbar layout.
2014-03-04 09:04:43 +00:00

1930 lines
No EOL
64 KiB
C#

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.GZBuilder.Data; //mxd
using CodeImp.DoomBuilder.GZBuilder.GZDoom; //mxd
#endregion
namespace CodeImp.DoomBuilder {
public sealed class MapManager {
#region ================== Constants
// Map header name in temporary file
internal const string TEMP_MAP_HEADER = "TEMPMAP";
internal const string BUILD_MAP_HEADER = "MAP01";
public const string CONFIG_MAP_HEADER = "~MAP";
#endregion
#region ================== Variables
// Status
private bool changed;
private bool scriptschanged;
// Map information
private string filetitle;
private string filepathname;
private string temppath;
private string origmapconfigname; //mxd. Map configuration, which was used to open the map.
// Main objects
private MapSet map;
private MapSetIO io;
private MapOptions options;
private ConfigurationInfo configinfo;
private GameConfiguration config;
private DataManager data;
private D3DDevice graphics;
private Renderer2D renderer2d;
private Renderer3D renderer3d;
private WAD tempwad;
private GridSetup grid;
private UndoManager undoredo;
private CopyPasteManager copypaste;
private Launcher launcher;
private ThingsFilter thingsfilter;
private ScriptEditorForm scriptwindow;
private List<CompilerError> errors;
private VisualCamera visualcamera;
//mxd
private List<ScriptItem> namedScripts;
private List<ScriptItem> numberedScripts;
private List<string> scriptincludes;
// Disposing
private bool isdisposed;
#endregion
#region ================== Properties
public string FilePathName { get { return filepathname; } }
public string FileTitle { get { return filetitle; } }
public string TempPath { get { return temppath; } }
public MapOptions Options { get { return options; } }
public MapSet Map { get { return map; } }
public DataManager Data { get { return data; } }
public bool IsChanged { get { return changed | CheckScriptChanged(); } set { changed |= value; } }
public bool IsDisposed { get { return isdisposed; } }
internal D3DDevice Graphics { get { return graphics; } }
public IRenderer2D Renderer2D { get { return renderer2d; } }
public IRenderer3D Renderer3D { get { return renderer3d; } }
internal Renderer2D CRenderer2D { get { return renderer2d; } }
internal Renderer3D CRenderer3D { get { return renderer3d; } }
public GameConfiguration Config { get { return config; } }
public ConfigurationInfo ConfigSettings { get { return configinfo; } }
public GridSetup Grid { get { return grid; } }
public UndoManager UndoRedo { get { return undoredo; } }
internal CopyPasteManager CopyPaste { get { return copypaste; } }
public IMapSetIO FormatInterface { get { return io; } }
internal Launcher Launcher { get { return launcher; } }
public ThingsFilter ThingsFilter { get { return thingsfilter; } }
internal List<CompilerError> Errors { get { return errors; } }
internal ScriptEditorForm ScriptEditor { get { return scriptwindow; } }
public VisualCamera VisualCamera { get { return visualcamera; } set { visualcamera = value; } }
public bool IsScriptsWindowOpen { get { return (scriptwindow != null) && !scriptwindow.IsDisposed; } }
//mxd
public bool UDMF { get { return config.FormatInterface == "UniversalMapSetIO"; } }
internal List<ScriptItem> NamedScripts { get { return namedScripts; } }
internal List<ScriptItem> NumberedScripts { get { return numberedScripts; } }
internal List<string> ScriptIncludes { get { return scriptincludes; } }
public ViewMode ViewMode { get { return renderer2d.ViewMode; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
internal MapManager() {
// We have no destructor
GC.SuppressFinalize(this);
// Create temporary path
temppath = General.MakeTempDirname();
Directory.CreateDirectory(temppath);
General.WriteLogLine("Temporary directory: " + temppath);
// Basic objects
grid = new GridSetup();
undoredo = new UndoManager();
copypaste = new CopyPasteManager();
launcher = new Launcher(this);
thingsfilter = new NullThingsFilter();
errors = new List<CompilerError>();
//mxd
numberedScripts = new List<ScriptItem>();
namedScripts = new List<ScriptItem>();
scriptincludes = new List<string>();
}
// Disposer
internal bool Dispose() {
// Not already disposed?
if (!isdisposed) {
// Let the plugins know
General.Plugins.OnMapCloseBegin();
// Stop processing
General.MainWindow.StopProcessing();
// Close script editor
CloseScriptEditor(false);
// Change to no mode
General.Editing.ChangeMode((EditMode)null);
// Unbind any methods
General.Actions.UnbindMethods(this);
// Dispose
if (grid != null) grid.Dispose();
if (launcher != null) launcher.Dispose();
if (copypaste != null) copypaste.Dispose();
if (undoredo != null) undoredo.Dispose();
General.WriteLogLine("Unloading data resources...");
if (data != null) data.Dispose();
General.WriteLogLine("Closing temporary file...");
if (tempwad != null) tempwad.Dispose();
General.WriteLogLine("Unloading map data...");
if (map != null) map.Dispose();
General.WriteLogLine("Stopping graphics device...");
if (renderer2d != null) renderer2d.Dispose();
if (renderer3d != null) renderer3d.Dispose();
if (graphics != null) graphics.Dispose();
visualcamera = null;
grid = null;
launcher = null;
copypaste = null;
undoredo = null;
data = null;
tempwad = null;
map = null;
renderer2d = null;
renderer3d = null;
graphics = null;
// We may spend some time to clean things up here
GC.Collect();
// Remove temp file
General.WriteLogLine("Removing temporary directory...");
try { Directory.Delete(temppath, true); } catch (Exception e) {
General.WriteLogLine(e.GetType().Name + ": " + e.Message);
General.WriteLogLine("Failed to remove temporary directory!");
}
// Let the plugins know
General.Plugins.OnMapCloseEnd();
// Done
isdisposed = true;
return true;
}
// Already closed
return true;
}
#endregion
#region ================== New / Open
// Initializes for a new map
internal bool InitializeNewMap(MapOptions options) {
string tempfile;
// Apply settings
this.filetitle = "unnamed.wad";
this.filepathname = "";
this.changed = false;
this.options = options;
General.WriteLogLine("Creating new map '" + options.CurrentName + "' with configuration '" + options.ConfigFile + "'");
// Initiate graphics
General.WriteLogLine("Initializing graphics device...");
graphics = new D3DDevice(General.MainWindow.Display);
if (!graphics.Initialize()) return false;
// Create renderers
renderer2d = new Renderer2D(graphics);
renderer3d = new Renderer3D(graphics);
// Load game configuration
General.WriteLogLine("Loading game configuration...");
configinfo = General.GetConfigurationInfo(options.ConfigFile);
config = new GameConfiguration(configinfo.Configuration); //mxd
origmapconfigname = configinfo.Name;//mxd
configinfo.ApplyDefaults(config);
General.Editing.UpdateCurrentEditModes();
// Create map data
map = new MapSet();
// Create temp wadfile
tempfile = General.MakeTempFilename(temppath);
General.WriteLogLine("Creating temporary file: " + tempfile);
#if DEBUG
tempwad = new WAD(tempfile);
#else
try { tempwad = new WAD(tempfile); }
catch(Exception e)
{
General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
#endif
// Read the map from temp file
General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "...");
io = MapSetIO.Create(config.FormatInterface, tempwad, this);
// Create required lumps
General.WriteLogLine("Creating map data structures...");
tempwad.Insert(TEMP_MAP_HEADER, 0, 0);
io.Write(map, TEMP_MAP_HEADER, 1);
CreateRequiredLumps(tempwad, TEMP_MAP_HEADER);
// Load data manager
General.WriteLogLine("Loading data resources...");
data = new DataManager();
data.Load(configinfo.Resources, options.Resources);
// Update structures
options.ApplyGridSettings();
map.UpdateConfiguration();
map.Update();
thingsfilter.Update();
namedScripts = new List<ScriptItem>(); //mxd
numberedScripts = new List<ScriptItem>(); //mxd
// Bind any methods
General.Actions.BindMethods(this);
// Set defaults
this.visualcamera = new VisualCamera();
General.Editing.ChangeMode(configinfo.StartMode);
ClassicMode cmode = (General.Editing.Mode as ClassicMode);
if (cmode != null) cmode.SetZoom(0.5f);
renderer2d.SetViewMode((ViewMode)General.Settings.DefaultViewMode);
General.Settings.SetDefaultThingFlags(config.DefaultThingFlags);
// Success
this.changed = false;
General.WriteLogLine("Map creation done");
return true;
}
// Initializes for an existing map
internal bool InitializeOpenMap(string filepathname, MapOptions options) {
WAD mapwad;
string tempfile;
DataLocation maplocation;
// Apply settings
this.filetitle = Path.GetFileName(filepathname);
this.filepathname = filepathname;
this.changed = false;
this.options = options;
General.WriteLogLine("Opening map '" + options.CurrentName + "' with configuration '" + options.ConfigFile + "'");
// Initiate graphics
General.WriteLogLine("Initializing graphics device...");
graphics = new D3DDevice(General.MainWindow.Display);
if (!graphics.Initialize()) return false;
// Create renderers
renderer2d = new Renderer2D(graphics);
renderer3d = new Renderer3D(graphics);
// Load game configuration
General.WriteLogLine("Loading game configuration...");
configinfo = General.GetConfigurationInfo(options.ConfigFile);
config = new GameConfiguration(configinfo.Configuration);
origmapconfigname = configinfo.Name;//mxd
configinfo.ApplyDefaults(config);
General.Editing.UpdateCurrentEditModes();
// Create map data
map = new MapSet();
// Create temp wadfile
tempfile = General.MakeTempFilename(temppath);
General.WriteLogLine("Creating temporary file: " + tempfile);
#if DEBUG
tempwad = new WAD(tempfile);
#else
try { tempwad = new WAD(tempfile); } catch(Exception e)
{
General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
#endif
// Now open the map file
General.WriteLogLine("Opening source file: " + filepathname);
#if DEBUG
mapwad = new WAD(filepathname, true);
#else
try { mapwad = new WAD(filepathname, true); } catch(Exception e)
{
General.ShowErrorMessage("Error while opening source wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
#endif
// Copy the map lumps to the temp file
General.WriteLogLine("Copying map lumps to temporary file...");
CopyLumpsByType(mapwad, options.CurrentName, tempwad, TEMP_MAP_HEADER,
true, true, true, true);
// Close the map file
mapwad.Dispose();
// Read the map from temp file
map.BeginAddRemove();
General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "...");
io = MapSetIO.Create(config.FormatInterface, tempwad, this);
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); } catch(Exception e)
{
General.ErrorLogger.Add(ErrorType.Error, "Unable to read the map data structures with the specified configuration. " + e.GetType().Name + ": " + e.Message);
General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK);
return false;
}
#endif
map.EndAddRemove();
// Load data manager
General.WriteLogLine("Loading data resources...");
data = new DataManager();
maplocation = new DataLocation(DataLocation.RESOURCE_WAD, filepathname, options.StrictPatches, false, false);
data.Load(configinfo.Resources, options.Resources, maplocation);
// Remove unused sectors
map.RemoveUnusedSectors(true);
// Update structures
options.ApplyGridSettings();
map.UpdateConfiguration();
map.SnapAllToAccuracy();
map.Update();
thingsfilter.Update();
//mxd. check script names
UpdateScriptNames();
// Bind any methods
General.Actions.BindMethods(this);
// Set defaults
this.visualcamera = new VisualCamera();
General.Editing.ChangeMode(configinfo.StartMode);
renderer2d.SetViewMode((ViewMode)General.Settings.DefaultViewMode);
General.Settings.SetDefaultThingFlags(config.DefaultThingFlags);
// Center map in screen
if (General.Editing.Mode is ClassicMode) (General.Editing.Mode as ClassicMode).CenterInScreen();
// Success
this.changed = false;
General.WriteLogLine("Map loading done");
return true;
}
//mxd. This switches to another map in the same wad
internal bool InitializeSwitchMap(MapOptions options) {
this.changed = false;
this.options = options;
// Create map data
MapSet newmap = new MapSet();
WAD mapwad;
// Create temp wadfile
string tempfile = General.MakeTempFilename(temppath);
General.WriteLogLine("Creating temporary file: " + tempfile);
if(tempwad != null) tempwad.Dispose();
#if DEBUG
tempwad = new WAD(tempfile);
#else
try { tempwad = new WAD(tempfile); } catch(Exception e)
{
General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
#endif
// Now open the map file
General.WriteLogLine("Opening source file: " + filepathname);
#if DEBUG
mapwad = new WAD(filepathname, true);
#else
try { mapwad = new WAD(filepathname, true); } catch(Exception e)
{
General.ShowErrorMessage("Error while opening source wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
#endif
// Copy the map lumps to the temp file
General.WriteLogLine("Copying map lumps to temporary file...");
CopyLumpsByType(mapwad, options.CurrentName, tempwad, TEMP_MAP_HEADER, true, true, true, true);
// Close the map file
mapwad.Dispose();
// Read the map from temp file
newmap.BeginAddRemove();
General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "...");
io = MapSetIO.Create(config.FormatInterface, tempwad, this);
General.WriteLogLine("Reading map data structures from file...");
#if DEBUG
newmap = io.Read(newmap, TEMP_MAP_HEADER);
#else
try { newmap = io.Read(newmap, TEMP_MAP_HEADER); } catch(Exception e)
{
General.ErrorLogger.Add(ErrorType.Error, "Unable to read the map data structures with the specified configuration. " + e.GetType().Name + ": " + e.Message);
General.ShowErrorMessage("Unable to read the map data structures with the specified configuration.", MessageBoxButtons.OK);
return false;
}
#endif
newmap.EndAddRemove();
ChangeMapSet(newmap);
data.UpdateUsedTextures();
//mxd. check script names
UpdateScriptNames();
// Center map in screen
if (General.Editing.Mode is ClassicMode) {
ClassicMode mode = General.Editing.Mode as ClassicMode;
mode.OnRedoEnd();
mode.CenterInScreen();
}
// Success
this.changed = false;
General.WriteLogLine("Map switching done");
return true;
}
#endregion
#region ================== Save
/// <summary>
/// This exports the structures from memory into a WAD file with the current map format.
/// </summary>
public bool ExportToFile(string filepathname) {
return SaveMap(filepathname, SavePurpose.Testing);
}
// Initializes for an existing map
internal bool SaveMap(string newfilepathname, SavePurpose purpose) {
MapSet outputset;
string nodebuildername, settingsfile;
StatusInfo oldstatus;
WAD targetwad;
int index;
bool includenodes;
string origmapname;
General.WriteLogLine("Saving map to file: " + newfilepathname);
// Scripts changed?
bool localscriptschanged = CheckScriptChanged();
// If the scripts window is open, save the scripts first
if (IsScriptsWindowOpen) scriptwindow.Editor.ImplicitSave();
// Only recompile scripts when the scripts have changed
// (not when only the map changed)
if (localscriptschanged) {
if (!CompileScriptLumps()) {
// Compiler failure
if (errors.Count > 0)
General.ShowErrorMessage("Error while compiling scripts: " + errors[0].description, MessageBoxButtons.OK);
else
General.ShowErrorMessage("Unknown compiler error while compiling scripts!", MessageBoxButtons.OK);
}
}
// Show script window if there are any errors and we are going to test the map
// and always update the errors on the scripts window.
if ((errors.Count > 0) && (scriptwindow == null) && (purpose == SavePurpose.Testing)) ShowScriptEditor();
if (scriptwindow != null) scriptwindow.Editor.ShowErrors(errors);
// Only write the map and rebuild nodes when the actual map has changed
// (not when only scripts have changed)
if (changed) {
// Make a copy of the map data
outputset = map.Clone();
// Remove all flags from all 3D Start things
foreach (Thing t in outputset.Things) {
if (t.Type == config.Start3DModeThingType) {
// We're not using SetFlag here, this doesn't have to be undone.
// Please note that this is totally exceptional!
List<string> flagkeys = new List<string>(t.Flags.Keys);
foreach (string k in flagkeys) t.Flags[k] = false;
}
}
// Do we need sidedefs compression?
if (map.Sidedefs.Count > io.MaxSidedefs) {
// Compress sidedefs
oldstatus = General.MainWindow.Status;
General.MainWindow.DisplayStatus(StatusType.Busy, "Compressing sidedefs...");
outputset.CompressSidedefs();
General.MainWindow.DisplayStatus(oldstatus);
// Check if it still doesnt fit
if (outputset.Sidedefs.Count > io.MaxSidedefs) {
// Problem! Can't save the map like this!
General.ShowErrorMessage("Unable to save the map: There are too many unique sidedefs!", MessageBoxButtons.OK);
return false;
}
}
// Check things
if (map.Things.Count > io.MaxThings) {
General.ShowErrorMessage("Unable to save the map: There are too many things!", MessageBoxButtons.OK);
return false;
}
// Check sectors
if (map.Sectors.Count > io.MaxSectors) {
General.ShowErrorMessage("Unable to save the map: There are too many sectors!", MessageBoxButtons.OK);
return false;
}
// Check linedefs
if (map.Linedefs.Count > io.MaxLinedefs) {
General.ShowErrorMessage("Unable to save the map: There are too many linedefs!", MessageBoxButtons.OK);
return false;
}
// Check vertices
if (map.Vertices.Count > io.MaxVertices) {
General.ShowErrorMessage("Unable to save the map: There are too many vertices!", MessageBoxButtons.OK);
return false;
}
// TODO: Check for more limitations
// Write to temporary file
General.WriteLogLine("Writing map data structures to file...");
index = tempwad.FindLumpIndex(TEMP_MAP_HEADER);
if (index == -1) index = 0;
io.Write(outputset, TEMP_MAP_HEADER, index);
outputset.Dispose();
// Get the corresponding nodebuilder
nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
// Build the nodes
oldstatus = General.MainWindow.Status;
General.MainWindow.DisplayStatus(StatusType.Busy, "Building map nodes...");
if (!string.IsNullOrEmpty(nodebuildername))
includenodes = BuildNodes(nodebuildername, true);
else
includenodes = false;
General.MainWindow.DisplayStatus(oldstatus);
}
else {
// Check if we have nodebuilder lumps
includenodes = VerifyNodebuilderLumps(tempwad, TEMP_MAP_HEADER);
}
// Suspend data resources
data.Suspend();
// Determine original map name
origmapname = (options.PreviousName != "") ? options.PreviousName : options.CurrentName;
try {
// Backup existing file, if any
if (File.Exists(newfilepathname)) {
if (File.Exists(newfilepathname + ".backup3")) File.Delete(newfilepathname + ".backup3");
if (File.Exists(newfilepathname + ".backup2")) File.Move(newfilepathname + ".backup2", newfilepathname + ".backup3");
if (File.Exists(newfilepathname + ".backup1")) File.Move(newfilepathname + ".backup1", newfilepathname + ".backup2");
File.Copy(newfilepathname, newfilepathname + ".backup1");
}
// Except when saving INTO another file,
// kill the target file if it is different from source file
if ((purpose != SavePurpose.IntoFile) && (newfilepathname != filepathname)) {
// Kill target file
if (File.Exists(newfilepathname)) File.Delete(newfilepathname);
// Kill .dbs settings file
settingsfile = newfilepathname.Substring(0, newfilepathname.Length - 4) + ".dbs";
if (File.Exists(settingsfile)) File.Delete(settingsfile);
}
// On Save AS we have to copy the previous file to the new file
if ((purpose == SavePurpose.AsNewFile) && (filepathname != "")) {
// Copy if original file still exists
if (File.Exists(filepathname)) File.Copy(filepathname, newfilepathname, true);
}
// If the target file exists, we need to rebuild it
if (File.Exists(newfilepathname)) {
// Move the target file aside
string origwadfile = newfilepathname + ".temp";
File.Move(newfilepathname, origwadfile);
// Open original file
WAD origwad = new WAD(origwadfile, true);
// Create new target file
targetwad = new WAD(newfilepathname);
// Copy all lumps, except the original map
GameConfiguration origcfg; //mxd
if (origmapconfigname == configinfo.Name) {
origcfg = config;
} else {
ConfigurationInfo ci = General.GetConfigurationInfo(origmapconfigname);
origcfg = new GameConfiguration(ci.Configuration);
}
CopyAllLumpsExceptMap(origwad, targetwad, origcfg, origmapname);
// Close original file and delete it
origwad.Dispose();
File.Delete(origwadfile);
}
else {
// Create new target file
targetwad = new WAD(newfilepathname);
}
} catch (IOException) {
General.ShowErrorMessage("IO Error while writing target file: " + newfilepathname + ". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK);
data.Resume();
General.WriteLogLine("Map saving failed");
return false;
} catch (UnauthorizedAccessException) {
General.ShowErrorMessage("Error while accessing target file: " + newfilepathname + ". Please make sure the location is accessible and not in use by another program.", MessageBoxButtons.OK);
data.Resume();
General.WriteLogLine("Map saving failed");
return false;
}
// Copy map lumps to target file
CopyLumpsByType(tempwad, TEMP_MAP_HEADER, targetwad, origmapname, true, true, includenodes, true);
// Was the map lump name renamed?
if ((options.PreviousName != options.CurrentName) &&
(options.PreviousName != "")) {
General.WriteLogLine("Renaming map lump name from " + options.PreviousName + " to " + options.CurrentName);
// Find the map header in target
index = targetwad.FindLumpIndex(options.PreviousName);
if (index > -1) {
// Rename the map lump name
targetwad.Lumps[index].Rename(options.CurrentName);
options.PreviousName = "";
}
else {
// Houston, we've got a problem!
General.ShowErrorMessage("Error renaming map lump name: the original map lump could not be found!", MessageBoxButtons.OK);
options.CurrentName = options.PreviousName;
options.PreviousName = "";
}
}
// Done with the target file
targetwad.Dispose();
// Resume data resources
data.Resume();
// Not saved for testing purpose?
if (purpose != SavePurpose.Testing) {
// Saved in a different file?
if (newfilepathname != filepathname) {
// Keep new filename
filepathname = newfilepathname;
filetitle = Path.GetFileName(filepathname);
// Reload resources
ReloadResources();
}
try {
// Open or create the map settings
settingsfile = newfilepathname.Substring(0, newfilepathname.Length - 4) + ".dbs";
options.WriteConfiguration(settingsfile);
} catch (Exception e) {
// Warning only
General.ErrorLogger.Add(ErrorType.Warning, "Could not write the map settings configuration file. " + e.GetType().Name + ": " + e.Message);
}
// Changes saved
changed = false;
scriptschanged = false;
}
// Success!
General.WriteLogLine("Map saving done");
return true;
}
#endregion
#region ================== Nodebuild
// This builds the nodes in the temproary file with the given configuration name
private bool BuildNodes(string nodebuildername, bool failaswarning) {
NodebuilderInfo nodebuilder;
string tempfile1, tempfile2, sourcefile;
bool lumpscomplete = false;
WAD buildwad;
// Find the nodebuilder
nodebuilder = General.GetNodebuilderByName(nodebuildername);
if (nodebuilder == null) {
// Problem! Can't find that nodebuilder!
General.ShowWarningMessage("Unable to build the nodes: The configured nodebuilder cannot be found.\nPlease check your game configuration settings!", MessageBoxButtons.OK);
return false;
}
else {
// Create the compiler interface that will run the nodebuilder
// This automatically creates a temporary directory for us
Compiler compiler = nodebuilder.CreateCompiler();
// Make temporary filename
tempfile1 = General.MakeTempFilename(compiler.Location);
// Make the temporary WAD file
General.WriteLogLine("Creating temporary build file: " + tempfile1);
#if DEBUG
buildwad = new WAD(tempfile1);
#else
try { buildwad = new WAD(tempfile1); }
catch(Exception e)
{
General.ShowErrorMessage("Error while creating a temporary wad file:\n" + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK);
return false;
}
#endif
// Determine source file
if (filepathname.Length > 0)
sourcefile = filepathname;
else
sourcefile = tempwad.Filename;
// Copy lumps to buildwad
General.WriteLogLine("Copying map lumps to temporary build file...");
CopyLumpsByType(tempwad, TEMP_MAP_HEADER, buildwad, BUILD_MAP_HEADER, true, false, false, true);
// Close buildwad
buildwad.Dispose();
// Does the nodebuilder require an output file?
if (nodebuilder.HasSpecialOutputFile) {
// Make a temporary output file for the nodebuilder
tempfile2 = General.MakeTempFilename(compiler.Location);
General.WriteLogLine("Temporary output file: " + tempfile2);
}
else {
// Output file is same as input file
tempfile2 = tempfile1;
}
// Run the nodebuilder
compiler.Parameters = nodebuilder.Parameters;
compiler.InputFile = Path.GetFileName(tempfile1);
compiler.OutputFile = Path.GetFileName(tempfile2);
compiler.SourceFile = sourcefile;
compiler.WorkingDirectory = Path.GetDirectoryName(tempfile1);
if (compiler.Run()) {
// Open the output file
try { buildwad = new WAD(tempfile2); } catch (Exception e) {
General.WriteLogLine(e.GetType().Name + " while reading build wad file: " + e.Message);
buildwad = null;
}
if (buildwad != null) {
// Output lumps complete?
lumpscomplete = VerifyNodebuilderLumps(buildwad, BUILD_MAP_HEADER);
}
if (lumpscomplete) {
// Copy nodebuilder lumps to temp file
General.WriteLogLine("Copying nodebuilder lumps to temporary file...");
CopyLumpsByType(buildwad, BUILD_MAP_HEADER, tempwad, TEMP_MAP_HEADER, false, false, true, false);
}
else {
//mxd. collect errors
string errors = "";
foreach (CompilerError e in compiler.Errors)
errors += "Error: " + Environment.NewLine + e.description;
// Nodebuilder did not build the lumps!
if (failaswarning)
General.ShowWarningMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures.\nThe map will be saved without the nodes." + (compiler.Errors.Length > 0 ? Environment.NewLine + errors : ""), MessageBoxButtons.OK);
else
General.ShowErrorMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures." + (compiler.Errors.Length > 0 ? Environment.NewLine + errors : ""), MessageBoxButtons.OK);
}
// Done with the build wad
if (buildwad != null) buildwad.Dispose();
}
else { //mxd
//collect errors
string errors = "";
foreach (CompilerError e in compiler.Errors)
errors += "Error: " + Environment.NewLine + e.description;
// Nodebuilder did not build the lumps!
General.ShowErrorMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures" + (compiler.Errors.Length > 0 ? ":" + Environment.NewLine + errors : "."), MessageBoxButtons.OK);
}
// Clean up
compiler.Dispose();
// Return result
return lumpscomplete;
}
}
// This verifies if the nodebuilder lumps exist in a WAD file
private bool VerifyNodebuilderLumps(WAD wad, string mapheader) {
bool lumpscomplete = false;
// Find the map header in source
int srcindex = wad.FindLumpIndex(mapheader);
if (srcindex > -1) {
// Go for all the map lump names
lumpscomplete = true;
foreach (DictionaryEntry ml in config.MapLumpNames) {
// Read lump settings from map config
bool lumpnodebuild = config.ReadSetting("maplumpnames." + ml.Key + ".nodebuild", false);
bool lumpallowempty = config.ReadSetting("maplumpnames." + ml.Key + ".allowempty", false);
// Check if this lump should exist
if (lumpnodebuild && !lumpallowempty) {
// Find the lump in the source
if (wad.FindLump(ml.Key.ToString(), srcindex, srcindex + config.MapLumpNames.Count + 2) == null) {
// Missing a lump!
lumpscomplete = false;
break;
}
}
}
}
return lumpscomplete;
}
#endregion
#region ================== Lumps
// This returns a copy of the requested lump stream data
// This is copied from the temp wad file and returns null when the lump is not found
public MemoryStream GetLumpData(string lumpname) {
Lump l = tempwad.FindLump(lumpname);
if (l != null) {
l.Stream.Seek(0, SeekOrigin.Begin);
return new MemoryStream(l.Stream.ReadAllBytes());
}
return null;
}
// This writes a copy of the data to a lump in the temp file
public void SetLumpData(string lumpname, MemoryStream data) {
int insertindex = tempwad.Lumps.Count;
// Remove the lump if it already exists
int li = tempwad.FindLumpIndex(lumpname);
if (li > -1) {
insertindex = li;
tempwad.RemoveAt(li);
}
// Insert new lump
Lump l = tempwad.Insert(lumpname, insertindex, (int)data.Length);
l.Stream.Seek(0, SeekOrigin.Begin);
data.WriteTo(l.Stream);
}
// This checks if the specified lump exists in the temp file
public bool LumpExists(string lumpname) {
return (tempwad.FindLumpIndex(lumpname) > -1);
}
// This creates empty lumps for those required
private void CreateRequiredLumps(WAD target, string mapname) {
int headerindex, insertindex, targetindex;
string lumpname;
bool lumprequired;
// Find the map header in target
headerindex = target.FindLumpIndex(mapname);
if (headerindex == -1) {
// If this header doesnt exists in the target
// then insert at the end of the target
headerindex = target.Lumps.Count;
}
// Begin inserting at target header index
insertindex = headerindex;
// Go for all the map lump names
foreach (DictionaryEntry ml in config.MapLumpNames) {
// Read lump settings from map config
lumprequired = config.ReadSetting("maplumpnames." + ml.Key + ".required", false);
// Check if this lump is required
if (lumprequired) {
// Get the lump name
lumpname = ml.Key.ToString();
if (lumpname == CONFIG_MAP_HEADER) lumpname = mapname;
// Check if the lump is missing at the target
targetindex = FindSpecificLump(target, lumpname, headerindex, mapname, config.MapLumpNames);
if (targetindex == -1) {
// Determine target index
insertindex++;
if (insertindex > target.Lumps.Count) insertindex = target.Lumps.Count;
// Create new, emtpy lump
General.WriteLogLine(lumpname + " is required! Created empty lump.");
target.Insert(lumpname, insertindex, 0);
}
else {
// Move insert index
insertindex = targetindex;
}
}
}
}
//mxd. This is called on tempwad, which should only have the current map inside it.
private void removeUnneededLumps(WAD target, string mapname) {
//Get the list of lumps required by current map format
List<string> requiredLumps = new List<string>();
foreach (DictionaryEntry ml in config.MapLumpNames){
string lumpname = ml.Key.ToString();
if(lumpname == CONFIG_MAP_HEADER) lumpname = mapname;
requiredLumps.Add(lumpname);
}
//Remove lumps, which are not required
List<Lump> toRemove = new List<Lump>();
foreach (Lump lump in target.Lumps)
if (!requiredLumps.Contains(lump.Name)) toRemove.Add(lump);
foreach (Lump lump in toRemove) target.Remove(lump);
}
// This copies all lumps, except those of a specific map
private void CopyAllLumpsExceptMap(WAD source, WAD target, GameConfiguration mapconfig, string sourcemapname) {
// Go for all lumps
bool skipping = false;
foreach (Lump srclump in source.Lumps) {
// Check if we should stop skipping lumps here
if (skipping && !mapconfig.MapLumpNames.Contains(srclump.Name)) {
// Stop skipping
skipping = false;
}
// Check if we should start skipping lumps here
if (!skipping && (srclump.Name == sourcemapname)) {
// We have encountered the map header, start skipping!
skipping = true;
}
// Not skipping this lump?
if (!skipping) {
// Copy lump over!
Lump tgtlump = target.Insert(srclump.Name, target.Lumps.Count, srclump.Length);
srclump.CopyTo(tgtlump);
}
}
}
// This copies specific map lumps from one WAD to another
private void CopyLumpsByType(WAD source, string sourcemapname,
WAD target, string targetmapname,
bool copyrequired, bool copyblindcopy,
bool copynodebuild, bool copyscript) {
bool lumprequired, lumpblindcopy, lumpnodebuild;
string lumpscript, srclumpname, tgtlumpname;
int srcheaderindex, tgtheaderindex, targetindex, sourceindex, lumpindex;
Lump lump, newlump;
// Find the map header in target
tgtheaderindex = target.FindLumpIndex(targetmapname);
if (tgtheaderindex == -1) {
// If this header doesnt exists in the target
// then insert at the end of the target
tgtheaderindex = target.Lumps.Count;
}
// Begin inserting at target header index
targetindex = tgtheaderindex;
// Find the map header in source
srcheaderindex = source.FindLumpIndex(sourcemapname);
if (srcheaderindex > -1) {
// Copy the map header from source to target
//newlump = target.Insert(targetmapname, tgtindex++, source.Lumps[srcindex].Length);
//source.Lumps[srcindex].CopyTo(newlump);
// Go for all the map lump names
foreach (DictionaryEntry ml in config.MapLumpNames) {
// Read lump settings from map config
lumprequired = config.ReadSetting("maplumpnames." + ml.Key + ".required", false);
lumpblindcopy = config.ReadSetting("maplumpnames." + ml.Key + ".blindcopy", false);
lumpnodebuild = config.ReadSetting("maplumpnames." + ml.Key + ".nodebuild", false);
lumpscript = config.ReadSetting("maplumpnames." + ml.Key + ".script", "");
// Check if this lump should be copied
if ((lumprequired && copyrequired) || (lumpblindcopy && copyblindcopy) ||
(lumpnodebuild && copynodebuild) || ((lumpscript.Length != 0) && copyscript)) {
// Get the lump name
srclumpname = ml.Key.ToString();
tgtlumpname = ml.Key.ToString();
if (srclumpname == CONFIG_MAP_HEADER) srclumpname = sourcemapname;
if (tgtlumpname == CONFIG_MAP_HEADER) tgtlumpname = targetmapname;
// Find the lump in the source
sourceindex = FindSpecificLump(source, srclumpname, srcheaderindex, sourcemapname, config.MapLumpNames);
if (sourceindex > -1) {
// Remove lump at target
lumpindex = RemoveSpecificLump(target, tgtlumpname, tgtheaderindex, targetmapname, config.MapLumpNames);
// Determine target index
// When original lump was found and removed then insert at that position
// otherwise insert after last insertion position
if (lumpindex > -1) targetindex = lumpindex; else targetindex++;
if (targetindex > target.Lumps.Count) targetindex = target.Lumps.Count;
// Copy the lump to the target
//General.WriteLogLine(srclumpname + " copying as " + tgtlumpname);
lump = source.Lumps[sourceindex];
newlump = target.Insert(tgtlumpname, targetindex, lump.Length);
lump.CopyTo(newlump);
}
else {
// We don't want to bother the user with this. There are a lot of lumps in
// the game configs that are trivial and don't need to be found.
if (lumprequired) {
General.ErrorLogger.Add(ErrorType.Warning, ml.Key + " (required lump) should be read but was not found in the WAD file.");
}
}
}
}
}
}
// This finds a lump within the range of known lump names
// Returns -1 when the lump cannot be found
internal static int FindSpecificLump(WAD source, string lumpname, int mapheaderindex, string mapheadername, IDictionary maplumps) {
// Use the configured map lump names to find the specific lump within range,
// because when an unknown lump is met, this search must stop.
// Go for all lumps in order to find the specified lump
for (int i = 0; i < maplumps.Count + 1; i++) {
// Still within bounds?
if ((mapheaderindex + i) < source.Lumps.Count) {
// Check if this is a known lump name
if (maplumps.Contains(source.Lumps[mapheaderindex + i].Name) ||
(maplumps.Contains(CONFIG_MAP_HEADER) && (source.Lumps[mapheaderindex + i].Name == mapheadername))) {
// Is this the lump we are looking for?
if (source.Lumps[mapheaderindex + i].Name == lumpname) {
// Return this index
return mapheaderindex + i;
}
}
else {
// Unknown lump hit, abort search
break;
}
}
}
// Nothing found
return -1;
}
// This removes a specific lump and returns the position where the lump was removed
// Returns -1 when the lump could not be found
internal static int RemoveSpecificLump(WAD source, string lumpname, int mapheaderindex, string mapheadername, IDictionary maplumps) {
int lumpindex;
// Find the specific lump index
lumpindex = FindSpecificLump(source, lumpname, mapheaderindex, mapheadername, maplumps);
if (lumpindex > -1) {
// Remove this lump
//General.WriteLogLine(lumpname + " removed");
source.RemoveAt(lumpindex);
}
else {
// Lump not found
//General.ErrorLogger.Add(ErrorType.Warning, lumpname + " should be removed but was not found!");
}
// Return result
return lumpindex;
}
#endregion
#region ================== Selection Groups
// This adds selection to a group
private void AddSelectionToGroup(int groupindex) {
General.Interface.SetCursor(Cursors.WaitCursor);
// Make undo. mxd: group assignment is not recorded
//undoredo.CreateUndo("Assign to group " + (groupindex + 1));
// Make selection
map.AddSelectionToGroup(groupindex); //mxd. switched groupmask to groupindex
General.Interface.DisplayStatus(StatusType.Action, "Assigned selection to group " + (groupindex + 1));
General.Interface.SetCursor(Cursors.Default);
}
// This selects a group
private void SelectGroup(int groupindex) {
// Select
int groupmask = 0x01 << groupindex;
map.SelectVerticesByGroup(groupmask);
map.SelectLinedefsByGroup(groupmask);
map.SelectSectorsByGroup(groupmask);
map.SelectThingsByGroup(groupmask);
// Redraw to show selection
General.Interface.DisplayStatus(StatusType.Action, "Selected group " + (groupindex + 1));
General.Interface.RedrawDisplay();
}
//mxd. This clears a group
private void ClearGroup(int groupindex) {
General.Interface.SetCursor(Cursors.WaitCursor);
// Clear group
map.ClearGroup(0x01 << groupindex, groupindex);
General.Interface.DisplayStatus(StatusType.Action, "Cleared group " + (groupindex + 1));
General.Interface.SetCursor(Cursors.Default);
}
// Select actions
[BeginAction("selectgroup1")]
internal void SelectGroup1() { SelectGroup(0); }
[BeginAction("selectgroup2")]
internal void SelectGroup2() { SelectGroup(1); }
[BeginAction("selectgroup3")]
internal void SelectGroup3() { SelectGroup(2); }
[BeginAction("selectgroup4")]
internal void SelectGroup4() { SelectGroup(3); }
[BeginAction("selectgroup5")]
internal void SelectGroup5() { SelectGroup(4); }
[BeginAction("selectgroup6")]
internal void SelectGroup6() { SelectGroup(5); }
[BeginAction("selectgroup7")]
internal void SelectGroup7() { SelectGroup(6); }
[BeginAction("selectgroup8")]
internal void SelectGroup8() { SelectGroup(7); }
[BeginAction("selectgroup9")]
internal void SelectGroup9() { SelectGroup(8); }
[BeginAction("selectgroup10")]
internal void SelectGroup10() { SelectGroup(9); }
// Assign actions
[BeginAction("assigngroup1")]
internal void AssignGroup1() { AddSelectionToGroup(0); }
[BeginAction("assigngroup2")]
internal void AssignGroup2() { AddSelectionToGroup(1); }
[BeginAction("assigngroup3")]
internal void AssignGroup3() { AddSelectionToGroup(2); }
[BeginAction("assigngroup4")]
internal void AssignGroup4() { AddSelectionToGroup(3); }
[BeginAction("assigngroup5")]
internal void AssignGroup5() { AddSelectionToGroup(4); }
[BeginAction("assigngroup6")]
internal void AssignGroup6() { AddSelectionToGroup(5); }
[BeginAction("assigngroup7")]
internal void AssignGroup7() { AddSelectionToGroup(6); }
[BeginAction("assigngroup8")]
internal void AssignGroup8() { AddSelectionToGroup(7); }
[BeginAction("assigngroup9")]
internal void AssignGroup9() { AddSelectionToGroup(8); }
[BeginAction("assigngroup10")]
internal void AssignGroup10() { AddSelectionToGroup(9); }
//mxd. Clear actions
[BeginAction("cleargroup1")]
internal void ClearGroup1() { ClearGroup(0); }
[BeginAction("cleargroup2")]
internal void ClearGroup2() { ClearGroup(1); }
[BeginAction("cleargroup3")]
internal void ClearGroup3() { ClearGroup(2); }
[BeginAction("cleargroup4")]
internal void ClearGroup4() { ClearGroup(3); }
[BeginAction("cleargroup5")]
internal void ClearGroup5() { ClearGroup(4); }
[BeginAction("cleargroup6")]
internal void ClearGroup6() { ClearGroup(5); }
[BeginAction("cleargroup7")]
internal void ClearGroup7() { ClearGroup(6); }
[BeginAction("cleargroup8")]
internal void ClearGroup8() { ClearGroup(7); }
[BeginAction("cleargroup9")]
internal void ClearGroup9() { ClearGroup(8); }
[BeginAction("cleargroup10")]
internal void ClearGroup10() { ClearGroup(9); }
#endregion
#region ================== Script Editing
// Show the script editor
[BeginAction("openscripteditor")]
internal void ShowScriptEditor() {
Cursor.Current = Cursors.WaitCursor;
if (scriptwindow == null) {
// Load the window
scriptwindow = new ScriptEditorForm();
}
// Window not yet visible?
if (!scriptwindow.Visible) {
// Show the window
if (General.Settings.ScriptOnTop) {
if (scriptwindow.Visible && (scriptwindow.Owner == null)) scriptwindow.Hide();
scriptwindow.Show(General.MainWindow);
}
else {
if (scriptwindow.Visible && (scriptwindow.Owner != null)) scriptwindow.Hide();
scriptwindow.Show();
}
}
scriptwindow.Activate();
scriptwindow.Focus();
Cursor.Current = Cursors.Default;
}
// This asks the user to save changes in script files
// Returns false when cancelled by the user
internal bool AskSaveScriptChanges() {
// Window open?
if (scriptwindow != null) {
// Ask to save changes
// This also saves implicitly
return scriptwindow.AskSaveAll();
}
// No problems
return true;
}
// This applies the changed status for internal scripts
internal void ApplyScriptChanged() {
// Remember if lumps are changed
scriptschanged |= scriptwindow.Editor.CheckImplicitChanges();
}
// Close the script editor
// Specify true for the closing parameter when
// the window is already in the closing process
internal void CloseScriptEditor(bool closing) {
if (scriptwindow != null) {
if (!scriptwindow.IsDisposed) {
// Remember what files were open
scriptwindow.Editor.WriteOpenFilesToConfiguration();
// Close now
if (!closing) scriptwindow.Close();
}
// Done
scriptwindow = null;
}
}
// This checks if the scripts are changed
internal bool CheckScriptChanged() {
if (scriptwindow != null) {
// Check if scripts are changed
return scriptschanged || scriptwindow.Editor.CheckImplicitChanges();
}
return scriptschanged;
}
// This compiles all lumps that require compiling and stores the results
// Returns true when our code worked properly (even when the compiler returned errors)
private bool CompileScriptLumps() {
bool success = true;
errors.Clear();
// Go for all the map lumps
foreach (MapLumpInfo lumpinfo in config.MapLumps.Values) {
// Is this a script lump?
if (lumpinfo.script != null) {
// Compile it now
success &= CompileLump(lumpinfo.name, false);
}
}
return success;
}
// This compiles a script lump and returns any errors that may have occurred
// Returns true when our code worked properly (even when the compiler returned errors)
internal bool CompileLump(string lumpname, bool clearerrors) {
string inputfile, outputfile, sourcefile;
Compiler compiler;
byte[] filedata;
string reallumpname = lumpname;
//mxd. Does lump require compiling?
ScriptConfiguration scriptconfig = config.MapLumps[lumpname].script;
if (scriptconfig.Compiler == null) return true;
// Find the lump
if (lumpname == CONFIG_MAP_HEADER) reallumpname = TEMP_MAP_HEADER;
Lump lump = tempwad.FindLump(reallumpname);
if (lump == null) throw new Exception("No such lump in temporary wad file '" + reallumpname + "'.");
// Determine source file
sourcefile = (filepathname.Length > 0 ? filepathname : tempwad.Filename);
// New list of errors
if (clearerrors) errors.Clear();
// Determine the script configuration to use
try {
// Initialize compiler
compiler = scriptconfig.Compiler.Create();
} catch(Exception e) {
// Fail
errors.Add(new CompilerError("Unable to initialize compiler. " + e.GetType().Name + ": " + e.Message));
return false;
}
try {
// Write lump data to temp script file in compiler's temp directory
inputfile = General.MakeTempFilename(compiler.Location, "tmp");
lump.Stream.Seek(0, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(lump.Stream);
File.WriteAllBytes(inputfile, reader.ReadBytes((int)lump.Stream.Length));
} catch(Exception e) {
// Fail
compiler.Dispose();
errors.Add(new CompilerError("Unable to write script to working file. " + e.GetType().Name + ": " + e.Message));
return false;
}
// Make random output filename
outputfile = General.MakeTempFilename(compiler.Location, "tmp");
// Run compiler
compiler.Parameters = scriptconfig.Parameters;
compiler.InputFile = Path.GetFileName(inputfile);
compiler.OutputFile = Path.GetFileName(outputfile);
compiler.SourceFile = sourcefile;
compiler.WorkingDirectory = Path.GetDirectoryName(inputfile);
if(compiler.Run()) {
// Process errors
foreach(CompilerError e in compiler.Errors) {
CompilerError newerror = e;
// If the error's filename equals our temporary file,
// use the lump name instead and prefix it with ?
if(string.Compare(e.filename, inputfile, true) == 0)
newerror.filename = "?" + reallumpname;
errors.Add(newerror);
}
// No errors?
if(compiler.Errors.Length == 0) {
// Output file exists?
if(File.Exists(outputfile)) {
// Copy output file data into a lump?
if(!string.IsNullOrEmpty(scriptconfig.ResultLump)) {
// Do that now then
try {
filedata = File.ReadAllBytes(outputfile);
} catch(Exception e) {
// Fail
compiler.Dispose();
errors.Add(new CompilerError("Unable to read compiler output file. " + e.GetType().Name + ": " + e.Message));
return false;
}
// Store data
MemoryStream stream = new MemoryStream(filedata);
SetLumpData(scriptconfig.ResultLump, stream);
}
}
}
// Clean up
compiler.Dispose();
// Done
return true;
}
// Fail
compiler.Dispose();
errors.Clear(); //mxd
return false;
}
// This clears all compiler errors
internal void ClearCompilerErrors() {
errors.Clear();
}
//mxd
internal void UpdateScriptNames() {
namedScripts = new List<ScriptItem>();
numberedScripts = new List<ScriptItem>();
// Load the script lumps
foreach (MapLumpInfo maplumpinfo in config.MapLumps.Values) {
// Is this a script lump?
if (maplumpinfo.script != null && maplumpinfo.name == "SCRIPTS") {
// Load the lump data
MemoryStream stream = GetLumpData(maplumpinfo.name);
if (stream != null) {
AcsParserSE parser = new AcsParserSE();
parser.OnInclude = updateScriptsFromLocation;
parser.Parse(stream, "SCRIPTS", true);
if(parser.NamedScripts.Count > 0 && (FormatInterface is DoomMapSetIO || FormatInterface is HexenMapSetIO)) {
List<string> names = new List<string>();
foreach(ScriptItem item in parser.NamedScripts)
names.Add("'" + item.Name + "'");
General.ErrorLogger.Add(ErrorType.Warning, "Current map format doesn't support named scripts! Following scripts will not work:" + string.Join(", ", names.ToArray()));
} else {
namedScripts.AddRange(parser.NamedScripts);
}
numberedScripts.AddRange(parser.NumberedScripts);
scriptincludes.AddRange(parser.Includes);
}
}
}
//sort
namedScripts.Sort(ScriptItem.SortByName);
numberedScripts.Sort(ScriptItem.SortByIndex);
}
//mxd
private void updateScriptsFromLocation(AcsParserSE parser, string path) {
MemoryStream s = General.Map.Data.LoadFile(path);
if(s != null && s.Length > 0) parser.Parse(s, path, true, true);
}
#endregion
#region ================== Methods
// This updates everything after the configuration or settings have been changed
internal void UpdateConfiguration() {
// Update map
map.UpdateConfiguration();
// Update settings
renderer3d.CreateProjection();
// Things filters
General.MainWindow.UpdateThingsFilters();
}
// This changes thing filter
public void ChangeThingFilter(ThingsFilter newfilter) {
// We have a special filter for null
if (newfilter == null) newfilter = new NullThingsFilter();
// Deactivate old filter
if (thingsfilter != null) thingsfilter.Deactivate();
// Change
thingsfilter = newfilter;
// Activate filter
thingsfilter.Activate();
// Update interface
General.MainWindow.ReflectThingsFilter();
// Redraw
General.MainWindow.RedrawDisplay();
}
// This sets a new mapset for editing
internal void ChangeMapSet(MapSet newmap) {
// Let the plugin and editing mode know
General.Plugins.OnMapSetChangeBegin();
if (General.Editing.Mode != null) General.Editing.Mode.OnMapSetChangeBegin();
this.visualcamera.Sector = null;
// Can't have a selection in an old map set
map.ClearAllSelected();
// Reset surfaces
renderer2d.Surfaces.Reset();
// Apply
map.Dispose();
map = newmap;
map.UpdateConfiguration();
map.SnapAllToAccuracy();
map.Update();
thingsfilter.Update();
// Let the plugin and editing mode know
General.Plugins.OnMapSetChangeEnd();
if (General.Editing.Mode != null) General.Editing.Mode.OnMapSetChangeEnd();
}
// This reloads resources
[BeginAction("reloadresources")]
internal void DoReloadResource() {
// Set this to false so we can see if errors are added
General.ErrorLogger.IsErrorAdded = false;
ReloadResources();
if (General.ErrorLogger.IsErrorAdded) {
// Show any errors if preferred
General.MainWindow.DisplayStatus(StatusType.Warning, "There were errors during resources loading!");
if (General.Settings.ShowErrorsWindow) General.MainWindow.ShowErrors();
}
else
General.MainWindow.DisplayReady();
}
internal void ReloadResources() {
// Keep old display info
StatusInfo oldstatus = General.MainWindow.Status;
Cursor oldcursor = Cursor.Current;
// Show status
General.MainWindow.DisplayStatus(StatusType.Busy, "Reloading data resources...");
Cursor.Current = Cursors.WaitCursor;
// Clean up
data.Dispose();
data = null;
config = null;
configinfo = null;
GC.Collect();
GC.WaitForPendingFinalizers();
// Reload game configuration
General.WriteLogLine("Reloading game configuration...");
configinfo = General.GetConfigurationInfo(options.ConfigFile);
config = new GameConfiguration(configinfo.Configuration); //mxd
General.Editing.UpdateCurrentEditModes();
// Reload data resources
General.WriteLogLine("Reloading data resources...");
data = new DataManager();
if (!string.IsNullOrEmpty(filepathname)) {
DataLocation maplocation = new DataLocation(DataLocation.RESOURCE_WAD, filepathname, false, false, false);
data.Load(configinfo.Resources, options.Resources, maplocation);
}
else {
data.Load(configinfo.Resources, options.Resources);
}
// Apply new settings to map elements
map.UpdateConfiguration();
// Re-link the background image
grid.LinkBackground();
// Inform all plugins that the resources are reloaded
General.Plugins.ReloadResources();
// Inform editing mode that the resources are reloaded
if (General.Editing.Mode != null) General.Editing.Mode.OnReloadResources();
// Reset status
General.MainWindow.DisplayStatus(oldstatus);
Cursor.Current = oldcursor;
//mxd
UpdateScriptNames();
}
// Game Configuration action
[BeginAction("mapoptions")]
internal void ShowMapOptions() {
// Cancel volatile mode, if any
General.Editing.DisengageVolatileMode();
// Show map options dialog
MapOptionsForm optionsform = new MapOptionsForm(options, false);
if (optionsform.ShowDialog(General.MainWindow) == DialogResult.OK) {
// Update interface
General.MainWindow.UpdateInterface();
// Stop data manager
data.Dispose();
// Apply new options
this.options = optionsform.Options;
// Load new game configuration
General.WriteLogLine("Loading game configuration '" + options.ConfigFile + "'...");
configinfo = General.GetConfigurationInfo(options.ConfigFile);
string oldFormatInterface = config.FormatInterface; //mxd
config = new GameConfiguration(configinfo.Configuration); //mxd
configinfo.ApplyDefaults(config);
General.Editing.UpdateCurrentEditModes();
// Setup new map format IO
General.WriteLogLine("Initializing map format interface " + config.FormatInterface + "...");
io = MapSetIO.Create(config.FormatInterface, tempwad, this);
//mxd. Some lumps may've become unneeded during map format conversion.
if(oldFormatInterface != config.FormatInterface)
removeUnneededLumps(tempwad, TEMP_MAP_HEADER);
// Create required lumps if they don't exist yet
CreateRequiredLumps(tempwad, TEMP_MAP_HEADER);
// Let the plugins know
General.Plugins.MapReconfigure();
//mxd. Update linedef color presets and flags if required
if(oldFormatInterface == "UniversalMapSetIO" && config.FormatInterface != "UniversalMapSetIO") {
foreach(Linedef l in General.Map.Map.Linedefs) l.TranslateFromUDMF();
foreach(Thing t in General.Map.Map.Things) t.TranslateFromUDMF();
} else if(oldFormatInterface != "UniversalMapSetIO" && config.FormatInterface == "UniversalMapSetIO") {
foreach(Linedef l in General.Map.Map.Linedefs) l.TranslateToUDMF();
foreach(Thing t in General.Map.Map.Things) t.TranslateToUDMF();
} else if(oldFormatInterface != "DoomMapSetIO" && config.FormatInterface == "DoomMapSetIO") { //drop all arguments
foreach (Linedef l in General.Map.Map.Linedefs)
for(int i = 0; i < l.Args.Length; i++) l.Args[i] = 0;
foreach (Thing t in General.Map.Map.Things)
for(int i = 0; i < t.Args.Length; i++) t.Args[i] = 0;
}
General.Map.Map.UpdateCustomLinedefColors();
// Update interface
General.MainWindow.SetupInterface();
General.MainWindow.UpdateThingsFilters();
General.MainWindow.UpdateInterface();
// Reload resources
ReloadResources();
UpdateScriptNames(); //mxd
// Done
General.MainWindow.DisplayReady();
General.MainWindow.RedrawDisplay(); //mxd
}
// Done
optionsform.Dispose();
}
// This shows the things filters setup
[BeginAction("thingsfilterssetup")]
internal void ShowThingsFiltersSetup() {
// Show things filter dialog
ThingsFiltersForm f = new ThingsFiltersForm();
f.ShowDialog(General.MainWindow);
f.Dispose();
General.MainWindow.UpdateThingsFilters();
}
// This returns true is the given type matches
public bool IsType(Type t) {
return io.GetType().Equals(t);
}
//mxd
public System.Drawing.SizeF GetTextSize(string text, float scale) {
return graphics.Font.GetTextSize(text, scale);
}
//mxd
[BeginAction("snapvertstogrid")]
public void SnapVerticesToGrid() {
ICollection<Vertex> verts = map.GetSelectedVertices(true);
//snap vertices?
if (verts.Count > 0) {
snapVertices(verts);
return;
}
//snap things?..
ICollection<Thing> things = map.GetSelectedThings(true);
if(things.Count == 0) {
General.Interface.DisplayStatus(StatusType.Warning, "Select any map element first!");
} else {
snapThings(things);
}
}
//mxd
private void snapVertices(ICollection<Vertex> verts) {
//we are terribly busy...
Cursor.Current = Cursors.AppStarting;
// Make undo for the snapping
General.Map.UndoRedo.CreateUndo("Snap vertices");
int snappedCount = 0;
List<Vertex> movedVerts = new List<Vertex>();
List<Linedef> movedLines = new List<Linedef>();
//snap them all!
foreach(Vertex v in verts) {
Vector2D pos = v.Position;
v.SnapToGrid();
if(v.Position.x != pos.x || v.Position.y != pos.y) {
snappedCount++;
movedVerts.Add(v);
foreach(Linedef l in v.Linedefs) {
if(!movedLines.Contains(l)) movedLines.Add(l);
}
}
}
//Create blockmap
RectangleF area = MapSet.CreateArea(General.Map.Map.Vertices);
BlockMap<BlockEntry> blockmap = new BlockMap<BlockEntry>(area);
blockmap.AddVerticesSet(General.Map.Map.Vertices);
//merge overlapping vertices using teh power of BLOCKMAP!!!11
BlockEntry block;
foreach(Vertex v in movedVerts) {
block = blockmap.GetBlockAt(v.Position);
if(block == null) continue;
foreach(Vertex blockVert in block.Vertices) {
if(blockVert.IsDisposed || blockVert.Index == v.Index || blockVert.Position != v.Position)
continue;
foreach (Linedef l in blockVert.Linedefs) {
if (!movedLines.Contains(l)) movedLines.Add(l);
}
v.Join(blockVert);
break;
}
}
// Update cached values of lines because we may need their length/angle
General.Map.Map.Update(true, false);
General.Map.Map.BeginAddRemove();
MapSet.RemoveLoopedLinedefs(movedLines);
MapSet.JoinOverlappingLines(movedLines);
General.Map.Map.EndAddRemove();
//get changed sectors
List<Sector> changedSectors = new List<Sector>();
foreach(Linedef l in movedLines) {
if(l == null || l.IsDisposed) continue;
if(l.Front != null && l.Front.Sector != null && !changedSectors.Contains(l.Front.Sector))
changedSectors.Add(l.Front.Sector);
if(l.Back != null && l.Back.Sector != null && !changedSectors.Contains(l.Back.Sector))
changedSectors.Add(l.Back.Sector);
}
// Now update area of sectors
General.Map.Map.Update(false, true);
//fix invalid sectors
foreach(Sector s in changedSectors) {
if(s.BBox.IsEmpty) {
s.Dispose();
} else if(s.Sidedefs.Count < 3) {
bool merged = false;
foreach(Sidedef side in s.Sidedefs) {
if(side.Other != null && side.Other.Sector != null) {
s.Join(side.Other.Sector);
merged = true;
break;
}
}
//oh well, I don't know what else I can do here...
if(!merged) s.Dispose();
}
}
//display status
General.Interface.DisplayStatus(StatusType.Info, "Snapped " + snappedCount + " vertices.");
MessageBox.Show("Snapped " + snappedCount + " vertices." + Environment.NewLine + "It's a good idea to run Map Analysis Mode now.");
//done
if(snappedCount > 0) {
// Update cached values
General.Map.Map.Update();
// Map is changed
General.Map.IsChanged = true;
}
General.Interface.RedrawDisplay();
Cursor.Current = Cursors.Default;
}
//mxd
private void snapThings(ICollection<Thing> things) {
//we are terribly busy...
Cursor.Current = Cursors.AppStarting;
// Make undo for the snapping
General.Map.UndoRedo.CreateUndo("Snap things");
int snappedCount = 0;
//snap them all!
foreach(Thing t in things) {
Vector2D pos = t.Position;
t.SnapToGrid();
if(t.Position.x != pos.x || t.Position.y != pos.y) snappedCount++;
}
//display status
General.Interface.DisplayStatus(StatusType.Info, "Snapped " + snappedCount + " things.");
//done
if(snappedCount > 0) {
// Update cached values
General.Map.Map.Update();
// Map is changed
General.Map.IsChanged = true;
}
//done, I said!
General.Interface.RedrawDisplay();
Cursor.Current = Cursors.Default;
}
#endregion
}
}