mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-12-22 01:50:50 +00:00
1625 lines
51 KiB
C#
1625 lines
51 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.Globalization;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Diagnostics;
|
|
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.Plugins;
|
|
using CodeImp.DoomBuilder.Compilers;
|
|
using CodeImp.DoomBuilder.VisualModes;
|
|
|
|
#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";
|
|
|
|
// Save modes
|
|
public const int SAVE_NORMAL = 0;
|
|
public const int SAVE_AS = 1;
|
|
public const int SAVE_INTO = 2;
|
|
public const int SAVE_TEST = 3;
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// Status
|
|
private bool changed;
|
|
private bool scriptschanged;
|
|
|
|
// Map information
|
|
private string filetitle;
|
|
private string filepathname;
|
|
private string temppath;
|
|
|
|
// 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;
|
|
|
|
// Disposing
|
|
private bool isdisposed = false;
|
|
|
|
#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; } }
|
|
internal 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; } }
|
|
|
|
#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>();
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
else
|
|
{
|
|
// 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(General.LoadGameConfiguration(options.ConfigFile));
|
|
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();
|
|
|
|
// 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(General.LoadGameConfiguration(options.ConfigFile));
|
|
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();
|
|
|
|
// 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;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Save
|
|
|
|
// Initializes for an existing map
|
|
internal bool SaveMap(string newfilepathname, int savemode)
|
|
{
|
|
MapSet outputset;
|
|
string nodebuildername, settingsfile;
|
|
StatusInfo oldstatus;
|
|
WAD targetwad;
|
|
int index;
|
|
bool includenodes = false;
|
|
string origmapname;
|
|
bool success = true;
|
|
|
|
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) && (savemode == SAVE_TEST)) 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(map.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;
|
|
}
|
|
}
|
|
|
|
// 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 = savemode == SAVE_TEST ? 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((savemode != SAVE_INTO) && (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((savemode == SAVE_AS) && (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
|
|
CopyAllLumpsExceptMap(origwad, targetwad, 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(savemode != SAVE_TEST)
|
|
{
|
|
// 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 success;
|
|
}
|
|
|
|
#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
|
|
{
|
|
// 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.", MessageBoxButtons.OK);
|
|
else
|
|
General.ShowErrorMessage("Unable to build the nodes: The nodebuilder failed to build the expected data structures.", MessageBoxButtons.OK);
|
|
}
|
|
|
|
// Done with the build wad
|
|
if(buildwad != null) buildwad.Dispose();
|
|
}
|
|
|
|
// 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
|
|
internal MemoryStream GetLumpData(string lumpname)
|
|
{
|
|
Lump l = tempwad.FindLump(lumpname);
|
|
if(l != null)
|
|
{
|
|
l.Stream.Seek(0, SeekOrigin.Begin);
|
|
return new MemoryStream(l.Stream.ReadAllBytes());
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// This writes a copy of the data to a lump in the temp file
|
|
internal 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 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This copies all lumps, except those of a specific map
|
|
private void CopyAllLumpsExceptMap(WAD source, WAD target, 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 && !config.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.ToString() + " (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
|
|
undoredo.CreateUndo("Assign to group " + groupindex);
|
|
|
|
// Make selection
|
|
map.AddSelectionToGroup(0x01 << groupindex);
|
|
|
|
General.Interface.DisplayStatus(StatusType.Action, "Assigned selection to group " + groupindex);
|
|
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);
|
|
General.Interface.RedrawDisplay();
|
|
}
|
|
|
|
// 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); }
|
|
|
|
#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();
|
|
}
|
|
else
|
|
{
|
|
// 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();
|
|
}
|
|
else
|
|
{
|
|
// Check if scripts are changed
|
|
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;
|
|
|
|
// 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
|
|
if(filepathname.Length > 0)
|
|
sourcefile = filepathname;
|
|
else
|
|
sourcefile = tempwad.Filename;
|
|
|
|
// New list of errors
|
|
if(clearerrors) errors.Clear();
|
|
|
|
// Determine the script configuration to use
|
|
ScriptConfiguration scriptconfig = config.MapLumps[lumpname].script;
|
|
if(scriptconfig.Compiler != null)
|
|
{
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
// Fail
|
|
compiler.Dispose();
|
|
errors = null;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No compiler to run for this script type
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// This clears all compiler errors
|
|
internal void ClearCompilerErrors()
|
|
{
|
|
errors.Clear();
|
|
}
|
|
|
|
#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()
|
|
{
|
|
DataLocation maplocation;
|
|
StatusInfo oldstatus;
|
|
Cursor oldcursor;
|
|
|
|
// Keep old display info
|
|
oldstatus = General.MainWindow.Status;
|
|
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(General.LoadGameConfiguration(options.ConfigFile));
|
|
General.Editing.UpdateCurrentEditModes();
|
|
|
|
// Reload data resources
|
|
General.WriteLogLine("Reloading data resources...");
|
|
data = new DataManager();
|
|
if(!string.IsNullOrEmpty(filepathname))
|
|
{
|
|
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;
|
|
}
|
|
|
|
// 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);
|
|
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...");
|
|
configinfo = General.GetConfigurationInfo(options.ConfigFile);
|
|
config = new GameConfiguration(General.LoadGameConfiguration(options.ConfigFile));
|
|
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);
|
|
|
|
// Create required lumps if they don't exist yet
|
|
CreateRequiredLumps(tempwad, TEMP_MAP_HEADER);
|
|
|
|
// Let the plugins know
|
|
General.Plugins.MapReconfigure();
|
|
|
|
// Update interface
|
|
General.MainWindow.SetupInterface();
|
|
General.MainWindow.UpdateThingsFilters();
|
|
General.MainWindow.UpdateInterface();
|
|
|
|
// Reload resources
|
|
ReloadResources();
|
|
|
|
// Done
|
|
General.MainWindow.DisplayReady();
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|