Look through SOC lumps for custom things and make them available in the editor (no sprites yet)

This commit is contained in:
MascaraSnake 2016-03-18 17:32:31 +01:00
parent 85225aaf1f
commit ed578eab72
7 changed files with 486 additions and 11 deletions

View file

@ -186,7 +186,9 @@
<Compile Include="Map\Node.cs" />
<Compile Include="Map\Seg.cs" />
<Compile Include="Map\Subsector.cs" />
<Compile Include="SRB2\SOCParser.cs" />
<Compile Include="SRB2\SRB2Object.cs" />
<Compile Include="SRB2\SOCObjectParser.cs" />
<Compile Include="SRB2\LevelHeaderParser.cs" />
<Compile Include="VisualModes\VisualBlockMap.cs" />
<Compile Include="VisualModes\VisualMode.cs" />
<Compile Include="General\Clock.cs" />

View file

@ -144,6 +144,7 @@ namespace CodeImp.DoomBuilder.Config
this.angletext = parent.angletext;
this.flagsvaluetext = parent.flagsvaluetext;
this.parametertext = parent.parametertext;
this.flags = parent.flags;
}
// Set default properties
else
@ -170,6 +171,7 @@ namespace CodeImp.DoomBuilder.Config
this.angletext = "Angle";
this.flagsvaluetext = "Flags value";
this.parametertext = "Parameter";
this.flags = new Dictionary<string, string>();
}
// We have no destructor

View file

@ -22,6 +22,7 @@ using System.Globalization;
using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.SRB2;
using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.Map;
using System.Drawing;
@ -396,8 +397,55 @@ namespace CodeImp.DoomBuilder.Config
GC.SuppressFinalize(this);
}
// Constructor
internal ThingTypeInfo(int index, ThingTypeInfo other)
// Constructor
internal ThingTypeInfo(ThingCategory cat, SRB2Object o)
{
// Initialize
this.index = o.mapThingNum;
this.category = cat;
this.title = o.name;
this.actor = null;
this.classname = string.Empty; //mxd
this.isknown = true;
this.bright = false; //mxd
this.args = new ArgumentInfo[Linedef.NUM_ARGS];
for (int i = 0; i < Linedef.NUM_ARGS; i++) this.args[i] = new ArgumentInfo(i);
// Read properties
this.sprite = cat.Sprite;
this.color = cat.Color;
this.alpha = cat.Alpha; //mxd
this.alphabyte = (byte)(this.alpha * 255); //mxd
this.renderstyle = cat.RenderStyle; //mxd
this.arrow = (cat.Arrow != 0);
this.radius = o.radius;
this.height = o.height;
this.hangs = (cat.Hangs != 0);
this.blocking = cat.Blocking;
this.errorcheck = cat.ErrorCheck;
this.fixedsize = cat.FixedSize;
this.fixedrotation = cat.FixedRotation; //mxd
this.absolutez = cat.AbsoluteZ;
this.spritescale = new SizeF(cat.SpriteScale, cat.SpriteScale);
this.flags = new Dictionary<string, string>(cat.Flags);
this.heightoffset = cat.HeightOffset;
this.isUnflippable = cat.IsUnflippable;
this.ignoreZ = cat.IgnoreZ;
this.centerHitbox = cat.CenterHitbox;
this.angletext = cat.AngleText;
this.flagsvaluetext = cat.FlagsValueText;
this.parametertext = cat.ParameterText;
// Safety
if (this.radius < 4f) this.radius = 8f;
if (this.hangs && this.absolutez) this.hangs = false; //mxd
// We have no destructor
GC.SuppressFinalize(this);
}
// Constructor
internal ThingTypeInfo(int index, ThingTypeInfo other)
{
// Initialize
this.index = index;

View file

@ -371,7 +371,7 @@ namespace CodeImp.DoomBuilder.Data
Dictionary<int, string> spawnnums, doomednums;
LoadMapInfo(out spawnnums, out doomednums);
int thingcount = LoadDecorateThings(spawnnums, doomednums);
int thingcount = General.Map.SRB2 ? LoadCustomObjects() : LoadDecorateThings(spawnnums, doomednums);
int spritecount = LoadThingSprites();
LoadInternalSprites();
LoadInternalTextures(); //mxd
@ -526,7 +526,7 @@ namespace CodeImp.DoomBuilder.Data
previews = null;
// Dispose decorate
decorate.Dispose();
if (decorate != null) decorate.Dispose();
// Dispose resources
foreach(KeyValuePair<long, ImageData> i in textures) i.Value.Dispose();
@ -1718,6 +1718,55 @@ namespace CodeImp.DoomBuilder.Data
return counter;
}
public int LoadCustomObjects()
{
SOCObjectParser parser = new SOCObjectParser { OnInclude = ParseFromLocation };
// Parse lumps
foreach (DataReader dr in containers)
{
currentreader = dr;
Dictionary<string, Stream> streams1 = dr.GetObjctcfgData();
Dictionary<string, Stream> streams2 = dr.GetMaincfgData();
Dictionary<string, Stream> streams3 = dr.GetSOCData();
Dictionary<string, Stream> streams = streams1.Concat(streams2).Concat(streams3).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
foreach (KeyValuePair<string, Stream> group in streams)
{
// Parse the data
parser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false);
}
}
currentreader = null;
if (!parser.HasError && parser.Objects.Count > 0)
{
ThingCategory cat = new ThingCategory(null, "customthings", "Custom Things");
foreach (KeyValuePair<string,SRB2Object> o in parser.Objects)
{
ThingTypeInfo t = new ThingTypeInfo(cat, o.Value);
cat.AddThing(t);
// Check if we can find this thing in our existing collection
if (thingtypes.ContainsKey(o.Value.mapThingNum))
{
// Update the thing
thingtypes[o.Value.mapThingNum].Category.RemoveThing(thingtypes[o.Value.mapThingNum]);
thingtypes[o.Value.mapThingNum] = t;
}
else
{
// Add new thing
thingtypes.Add(t.Index, t);
}
}
thingcategories.Add(cat);
}
return parser.Objects.Count;
}
//mxd
private static ThingCategory GetThingCategory(ThingCategory parent, List<ThingCategory> categories, string[] catnames)
{
@ -2096,7 +2145,7 @@ namespace CodeImp.DoomBuilder.Data
{
if (General.Map.SRB2)
{
SOCParser parser = new SOCParser { OnInclude = ParseFromLocation };
LevelHeaderParser parser = new LevelHeaderParser { OnInclude = ParseFromLocation };
// Parse mapinfo
foreach (DataReader dr in containers)

View file

@ -13,11 +13,11 @@ using CodeImp.DoomBuilder.GZBuilder.Data;
namespace CodeImp.DoomBuilder.SRB2
{
internal sealed class SOCParser : ZDTextParser
internal sealed class LevelHeaderParser : ZDTextParser
{
#region ================== Delegates
public delegate void IncludeDelegate(SOCParser parser, string includefile, bool clearerror);
public delegate void IncludeDelegate(LevelHeaderParser parser, string includefile, bool clearerror);
public IncludeDelegate OnInclude;
#endregion
@ -40,7 +40,7 @@ namespace CodeImp.DoomBuilder.SRB2
#region ================== Constructor
public SOCParser()
public LevelHeaderParser()
{
// Syntax
whitespace = "\n \t\r\u00A0";
@ -71,9 +71,9 @@ namespace CodeImp.DoomBuilder.SRB2
while (!streamreader.EndOfStream)
{
string line = streamreader.ReadLine();
string line = RemoveComments(streamreader.ReadLine());
linenumber++;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n") || line.StartsWith("#")) continue;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) continue;
string[] tokens = line.Split(new char[] { ' ' });
switch (tokens[0].ToUpperInvariant())
{
@ -109,6 +109,7 @@ namespace CodeImp.DoomBuilder.SRB2
linenumber++;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) break;
if (line.StartsWith("#")) continue;
line = RemoveComments(line);
string[] tokens = line.Split(new char[] { '=' });
if (tokens.Length != 2)
{
@ -206,6 +207,12 @@ namespace CodeImp.DoomBuilder.SRB2
return "SOC";
}
private string RemoveComments(string line)
{
string[] tokens = line.Split(new char[] { '#' });
return tokens[0];
}
#endregion
}
}

View file

@ -0,0 +1,310 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using CodeImp.DoomBuilder.Compilers;
using CodeImp.DoomBuilder.ZDoom;
using CodeImp.DoomBuilder.GZBuilder.Data;
#endregion
namespace CodeImp.DoomBuilder.SRB2
{
internal sealed class SOCObjectParser : ZDTextParser
{
#region ================== Delegates
public delegate void IncludeDelegate(SOCObjectParser parser, string includefile, bool clearerror);
public IncludeDelegate OnInclude;
#endregion
#region ================== Variables
private Dictionary<string, SRB2Object> objects;
/*private Dictionary<string, SRB2State> states;
private List<string> objectfreeslots;
private List<string> statefreeslots;
private List<string> spritefreeslots;*/
private StreamReader streamreader;
private int linenumber;
#endregion
#region ================== Properties
public Dictionary<string, SRB2Object> Objects { get { return objects; } }
#endregion
#region ================== Constructor
public SOCObjectParser()
{
// Syntax
whitespace = "\n \t\r\u00A0";
specialtokens = "=\n";
objects = new Dictionary<string,SRB2Object>();
/*states = new Dictionary<string,SRB2State>();
objectfreeslots = new List<string>();
statefreeslots = new List<string>();
spritefreeslots = new List<string>();*/
}
// Disposer
public void Dispose()
{
objects = null;
/*states = null;
objectfreeslots = null;
statefreeslots = null;
spritefreeslots = null;*/
}
#endregion
#region ================== Parsing
override public bool Parse(Stream stream, string sourcefilename, bool clearerrors)
{
if (!base.Parse(stream, sourcefilename, clearerrors)) return false;
// Keep local data
streamreader = new StreamReader(stream, Encoding.ASCII);
linenumber = -1;
while (!streamreader.EndOfStream)
{
string line = RemoveComments(streamreader.ReadLine());
linenumber++;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) continue;
string[] tokens = line.Split(new char[] { ' ' });
switch (tokens[0].ToUpperInvariant())
{
/*case "FREESLOT":
if (!ParseFreeslots()) return false;
break;*/
case "OBJECT":
case "MOBJ":
case "THING":
if (tokens.Length < 2 || String.IsNullOrEmpty(tokens[1]))
{
ReportError("Object block is missing an object name");
break;
}
if (!ParseObject(tokens[1].ToUpperInvariant())) return false;
break;
/*case "STATE":
case "FRAME":
if (tokens.Length < 2 || String.IsNullOrEmpty(tokens[1]))
{
ReportError("State block is missing an state name");
break;
}
if (!ParseState(tokens[1].ToUpperInvariant())) return false;
break;*/
}
}
// All done
return !this.HasError;
}
#endregion
#region ================== Map block parsing
/*private bool ParseFreeslots()
{
while (!streamreader.EndOfStream)
{
string line = streamreader.ReadLine();
linenumber++;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) break;
if (line.StartsWith("#")) continue;
line = RemoveComments(line).Trim();
if (line.StartsWith("MT_")) objectfreeslots.Add(line);
else if (line.StartsWith("S_")) statefreeslots.Add(line);
else if (line.StartsWith("SPR_")) spritefreeslots.Add(line);
}
return true;
}*/
private bool ParseObject(string name)
{
if (name == null) return false;
string[] states = new string[8];
int mapThingNum = -1;
int radius = 0;
int height = 0;
while (!streamreader.EndOfStream)
{
string line = streamreader.ReadLine();
linenumber++;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) break;
if (line.StartsWith("#")) continue;
line = RemoveComments(line);
string[] tokens = line.Split(new char[] { '=' });
if (tokens.Length != 2)
{
ReportError("Invalid line");
return false;
}
tokens[0] = tokens[0].Trim().ToUpperInvariant();
tokens[1] = tokens[1].Trim().ToUpperInvariant();
switch(tokens[0])
{
case "MAPTHINGNUM":
if (!int.TryParse(tokens[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out mapThingNum))
{
ReportError("Invalid map thing number");
return false;
}
break;
case "RADIUS":
if (!ParseWithArithmetic(tokens[1], out radius))
{
ReportError("Invalid radius");
return false;
}
radius /= 65536;
break;
case "HEIGHT":
if (!ParseWithArithmetic(tokens[1], out height))
{
ReportError("Invalid height");
return false;
}
height /= 65536;
break;
case "SPAWNSTATE":
states[0] = tokens[1];
break;
case "SEESTATE":
states[1] = tokens[1];
break;
case "PAINSTATE":
states[2] = tokens[1];
break;
case "MELEESTATE":
states[3] = tokens[1];
break;
case "MISSILESTATE":
states[4] = tokens[1];
break;
case "DEATHSTATE":
states[5] = tokens[1];
break;
case "XDEATHSTATE":
states[6] = tokens[1];
break;
case "RAISESTATE":
states[7] = tokens[1];
break;
}
}
if (mapThingNum > 0)
{
SRB2Object o = new SRB2Object(name, states, mapThingNum, radius, height);
if (objects.ContainsKey(name))
objects[name] = o;
else
objects.Add(name, o);
}
return true;
}
/*private bool ParseState(string name)
{
if (name == null) return false;
string spritename = "";
int spriteframe = 0;
string next = "";
while (!streamreader.EndOfStream)
{
string line = streamreader.ReadLine();
linenumber++;
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) break;
if (line.StartsWith("#")) continue;
line = RemoveComments(line);
string[] tokens = line.Split(new char[] { '=' });
if (tokens.Length != 2)
{
ReportError("Invalid line");
return false;
}
tokens[0] = tokens[0].Trim().ToUpperInvariant();
tokens[1] = tokens[1].Trim().ToUpperInvariant();
switch (tokens[0])
{
case "SPRITENAME":
case "SPRITENUMBER":
spritename = tokens[1];
break;
case "SPRITEFRAME":
case "SPRITESUBNUMBER":
//TODO: Strip flags
spriteframe = ParseSpriteFrame(tokens[1]);
break;
case "NEXT":
next = tokens[1];
break;
}
}
states.Add(new SRB2State(name, spritename, spriteframe, next));
return true;
}*/
#endregion
#region ================== Methods
private bool ParseWithArithmetic(string input, out int output)
{
output = 1;
string[] tokens = input.Split(new char[] { '*' });
foreach (string t in tokens)
{
string trimmed = t.Trim();
int val = 1;
if (trimmed == "FRACUNIT") val = 65536;
else if (!int.TryParse(trimmed, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
{
ReportError("Invalid radius");
return false;
}
output *= val;
}
return true;
}
// This reports an error
protected internal override void ReportError(string message)
{
// Set error information
errordesc = message;
errorline = (streamreader != null ? linenumber : CompilerError.NO_LINE_NUMBER); //mxd
errorsource = sourcename;
}
private string RemoveComments(string line)
{
string[] tokens = line.Split(new char[] { '#' });
return tokens[0];
}
protected override string GetLanguageType()
{
return "SOC";
}
#endregion
}
}

View file

@ -0,0 +1,57 @@
#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.Generic;
using System.Globalization;
#endregion
namespace CodeImp.DoomBuilder.SRB2
{
public struct SRB2Object
{
#region ================== Constants
#endregion
#region ================== Variables
public readonly string name;
public readonly string[] states;
public readonly int mapThingNum;
public readonly int radius;
public readonly int height;
#endregion
#region ================== Constructor / Disposer
// Constructor
internal SRB2Object(string name, string[] states, int mapThingNum, int radius, int height)
{
this.name = name;
this.states = states;
this.mapThingNum = mapThingNum;
this.radius = radius;
this.height = height;
}
#endregion
}
}