Add Lua parser for custom objects

This commit is contained in:
spherallic 2023-09-15 21:28:20 +02:00
parent 89f781bdc4
commit f8e7bd3739
10 changed files with 436 additions and 7 deletions

View file

@ -312,6 +312,8 @@
<Compile Include="Rendering\Vector4.cs" />
<Compile Include="Rendering\VertexBuffer.cs" />
<Compile Include="Rendering\VisualSlopeHandle.cs" />
<Compile Include="SRB2\LuaMobjStructure.cs" />
<Compile Include="SRB2\LuaParser.cs" />
<Compile Include="VisualModes\VisualSlope.cs" />
<Compile Include="Windows\ILinedefEditForm.cs" />
<Compile Include="Windows\ISectorEditForm.cs" />

View file

@ -58,7 +58,9 @@ namespace CodeImp.DoomBuilder.Config
KEYCONF,
FONTDEFS,
ZSCRIPT,
DECALDEF
DECALDEF,
//SOC,
LUA
}
public class ScriptConfiguration : IComparable<ScriptConfiguration>

View file

@ -133,6 +133,7 @@ namespace CodeImp.DoomBuilder.Data
// Things combined with things created from Decorate
private DecorateParser decorate;
private ZScriptParser zscript;
private LuaParser lua;
private Dictionary<string, ActorStructure> zdoomclasses;
private List<ThingCategory> thingcategories;
private Dictionary<int, ThingTypeInfo> thingtypes;
@ -460,6 +461,7 @@ namespace CodeImp.DoomBuilder.Data
LoadDehackedThings();
LoadZScriptThings();
LoadDecorateThings();
LoadLuaThings();
ApplyDehackedThings();
FixRenamedDehackedSprites();
int thingcount = ApplyZDoomThings(spawnnums, doomednums);
@ -1844,8 +1846,42 @@ namespace CodeImp.DoomBuilder.Data
}
}
// [ZZ] this retrieves ZDoom actor structure by class name.
public ActorStructure GetZDoomActor(string classname)
// sphere: This loads things from SRB2 Lua files.
private void LoadLuaThings()
{
// Create new parser
lua = new LuaParser();
// Go for all opened containers
foreach (DataReader dr in containers)
{
// Load Lua info cumulatively (the last Lua is added to the previous)
// I'm not sure if this is the right thing to do though.
currentreader = dr;
foreach (TextResourceData data in dr.GetLuaData())
{
// Parse the data
data.Stream.Seek(0, SeekOrigin.Begin);
lua.Parse(data, true);
//mxd. DECORATE lumps are interdepandable. Can't carry on...
if (lua.HasError)
{
lua.LogError();
break;
}
}
}
//mxd. Add to text resources collection
currentreader = null;
if (lua.HasError)
lua.ClearActors();
}
// [ZZ] this retrieves ZDoom actor structure by class name.
public ActorStructure GetZDoomActor(string classname)
{
classname = classname.ToLowerInvariant();
ActorStructure outv;
@ -1887,6 +1923,37 @@ namespace CodeImp.DoomBuilder.Data
Dictionary<string, ActorStructure> mergedAllActorsByClass = decorate.AllActorsByClass.Concat(zscript.AllActorsByClass.Where(x => !decorate.AllActorsByClass.ContainsKey(x.Key))).ToDictionary(k => k.Key, v => v.Value);
zdoomclasses = mergedAllActorsByClass;
// Parse SRB2 Lua/SOC objects
IEnumerable<ActorStructure> mobjs = lua.Mobjs; // lua.Mobjs.Union(soc.Mobjs);
foreach (ActorStructure actor in mobjs)
{
// Check if we want to add this actor
if (actor.DoomEdNum > 0)
{
// Check if we can find this thing in our existing collection
if (thingtypes.ContainsKey(actor.DoomEdNum))
{
// Update the thing
thingtypes[actor.DoomEdNum].ModifyByDecorateActor(actor);
}
else
{
// Find the category to put the actor in
ThingCategory cat = GetThingCategory(null, thingcategories, GetCategoryInfo(actor)); //mxd
// Add new thing
ThingTypeInfo t = new ThingTypeInfo(cat, actor); ;
cat.AddThing(t);
thingtypes.Add(t.Index, t);
}
// Count
counter++;
}
}
// Dictionary of replaced actors that have to be recategorized
Dictionary<int, ActorStructure> recategorizeactors = new Dictionary<int, ActorStructure>();

View file

@ -260,6 +260,9 @@ namespace CodeImp.DoomBuilder.Data
// When implemented, this returns the list of IWAD infos
public abstract List<IWadInfo> GetIWadInfos();
// When implemented, this returns Lua lumps
public abstract IEnumerable<TextResourceData> GetLuaData();
#endregion
#region ================== Load/Save (mxd)

View file

@ -766,6 +766,35 @@ namespace CodeImp.DoomBuilder.Data
#endregion
#region ================== Lua
// sphere
public override IEnumerable<TextResourceData> GetLuaData()
{
// Error when suspended
if (issuspended) throw new Exception("Data reader is suspended");
List<TextResourceData> result = new List<TextResourceData>();
List<string> files = new List<string>();
// Can be several entries
files.AddRange(GetAllFilesWhichTitleStartsWith("", "LUA_", true));
files.AddRange(GetFilesWithExt("", "lua", true));
// Add to collection
foreach (string s in files)
result.Add(new TextResourceData(this, LoadFile(s), s, true));
// Find in any of the wad files
foreach (WADReader wr in wads) result.AddRange(wr.GetLuaData());
return result;
}
#endregion
#region ================== Generic text lumps loading (mxd)
public override IEnumerable<TextResourceData> GetTextLumpData(ScriptType scripttype, bool singular, bool partialtitlematch)

View file

@ -1135,6 +1135,14 @@ namespace CodeImp.DoomBuilder.Data
return result;
}
// sphere
public override IEnumerable<TextResourceData> GetLuaData()
{
if (issuspended) throw new Exception("Data reader is suspended");
return GetAllLumpsData("LUA_");
}
//mxd
public override IEnumerable<TextResourceData> GetTextLumpData(ScriptType scripttype, bool singular, bool ignored)
{

View file

@ -0,0 +1,141 @@
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Types;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace CodeImp.DoomBuilder.ZDoom
{
public sealed class LuaMobjStructure : ActorStructure
{
#region ================== DECORATE Actor Structure parsing
internal LuaMobjStructure(ZDTextParser zdparser, string objname)
{
classname = string.Empty;
LuaParser parser = (LuaParser)zdparser;
bool done = false; //mxd
General.WriteLogLine(objname);
if (string.IsNullOrEmpty(objname))
{
parser.ReportError("Expected actor class name");
return;
}
// Now parse the contents of actor structure
while (parser.SkipWhitespace(true))
{
string token = parser.ReadToken();
token = token.ToLowerInvariant();
switch (token)
{
case "}":
// Actor scope ends here, break out of this parse loop
done = true;
break;
// Property
default:
General.WriteLogLine(token);
// Property begins with $? Then the whole line is a single value
if (token.Contains("$"))
{
bool isflag = false;
bool isname = false;
switch (token)
{
case "$name":
token = "$title";
isname = true;
break;
case "$wallsprite":
case "$flatsprite":
case "$spawnceiling":
isflag = true;
break;
}
// This is for editor-only properties such as $sprite and $category
string t = token;
if (isflag)
{
parser.SkipWhitespace(false);
string val = parser.ReadLine();
flags[t.Substring(1)] = (val == "true" || val == "1") ? true : false;
}
else
props[token] = new List<string> { (parser.SkipWhitespace(false) ? parser.ReadLine() : "") };
if (isname)
{
General.WriteLogLine("name = " + props["$title"][0]);
}
}
else
{
string tokenname = token;
parser.SkipWhitespace(true);
token = parser.ReadToken();
if (token != "=")
{
parser.ReportError("Invalid Lua object parameter definition, missing =");
return;
}
parser.SkipWhitespace(true);
token = parser.ReadToken();
if (!token.EndsWith(","))
{
done = true;
}
List<string> values = new List<string>() { token.TrimEnd(new char[] { ',' }) };
//mxd. Translate scale to xscale and yscale
switch (tokenname)
{
case "scale":
props["xscale"] = values;
props["yscale"] = values;
break;
case "doomednum":
doomednum = int.Parse(values[0]);
General.WriteLogLine("parsed doomednum: " + DoomEdNum);
goto default;
case "height":
case "radius":
props[tokenname] = new List<string>() { GetNumbers(values[0]).ToString() };
break;
default:
props[tokenname] = values;
break;
}
}
break;
}
if (done) break; //mxd
}
// parsing done, process thing arguments
ParseCustomArguments();
}
private static string GetNumbers(string input)
{
return new string(input.Where(c => char.IsDigit(c)).ToArray());
}
#endregion
}
}

View file

@ -0,0 +1,176 @@
#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;
using System.IO;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Types;
#endregion
namespace CodeImp.DoomBuilder.ZDoom
{
public sealed class LuaParser : ZDTextParser
{
#region ================== Delegates
public delegate void IncludeDelegate(LuaParser parser, string includefile);
public IncludeDelegate OnInclude;
#endregion
#region ================== Constants
#endregion
#region ================== Variables
//mxd. Script type
internal override ScriptType ScriptType { get { return ScriptType.LUA; } }
// SRB2 mobjs
private Dictionary<int, ActorStructure> mobjs;
//mxd. Includes tracking
private HashSet<string> parsedlumps;
//mxd. Disposing. Is that really needed?..
private bool isdisposed;
//
public bool NoWarnings = false;
#endregion
#region ================== Properties
/// <summary>
/// All mobjs that are supported by the current game.
/// </summary>
public ICollection<ActorStructure> Mobjs { get { return mobjs.Values; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public LuaParser()
{
// Syntax
whitespace = "\n \t\r\u00A0"; //mxd. non-breaking space is also space :)
specialtokens = "=\n";
skipregions = false; //mxd
ClearActors();
}
// Disposer
public void Dispose()
{
mobjs = null;
isdisposed = true;
}
#endregion
#region ================== Parsing
protected internal override void LogWarning(string message, int linenumber)
{
if (NoWarnings)
return;
base.LogWarning(message, linenumber);
}
// This parses the given Lua stream
// Returns false on errors
public override bool Parse(TextResourceData data, bool clearerrors)
{
//mxd. Already parsed?
if(!base.AddTextResource(data))
{
if(clearerrors) ClearError();
return true;
}
// Cannot process?
if(!base.Parse(data, clearerrors)) return false;
// Continue until at the end of the stream
while (SkipWhitespace(true))
{
// Read a token
string token = ReadToken();
if (!string.IsNullOrEmpty(token))
{
if (!token.StartsWith("mobjinfo[") || !token.EndsWith("]")) continue;
string objname = token.Substring(9).TrimEnd(new char[] { ']' });
SkipWhitespace(true);
token = ReadToken();
if (token != "=")
{
continue;
}
SkipWhitespace(true);
token = ReadToken();
if (token != "{")
{
ReportError("Invalid object definition, missing {");
return false;
}
// Read actor structure
ActorStructure mobj = new LuaMobjStructure(this, objname);
if (mobj.props.ContainsKey("doomednum"))
General.WriteLogLine("Mobj doomednum = " + mobj.props["doomednum"][0]);
if (this.HasError) return false;
mobjs[mobj.DoomEdNum] = mobj;
}
}
// Return true when no errors occurred
return (ErrorDescription == null);
}
#endregion
#region ================== Methods
public ActorStructure GetMobjByDoomEdNum(int doomednum)
{
return mobjs.ContainsKey(doomednum) ? mobjs[doomednum] : null;
}
internal void ClearActors()
{
// Initialize
mobjs = new Dictionary<int, ActorStructure>();
parsedlumps = new HashSet<string>(StringComparer.OrdinalIgnoreCase); //mxd
}
#endregion
}
}

View file

@ -22,6 +22,7 @@ using System.Globalization;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.Map;
#endregion
@ -60,7 +61,7 @@ namespace CodeImp.DoomBuilder.ZDoom
internal DecorateCategoryInfo catinfo;
// [ZZ] direct ArgumentInfos (from game configuration), or own ArgumentInfos (from props)
internal ArgumentInfo[] args = new ArgumentInfo[5];
internal ArgumentInfo[] args = new ArgumentInfo[Thing.NUM_ARGS];
// States
internal Dictionary<string, StateStructure> states;
@ -404,7 +405,7 @@ namespace CodeImp.DoomBuilder.ZDoom
/// </summary>
public void ParseCustomArguments()
{
for (int i = 0; i < 5; i++)
for (int i = 0; i < Thing.NUM_ARGS; i++)
{
if (HasProperty("$arg" + i))
args[i] = new ArgumentInfo(this, i);

View file

@ -240,11 +240,11 @@ namespace CodeImp.DoomBuilder.ZDoom
c = (char)datareader.ReadByte();
// Check if this is comment
if(c == '/')
if (c == '/' || c == '-') // SRB2 Lua allows -- as comment syntax
{
if(datastream.Position == datastream.Length) return false;
char c2 = (char)datareader.ReadByte();
if(c2 == '/')
if(c2 == c)
{
// Check if not a special comment with a token
if(datastream.Position == datastream.Length) return false;