2017-01-16 11:18:46 +00:00
using CodeImp.DoomBuilder.Config ;
using CodeImp.DoomBuilder.Data ;
using CodeImp.DoomBuilder.Types ;
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Text ;
namespace CodeImp.DoomBuilder.ZDoom
{
public sealed class DecorateStateStructure : StateStructure
{
#region = = = = = = = = = = = = = = = = = = DECORATE State Structure parsing
2017-01-17 09:40:58 +00:00
internal DecorateStateStructure ( ActorStructure actor , ZDTextParser zdparser )
2017-01-16 11:18:46 +00:00
{
DecorateParser parser = ( DecorateParser ) zdparser ;
string lasttoken = "" ;
// Skip whitespace
while ( parser . SkipWhitespace ( true ) )
{
// Read first token
string token = parser . ReadToken ( ) . ToLowerInvariant ( ) ;
// One of the flow control statements?
if ( ( token = = "loop" ) | | ( token = = "stop" ) | | ( token = = "wait" ) | | ( token = = "fail" ) )
{
// Ignore flow control
2017-03-08 21:34:21 +00:00
// [ZZ] sometimes "fail" is a sprite name... (Skulltag, Zandronum)
// probably the same can happen to other single-word flow control keywords.
// check if next token is newline.
long cpos = parser . DataStream . Position ;
parser . SkipWhitespace ( false ) ;
string newline = parser . ReadToken ( ) ;
parser . DataStream . Position = cpos ;
if ( newline = = "\n" ) // this is actually a loop/stop/wait/fail directive and not a sprite name or something
{
lasttoken = token ;
continue ;
}
2017-01-16 11:18:46 +00:00
}
2017-03-08 21:34:21 +00:00
2017-01-16 11:18:46 +00:00
// Goto?
2017-03-08 21:34:21 +00:00
if ( token = = "goto" )
2017-01-16 11:18:46 +00:00
{
gotostate = new DecorateStateGoto ( actor , parser ) ;
if ( parser . HasError ) return ;
}
// Label?
else if ( token = = ":" )
{
// Rewind so that this label can be read again
if ( ! string . IsNullOrEmpty ( lasttoken ) )
parser . DataStream . Seek ( - ( lasttoken . Length + 1 ) , SeekOrigin . Current ) ;
// Done here
2017-01-18 06:35:26 +00:00
goto endofallthings ;
2017-01-16 11:18:46 +00:00
}
//mxd. Start of inner scope?
else if ( token = = "{" )
{
int bracelevel = 1 ;
while ( ! string . IsNullOrEmpty ( token ) & & bracelevel > 0 )
{
parser . SkipWhitespace ( false ) ;
token = parser . ReadToken ( ) ;
switch ( token )
{
case "{" : bracelevel + + ; break ;
case "}" : bracelevel - - ; break ;
}
}
}
// End of scope?
else if ( token = = "}" )
{
// Rewind so that this scope end can be read again
parser . DataStream . Seek ( - 1 , SeekOrigin . Current ) ;
// Done here
2017-01-18 06:35:26 +00:00
goto endofallthings ;
2017-01-16 11:18:46 +00:00
}
else
{
// First part of the sprite name
token = parser . StripTokenQuotes ( token ) ; //mxd. First part of the sprite name can be quoted
if ( string . IsNullOrEmpty ( token ) )
{
parser . ReportError ( "Expected sprite name" ) ;
return ;
}
// Frames of the sprite name
parser . SkipWhitespace ( true ) ;
string spriteframes = parser . StripTokenQuotes ( parser . ReadToken ( ) ) ; //mxd. Frames can be quoted
if ( string . IsNullOrEmpty ( spriteframes ) )
{
parser . ReportError ( "Expected sprite frame" ) ;
return ;
}
// Label?
if ( spriteframes = = ":" )
{
// Rewind so that this label can be read again
parser . DataStream . Seek ( - ( token . Length + 1 ) , SeekOrigin . Current ) ;
// Done here
2017-01-18 06:35:26 +00:00
goto endofallthings ;
2017-01-16 11:18:46 +00:00
}
// No first sprite yet?
FrameInfo info = new FrameInfo ( ) ; //mxd
if ( spriteframes . Length > 0 )
{
//mxd. I'm not even 50% sure the parser handles all bizzare cases without shifting sprite name / frame blocks,
// so let's log it as a warning, not an error...
if ( token . Length ! = 4 )
{
parser . LogWarning ( "Invalid sprite name \"" + token . ToUpperInvariant ( ) + "\". Sprite names must be exactly 4 characters long" ) ;
}
else
{
// Make the sprite name
string spritename = ( token + spriteframes [ 0 ] ) . ToUpperInvariant ( ) ;
// Ignore some odd ZDoom things
2017-01-17 04:58:52 +00:00
if ( /*!realspritename.StartsWith("TNT1") && */ ! spritename . StartsWith ( "----" ) & & ! spritename . Contains ( "#" ) ) // [ZZ] some actors have only TNT1 state and receive a random image because of this
2017-01-16 11:18:46 +00:00
{
info . Sprite = spritename ; //mxd
2017-03-04 00:13:39 +00:00
int duration = - 1 ;
parser . SkipWhitespace ( false ) ;
string durationstr = parser . ReadToken ( ) ;
if ( durationstr = = "-" )
durationstr + = parser . ReadToken ( ) ;
if ( string . IsNullOrEmpty ( durationstr ) | | durationstr = = "\n" )
{
parser . ReportError ( "Expected frame duration" ) ;
return ;
}
if ( ! int . TryParse ( durationstr . Trim ( ) , out duration ) )
parser . DataStream . Seek ( - ( durationstr . Length ) , SeekOrigin . Current ) ;
info . Duration = duration ;
2017-01-16 11:18:46 +00:00
sprites . Add ( info ) ;
}
}
}
// Continue until the end of the line
parser . SkipWhitespace ( false ) ;
string t = parser . ReadToken ( ) ;
while ( ! string . IsNullOrEmpty ( t ) & & t ! = "\n" )
{
//mxd. Bright keyword support...
if ( t = = "bright" )
{
info . Bright = true ;
}
//mxd. Light() expression support...
else if ( t = = "light" )
{
if ( ! parser . NextTokenIs ( "(" ) ) return ;
if ( ! parser . SkipWhitespace ( true ) )
{
parser . ReportError ( "Unexpected end of the structure" ) ;
return ;
}
info . LightName = parser . StripTokenQuotes ( parser . ReadToken ( ) ) ;
if ( string . IsNullOrEmpty ( info . LightName ) )
{
parser . ReportError ( "Expected dynamic light name" ) ;
return ;
}
if ( ! parser . SkipWhitespace ( true ) )
{
parser . ReportError ( "Unexpected end of the structure" ) ;
return ;
}
2017-01-18 06:35:26 +00:00
if ( ! parser . NextTokenIs ( ")" ) )
{
parser . ReportError ( "Expected closing parenthesis in Light()" ) ;
return ;
}
2017-01-16 11:18:46 +00:00
}
//mxd. Inner scope start. Step back and reparse using parent loop
else if ( t = = "{" )
{
// Rewind so that this scope end can be read again
parser . DataStream . Seek ( - 1 , SeekOrigin . Current ) ;
// Break out of this loop
break ;
}
//mxd. Function params start (those can span multiple lines)
else if ( t = = "(" )
{
int bracelevel = 1 ;
while ( ! string . IsNullOrEmpty ( token ) & & bracelevel > 0 )
{
parser . SkipWhitespace ( true ) ;
token = parser . ReadToken ( ) ;
switch ( token )
{
case "(" : bracelevel + + ; break ;
case ")" : bracelevel - - ; break ;
}
}
}
//mxd. Because stuff like this is also valid: "Actor Oneliner { States { Spawn: WOOT A 1 A_FadeOut(0.1) Loop }}"
else if ( t = = "}" )
{
// Rewind so that this scope end can be read again
parser . DataStream . Seek ( - 1 , SeekOrigin . Current ) ;
// Done here
2017-01-18 06:35:26 +00:00
goto endofallthings ;
2017-01-16 11:18:46 +00:00
}
// Read next token
parser . SkipWhitespace ( false ) ;
t = parser . ReadToken ( ) . ToLowerInvariant ( ) ;
}
}
lasttoken = token ;
}
2017-01-18 06:35:26 +00:00
// return
endofallthings :
TrimLeft ( ) ;
2017-01-16 11:18:46 +00:00
}
#endregion
}
}