ZScript: added support for extending classes. Resolves #597

This commit is contained in:
biwa 2021-08-21 20:42:06 +02:00
parent be84f19da1
commit 45dc796dca

View file

@ -22,6 +22,8 @@ namespace CodeImp.DoomBuilder.ZDoom
public ZScriptActorStructure Actor { get; internal set; } public ZScriptActorStructure Actor { get; internal set; }
internal DecorateCategoryInfo Region; internal DecorateCategoryInfo Region;
public bool IsMixin { get; internal set; } public bool IsMixin { get; internal set; }
public bool IsExtension { get; internal set; }
public List<ZScriptClassStructure> Extensions { get; internal set; }
// these are used for parsing and error reporting // these are used for parsing and error reporting
public ZScriptParser Parser { get; internal set; } public ZScriptParser Parser { get; internal set; }
@ -34,7 +36,7 @@ namespace CodeImp.DoomBuilder.ZDoom
// textresourcepath // textresourcepath
public string TextResourcePath { get; internal set; } public string TextResourcePath { get; internal set; }
internal ZScriptClassStructure(ZScriptParser parser, string classname, string replacesname, string parentname, bool ismixin, DecorateCategoryInfo region) internal ZScriptClassStructure(ZScriptParser parser, string classname, string replacesname, string parentname, bool ismixin, bool isextension, DecorateCategoryInfo region)
{ {
Parser = parser; Parser = parser;
@ -52,6 +54,8 @@ namespace CodeImp.DoomBuilder.ZDoom
Region = region; Region = region;
IsMixin = ismixin; IsMixin = ismixin;
IsExtension = isextension;
Extensions = new List<ZScriptClassStructure>();
} }
internal void RestoreStreamData() internal void RestoreStreamData()
@ -97,7 +101,7 @@ namespace CodeImp.DoomBuilder.ZDoom
if (ReplacementName != null) log_inherits += ((log_inherits.Length > 0) ? ", " : "") + "replaces " + ReplacementName; if (ReplacementName != null) log_inherits += ((log_inherits.Length > 0) ? ", " : "") + "replaces " + ReplacementName;
if (log_inherits.Length > 0) log_inherits = " (" + log_inherits + ")"; if (log_inherits.Length > 0) log_inherits = " (" + log_inherits + ")";
if (isactor || IsMixin) if (isactor || IsMixin || IsExtension)
{ {
Actor = new ZScriptActorStructure(Parser, Region, ClassName, ReplacementName, ParentName); Actor = new ZScriptActorStructure(Parser, Region, ClassName, ReplacementName, ParentName);
if (Parser.HasError) if (Parser.HasError)
@ -106,26 +110,29 @@ namespace CodeImp.DoomBuilder.ZDoom
return false; return false;
} }
// check actor replacement. if (!IsExtension)
Parser.archivedactors[Actor.ClassName.ToLowerInvariant()] = Actor; {
Parser.realarchivedactors[Actor.ClassName.ToLowerInvariant()] = Actor; // check actor replacement.
if (Actor.CheckActorSupported()) Parser.archivedactors[Actor.ClassName.ToLowerInvariant()] = Actor;
Parser.actors[Actor.ClassName.ToLowerInvariant()] = Actor; Parser.realarchivedactors[Actor.ClassName.ToLowerInvariant()] = Actor;
if (Actor.CheckActorSupported())
Parser.actors[Actor.ClassName.ToLowerInvariant()] = Actor;
// Replace an actor? // Replace an actor?
if (Actor.ReplacesClass != null) if (Actor.ReplacesClass != null)
{ {
if (Parser.GetArchivedActorByName(Actor.ReplacesClass, false) != null) if (Parser.GetArchivedActorByName(Actor.ReplacesClass, false) != null)
Parser.archivedactors[Actor.ReplacesClass.ToLowerInvariant()] = Actor; Parser.archivedactors[Actor.ReplacesClass.ToLowerInvariant()] = Actor;
else else
Parser.LogWarning("Unable to find \"" + Actor.ReplacesClass + "\" class to replace, while parsing \"" + Actor.ClassName + "\""); Parser.LogWarning("Unable to find \"" + Actor.ReplacesClass + "\" class to replace, while parsing \"" + Actor.ClassName + "\"");
if (Actor.CheckActorSupported() && Parser.GetActorByName(Actor.ReplacesClass) != null) if (Actor.CheckActorSupported() && Parser.GetActorByName(Actor.ReplacesClass) != null)
Parser.actors[Actor.ReplacesClass.ToLowerInvariant()] = Actor; Parser.actors[Actor.ReplacesClass.ToLowerInvariant()] = Actor;
} }
//mxd. Add to current text resource //mxd. Add to current text resource
if (!Parser.scriptresources[TextResourcePath].Entries.Contains(Actor.ClassName)) Parser.scriptresources[TextResourcePath].Entries.Add(Actor.ClassName); if (!Parser.scriptresources[TextResourcePath].Entries.Contains(Actor.ClassName)) Parser.scriptresources[TextResourcePath].Entries.Add(Actor.ClassName);
}
} }
//Parser.LogWarning(string.Format("Parsed {0}class {1}{2}", isactor?"actor ":"", ClassName, log_inherits)); //Parser.LogWarning(string.Format("Parsed {0}class {1}{2}", isactor?"actor ":"", ClassName, log_inherits));
@ -750,7 +757,7 @@ namespace CodeImp.DoomBuilder.ZDoom
// now if we are a class, and we inherit actor, parse this entry as an actor. don't process extensions. // now if we are a class, and we inherit actor, parse this entry as an actor. don't process extensions.
if (!isstruct && !extend && !mixin) if (!isstruct && !extend && !mixin)
{ {
ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, (tok_replacename != null) ? tok_replacename.Value : null, (tok_parentname != null) ? tok_parentname.Value : null, false, region); ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, (tok_replacename != null) ? tok_replacename.Value : null, (tok_parentname != null) ? tok_parentname.Value : null, false, false, region);
cls.Position = cpos; cls.Position = cpos;
string clskey = cls.ClassName.ToLowerInvariant(); string clskey = cls.ClassName.ToLowerInvariant();
if (allclasses.ContainsKey(clskey)) if (allclasses.ContainsKey(clskey))
@ -762,11 +769,32 @@ namespace CodeImp.DoomBuilder.ZDoom
allclasses.Add(cls.ClassName.ToLowerInvariant(), cls); allclasses.Add(cls.ClassName.ToLowerInvariant(), cls);
allclasseslist.Add(cls); allclasseslist.Add(cls);
} }
else if(!isstruct && extend)
{
string clskey = tok_classname.Value.ToLowerInvariant();
if(!allclasses.ContainsKey(clskey))
{
ReportError("Trying to extend class " + tok_classname.Value + " before it was defined");
return false;
}
// GZDoom doesn't allow extending classes that are from another archive
if(!allclasses[clskey].DataLocation.Equals(datalocation))
{
ReportError("Trying to extend class " + tok_classname.Value + " that's not part of data location \"" + datalocation + "\", but \"" + allclasses[clskey].DataLocation + "\"");
return false;
}
ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, null, null, false, true, region);
cls.Position = cpos;
allclasses[clskey].Extensions.Add(cls);
}
else if (mixin) else if (mixin)
{ {
// This is a bit ugly. We're treating mixin classes as actors, even though they aren't. But otherwise the parser // This is a bit ugly. We're treating mixin classes as actors, even though they aren't. But otherwise the parser
// doesn't parse all the actor info we need // doesn't parse all the actor info we need
ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, null, null, true, region); ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, null, null, true, false, region);
cls.Position = cpos; cls.Position = cpos;
string clskey = cls.ClassName.ToLowerInvariant(); string clskey = cls.ClassName.ToLowerInvariant();
if(mixinclasses.ContainsKey(clskey)) if(mixinclasses.ContainsKey(clskey))
@ -987,6 +1015,11 @@ namespace CodeImp.DoomBuilder.ZDoom
{ {
if (!cls.Process()) if (!cls.Process())
return false; return false;
// Process extensions
foreach (ZScriptClassStructure extension in cls.Extensions)
if (!extension.Process())
return false;
} }
// Parse mixin class data // Parse mixin class data
@ -1113,6 +1146,40 @@ namespace CodeImp.DoomBuilder.ZDoom
actor.flags["solid"] = true; actor.flags["solid"] = true;
} }
} }
// Extensions. https://zdoom.org/wiki/ZScript_classes#Extending_Classes
if (cls.Extensions.Count > 0)
{
foreach(ZScriptClassStructure extension in cls.Extensions)
{
ActorStructure extenseionactor = extension.Actor;
if (extenseionactor == null)
continue;
// States
if (extenseionactor.states.ContainsKey("spawn"))
actor.states["spawn"] = extenseionactor.GetState("spawn");
// Properties
if (extenseionactor.props.ContainsKey("height"))
actor.props["height"] = new List<string>(extenseionactor.props["height"]);
if (extenseionactor.props.ContainsKey("radius"))
actor.props["radius"] = new List<string>(extenseionactor.props["radius"]);
// Flags
if (extenseionactor.flags.ContainsKey("spawnceiling"))
actor.flags["spawnceiling"] = true;
if (extenseionactor.flags.ContainsKey("solid"))
actor.flags["solid"] = true;
// user_ variables
foreach (string uservarname in extenseionactor.uservars.Keys)
actor.uservars[uservarname] = extenseionactor.uservars[uservarname];
}
}
} }
} }