mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2024-11-10 06:41:49 +00:00
1952932cc1
- Make Lua parser ignore unknown properties instead of throwing an error - Add Title/Angled/NotAngled properties as valid alternatives for Name/Arrow/NoArrow
448 lines
16 KiB
C#
448 lines
16 KiB
C#
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Text;
|
|
using CodeImp.DoomBuilder.Compilers;
|
|
using CodeImp.DoomBuilder.Data;
|
|
using CodeImp.DoomBuilder.ZDoom;
|
|
using CodeImp.DoomBuilder.GZBuilder.Data;
|
|
using System.Windows.Forms;
|
|
|
|
#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 IDictionary<string, int> flagValues = new Dictionary<string, int>
|
|
{
|
|
{ "MF_SPECIAL", 0x1 },
|
|
{ "MF_SOLID", 0x2 },
|
|
{ "MF_SHOOTABLE", 0x4 },
|
|
{ "MF_NOSECTOR", 0x8 },
|
|
{ "MF_NOBLOCKMAP", 0x10 },
|
|
{ "MF_PAPERCOLLISION", 0x20 },
|
|
{ "MF_PUSHABLE", 0x40 },
|
|
{ "MF_BOSS", 0x80 },
|
|
{ "MF_SPAWNCEILING", 0x100 },
|
|
{ "MF_NOGRAVITY", 0x200 },
|
|
{ "MF_AMBIENT", 0x400 },
|
|
{ "MF_SLIDEME", 0x800 },
|
|
{ "MF_NOCLIP", 0x1000 },
|
|
{ "MF_FLOAT", 0x2000 },
|
|
{ "MF_BOXICON", 0x4000 },
|
|
{ "MF_MISSILE", 0x8000 },
|
|
{ "MF_SPRING", 0x10000 },
|
|
{ "MF_BOUNCE", 0x20000 },
|
|
{ "MF_MONITOR", 0x40000 },
|
|
{ "MF_NOTHINK", 0x80000 },
|
|
{ "MF_FIRE", 0x100000 },
|
|
{ "MF_NOCLIPHEIGHT", 0x200000 },
|
|
{ "MF_ENEMY", 0x400000 },
|
|
{ "MF_SCENERY", 0x800000 },
|
|
{ "MF_PAIN", 0x1000000 },
|
|
{ "MF_STICKY", 0x2000000 },
|
|
{ "MF_NIGHTSITEM", 0x4000000 },
|
|
{ "MF_NOCLIPTHING", 0x8000000 },
|
|
{ "MF_GRENADEBOUNCE", 0x10000000 },
|
|
{ "MF_RUNSPAWNFUNC", 0x20000000 }
|
|
};
|
|
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 objname)
|
|
{
|
|
if (objname == null) return false;
|
|
string name = objname;
|
|
string category = "";
|
|
string sprite = DataManager.INTERNAL_PREFIX + "unknownthing";
|
|
string[] states = new string[8];
|
|
int mapThingNum = -1;
|
|
int radius = 0;
|
|
int height = 0;
|
|
int flags = 0;
|
|
Dictionary<string, string> newflags = new Dictionary<string, string>();
|
|
string angletext = "";
|
|
string parametertext = "";
|
|
string flagsvaluetext = "";
|
|
bool arrow = false;
|
|
bool tagthing = false;
|
|
while (!streamreader.EndOfStream)
|
|
{
|
|
string line = streamreader.ReadLine();
|
|
linenumber++;
|
|
if (String.IsNullOrEmpty(line) || line.StartsWith("\n")) break;
|
|
if (line.StartsWith("#$Sprite "))
|
|
{
|
|
string spritename = line.Substring(9);
|
|
if (((spritename.Length > DataManager.INTERNAL_PREFIX.Length) &&
|
|
spritename.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX)) ||
|
|
General.Map.Data.GetSpriteExists(spritename))
|
|
{
|
|
sprite = spritename;
|
|
continue;
|
|
}
|
|
LogWarning("The sprite \"" + spritename + "\" assigned by the \"$sprite\" property does not exist");
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$Name "))
|
|
{
|
|
name = ZDTextParser.StripQuotes(line.Substring(7));
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$Title "))
|
|
{
|
|
name = ZDTextParser.StripQuotes(line.Substring(8));
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$Category "))
|
|
{
|
|
category = line.Substring(11);
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$Flags"))
|
|
{
|
|
string index = line.Substring(7, 1);
|
|
if (line.Substring(8, 5) == "Text ")
|
|
newflags[index] = "[" + index + "] " + ZDTextParser.StripQuotes(line.Substring(13));
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$AngleText "))
|
|
{
|
|
angletext = ZDTextParser.StripQuotes(line.Substring(12));
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$ParameterText "))
|
|
{
|
|
parametertext = ZDTextParser.StripQuotes(line.Substring(16));
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$FlagsValueText "))
|
|
{
|
|
flagsvaluetext = ZDTextParser.StripQuotes(line.Substring(17));
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$Arrow") || line.StartsWith("#$Angled"))
|
|
{
|
|
arrow = true;
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$NoArrow") || line.StartsWith("#$NotAngled"))
|
|
{
|
|
arrow = false;
|
|
continue;
|
|
}
|
|
if (line.StartsWith("#$TagThing"))
|
|
{
|
|
tagthing = true;
|
|
continue;
|
|
}
|
|
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))
|
|
LogWarning("Could not parse map thing number");
|
|
break;
|
|
case "RADIUS":
|
|
if (!ParseWithArithmetic(tokens[1], out radius))
|
|
LogWarning("Could not parse radius");
|
|
radius /= 65536;
|
|
break;
|
|
case "HEIGHT":
|
|
if (!ParseWithArithmetic(tokens[1], out height))
|
|
LogWarning("Could not parse height");
|
|
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;
|
|
case "FLAGS":
|
|
if (!ParseFlags(tokens[1], out flags))
|
|
LogWarning("Could not parse flags");
|
|
break;
|
|
}
|
|
}
|
|
if (mapThingNum > 0)
|
|
{
|
|
SRB2Object o = new SRB2Object(name, sprite, category, states, mapThingNum, radius, height, flags,
|
|
newflags, angletext, parametertext, flagsvaluetext, arrow, tagthing);
|
|
if (objects.ContainsKey(objname))
|
|
objects[objname] = o;
|
|
else
|
|
objects.Add(objname, 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 = 0;
|
|
string[] tokens = input.Split(new char[] { '+' });
|
|
foreach (string t in tokens)
|
|
{
|
|
int val = 0;
|
|
if (!ParseMultiplication(t, out val))
|
|
return false;
|
|
output += val;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool ParseMultiplication(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))
|
|
return false;
|
|
output *= val;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool ParseFlags(string input, out int output)
|
|
{
|
|
output = 0;
|
|
string[] tokens = input.Split(new char[] { '|' });
|
|
foreach (string token in tokens)
|
|
{
|
|
if (flagValues.ContainsKey(token))
|
|
{
|
|
output |= flagValues[token];
|
|
}
|
|
else
|
|
{
|
|
int val = 0;
|
|
if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
|
|
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
|
|
}
|
|
}
|