Added Lua parser for custom things (longform object definition only). Print error messages if custom thing parsing fails.

This commit is contained in:
MascaraSnake 2016-04-06 14:58:15 +02:00
parent cd54edc410
commit 2925986234
5 changed files with 339 additions and 5 deletions

View File

@ -186,6 +186,7 @@
<Compile Include="Map\Node.cs" />
<Compile Include="Map\Seg.cs" />
<Compile Include="Map\Subsector.cs" />
<Compile Include="SRB2\LuaObjectParser.cs" />
<Compile Include="SRB2\SRB2Object.cs" />
<Compile Include="SRB2\SOCObjectParser.cs" />
<Compile Include="SRB2\LevelHeaderParser.cs" />

View File

@ -1720,7 +1720,27 @@ namespace CodeImp.DoomBuilder.Data
public int LoadCustomObjects()
{
SOCObjectParser parser = new SOCObjectParser { OnInclude = ParseFromLocation };
LuaObjectParser LuaParser = new LuaObjectParser { OnInclude = ParseFromLocation };
// Parse lumps
foreach (DataReader dr in containers)
{
currentreader = dr;
Dictionary<string, Stream> stream = dr.GetLuaData();
foreach (KeyValuePair<string, Stream> group in stream)
{
// Parse the data
LuaParser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false);
}
}
if (LuaParser.HasError)
{
LuaParser.LogError();
}
SOCObjectParser SOCParser = new SOCObjectParser { OnInclude = ParseFromLocation };
// Parse lumps
foreach (DataReader dr in containers)
@ -1735,16 +1755,25 @@ namespace CodeImp.DoomBuilder.Data
foreach (KeyValuePair<string, Stream> group in streams)
{
// Parse the data
parser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false);
SOCParser.Parse(group.Value, Path.Combine(dr.Location.location, group.Key), false);
}
}
if (SOCParser.HasError)
{
SOCParser.LogError();
}
currentreader = null;
if (!parser.HasError && parser.Objects.Count > 0)
IDictionary<string, SRB2Object> objects = new Dictionary<string, SRB2Object>();
if (!LuaParser.HasError) objects = objects.Concat(LuaParser.Objects).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
if (!SOCParser.HasError) objects = objects.Concat(SOCParser.Objects).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
if (objects.Count > 0)
{
ThingCategory cat = new ThingCategory(null, "customthings", "Custom Things");
foreach (KeyValuePair<string,SRB2Object> o in parser.Objects)
foreach (KeyValuePair<string,SRB2Object> o in objects)
{
ThingTypeInfo t = new ThingTypeInfo(cat, o.Value);
cat.AddThing(t);
@ -1764,7 +1793,7 @@ namespace CodeImp.DoomBuilder.Data
thingcategories.Add(cat);
}
return parser.Objects.Count;
return objects.Count;
}
//mxd

View File

@ -170,6 +170,9 @@ namespace CodeImp.DoomBuilder.Data
//mxd. When implemented, this returns the SOC_ lumps
public virtual Dictionary<string, Stream> GetSOCData() { return new Dictionary<string, Stream>(); }
//mxd. When implemented, this returns the LUA_ lumps
public virtual Dictionary<string, Stream> GetLuaData() { return new Dictionary<string, Stream>(); }
//mxd. When implemented, this returns the GLDEFS lump
public abstract Dictionary<string, Stream> GetGldefsData(GameType gametype); // { return new Dictionary<string, Stream>(); }

View File

