mirror of
https://git.do.srb2.org/STJr/ZoneBuilder.git
synced 2024-11-10 06:41:49 +00:00
Added Lua parser for custom things (longform object definition only). Print error messages if custom thing parsing fails.
This commit is contained in:
parent
cd54edc410
commit
2925986234
5 changed files with 339 additions and 5 deletions
|
@ -186,6 +186,7 @@
|
||||||
<Compile Include="Map\Node.cs" />
|
<Compile Include="Map\Node.cs" />
|
||||||
<Compile Include="Map\Seg.cs" />
|
<Compile Include="Map\Seg.cs" />
|
||||||
<Compile Include="Map\Subsector.cs" />
|
<Compile Include="Map\Subsector.cs" />
|
||||||
|
<Compile Include="SRB2\LuaObjectParser.cs" />
|
||||||
<Compile Include="SRB2\SRB2Object.cs" />
|
<Compile Include="SRB2\SRB2Object.cs" />
|
||||||
<Compile Include="SRB2\SOCObjectParser.cs" />
|
<Compile Include="SRB2\SOCObjectParser.cs" />
|
||||||
<Compile Include="SRB2\LevelHeaderParser.cs" />
|
<Compile Include="SRB2\LevelHeaderParser.cs" />
|
||||||
|
|
|
@ -1720,7 +1720,27 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
|
|
||||||
public int LoadCustomObjects()
|
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
|
// Parse lumps
|
||||||
foreach (DataReader dr in containers)
|
foreach (DataReader dr in containers)
|
||||||
|
@ -1735,16 +1755,25 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
foreach (KeyValuePair<string, Stream> group in streams)
|
foreach (KeyValuePair<string, Stream> group in streams)
|
||||||
{
|
{
|
||||||
// Parse the data
|
// 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;
|
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");
|
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);
|
ThingTypeInfo t = new ThingTypeInfo(cat, o.Value);
|
||||||
cat.AddThing(t);
|
cat.AddThing(t);
|
||||||
|
@ -1764,7 +1793,7 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
thingcategories.Add(cat);
|
thingcategories.Add(cat);
|
||||||
|
|
||||||
}
|
}
|
||||||
return parser.Objects.Count;
|
return objects.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
//mxd
|
//mxd
|
||||||
|
|
|
@ -170,6 +170,9 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
//mxd. When implemented, this returns the SOC_ lumps
|
//mxd. When implemented, this returns the SOC_ lumps
|
||||||
public virtual Dictionary<string, Stream> GetSOCData() { return new Dictionary<string, Stream>(); }
|
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
|
//mxd. When implemented, this returns the GLDEFS lump
|
||||||
public abstract Dictionary<string, Stream> GetGldefsData(GameType gametype); // { return new Dictionary<string, Stream>(); }
|
public abstract Dictionary<string, Stream> GetGldefsData(GameType gametype); // { return new Dictionary<string, Stream>(); }
|
||||||
|
|
||||||
|
|
|
@ -880,6 +880,23 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
return streams;
|
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
|
//mxd
|
||||||
public override Dictionary<string, Stream> GetGldefsData(GameType gameType)
|
public override Dictionary<string, Stream> GetGldefsData(GameType gameType)
|
||||||
{
|
{
|
||||||
|
|
284
Source/Core/SRB2/LuaObjectParser.cs
Normal file
284
Source/Core/SRB2/LuaObjectParser.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue