internal ZScriptStateStructure(ActorStructure actor, ZDTextParser zdparser) { ZScriptParser parser = (ZScriptParser)zdparser; Stream stream = parser.DataStream; ZScriptTokenizer tokenizer = new ZScriptTokenizer(parser.DataReader); parser.tokenizer = tokenizer; // todo: parse stuff // string[] control_keywords = new string[] { "goto", "loop", "wait", "fail", "stop" }; while (true) { // expect identifier or string. // if it's an identifier, it can be goto/loop/wait/fail/stop. // if it's a string, it's always a sprite name. tokenizer.SkipWhitespace(); long cpos = stream.Position; ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.String, ZScriptTokenType.CloseCurly); if (token == null || !token.IsValid) { ZScriptToken _token = TryReadSprite(parser, stream, tokenizer); if (_token == null) { parser.ReportError("Expected identifier or string, got " + ((Object)token ?? "<null>").ToString()); return; } token = _token; } if (token.Type == ZScriptTokenType.CloseCurly) { stream.Position--; break; // done } else if (token.Type == ZScriptTokenType.Identifier) { string s_keyword = token.Value.ToLowerInvariant(); if (control_keywords.Contains(s_keyword)) { if (s_keyword == "goto") // just use decorate goto here. should work. but check for semicolon! { gotostate = new ZScriptStateGoto(actor, parser); parser.tokenizer = tokenizer; } //parser.LogWarning(string.Format("keyword {0}", s_keyword)); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected ;, got " + ((Object)token ?? "<null>").ToString()); return; } continue; } } // make sure it's not a label of the next state. if it is, break out. long cpos2 = stream.Position; // local rewind point ZScriptToken token2 = tokenizer.ExpectToken(ZScriptTokenType.Colon, ZScriptTokenType.Dot); bool nextstate = (token2 != null && token2.IsValid); stream.Position = cpos2; // rewind to before state label read if (nextstate) { stream.Position = cpos; break; } // it's a frame definition. read it. string spritename = token.Value.ToLowerInvariant(); if (spritename.Length != 4) { parser.ReportError("Sprite name should be exactly 4 characters long (got " + spritename + ")"); return; } tokenizer.SkipWhitespace(); token = TryReadSprite(parser, stream, tokenizer); if (token == null) { parser.ReportError("Expected sprite frame(s)"); return; } string spriteframes = token.Value; //parser.LogWarning(string.Format("sprite {0} {1}", spritename, spriteframes)); // duration int duration; tokenizer.SkipWhitespace(); // this can be a function call, or a constant. token = tokenizer.ExpectToken(ZScriptTokenType.Identifier); if (token != null && token.IsValid) { duration = -1; tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); if (token != null && token.IsValid) { List <ZScriptToken> tokens = parser.ParseExpression(true); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } } } else { if (!parser.ParseInteger(out duration)) { return; } } // now, it can also contain BRIGHT, LIGHT(), OFFSET() string[] allspecials = new string[] { "bright", "light", "offset", "fast", "slow", "nodelay", "canraise" }; HashSet <string> specials = new HashSet <string>(); // maybe something else. I don't know. FrameInfo info = new FrameInfo(); // Make the sprite name string realspritename = (spritename + spriteframes[0]).ToUpperInvariant(); // Ignore some odd ZDoom things if (/*!realspritename.StartsWith("TNT1") && */ !realspritename.StartsWith("----") && !realspritename.Contains("#")) // [ZZ] some actors have only TNT1 state and receive a random image because of this { info.Sprite = realspritename; //mxd info.Duration = duration; sprites.Add(info); } while (true) { tokenizer.SkipWhitespace(); cpos2 = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Identifier, ZScriptTokenType.Semicolon, ZScriptTokenType.OpenCurly); if (token == null || !token.IsValid) { parser.ReportError("Expected identifier, ;, or {, got " + ((Object)token ?? "<null>").ToString()); return; } // if it's opencurly, it means that everything else is an anonymous block. // skip/parse that. // if it's semicolon, it means end of the frame. // if it's BRIGHT, LIGHT() or OFFSET(), it should be processed. // if it's something else (but identifier), then it's an action function call, process it. if (token.Type == ZScriptTokenType.OpenCurly) { stream.Position--; if (!parser.SkipBlock()) { return; } break; // this block is done } else if (token.Type == ZScriptTokenType.Semicolon) { break; // done } else // identifier { string special = token.Value.ToLowerInvariant(); if (allspecials.Contains(special)) { if (specials.Contains(special)) { parser.ReportError("'" + special + "' cannot be used twice"); return; } specials.Add(special); if (special == "bright") { info.Bright = true; } else if (special == "light" || special == "offset") { tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); if (token == null || !token.IsValid) { parser.ReportError("Expected (, got " + ((Object)token ?? "<null>").ToString()); return; } List <ZScriptToken> tokens = parser.ParseExpression(true); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } // parse the light expression. if (special == "light") { if (tokens.Count != 1 || (tokens[0].Type != ZScriptTokenType.String && tokens[0].Type != ZScriptTokenType.Identifier)) { parser.ReportError("Light() special takes one string argument"); return; } info.LightName = tokens[0].Value; } } } else { // stream.Position = cpos2; string actionfunction = parser.ParseDottedIdentifier(); //parser.LogWarning("actionfunction = " + actionfunction); if (actionfunction == null) { return; } // tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpenParen); if (token != null && token.IsValid) { List <ZScriptToken> tokens = parser.ParseExpression(true); tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.CloseParen); if (token == null || !token.IsValid) { parser.ReportError("Expected ), got " + ((Object)token ?? "<null>").ToString()); return; } // possibly do something with the arguments? not now though. } // expect semicolon and break. tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected ;, got " + ((Object)token ?? "<null>").ToString()); return; } break; } // if not special } // if identifier } // frame parsing loop (inner) } // state parsing loop (outer) TrimLeft(); }
private bool ParseDefaultBlock() { tokenizer.SkipWhitespace(); ZScriptToken token = tokenizer.ExpectToken(ZScriptTokenType.OpenCurly); if (token == null || !token.IsValid) { parser.ReportError("Expected {, got " + ((Object)token ?? "<null>").ToString()); return(false); } ZScriptTokenType[] whitespacetypes = new ZScriptTokenType[] { ZScriptTokenType.Newline, ZScriptTokenType.Whitespace, ZScriptTokenType.BlockComment, ZScriptTokenType.LineComment }; // todo parse defaults block while (true) { long cpos = stream.Position; token = tokenizer.ExpectToken(ZScriptTokenType.Whitespace, ZScriptTokenType.BlockComment, ZScriptTokenType.Newline, ZScriptTokenType.LineComment, ZScriptTokenType.OpAdd, ZScriptTokenType.OpSubtract, ZScriptTokenType.Identifier, ZScriptTokenType.CloseCurly, ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected comment, flag, property, or }, got " + ((Object)token ?? "<null>").ToString()); return(false); } //if (ClassName == "Enforcer") // parser.LogWarning(token.ToString()); if (token.Type == ZScriptTokenType.CloseCurly) { break; } switch (token.Type) { case ZScriptTokenType.Whitespace: case ZScriptTokenType.BlockComment: case ZScriptTokenType.Newline: break; case ZScriptTokenType.LineComment: ParseGZDBComment(props, token.Value); break; // flag definition (+/-) case ZScriptTokenType.OpAdd: case ZScriptTokenType.OpSubtract: { bool flagset = (token.Type == ZScriptTokenType.OpAdd); string flagname = parser.ParseDottedIdentifier(); if (flagname == null) { return(false); } //parser.LogWarning(string.Format("{0}{1}", (flagset ? '+' : '-'), flagname)); // set flag flags[flagname] = flagset; break; } // property or combo definition case ZScriptTokenType.Identifier: { stream.Position = cpos; string propertyname = parser.ParseDottedIdentifier(); if (propertyname == null) { return(false); } List <string> propertyvalues = new List <string>(); // read in property values, until semicolon reached while (true) { tokenizer.SkipWhitespace(); List <ZScriptToken> expr = parser.ParseExpression(); string exprstring = ZScriptTokenizer.TokensToString(expr); token = tokenizer.ExpectToken(ZScriptTokenType.Comma, ZScriptTokenType.Semicolon); if (token == null || !token.IsValid) { parser.ReportError("Expected comma or ;, got " + ((Object)token ?? "<null>").ToString()); return(false); } propertyvalues.Add(exprstring); if (token.Type == ZScriptTokenType.Semicolon) { break; } } //parser.LogWarning(string.Format("{0} = [{1}]", propertyname, string.Join(", ", propertyvalues.ToArray()))); // set property // translate "scale" to x and y scale if (propertyname == "scale") { props["xscale"] = props["yscale"] = propertyvalues; } else { props[propertyname] = propertyvalues; } break; } } } return(true); }
internal ZScriptStateGoto(ActorStructure actor, ZDTextParser zdparser) { // goto syntax that is accepted by GZDB is [classname::]statename[+offset] ZScriptParser parser = (ZScriptParser)zdparser; Stream stream = parser.DataStream; ZScriptTokenizer tokenizer = new ZScriptTokenizer(parser.DataReader); parser.tokenizer = tokenizer; tokenizer.SkipWhitespace(); string firsttarget = parser.ParseDottedIdentifier(); if (firsttarget == null) { return; } ZScriptToken token; string secondtarget = null; int offset = 0; tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.DoubleColon); if (token != null && token.IsValid) { secondtarget = parser.ParseDottedIdentifier(); if (secondtarget == null) { return; } } tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.OpAdd); if (token != null && token.IsValid) { tokenizer.SkipWhitespace(); token = tokenizer.ExpectToken(ZScriptTokenType.Integer); if (token == null || !token.IsValid) { parser.ReportError("Expected state offset, got " + ((Object)token ?? "<null>").ToString()); return; } offset = token.ValueInt; } // Check if we don't have the class specified if (string.IsNullOrEmpty(secondtarget)) { // First target is the state to go to classname = actor.ClassName; statename = firsttarget.ToLowerInvariant().Trim(); } else { // First target is the base class to use // Second target is the state to go to classname = firsttarget.ToLowerInvariant().Trim(); statename = secondtarget.ToLowerInvariant().Trim(); } spriteoffset = offset; if ((classname == "super") && (actor.BaseClass != null)) { classname = actor.BaseClass.ClassName; } }