@ -880,6 +880,23 @@ namespace CodeImp.DoomBuilder.Data
return streams;
}
public override Dictionary<string, Stream> GetLuaData()
{
if (issuspended) throw new Exception("Data reader is suspended");
Dictionary<string, Stream> streams = new Dictionary<string, Stream>(StringComparer.Ordinal);
string prefix = "LUA_";
foreach (Lump lump in file.Lumps)
{
if (lump.Name.StartsWith(prefix))
{
streams.Add(lump.Name, lump.Stream);
}
}
return streams;
}
//mxd
public override Dictionary<string, Stream> GetGldefsData(GameType gameType)
{

View File

@ -0,0 +1,284 @@
#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;
#endregion
namespace CodeImp.DoomBuilder.SRB2
{
internal sealed class LuaObjectParser : ZDTextParser
{
#region ================== Delegates
public delegate void IncludeDelegate(LuaObjectParser 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;*/
#endregion
#region ================== Properties
public Dictionary<string, SRB2Object> Objects { get { return objects; } }
#endregion
#region ================== Constructor
public LuaObjectParser()
{
// 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
Stream localstream = datastream;
string localsourcename = sourcename;
BinaryReader localreader = datareader;
string token;
while (SkipWhitespace(true))
{
token = ReadToken();
if (!string.IsNullOrEmpty(token))
{
if (!token.StartsWith("mobjinfo[") || !token.EndsWith("]")) continue;
string objname = token.Substring(9);
string sprite = DataManager.INTERNAL_PREFIX + "unknownthing";
string[] states = new string[8];
int mapThingNum = -1;
int radius = 0;
int height = 0;
objname = objname.TrimEnd(new char[] { ']' });
SkipWhitespace(true);
token = ReadToken();
if (token != "=")
{
ReportError("Invalid object definition, missing =");
return false;
}
SkipWhitespace(true);
token = ReadToken();
if (token != "{")
{
ReportError("Invalid object definition, missing {");
return false;
}
SkipWhitespace(true);
token = ReadToken();
bool finished = false;
while (token != null)
{
if (finished) break;
switch (token)
{
case "$Sprite":
SkipWhitespace(true);
token = ReadToken();
if (((token.Length > DataManager.INTERNAL_PREFIX.Length) &&
token.ToLowerInvariant().StartsWith(DataManager.INTERNAL_PREFIX)) ||
General.Map.Data.GetSpriteExists(token))
{
sprite = token;
break;
}
ReportError("The sprite \"" + token + "\" assigned by the \"$sprite\" property does not exist");
return false;
case "doomednum":
if (!ReadParameter(out token, out finished)) return false;
if (!int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out mapThingNum))
{
ReportError("Invalid map thing number");
return false;
}
break;
case "radius":
if (!ReadParameter(out token, out finished)) return false;
if (!ParseWithArithmetic(token, out radius))
{
ReportError("Invalid radius");
return false;
}
radius /= 65536;
break;
case "height":
if (!ReadParameter(out token, out finished)) return false;
if (!ParseWithArithmetic(token, out height))
{
ReportError("Invalid height");
return false;
}
height /= 65536;
break;
case "spawnstate":
if (!ReadParameter(out token, out finished)) return false;
states[0] = token;
break;
case "seestate":
if (!ReadParameter(out token, out finished)) return false;
states[1] = token;
break;
case "painstate":
if (!ReadParameter(out token, out finished)) return false;
states[2] = token;
break;
case "meleestate":
if (!ReadParameter(out token, out finished)) return false;
states[3] = token;
break;
case "missilestate":
if (!ReadParameter(out token, out finished)) return false;
states[4] = token;
break;
case "deathstate":
if (!ReadParameter(out token, out finished)) return false;
states[5] = token;
break;
case "xdeathstate":
if (!ReadParameter(out token, out finished)) return false;
states[6] = token;
break;
case "raisestate":
if (!ReadParameter(out token, out finished)) return false;
states[7] = token;
break;
case "spawnhealth":
case "seesound":
case "reactiontime":
case "attacksound":
case "painchance":
case "painsound":
case "deathsound":
case "speed":
case "dispoffset":
case "mass":
case "damage":
case "activesound":
case "flags":
if (!ReadParameter(out token, out finished)) return false;
break;
default:
ReportError("Unknown object definition parameter " + token);
return false;
}
SkipWhitespace(true);
token = ReadToken();
}
if (token != "}")
{
ReportError("Invalid object definition, missing }");
return false;
}
if (mapThingNum > 0)
{
SRB2Object o = new SRB2Object(objname, sprite, states, mapThingNum, radius, height);
if (objects.ContainsKey(objname))
objects[objname] = o;
else
objects.Add(objname, o);
}
}
}
// All done
return !this.HasError;
}
#endregion
#region ================== Methods
private bool ReadParameter(out string output, out bool finished)
{
output = "";
finished = false;
SkipWhitespace(true);
string token = ReadToken();
if (token != "=")
{
ReportError("Invalid parameter definition, missing =");
return false;
}
SkipWhitespace(true);
token = ReadToken();
if (!token.EndsWith(","))
{
finished = true;
}
output = token.TrimEnd(new char[] { ',' });
return true;
}
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;
}
protected override string GetLanguageType()
{
return "Lua";
}
#endregion
}
}