2013-12-17 08:19:40 +00:00
|
|
|
|
#region ================== Namespaces
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using CodeImp.DoomBuilder.Map;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using CodeImp.DoomBuilder.Config;
|
|
|
|
|
using CodeImp.DoomBuilder.Types;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
namespace CodeImp.DoomBuilder.IO
|
|
|
|
|
{
|
|
|
|
|
internal class ClipboardStreamWriter
|
|
|
|
|
{
|
|
|
|
|
#region ================== Constants
|
|
|
|
|
|
|
|
|
|
private const string UDMF_CONFIG_NAME = "UDMF.cfg";
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Variables
|
|
|
|
|
|
|
|
|
|
private Configuration config;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
|
|
|
|
|
|
public ClipboardStreamWriter() {
|
|
|
|
|
// Make configurations
|
|
|
|
|
config = new Configuration();
|
|
|
|
|
|
|
|
|
|
// Find a resource named UDMF.cfg
|
|
|
|
|
string[] resnames = General.ThisAssembly.GetManifestResourceNames();
|
|
|
|
|
foreach(string rn in resnames) {
|
|
|
|
|
// Found it?
|
|
|
|
|
if(rn.EndsWith(UDMF_CONFIG_NAME, StringComparison.InvariantCultureIgnoreCase)) {
|
|
|
|
|
// Get a stream from the resource
|
|
|
|
|
Stream udmfcfg = General.ThisAssembly.GetManifestResourceStream(rn);
|
|
|
|
|
StreamReader udmfcfgreader = new StreamReader(udmfcfg, Encoding.ASCII);
|
|
|
|
|
|
|
|
|
|
// Load configuration from stream
|
|
|
|
|
config.InputConfiguration(udmfcfgreader.ReadToEnd());
|
|
|
|
|
|
|
|
|
|
// Now we add the linedef flags, activations and thing flags
|
|
|
|
|
// to this list, so that these don't show up in the custom
|
|
|
|
|
// fields list either. We use true as dummy value (it has no meaning)
|
|
|
|
|
|
|
|
|
|
// Add linedef flags
|
|
|
|
|
foreach(KeyValuePair<string, string> flag in General.Map.Config.LinedefFlags)
|
|
|
|
|
config.WriteSetting("managedfields.linedef." + flag.Key, true);
|
|
|
|
|
|
|
|
|
|
// Add linedef activations
|
|
|
|
|
foreach(LinedefActivateInfo activate in General.Map.Config.LinedefActivates)
|
|
|
|
|
config.WriteSetting("managedfields.linedef." + activate.Key, true);
|
|
|
|
|
|
|
|
|
|
//mxd. Add sidedef flags
|
|
|
|
|
foreach(KeyValuePair<string, string> flag in General.Map.Config.SidedefFlags)
|
|
|
|
|
config.WriteSetting("managedfields.sidedef." + flag.Key, true);
|
|
|
|
|
|
|
|
|
|
//mxd. Add sector flags
|
|
|
|
|
foreach(KeyValuePair<string, string> flag in General.Map.Config.SectorFlags)
|
|
|
|
|
config.WriteSetting("managedfields.sector." + flag.Key, true);
|
|
|
|
|
|
|
|
|
|
// Add thing flags
|
|
|
|
|
foreach(KeyValuePair<string, string> flag in General.Map.Config.ThingFlags)
|
|
|
|
|
config.WriteSetting("managedfields.thing." + flag.Key, true);
|
|
|
|
|
|
|
|
|
|
// Done
|
|
|
|
|
udmfcfgreader.Dispose();
|
|
|
|
|
udmfcfg.Dispose();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region ================== Writing
|
|
|
|
|
|
2014-11-25 11:52:01 +00:00
|
|
|
|
public void Write(MapSet map, Stream stream, bool longtexturenames) {
|
|
|
|
|
Write(map.Vertices, map.Linedefs, map.Sidedefs, map.Sectors, map.Things, stream, longtexturenames);
|
2013-12-17 08:19:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(ICollection<Vertex> vertices, ICollection<Linedef> linedefs, ICollection<Sidedef> sidedefs,
|
2014-11-25 11:52:01 +00:00
|
|
|
|
ICollection<Sector> sectors, ICollection<Thing> things, Stream stream, bool longtexturenames) {
|
2013-12-17 08:19:40 +00:00
|
|
|
|
// Create collections
|
|
|
|
|
Dictionary<Vertex, int> vertexids = new Dictionary<Vertex, int>();
|
|
|
|
|
Dictionary<Sidedef, int> sidedefids = new Dictionary<Sidedef, int>();
|
|
|
|
|
Dictionary<Sector, int> sectorids = new Dictionary<Sector, int>();
|
|
|
|
|
|
|
|
|
|
// Index the elements in the data structures
|
|
|
|
|
foreach(Vertex v in vertices) vertexids.Add(v, vertexids.Count);
|
|
|
|
|
foreach(Sidedef sd in sidedefs) sidedefids.Add(sd, sidedefids.Count);
|
|
|
|
|
foreach(Sector s in sectors) sectorids.Add(s, sectorids.Count);
|
|
|
|
|
|
|
|
|
|
BinaryWriter writer = new BinaryWriter(stream);
|
|
|
|
|
// Write the data structures to stream
|
2014-11-25 11:52:01 +00:00
|
|
|
|
writer.Write(longtexturenames); //mxd
|
2013-12-17 08:19:40 +00:00
|
|
|
|
WriteVertices(vertices, writer);
|
|
|
|
|
WriteSectors(sectors, writer);
|
|
|
|
|
WriteSidedefs(sidedefs, writer, sectorids);
|
|
|
|
|
WriteLinedefs(linedefs, writer, sidedefids, vertexids);
|
|
|
|
|
WriteThings(things, writer);
|
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteVertices(ICollection<Vertex> vertices, BinaryWriter writer) {
|
|
|
|
|
writer.Write(vertices.Count);
|
|
|
|
|
|
|
|
|
|
foreach(Vertex v in vertices) {
|
|
|
|
|
//write "static" properties
|
|
|
|
|
writer.Write(v.Position.x);
|
|
|
|
|
writer.Write(v.Position.y);
|
|
|
|
|
writer.Write(v.ZCeiling);
|
|
|
|
|
writer.Write(v.ZFloor);
|
|
|
|
|
|
|
|
|
|
// Write custom fields
|
|
|
|
|
AddCustomFields(v.Fields, "vertex", writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This adds linedefs
|
|
|
|
|
private void WriteLinedefs(ICollection<Linedef> linedefs, BinaryWriter writer, IDictionary<Sidedef, int> sidedefids, IDictionary<Vertex, int> vertexids) {
|
|
|
|
|
writer.Write(linedefs.Count);
|
|
|
|
|
|
|
|
|
|
// Go for all linedefs
|
|
|
|
|
foreach(Linedef l in linedefs) {
|
|
|
|
|
//write "static" properties
|
|
|
|
|
writer.Write(l.Tag);
|
|
|
|
|
writer.Write(vertexids[l.Start]);
|
|
|
|
|
writer.Write(vertexids[l.End]);
|
|
|
|
|
|
|
|
|
|
//sidedefs
|
|
|
|
|
writer.Write((l.Front != null && sidedefids.ContainsKey(l.Front)) ? sidedefids[l.Front] : -1);
|
|
|
|
|
writer.Write((l.Back != null && sidedefids.ContainsKey(l.Back)) ? sidedefids[l.Back] : -1);
|
|
|
|
|
|
|
|
|
|
//action and args
|
|
|
|
|
writer.Write(l.Action);
|
|
|
|
|
for(int i = 0; i < l.Args.Length; i++) writer.Write(l.Args[i]);
|
|
|
|
|
|
|
|
|
|
AddFlags(l.Flags, writer);
|
|
|
|
|
AddCustomFields(l.Fields, "linedef", writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This adds sidedefs
|
|
|
|
|
private void WriteSidedefs(ICollection<Sidedef> sidedefs, BinaryWriter writer, IDictionary<Sector, int> sectorids) {
|
|
|
|
|
writer.Write(sidedefs.Count);
|
|
|
|
|
|
|
|
|
|
// Go for all sidedefs
|
|
|
|
|
foreach(Sidedef s in sidedefs) {
|
|
|
|
|
//write "static" properties
|
|
|
|
|
writer.Write(s.OffsetX);
|
|
|
|
|
writer.Write(s.OffsetY);
|
|
|
|
|
writer.Write(sectorids[s.Sector]);
|
|
|
|
|
|
|
|
|
|
//textures
|
|
|
|
|
writer.Write(s.HighTexture.Length);
|
|
|
|
|
writer.Write(s.HighTexture.ToCharArray());
|
|
|
|
|
writer.Write(s.MiddleTexture.Length);
|
|
|
|
|
writer.Write(s.MiddleTexture.ToCharArray());
|
|
|
|
|
writer.Write(s.LowTexture.Length);
|
|
|
|
|
writer.Write(s.LowTexture.ToCharArray());
|
|
|
|
|
|
|
|
|
|
AddFlags(s.Flags, writer);
|
|
|
|
|
AddCustomFields(s.Fields, "sidedef", writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This adds sectors
|
|
|
|
|
private void WriteSectors(ICollection<Sector> sectors, BinaryWriter writer) {
|
|
|
|
|
writer.Write(sectors.Count);
|
|
|
|
|
|
|
|
|
|
// Go for all sectors
|
|
|
|
|
foreach(Sector s in sectors) {
|
|
|
|
|
//write "static" properties
|
|
|
|
|
writer.Write(s.Tag);
|
|
|
|
|
writer.Write(s.Effect);
|
|
|
|
|
writer.Write(s.FloorHeight);
|
|
|
|
|
writer.Write(s.CeilHeight);
|
|
|
|
|
writer.Write(s.Brightness);
|
|
|
|
|
|
|
|
|
|
//textures
|
|
|
|
|
writer.Write(s.FloorTexture.Length);
|
|
|
|
|
writer.Write(s.FloorTexture.ToCharArray());
|
|
|
|
|
writer.Write(s.CeilTexture.Length);
|
|
|
|
|
writer.Write(s.CeilTexture.ToCharArray());
|
|
|
|
|
|
2014-08-25 11:15:19 +00:00
|
|
|
|
//mxd. Slopes
|
|
|
|
|
writer.Write(s.FloorSlopeOffset);
|
|
|
|
|
writer.Write(s.FloorSlope.x);
|
|
|
|
|
writer.Write(s.FloorSlope.y);
|
|
|
|
|
writer.Write(s.FloorSlope.z);
|
2014-10-17 11:55:08 +00:00
|
|
|
|
writer.Write(s.CeilSlopeOffset);
|
|
|
|
|
writer.Write(s.CeilSlope.x);
|
|
|
|
|
writer.Write(s.CeilSlope.y);
|
|
|
|
|
writer.Write(s.CeilSlope.z);
|
2014-08-25 11:15:19 +00:00
|
|
|
|
|
2013-12-17 08:19:40 +00:00
|
|
|
|
AddFlags(s.Flags, writer);
|
|
|
|
|
AddCustomFields(s.Fields, "sector", writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This adds things
|
|
|
|
|
private void WriteThings(ICollection<Thing> things, BinaryWriter writer) {
|
|
|
|
|
writer.Write(things.Count);
|
|
|
|
|
|
|
|
|
|
// Go for all things
|
|
|
|
|
foreach(Thing t in things) {
|
|
|
|
|
//write "static" properties
|
|
|
|
|
writer.Write(t.Tag);
|
|
|
|
|
writer.Write(t.Position.x);
|
|
|
|
|
writer.Write(t.Position.y);
|
|
|
|
|
writer.Write(t.Position.z);
|
|
|
|
|
writer.Write(t.AngleDoom);
|
Model rendering (all modes): UDMF scale, pitch and roll are now displayed.
Thing Edit Form, UDMF: added controls for setting pitch, roll, scale, render style, fill color, alpha, health and score.
Visual mode, UDMF: UDMF scale is now applied when rendering sprites.
Added Thing Statistics form (Edit -> View Thing Types...), which shows all loaded thing types with some additional info.
Visual mode: sprites with negative ScaleX and positive ScaleY were not rendered properly.
Classic modes: display was not updated after loading a sprite.
Current testing engine change was not saved on closing the program when no other game configuration settings were changed.
2014-04-30 10:01:22 +00:00
|
|
|
|
writer.Write(t.Pitch); //mxd
|
|
|
|
|
writer.Write(t.Roll); //mxd
|
|
|
|
|
writer.Write(t.ScaleX); //mxd
|
|
|
|
|
writer.Write(t.ScaleY); //mxd
|
2013-12-17 08:19:40 +00:00
|
|
|
|
writer.Write(t.Type);
|
|
|
|
|
writer.Write(t.Action);
|
|
|
|
|
for(int i = 0; i < t.Args.Length; i++) writer.Write(t.Args[i]);
|
|
|
|
|
|
|
|
|
|
AddFlags(t.Flags, writer);
|
|
|
|
|
AddCustomFields(t.Fields, "thing", writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddCustomFields(UniFields elementFields, string elementname, BinaryWriter writer) {
|
2014-02-26 14:11:06 +00:00
|
|
|
|
Dictionary<string, UniValue> fields = new Dictionary<string, UniValue>(StringComparer.Ordinal);
|
2013-12-17 08:19:40 +00:00
|
|
|
|
|
|
|
|
|
foreach(KeyValuePair<string, UniValue> f in elementFields) {
|
|
|
|
|
if(config.SettingExists("managedfields." + elementname + "." + f.Key)) continue;
|
|
|
|
|
fields.Add(f.Key, f.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.Write(fields.Count);
|
|
|
|
|
|
|
|
|
|
foreach(KeyValuePair<string, UniValue> f in fields) {
|
|
|
|
|
writer.Write(f.Key.Length);
|
|
|
|
|
writer.Write(f.Key.ToCharArray());
|
|
|
|
|
|
|
|
|
|
writer.Write(f.Value.Type);
|
|
|
|
|
if(f.Value.Value is bool) {
|
|
|
|
|
writer.Write((int)UniversalType.Boolean);
|
|
|
|
|
writer.Write((bool)f.Value.Value);
|
|
|
|
|
} else if(f.Value.Value is float) {
|
|
|
|
|
writer.Write((int)UniversalType.Float);
|
|
|
|
|
writer.Write((float)f.Value.Value);
|
|
|
|
|
} else if(f.Value.Value.GetType().IsPrimitive) {
|
|
|
|
|
writer.Write((int)UniversalType.Integer);
|
|
|
|
|
writer.Write((int)f.Value.Value);
|
|
|
|
|
} else if(f.Value.Value is string) {
|
|
|
|
|
writer.Write((int)UniversalType.String);
|
|
|
|
|
string s = (string)f.Value.Value;
|
|
|
|
|
writer.Write(s.Length);
|
|
|
|
|
writer.Write(s.ToCharArray());
|
|
|
|
|
} else { //WOLOLO! ERRORS!
|
|
|
|
|
General.ErrorLogger.Add(ErrorType.Error, "Unable to copy Universal Field '" + f.Key + "' to clipboard: unknown value type '" + f.Value.Type + "'!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-20 09:09:28 +00:00
|
|
|
|
private static void AddFlags(Dictionary<string, bool> elementFlags, BinaryWriter writer) {
|
2013-12-17 08:19:40 +00:00
|
|
|
|
List<string> flags = new List<string>();
|
|
|
|
|
|
|
|
|
|
foreach(KeyValuePair<string, bool> f in elementFlags) {
|
|
|
|
|
if(!f.Value) continue;
|
|
|
|
|
flags.Add(f.Key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.Write(flags.Count);
|
|
|
|
|
|
|
|
|
|
foreach(string s in flags) {
|
|
|
|
|
writer.Write(s.Length);
|
|
|
|
|
writer.Write(s.ToCharArray());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|