From 5726421c9dc506375e6dccfd0812ea2f9701fcc3 Mon Sep 17 00:00:00 2001 From: biwa <6475593+biwa@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:36:31 +0200 Subject: [PATCH 1/2] GZDoom game configuration: removed support for path nodes, because the feature was removed from GZDoom --- Build/Configurations/Includes/ZDoom_misc.cfg | 20 --- .../Configurations/Includes/ZDoom_things.cfg | 35 ------ .../Scripts/Examples/GZDoom/ConnectNode.js | 114 ------------------ .../Scripts/Examples/GZDoom/NewNode.js | 87 ------------- 4 files changed, 256 deletions(-) delete mode 100644 Build/UDBScript/Scripts/Examples/GZDoom/ConnectNode.js delete mode 100644 Build/UDBScript/Scripts/Examples/GZDoom/NewNode.js diff --git a/Build/Configurations/Includes/ZDoom_misc.cfg b/Build/Configurations/Includes/ZDoom_misc.cfg index bc221de2..bb6165f1 100755 --- a/Build/Configurations/Includes/ZDoom_misc.cfg +++ b/Build/Configurations/Includes/ZDoom_misc.cfg @@ -222,26 +222,6 @@ secact_flagsrename } } -pathnode_flagsrename -{ - DoomMapSetIO - { - 8 = "Transition"; - } - - HexenMapSetIO - { - 8 = "Transition"; - 16384 = "Invert Size Check"; - } - - UniversalMapSetIO - { - ambush = "Transition"; - standing = "Invert Size Check"; - } -} - // Default sector brightness levels sectorbrightness { diff --git a/Build/Configurations/Includes/ZDoom_things.cfg b/Build/Configurations/Includes/ZDoom_things.cfg index 6d1c2617..32780fd1 100755 --- a/Build/Configurations/Includes/ZDoom_things.cfg +++ b/Build/Configurations/Includes/ZDoom_things.cfg @@ -1284,41 +1284,6 @@ zdoom } } - 9022 - { - title = "Path Node"; - sprite = "internal:PathFollower"; - class = "PathNode"; - flagsrename { include("ZDoom_misc.cfg", "pathnode_flagsrename") } - radius = 16; - height = 56; - arg0 - { - title = "TID 1"; - type = 14; - } - arg1 - { - title = "TID 2"; - type = 14; - } - arg2 - { - title = "TID 3"; - type = 14; - } - arg3 - { - title = "TID 4"; - type = 14; - } - arg4 - { - title = "TID 5"; - type = 14; - } - } - 9024 { title = "Patrol Point"; diff --git a/Build/UDBScript/Scripts/Examples/GZDoom/ConnectNode.js b/Build/UDBScript/Scripts/Examples/GZDoom/ConnectNode.js deleted file mode 100644 index 0a5c1ff9..00000000 --- a/Build/UDBScript/Scripts/Examples/GZDoom/ConnectNode.js +++ /dev/null @@ -1,114 +0,0 @@ -/// - -`#version 4`; - -`#name Connect Nodes`; - -`#description Connects nodes to/from the first selected PathNode.`; - -`#scriptoptions - -direction -{ - description = "Assignment Direction"; - default = 2; - type = 11; // Enum - enumvalues { - 0 = "To First"; - 1 = "From First"; - 2 = "Both"; - } -} - -doclear -{ - description = "Clear first thing's arguments"; - default = "False"; - type = 3; // Boolean -} - -`; - -let things = UDB.Map.getSelectedThings(); - -if(things.length < 2) - UDB.die('You have to select at least 2 things.'); - -let dir = UDB.ScriptOptions.direction; -let clr = UDB.ScriptOptions.doclear; - -let receiver = things[0]; - -let pos = 0; - -let i = 0; -if (clr) -{ - for (i; i < 5; i++) - receiver.args[i] = 0; -} - -things.forEach(n => -{ - if (n != receiver) - { - if (dir > 0) - { - // look for the tid first, make sure it's not already assigned. - let found = false; - for (i = 0; i < 5; i++) - { - if (n.args[i] == receiver.tag) - { - found = true; - break; - } - } - // Not found, so assign it. - if (!found) for (i = 0; i < 5; i++) - { - - if (n.args[i] == 0) - { - n.args[i] = receiver.tag; - break; - } - } - - } - if ((dir == 0 || dir == 2) && (pos < 5 && n.tag != 0)) - { - - if (clr) // No special management necessary. - { - receiver.args[pos] = n.tag; - pos++; - } - else // Look for a free spot. - { - let found = false; - for (i = 0; i < 5; i++) - { - if (receiver.args[i] == n.tag) - { - found = true; - break; - } - } - if (!found) - { - for (i = pos; i < 5; i++) - { - if (receiver.args[i] == 0) - { - receiver.args[i] = n.tag; - pos = i; - found = true; - break; - } - } - } - } - } - } -}); \ No newline at end of file diff --git a/Build/UDBScript/Scripts/Examples/GZDoom/NewNode.js b/Build/UDBScript/Scripts/Examples/GZDoom/NewNode.js deleted file mode 100644 index dabd96d8..00000000 --- a/Build/UDBScript/Scripts/Examples/GZDoom/NewNode.js +++ /dev/null @@ -1,87 +0,0 @@ -/// - -`#version 4`; - -`#name New Path Node`; - -`#description Creates a new node and assigns it a TID. If a Path Node is already selected, connects the two automatically.`; - -`#scriptoptions - -gridsnap -{ - description = "Grid Snap"; - default = 1; - type = 11; // enum - enumvalues { - 0 = "Disabled"; - 1 = "Enabled"; - } -} -`; - - -let mpos = UDB.Map.mousePosition; - -if(!mpos.isFinite()) - UDB.die('Mouse cursor must be inside the map'); - - -let nodetype = 9022; -let ntag = UDB.Map.getNewTag(); -let things = UDB.Map.getSelectedThings(); - -let node = UDB.Map.createThing(mpos, nodetype); -if (UDB.ScriptOptions.gridsnap > 0) - node.snapToGrid(); -node.tag = ntag; - -let i = 0; -let count = 0; -if(things.length > 0) -{ - things.forEach(n => - { - if (n.type == nodetype) - { - // For the new node, assign the path IDs to it, up to max arguments. - if (n.tag != 0 && count < 5) - { - node.args[count] = n.tag; - count++; - } - // For the selected nodes, check for an empty slot. - let pos = -1; - - for(i = 0; i < 5; i++) - { - if (n.args[i] != 0) - continue; - - pos = i; - break; - } - - - - // Check if the tag is already used first. - if (pos >= 0) - { - for (i = 0; i < 5; i++) - { - if (n.args[i] == ntag) - { - pos = -1; - break; - } - } - } - // Free slot, and unpresent. Set it in. - if (pos >= 0) - n.args[pos] = ntag; - } - }); -} - -UDB.Map.clearSelectedThings(); -node.selected = true; From df743740c0b91b152988f0d1b0a032c71da3cfd9 Mon Sep 17 00:00:00 2001 From: biwa <6475593+biwa@users.noreply.github.com> Date: Sun, 31 Mar 2024 22:56:41 +0200 Subject: [PATCH 2/2] ZScript parser: added support for "final" and "sealed" classes. Fixes #1033 --- Source/Core/ZDoom/ZScriptParser.cs | 99 +++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 10 deletions(-) diff --git a/Source/Core/ZDoom/ZScriptParser.cs b/Source/Core/ZDoom/ZScriptParser.cs index 2ffb113a..28c1cdf3 100755 --- a/Source/Core/ZDoom/ZScriptParser.cs +++ b/Source/Core/ZDoom/ZScriptParser.cs @@ -24,9 +24,11 @@ namespace CodeImp.DoomBuilder.ZDoom public bool IsMixin { get; internal set; } public bool IsExtension { get; internal set; } public List Extensions { get; internal set; } + public bool IsFinal { get; internal set; } + public List PermittedInheritedClassNames { get; internal set; } - // these are used for parsing and error reporting - public ZScriptParser Parser { get; internal set; } + // these are used for parsing and error reporting + public ZScriptParser Parser { get; internal set; } public Stream Stream { get; internal set; } public long Position { get; internal set; } public BinaryReader DataReader { get; internal set; } @@ -36,7 +38,7 @@ namespace CodeImp.DoomBuilder.ZDoom // textresourcepath public string TextResourcePath { get; internal set; } - internal ZScriptClassStructure(ZScriptParser parser, string classname, string replacesname, string parentname, bool ismixin, bool isextension, DecorateCategoryInfo region) + internal ZScriptClassStructure(ZScriptParser parser, string classname, DecorateCategoryInfo region, string replacesname=null, string parentname=null, bool ismixin=false, bool isextension=false, bool isfinal=false, List permittedinheritedclassnames=null) { Parser = parser; @@ -56,6 +58,8 @@ namespace CodeImp.DoomBuilder.ZDoom IsMixin = ismixin; IsExtension = isextension; Extensions = new List(); + IsFinal = isfinal; + PermittedInheritedClassNames = permittedinheritedclassnames == null ? new List() : new List(permittedinheritedclassnames); // for the "sealed" class modifier } internal void RestoreStreamData() @@ -100,6 +104,20 @@ namespace CodeImp.DoomBuilder.ZDoom Parser.ReportError("Fatal: Class \"" + _cname + "\" is trying to inherit from \"" + _pname + "\" which does not exist."); return false; } + + // Make sure that the parent class isn't "final" + if(_pstruct.IsFinal) + { + Parser.ReportError($"Fatal: Class \"{_cname}\" is trying to inherit from \"{_pname}\" which is final"); + return false; + } + + // Make sure we're allowed to inherit from parent class + if(_pstruct.PermittedInheritedClassNames.Count > 0 && !_pstruct.PermittedInheritedClassNames.Contains(_cname.ToLowerInvariant())) + { + Parser.ReportError($"Fatal: Class \"{_cname}\" is not allowed to inherit from \"{_pname}\""); + return false; + } } else _pstruct = null; } @@ -730,8 +748,10 @@ namespace CodeImp.DoomBuilder.ZDoom ZScriptToken tok_native = null; ZScriptToken tok_scope = null; ZScriptToken tok_version = null; + ZScriptToken tok_final = null; string[] class_scope_modifiers = new string[] { "clearscope", "ui", "play" }; string[] other_modifiers = new string[] { "abstract" }; + List permitted_inherited_class_names = new List(); while (true) { tokenizer.SkipWhitespace(); @@ -777,6 +797,20 @@ namespace CodeImp.DoomBuilder.ZDoom tok_native = token; } + else if(token.Value.ToLowerInvariant() == "final") + { + if(tok_final != null) + { + ReportError("Cannot have two final keywords"); + return false; + } + + tok_final = token; + } + else if(token.Value.ToLowerInvariant() == "sealed") + { + permitted_inherited_class_names = ParseSealed(); + } else if (Array.IndexOf(class_scope_modifiers, token.Value.ToLowerInvariant()) >= 0) { if (tok_scope != null) @@ -874,7 +908,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. 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, false, region); + ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, region, (tok_replacename != null) ? tok_replacename.Value : null, (tok_parentname != null) ? tok_parentname.Value : null, false, false, tok_final != null, permitted_inherited_class_names); cls.Position = cpos; string clskey = cls.ClassName.ToLowerInvariant(); if (allclasses.ContainsKey(clskey)) @@ -904,7 +938,7 @@ namespace CodeImp.DoomBuilder.ZDoom return false; } - ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, null, null, false, true, region); + ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, region, isextension: true); cls.Position = cpos; allclasses[clskey].Extensions.Add(cls); } @@ -912,7 +946,8 @@ namespace CodeImp.DoomBuilder.ZDoom { // 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 - ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, null, null, true, false, region); + // ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, null, null, true, false, false, null, region); + ZScriptClassStructure cls = new ZScriptClassStructure(this, tok_classname.Value, region, ismixin: true); cls.Position = cpos; string clskey = cls.ClassName.ToLowerInvariant(); if(mixinclasses.ContainsKey(clskey)) @@ -930,9 +965,9 @@ namespace CodeImp.DoomBuilder.ZDoom return true; } - // This parses the given decorate stream - // Returns false on errors - public override bool Parse(TextResourceData data, bool clearerrors) + // This parses the given decorate stream + // Returns false on errors + public override bool Parse(TextResourceData data, bool clearerrors) { if (clearerrors) LastClasses = new HashSet(); @@ -1136,7 +1171,51 @@ namespace CodeImp.DoomBuilder.ZDoom return true; } - public bool Finalize() + /// + /// Parses the class names after the "sealed" class modifier. + /// + /// A list of strings with the class names that can inherit this class + private List ParseSealed() + { + ZScriptToken token; + List permittedinheritedclassnames = new List(); + + tokenizer.SkipWhitespace(); + token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); + if(token == null || !token.IsValid) + { + ReportError("Expected (, got " + ((Object)token ?? "").ToString()); + return null; + } + + while(true) + { + tokenizer.SkipWhitespace(); + token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); + if(token == null || !token.IsValid) + { + ReportError("Expected class name, got " + ((Object)token ?? "").ToString()); + return null; + } + + permittedinheritedclassnames.Add(token.Value.ToLowerInvariant()); + + tokenizer.SkipWhitespace(); + token = tokenizer.ExpectToken(ZScriptTokenType.Comma, ZScriptTokenType.CloseParen); + if(token == null || !token.IsValid) + { + ReportError("Expected , or ), got " + ((Object)token ?? "").ToString()); + return null; + } + + if (token.Type == ZScriptTokenType.CloseParen) + break; + } + + return permittedinheritedclassnames; + } + + public bool Finalize() { ClearError();