mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-19 23:11:03 +00:00
Add Lua parser for custom objects
This commit is contained in:
parent
89f781bdc4
commit
f8e7bd3739
10 changed files with 436 additions and 7 deletions
|
@ -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" />
|
||||
|
|
|
@ -58,7 +58,9 @@ namespace CodeImp.DoomBuilder.Config
|
|||
KEYCONF,
|
||||
FONTDEFS,
|
||||
ZSCRIPT,
|
||||
DECALDEF
|
||||
DECALDEF,
|
||||
//SOC,
|
||||
LUA
|
||||
}
|
||||
|
||||
public class ScriptConfiguration : IComparable<ScriptConfiguration>
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
141
Source/Core/SRB2/LuaMobjStructure.cs
Normal file
141
Source/Core/SRB2/LuaMobjStructure.cs
Normal 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
|
||||
}
|
||||
}
|
176
Source/Core/SRB2/LuaParser.cs
Normal file
176
Source/Core/SRB2/LuaParser.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue