2009-04-19 18:07:22 +00:00
#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
2014-02-26 14:11:06 +00:00
using System ;
2009-04-19 18:07:22 +00:00
using System.Collections.Generic ;
using System.Globalization ;
2013-04-19 11:35:56 +00:00
using CodeImp.DoomBuilder.Config ;
Fixed, Visual mode: in some cases ceiling glow effect was interfering with Transfer Brightness effect resulting in incorrectly lit sidedef geometry.
Fixed, Visual mode: UDMF sidedef brightness should be ignored when a wall section is affected by Transfer Brightness effect.
Fixed, Visual mode: any custom fog should be rendered regardless of sector brightness.
Fixed, Visual mode: "fogdensity" and "outsidefogdensity" MAPINFO values were processed incorrectly.
Fixed, Visual mode: in some cases Things were rendered twice during a render pass.
Fixed, Visual mode: floor glow effect should affect thing brightness only when applied to floor of the sector thing is in.
Fixed, TEXTURES parser: TEXTURES group was named incorrectly in the Textures Browser window when parsed from a WAD file.
Fixed, MAPINFO, GLDEFS, DECORATE parsers: "//$GZDB_SKIP" special comment was processed incorrectly.
Fixed, MAPINFO parser: "fogdensity" and "outsidefogdensity" properties are now initialized using GZDoom default value (255) instead of 0.
2016-01-25 13:42:53 +00:00
using CodeImp.DoomBuilder.Data ;
2016-02-08 21:51:03 +00:00
using CodeImp.DoomBuilder.Types ;
2009-04-19 18:07:22 +00:00
#endregion
namespace CodeImp.DoomBuilder.ZDoom
{
public sealed class ActorStructure
{
#region = = = = = = = = = = = = = = = = = = Constants
2014-01-03 10:33:45 +00:00
private readonly string [ ] SPRITE_POSTFIXES = new [ ] { "2C8" , "2D8" , "2A8" , "2B8" , "1C1" , "1D1" , "1A1" , "1B1" , "A2" , "A1" , "A0" , "2" , "1" , "0" } ;
internal const string ACTOR_CLASS_SPECIAL_TOKENS = ":{}\n;," ; //mxd
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
// Declaration
2015-03-17 12:28:42 +00:00
private readonly string classname ;
private readonly string inheritclass ;
private readonly string replaceclass ;
2015-04-14 11:33:57 +00:00
private int doomednum = - 1 ;
2009-04-19 18:07:22 +00:00
2010-08-17 10:02:07 +00:00
// Inheriting
private ActorStructure baseclass ;
2015-03-17 12:28:42 +00:00
private readonly bool skipsuper ;
2010-08-17 10:02:07 +00:00
2009-04-19 18:07:22 +00:00
// Flags
private Dictionary < string , bool > flags ;
// Properties
2009-08-15 08:41:43 +00:00
private Dictionary < string , List < string > > props ;
2016-02-08 21:51:03 +00:00
private readonly Dictionary < string , UniversalType > uservars ; //mxd
2009-04-19 18:07:22 +00:00
// States
private Dictionary < string , StateStructure > states ;
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
public Dictionary < string , bool > Flags { get { return flags ; } }
2009-08-15 08:41:43 +00:00
public Dictionary < string , List < string > > Properties { get { return props ; } }
2009-04-19 18:07:22 +00:00
public string ClassName { get { return classname ; } }
public string InheritsClass { get { return inheritclass ; } }
public string ReplacesClass { get { return replaceclass ; } }
2010-08-17 10:02:07 +00:00
public ActorStructure BaseClass { get { return baseclass ; } }
2015-04-14 11:33:57 +00:00
internal int DoomEdNum { get { return doomednum ; } set { doomednum = value ; } }
2016-02-08 21:51:03 +00:00
public Dictionary < string , UniversalType > UserVars { get { return uservars ; } } //mxd
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
internal ActorStructure ( DecorateParser parser )
{
// Initialize
2016-02-08 21:51:03 +00:00
flags = new Dictionary < string , bool > ( StringComparer . OrdinalIgnoreCase ) ;
props = new Dictionary < string , List < string > > ( StringComparer . OrdinalIgnoreCase ) ;
states = new Dictionary < string , StateStructure > ( StringComparer . OrdinalIgnoreCase ) ;
uservars = new Dictionary < string , UniversalType > ( StringComparer . OrdinalIgnoreCase ) ; //mxd
2013-12-18 09:11:04 +00:00
bool done = false ; //mxd
2010-08-17 10:02:07 +00:00
2009-08-15 08:41:43 +00:00
// Always define a game property, but default to 0 values
props [ "game" ] = new List < string > ( ) ;
2009-04-19 18:07:22 +00:00
inheritclass = "actor" ;
replaceclass = null ;
2010-08-17 10:02:07 +00:00
baseclass = null ;
skipsuper = false ;
2009-04-19 18:07:22 +00:00
// First next token is the class name
parser . SkipWhitespace ( true ) ;
2014-01-03 10:33:45 +00:00
classname = parser . StripTokenQuotes ( parser . ReadToken ( ACTOR_CLASS_SPECIAL_TOKENS ) ) ;
2009-04-19 18:07:22 +00:00
if ( string . IsNullOrEmpty ( classname ) )
{
parser . ReportError ( "Expected actor class name" ) ;
return ;
}
// Parse tokens before entering the actor scope
while ( parser . SkipWhitespace ( true ) )
{
string token = parser . ReadToken ( ) ;
if ( ! string . IsNullOrEmpty ( token ) )
{
token = token . ToLowerInvariant ( ) ;
2013-12-18 09:11:04 +00:00
2015-12-17 10:07:28 +00:00
switch ( token )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
case ":" :
// The next token must be the class to inherit from
parser . SkipWhitespace ( true ) ;
inheritclass = parser . StripTokenQuotes ( parser . ReadToken ( ) ) ;
Added, Sector Edit window, UDMF: added UI for sector damage-realted properties.
Added, DECORATE parser: damage types are now parsed.
Added: the editor now reports duplicate textures/flats/patches/sprites/colormaps/voxels in the loaded wads.
Added, all text parsers: added #region/#endregion support.
Added TERRAIN parser.
Added, Script Editor: added special handling for DECORATE special comments.
Added, Sector Edit window, UDMF: Soundsequence value was setup incorrectly when showing the window for multiple sectors with mixed Soundsequence value.
Fixed, Map Options window: "Strictly load patches between P_START and P_END" was not applied when applying the changes.
Fixed, MAPINFO parser: MapInfo should be treated as defined when a map MAPINFO block corresponding to current map is encountered even if it doesn't define any properties recognized by the editor.
Fixed, all text parsers: in some cases error line was calculated incorrectly when reporting an error detected by a text parser.
Cosmetic: changed ' to " in the rest of Error and Warning messages.
Internal: added text resource tracking.
Updated ZDoom_DECORATE.cfg.
Updated documentation ("Game Configuration - Basic Settings" page).
2016-02-22 12:33:19 +00:00
if ( string . IsNullOrEmpty ( inheritclass ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
parser . ReportError ( "Expected class name to inherit from" ) ;
return ;
}
2015-09-28 19:49:15 +00:00
2009-04-19 18:07:22 +00:00
// Find the actor to inherit from
2010-08-17 10:02:07 +00:00
baseclass = parser . GetArchivedActorByName ( inheritclass ) ;
2013-12-18 09:11:04 +00:00
break ;
case "replaces" :
// The next token must be the class to replace
parser . SkipWhitespace ( true ) ;
replaceclass = parser . StripTokenQuotes ( parser . ReadToken ( ) ) ;
Added, Sector Edit window, UDMF: added UI for sector damage-realted properties.
Added, DECORATE parser: damage types are now parsed.
Added: the editor now reports duplicate textures/flats/patches/sprites/colormaps/voxels in the loaded wads.
Added, all text parsers: added #region/#endregion support.
Added TERRAIN parser.
Added, Script Editor: added special handling for DECORATE special comments.
Added, Sector Edit window, UDMF: Soundsequence value was setup incorrectly when showing the window for multiple sectors with mixed Soundsequence value.
Fixed, Map Options window: "Strictly load patches between P_START and P_END" was not applied when applying the changes.
Fixed, MAPINFO parser: MapInfo should be treated as defined when a map MAPINFO block corresponding to current map is encountered even if it doesn't define any properties recognized by the editor.
Fixed, all text parsers: in some cases error line was calculated incorrectly when reporting an error detected by a text parser.
Cosmetic: changed ' to " in the rest of Error and Warning messages.
Internal: added text resource tracking.
Updated ZDoom_DECORATE.cfg.
Updated documentation ("Game Configuration - Basic Settings" page).
2016-02-22 12:33:19 +00:00
if ( string . IsNullOrEmpty ( replaceclass ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
parser . ReportError ( "Expected class name to replace" ) ;
return ;
}
break ;
case "native" :
// Igore this token
break ;
case "{" :
// Actor scope begins here,
// break out of this parse loop
done = true ;
break ;
case "-" :
// This could be a negative doomednum (but our parser sees the - as separate token)
// So read whatever is after this token and ignore it (negative doomednum indicates no doomednum)
parser . ReadToken ( ) ;
break ;
default :
2014-10-23 12:48:31 +00:00
//mxd. Property begins with $? Then the whole line is a single value
if ( token . StartsWith ( "$" ) )
{
// This is for editor-only properties such as $sprite and $category
props [ token ] = new List < string > { ( parser . SkipWhitespace ( false ) ? parser . ReadLine ( ) : "" ) } ;
2016-02-28 21:21:50 +00:00
continue ;
}
if ( ! int . TryParse ( token , NumberStyles . Integer , CultureInfo . InvariantCulture , out doomednum ) ) // Check if numeric
2014-10-23 12:48:31 +00:00
{
2013-12-18 09:11:04 +00:00
// Not numeric!
2016-02-28 21:21:50 +00:00
parser . ReportError ( "Expected editor number or start of actor scope while parsing \"" + classname + "\"" ) ;
return ;
}
//mxd. Range check
if ( ( doomednum < General . Map . FormatInterface . MinThingType ) | | ( doomednum > General . Map . FormatInterface . MaxThingType ) )
{
// Out of bounds!
parser . ReportError ( "\"" + classname + "\" actor's editor number must be between "
+ General . Map . FormatInterface . MinThingType + " and " + General . Map . FormatInterface . MaxThingType ) ;
2013-12-18 09:11:04 +00:00
return ;
}
break ;
2009-04-19 18:07:22 +00:00
}
2013-12-18 09:11:04 +00:00
2015-12-17 10:07:28 +00:00
if ( done ) break ; //mxd
2009-04-19 18:07:22 +00:00
}
else
{
parser . ReportError ( "Unexpected end of structure" ) ;
return ;
}
}
// Now parse the contents of actor structure
string previoustoken = "" ;
2013-12-18 09:11:04 +00:00
done = false ; //mxd
2009-04-19 18:07:22 +00:00
while ( parser . SkipWhitespace ( true ) )
{
string token = parser . ReadToken ( ) ;
token = token . ToLowerInvariant ( ) ;
2009-08-15 08:41:43 +00:00
2015-12-17 10:07:28 +00:00
switch ( token )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
case "+" :
case "-" :
// Next token is a flag (option) to set or remove
bool flagvalue = ( token = = "+" ) ;
parser . SkipWhitespace ( true ) ;
string flagname = parser . ReadToken ( ) ;
2015-12-17 10:07:28 +00:00
if ( ! string . IsNullOrEmpty ( flagname ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
// Add the flag with its value
flagname = flagname . ToLowerInvariant ( ) ;
flags [ flagname ] = flagvalue ;
2014-12-03 23:15:26 +00:00
}
else
{
2013-12-18 09:11:04 +00:00
parser . ReportError ( "Expected flag name" ) ;
return ;
}
break ;
case "action" :
case "native" :
// We don't need this, ignore up to the first next ;
2015-12-17 10:07:28 +00:00
while ( parser . SkipWhitespace ( true ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
string t = parser . ReadToken ( ) ;
2015-12-17 10:07:28 +00:00
if ( string . IsNullOrEmpty ( t ) | | t = = ";" ) break ;
2013-12-18 09:11:04 +00:00
}
break ;
case "skip_super" :
skipsuper = true ;
break ;
case "states" :
// Now parse actor states until we reach the end of the states structure
2015-12-17 10:07:28 +00:00
while ( parser . SkipWhitespace ( true ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
string statetoken = parser . ReadToken ( ) ;
2015-12-17 10:07:28 +00:00
if ( ! string . IsNullOrEmpty ( statetoken ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
// Start of scope?
2015-12-17 10:07:28 +00:00
if ( statetoken = = "{" )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
// This is fine
2009-04-19 18:07:22 +00:00
}
2014-12-03 23:15:26 +00:00
// End of scope?
2015-12-17 10:07:28 +00:00
else if ( statetoken = = "}" )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
// Done with the states,
// break out of this parse loop
break ;
2009-04-19 18:07:22 +00:00
}
2014-12-03 23:15:26 +00:00
// State label?
2015-12-17 10:07:28 +00:00
else if ( statetoken = = ":" )
2014-12-03 23:15:26 +00:00
{
2015-12-17 10:07:28 +00:00
if ( ! string . IsNullOrEmpty ( previoustoken ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
// Parse actor state
2014-02-26 14:11:06 +00:00
StateStructure st = new StateStructure ( this , parser ) ;
2015-12-17 10:07:28 +00:00
if ( parser . HasError ) return ;
2013-12-18 09:11:04 +00:00
states [ previoustoken . ToLowerInvariant ( ) ] = st ;
2014-12-03 23:15:26 +00:00
}
else
{
2016-03-14 21:53:53 +00:00
parser . ReportError ( "Expected actor state name" ) ;
2013-12-18 09:11:04 +00:00
return ;
}
2014-12-03 23:15:26 +00:00
}
else
{
2013-12-18 09:11:04 +00:00
// Keep token
previoustoken = statetoken ;
}
2014-12-03 23:15:26 +00:00
}
else
{
2013-12-18 09:11:04 +00:00
parser . ReportError ( "Unexpected end of structure" ) ;
return ;
2009-04-19 18:07:22 +00:00
}
}
2013-12-18 09:11:04 +00:00
break ;
case "var" : //mxd
2016-02-08 21:51:03 +00:00
// Type
parser . SkipWhitespace ( true ) ;
string typestr = parser . ReadToken ( ) . ToUpperInvariant ( ) ;
UniversalType type = UniversalType . EnumOption ; // There is no Unknown type, so let's use something impossiburu...
switch ( typestr )
2014-12-03 23:15:26 +00:00
{
2016-03-02 21:55:33 +00:00
case "INT" : type = UniversalType . Integer ; break ;
case "FLOAT" : type = UniversalType . Float ; break ;
default : parser . LogWarning ( "Unknown user variable type" ) ; break ;
2016-02-08 21:51:03 +00:00
}
// Name
parser . SkipWhitespace ( true ) ;
string name = parser . ReadToken ( ) ;
if ( string . IsNullOrEmpty ( name ) )
{
parser . ReportError ( "Expected User Variable name" ) ;
return ;
}
if ( ! name . StartsWith ( "user_" , StringComparison . OrdinalIgnoreCase ) )
{
parser . ReportError ( "User Variable name must start with \"user_\" prefix" ) ;
return ;
}
if ( uservars . ContainsKey ( name ) )
{
Added, Sector Edit window, UDMF: added UI for sector damage-realted properties.
Added, DECORATE parser: damage types are now parsed.
Added: the editor now reports duplicate textures/flats/patches/sprites/colormaps/voxels in the loaded wads.
Added, all text parsers: added #region/#endregion support.
Added TERRAIN parser.
Added, Script Editor: added special handling for DECORATE special comments.
Added, Sector Edit window, UDMF: Soundsequence value was setup incorrectly when showing the window for multiple sectors with mixed Soundsequence value.
Fixed, Map Options window: "Strictly load patches between P_START and P_END" was not applied when applying the changes.
Fixed, MAPINFO parser: MapInfo should be treated as defined when a map MAPINFO block corresponding to current map is encountered even if it doesn't define any properties recognized by the editor.
Fixed, all text parsers: in some cases error line was calculated incorrectly when reporting an error detected by a text parser.
Cosmetic: changed ' to " in the rest of Error and Warning messages.
Internal: added text resource tracking.
Updated ZDoom_DECORATE.cfg.
Updated documentation ("Game Configuration - Basic Settings" page).
2016-02-22 12:33:19 +00:00
parser . ReportError ( "User Variable \"" + name + "\" is double defined" ) ;
2016-02-08 21:51:03 +00:00
return ;
}
if ( ! skipsuper & & baseclass ! = null & & baseclass . uservars . ContainsKey ( name ) )
{
parser . ReportError ( "User variable \"" + name + "\" is already defined in one of the parent classes" ) ;
return ;
}
// Rest
parser . SkipWhitespace ( true ) ;
string next = parser . ReadToken ( ) ;
if ( next = = "[" ) // that's User Array. Let's skip it...
{
int arrlen = - 1 ;
if ( ! parser . ReadSignedInt ( ref arrlen ) )
{
parser . ReportError ( "Expected User Array length, but got \"" + next + "\"" ) ;
return ;
}
if ( arrlen < 1 )
{
parser . ReportError ( "User Array length must be a positive value" ) ;
return ;
}
if ( ! parser . NextTokenIs ( "]" ) | | ! parser . NextTokenIs ( ";" ) )
{
return ;
}
}
else if ( next ! = ";" )
{
parser . ReportError ( "Expected \";\", but got \"" + next + "\"" ) ;
return ;
}
else
{
// Add to collection
uservars . Add ( name , type ) ;
2009-04-19 18:07:22 +00:00
}
2013-12-18 09:11:04 +00:00
break ;
case "}" :
2016-02-08 21:51:03 +00:00
//mxd. Get user vars from the BaseClass, if we have one
if ( ! skipsuper & & baseclass ! = null & & baseclass . uservars . Count > 0 )
{
foreach ( var group in baseclass . uservars )
uservars . Add ( group . Key , group . Value ) ;
}
// Actor scope ends here, break out of this parse loop
2013-12-18 09:11:04 +00:00
done = true ;
break ;
// Monster property?
case "monster" :
// This sets certain flags we are interested in
flags [ "shootable" ] = true ;
flags [ "countkill" ] = true ;
flags [ "solid" ] = true ;
flags [ "canpushwalls" ] = true ;
flags [ "canusewalls" ] = true ;
flags [ "activatemcross" ] = true ;
flags [ "canpass" ] = true ;
flags [ "ismonster" ] = true ;
break ;
// Projectile property?
case "projectile" :
// This sets certain flags we are interested in
flags [ "noblockmap" ] = true ;
flags [ "nogravity" ] = true ;
flags [ "dropoff" ] = true ;
flags [ "missile" ] = true ;
flags [ "activateimpact" ] = true ;
flags [ "activatepcross" ] = true ;
flags [ "noteleport" ] = true ;
break ;
// Clearflags property?
case "clearflags" :
// Clear all flags
flags . Clear ( ) ;
break ;
// Game property?
case "game" :
// Include all tokens on the same line
List < string > games = new List < string > ( ) ;
2015-12-17 10:07:28 +00:00
while ( parser . SkipWhitespace ( false ) )
2014-12-03 23:15:26 +00:00
{
2009-08-15 08:41:43 +00:00
string v = parser . ReadToken ( ) ;
2015-12-17 10:07:28 +00:00
if ( string . IsNullOrEmpty ( v ) )
2014-12-03 23:15:26 +00:00
{
2016-03-14 21:53:53 +00:00
parser . ReportError ( "Expected \"Game\" property value" ) ;
2009-08-15 08:41:43 +00:00
return ;
}
2015-12-17 10:07:28 +00:00
if ( v = = "\n" ) break ;
if ( v = = "}" ) return ; //mxd
if ( v ! = "," ) games . Add ( v . ToLowerInvariant ( ) ) ;
2009-08-15 08:41:43 +00:00
}
2013-12-18 09:11:04 +00:00
props [ token ] = games ;
break ;
// Property
default :
// Property begins with $? Then the whole line is a single value
2015-12-17 10:07:28 +00:00
if ( token . StartsWith ( "$" ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
// This is for editor-only properties such as $sprite and $category
2014-10-23 12:48:31 +00:00
props [ token ] = new List < string > { ( parser . SkipWhitespace ( false ) ? parser . ReadLine ( ) : "" ) } ;
2014-12-03 23:15:26 +00:00
}
else
{
2013-12-18 09:11:04 +00:00
// Next tokens up until the next newline are values
List < string > values = new List < string > ( ) ;
2015-12-17 10:07:28 +00:00
while ( parser . SkipWhitespace ( false ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
string v = parser . ReadToken ( ) ;
2015-12-17 10:07:28 +00:00
if ( string . IsNullOrEmpty ( v ) )
2014-12-03 23:15:26 +00:00
{
2013-12-18 09:11:04 +00:00
parser . ReportError ( "Unexpected end of structure" ) ;
return ;
}
2015-12-17 10:07:28 +00:00
if ( v = = "\n" ) break ;
if ( v = = "}" ) return ; //mxd
if ( v ! = "," ) values . Add ( v ) ;
2013-12-18 09:11:04 +00:00
}
2015-01-27 13:27:49 +00:00
//mxd. Translate scale to xscale and yscale
if ( token = = "scale" )
{
props [ "xscale" ] = values ;
props [ "yscale" ] = values ;
}
else
{
props [ token ] = values ;
}
2013-12-18 09:11:04 +00:00
}
break ;
2009-04-19 18:07:22 +00:00
}
2013-12-18 09:11:04 +00:00
2015-12-17 10:07:28 +00:00
if ( done ) break ; //mxd
2013-12-18 09:11:04 +00:00
2009-04-19 18:07:22 +00:00
// Keep token
previoustoken = token ;
}
2013-04-19 11:35:56 +00:00
//mxd. Check if baseclass is valid
2015-03-17 12:28:42 +00:00
if ( inheritclass . ToLowerInvariant ( ) ! = "actor" & & doomednum > - 1 & & baseclass = = null )
2014-12-03 23:15:26 +00:00
{
2013-04-19 11:35:56 +00:00
//check if this class inherits from a class defined in game configuration
Dictionary < int , ThingTypeInfo > things = General . Map . Config . GetThingTypes ( ) ;
2015-03-17 12:28:42 +00:00
string inheritclasscheck = inheritclass . ToLowerInvariant ( ) ;
2013-04-19 11:35:56 +00:00
2014-12-03 23:15:26 +00:00
foreach ( KeyValuePair < int , ThingTypeInfo > ti in things )
{
2015-03-17 12:28:42 +00:00
if ( ! string . IsNullOrEmpty ( ti . Value . ClassName ) & & ti . Value . ClassName . ToLowerInvariant ( ) = = inheritclasscheck )
2014-12-03 23:15:26 +00:00
{
2013-04-19 11:35:56 +00:00
//states
if ( states . Count = = 0 & & ! string . IsNullOrEmpty ( ti . Value . Sprite ) )
states . Add ( "spawn" , new StateStructure ( ti . Value . Sprite . Substring ( 0 , 4 ) ) ) ;
//flags
if ( ti . Value . Hangs & & ! flags . ContainsKey ( "spawnceiling" ) )
flags [ "spawnceiling" ] = true ;
if ( ti . Value . Blocking > 0 & & ! flags . ContainsKey ( "solid" ) )
flags [ "solid" ] = true ;
//properties
if ( ! props . ContainsKey ( "height" ) )
2015-12-17 10:07:28 +00:00
props [ "height" ] = new List < string > { ti . Value . Height . ToString ( ) } ;
2013-04-19 11:35:56 +00:00
if ( ! props . ContainsKey ( "radius" ) )
2015-12-17 10:07:28 +00:00
props [ "radius" ] = new List < string > { ti . Value . Radius . ToString ( ) } ;
2013-04-19 11:35:56 +00:00
return ;
}
}
2016-02-08 21:51:03 +00:00
parser . LogWarning ( "Unable to find \"" + inheritclass + "\" class to inherit from, while parsing \"" + classname + ":" + doomednum + "\"" ) ;
2013-04-19 11:35:56 +00:00
}
2009-04-19 18:07:22 +00:00
}
2010-08-19 15:06:15 +00:00
// Disposer
public void Dispose ( )
{
baseclass = null ;
flags = null ;
props = null ;
states = null ;
}
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
2009-08-15 08:41:43 +00:00
/// <summary>
/// This checks if the actor has a specific property.
/// </summary>
public bool HasProperty ( string propname )
{
2015-12-17 10:07:28 +00:00
if ( props . ContainsKey ( propname ) ) return true ;
if ( ! skipsuper & & ( baseclass ! = null ) ) return baseclass . HasProperty ( propname ) ;
return false ;
2009-08-15 08:41:43 +00:00
}
/// <summary>
/// This checks if the actor has a specific property with at least one value.
/// </summary>
public bool HasPropertyWithValue ( string propname )
{
2015-12-17 10:07:28 +00:00
if ( props . ContainsKey ( propname ) & & ( props [ propname ] . Count > 0 ) ) return true ;
if ( ! skipsuper & & ( baseclass ! = null ) ) return baseclass . HasPropertyWithValue ( propname ) ;
return false ;
2009-08-15 08:41:43 +00:00
}
/// <summary>
/// This returns values of a specific property as a complete string. Returns an empty string when the propery has no values.
/// </summary>
public string GetPropertyAllValues ( string propname )
{
2010-08-17 10:02:07 +00:00
if ( props . ContainsKey ( propname ) & & ( props [ propname ] . Count > 0 ) )
return string . Join ( " " , props [ propname ] . ToArray ( ) ) ;
2015-12-17 10:07:28 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
2010-08-17 10:02:07 +00:00
return baseclass . GetPropertyAllValues ( propname ) ;
2015-12-17 10:07:28 +00:00
return "" ;
2009-08-15 08:41:43 +00:00
}
2010-08-17 10:02:07 +00:00
2009-08-15 08:41:43 +00:00
/// <summary>
/// This returns a specific value of a specific property as a string. Returns an empty string when the propery does not have the specified value.
/// </summary>
2016-02-26 22:06:42 +00:00
public string GetPropertyValueString ( string propname , int valueindex ) { return GetPropertyValueString ( propname , valueindex , true ) ; } //mxd. Added "stripquotes" parameter
public string GetPropertyValueString ( string propname , int valueindex , bool stripquotes )
2009-08-15 08:41:43 +00:00
{
2010-08-17 10:02:07 +00:00
if ( props . ContainsKey ( propname ) & & ( props [ propname ] . Count > valueindex ) )
2016-02-26 22:06:42 +00:00
return ( stripquotes ? ZDTextParser . StripQuotes ( props [ propname ] [ valueindex ] ) : props [ propname ] [ valueindex ] ) ;
2015-12-17 10:07:28 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
2016-02-26 22:06:42 +00:00
return baseclass . GetPropertyValueString ( propname , valueindex , stripquotes ) ;
2015-12-17 10:07:28 +00:00
return "" ;
2009-08-15 08:41:43 +00:00
}
/// <summary>
/// This returns a specific value of a specific property as an integer. Returns 0 when the propery does not have the specified value.
/// </summary>
public int GetPropertyValueInt ( string propname , int valueindex )
{
2016-02-26 22:06:42 +00:00
string str = GetPropertyValueString ( propname , valueindex , false ) ;
2015-01-27 13:27:49 +00:00
//mxd. It can be negative...
2016-02-26 22:06:42 +00:00
if ( str = = "-" & & props . Count > valueindex + 1 )
str + = GetPropertyValueString ( propname , valueindex + 1 , false ) ;
2010-08-17 10:02:07 +00:00
int intvalue ;
if ( int . TryParse ( str , NumberStyles . Integer , CultureInfo . InvariantCulture , out intvalue ) )
return intvalue ;
2013-12-23 09:51:52 +00:00
return 0 ;
2009-08-15 08:41:43 +00:00
}
2010-08-17 10:02:07 +00:00
2009-08-15 08:41:43 +00:00
/// <summary>
/// This returns a specific value of a specific property as a float. Returns 0.0f when the propery does not have the specified value.
/// </summary>
public float GetPropertyValueFloat ( string propname , int valueindex )
{
2016-02-26 22:06:42 +00:00
string str = GetPropertyValueString ( propname , valueindex , false ) ;
2015-01-27 13:27:49 +00:00
//mxd. It can be negative...
if ( str = = "-" & & props . Count > valueindex + 1 )
2016-02-26 22:06:42 +00:00
str + = GetPropertyValueString ( propname , valueindex + 1 , false ) ;
2015-01-27 13:27:49 +00:00
2010-08-17 10:02:07 +00:00
float fvalue ;
if ( float . TryParse ( str , NumberStyles . Float , CultureInfo . InvariantCulture , out fvalue ) )
return fvalue ;
2013-12-23 09:51:52 +00:00
return 0.0f ;
2009-08-15 08:41:43 +00:00
}
2010-08-17 10:02:07 +00:00
2009-08-15 08:41:43 +00:00
/// <summary>
/// This returns the status of a flag.
/// </summary>
2009-04-19 18:07:22 +00:00
public bool HasFlagValue ( string flag )
{
2015-12-17 10:07:28 +00:00
if ( flags . ContainsKey ( flag ) ) return true ;
if ( ! skipsuper & & ( baseclass ! = null ) ) return baseclass . HasFlagValue ( flag ) ;
2013-12-23 09:51:52 +00:00
return false ;
2009-04-19 18:07:22 +00:00
}
2009-08-15 08:41:43 +00:00
/// <summary>
/// This returns the status of a flag.
/// </summary>
2009-04-19 18:07:22 +00:00
public bool GetFlagValue ( string flag , bool defaultvalue )
{
2015-12-17 10:07:28 +00:00
if ( flags . ContainsKey ( flag ) ) return flags [ flag ] ;
if ( ! skipsuper & & ( baseclass ! = null ) ) return baseclass . GetFlagValue ( flag , defaultvalue ) ;
2013-12-23 09:51:52 +00:00
return defaultvalue ;
2009-04-19 18:07:22 +00:00
}
2010-08-17 10:02:07 +00:00
/// <summary>
/// This checks if a state has been defined.
/// </summary>
public bool HasState ( string statename )
{
2015-12-17 10:07:28 +00:00
if ( states . ContainsKey ( statename ) ) return true ;
if ( ! skipsuper & & ( baseclass ! = null ) ) return baseclass . HasState ( statename ) ;
2013-12-23 09:51:52 +00:00
return false ;
2010-08-17 10:02:07 +00:00
}
/// <summary>
/// This returns a specific state, or null when the state can't be found.
/// </summary>
2010-08-22 11:36:09 +00:00
public StateStructure GetState ( string statename )
2010-08-17 10:02:07 +00:00
{
2015-12-17 10:07:28 +00:00
if ( states . ContainsKey ( statename ) ) return states [ statename ] ;
if ( ! skipsuper & & ( baseclass ! = null ) ) return baseclass . GetState ( statename ) ;
2013-12-23 09:51:52 +00:00
return null ;
2010-08-17 10:02:07 +00:00
}
/// <summary>
/// This creates a list of all states, also those inherited from the base class.
/// </summary>
2010-08-22 11:36:09 +00:00
public Dictionary < string , StateStructure > GetAllStates ( )
2010-08-17 10:02:07 +00:00
{
2016-02-08 21:51:03 +00:00
Dictionary < string , StateStructure > list = new Dictionary < string , StateStructure > ( states , StringComparer . OrdinalIgnoreCase ) ;
2010-08-17 10:02:07 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
{
Dictionary < string , StateStructure > baselist = baseclass . GetAllStates ( ) ;
foreach ( KeyValuePair < string , StateStructure > s in baselist )
if ( ! list . ContainsKey ( s . Key ) ) list . Add ( s . Key , s . Value ) ;
}
return list ;
}
2009-08-15 08:41:43 +00:00
/// <summary>
/// This checks if this actor is meant for the current decorate game support
/// </summary>
2009-04-19 18:07:22 +00:00
public bool CheckActorSupported ( )
{
// Check if we want to include this actor
string includegames = General . Map . Config . DecorateGames . ToLowerInvariant ( ) ;
2009-08-15 08:41:43 +00:00
bool includeactor = ( props [ "game" ] . Count = = 0 ) ;
foreach ( string g in props [ "game" ] )
2009-04-19 18:07:22 +00:00
includeactor | = includegames . Contains ( g ) ;
return includeactor ;
}
2009-08-15 08:41:43 +00:00
/// <summary>
/// This finds the best suitable sprite to use when presenting this actor to the user.
/// </summary>
2016-04-20 20:06:16 +00:00
public StateStructure . FrameInfo FindSuitableSprite ( )
2009-04-19 18:07:22 +00:00
{
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo result = new StateStructure . FrameInfo ( ) ; //mxd
2009-04-19 18:07:22 +00:00
// Sprite forced?
2010-08-17 10:02:07 +00:00
if ( HasPropertyWithValue ( "$sprite" ) )
2009-04-19 18:07:22 +00:00
{
2016-02-26 22:06:42 +00:00
string sprite = GetPropertyValueString ( "$sprite" , 0 , true ) ; //mxd
2016-04-20 20:06:16 +00:00
//mxd. Valid when internal or exists
if ( sprite . StartsWith ( DataManager . INTERNAL_PREFIX , StringComparison . OrdinalIgnoreCase ) | | General . Map . Data . GetSpriteExists ( sprite ) )
{
result . Sprite = sprite ; //mxd
return result ;
}
2016-01-17 23:50:07 +00:00
//mxd. Bitch and moan
General . ErrorLogger . Add ( ErrorType . Warning , "DECORATE warning in " + classname + ":" + doomednum + ". The sprite \"" + sprite + "\" assigned by the \"$sprite\" property does not exist." ) ;
2009-04-19 18:07:22 +00:00
}
2016-01-17 23:50:07 +00:00
// Try the idle state
if ( HasState ( "idle" ) )
2009-04-19 18:07:22 +00:00
{
2016-01-17 23:50:07 +00:00
StateStructure s = GetState ( "idle" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info ;
2016-01-17 23:50:07 +00:00
}
// Try the see state
2016-04-20 20:06:16 +00:00
if ( string . IsNullOrEmpty ( result . Sprite ) & & HasState ( "see" ) )
2016-01-17 23:50:07 +00:00
{
StateStructure s = GetState ( "see" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info ;
2016-01-17 23:50:07 +00:00
}
// Try the inactive state
2016-04-20 20:06:16 +00:00
if ( string . IsNullOrEmpty ( result . Sprite ) & & HasState ( "inactive" ) )
2016-01-17 23:50:07 +00:00
{
StateStructure s = GetState ( "inactive" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info ;
2016-01-17 23:50:07 +00:00
}
// Try the spawn state
2016-04-20 20:06:16 +00:00
if ( string . IsNullOrEmpty ( result . Sprite ) & & HasState ( "spawn" ) )
2016-01-17 23:50:07 +00:00
{
StateStructure s = GetState ( "spawn" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info ;
2016-01-17 23:50:07 +00:00
}
// Still no sprite found? then just pick the first we can find
2016-04-20 20:06:16 +00:00
if ( string . IsNullOrEmpty ( result . Sprite ) )
2016-01-17 23:50:07 +00:00
{
Dictionary < string , StateStructure > list = GetAllStates ( ) ;
foreach ( StateStructure s in list . Values )
2010-08-12 05:49:22 +00:00
{
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) )
2009-04-19 18:07:22 +00:00
{
2016-04-20 20:06:16 +00:00
result = info ;
2016-01-17 23:50:07 +00:00
break ;
2009-04-19 18:07:22 +00:00
}
}
2016-01-17 23:50:07 +00:00
}
2016-04-20 20:06:16 +00:00
if ( ! string . IsNullOrEmpty ( result . Sprite ) )
2016-01-17 23:50:07 +00:00
{
// The sprite name is not actually complete, we still have to append
// the direction characters to it. Find an existing sprite with direction.
foreach ( string postfix in SPRITE_POSTFIXES )
2009-04-19 18:07:22 +00:00
{
2016-04-20 20:06:16 +00:00
if ( General . Map . Data . GetSpriteExists ( result . Sprite + postfix ) )
{
result . Sprite + = postfix ;
return result ;
}
2009-04-19 18:07:22 +00:00
}
}
// No sprite found
2016-04-20 20:06:16 +00:00
return result ;
2014-01-03 10:33:45 +00:00
}
//mxd.
///TODO: rewrite this
Added, Sector Edit window, UDMF: added UI for sector damage-realted properties.
Added, DECORATE parser: damage types are now parsed.
Added: the editor now reports duplicate textures/flats/patches/sprites/colormaps/voxels in the loaded wads.
Added, all text parsers: added #region/#endregion support.
Added TERRAIN parser.
Added, Script Editor: added special handling for DECORATE special comments.
Added, Sector Edit window, UDMF: Soundsequence value was setup incorrectly when showing the window for multiple sectors with mixed Soundsequence value.
Fixed, Map Options window: "Strictly load patches between P_START and P_END" was not applied when applying the changes.
Fixed, MAPINFO parser: MapInfo should be treated as defined when a map MAPINFO block corresponding to current map is encountered even if it doesn't define any properties recognized by the editor.
Fixed, all text parsers: in some cases error line was calculated incorrectly when reporting an error detected by a text parser.
Cosmetic: changed ' to " in the rest of Error and Warning messages.
Internal: added text resource tracking.
Updated ZDoom_DECORATE.cfg.
Updated documentation ("Game Configuration - Basic Settings" page).
2016-02-22 12:33:19 +00:00
public string FindSuitableVoxel ( HashSet < string > voxels )
2014-12-03 23:15:26 +00:00
{
2014-01-03 10:33:45 +00:00
string result = string . Empty ;
// Try the idle state
2014-12-03 23:15:26 +00:00
if ( HasState ( "idle" ) )
{
2014-01-03 10:33:45 +00:00
StateStructure s = GetState ( "idle" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info . Sprite ;
2014-01-03 10:33:45 +00:00
}
// Try the see state
2014-12-03 23:15:26 +00:00
if ( string . IsNullOrEmpty ( result ) & & HasState ( "see" ) )
{
2014-01-03 10:33:45 +00:00
StateStructure s = GetState ( "see" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info . Sprite ;
2014-01-03 10:33:45 +00:00
}
// Try the inactive state
2014-12-03 23:15:26 +00:00
if ( string . IsNullOrEmpty ( result ) & & HasState ( "inactive" ) )
{
2014-01-03 10:33:45 +00:00
StateStructure s = GetState ( "inactive" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info . Sprite ;
2014-01-03 10:33:45 +00:00
}
// Try the spawn state
2014-12-03 23:15:26 +00:00
if ( string . IsNullOrEmpty ( result ) & & HasState ( "spawn" ) )
{
2014-01-03 10:33:45 +00:00
StateStructure s = GetState ( "spawn" ) ;
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) ) result = info . Sprite ;
2014-01-03 10:33:45 +00:00
}
// Still no sprite found? then just pick the first we can find
2014-12-03 23:15:26 +00:00
if ( string . IsNullOrEmpty ( result ) )
{
2014-01-03 10:33:45 +00:00
Dictionary < string , StateStructure > list = GetAllStates ( ) ;
2014-12-03 23:15:26 +00:00
foreach ( StateStructure s in list . Values )
{
2016-04-20 20:06:16 +00:00
StateStructure . FrameInfo info = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( info . Sprite ) )
2014-12-03 23:15:26 +00:00
{
2016-04-20 20:06:16 +00:00
result = info . Sprite ;
2014-01-03 10:33:45 +00:00
break ;
}
}
}
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( result ) )
{
Added, Sector Edit window, UDMF: added UI for sector damage-realted properties.
Added, DECORATE parser: damage types are now parsed.
Added: the editor now reports duplicate textures/flats/patches/sprites/colormaps/voxels in the loaded wads.
Added, all text parsers: added #region/#endregion support.
Added TERRAIN parser.
Added, Script Editor: added special handling for DECORATE special comments.
Added, Sector Edit window, UDMF: Soundsequence value was setup incorrectly when showing the window for multiple sectors with mixed Soundsequence value.
Fixed, Map Options window: "Strictly load patches between P_START and P_END" was not applied when applying the changes.
Fixed, MAPINFO parser: MapInfo should be treated as defined when a map MAPINFO block corresponding to current map is encountered even if it doesn't define any properties recognized by the editor.
Fixed, all text parsers: in some cases error line was calculated incorrectly when reporting an error detected by a text parser.
Cosmetic: changed ' to " in the rest of Error and Warning messages.
Internal: added text resource tracking.
Updated ZDoom_DECORATE.cfg.
Updated documentation ("Game Configuration - Basic Settings" page).
2016-02-22 12:33:19 +00:00
if ( voxels . Contains ( result ) ) return result ;
2014-01-03 10:33:45 +00:00
// The sprite name may be incomplete. Find an existing sprite with direction.
2014-12-03 23:15:26 +00:00
foreach ( string postfix in SPRITE_POSTFIXES )
Added, Sector Edit window, UDMF: added UI for sector damage-realted properties.
Added, DECORATE parser: damage types are now parsed.
Added: the editor now reports duplicate textures/flats/patches/sprites/colormaps/voxels in the loaded wads.
Added, all text parsers: added #region/#endregion support.
Added TERRAIN parser.
Added, Script Editor: added special handling for DECORATE special comments.
Added, Sector Edit window, UDMF: Soundsequence value was setup incorrectly when showing the window for multiple sectors with mixed Soundsequence value.
Fixed, Map Options window: "Strictly load patches between P_START and P_END" was not applied when applying the changes.
Fixed, MAPINFO parser: MapInfo should be treated as defined when a map MAPINFO block corresponding to current map is encountered even if it doesn't define any properties recognized by the editor.
Fixed, all text parsers: in some cases error line was calculated incorrectly when reporting an error detected by a text parser.
Cosmetic: changed ' to " in the rest of Error and Warning messages.
Internal: added text resource tracking.
Updated ZDoom_DECORATE.cfg.
Updated documentation ("Game Configuration - Basic Settings" page).
2016-02-22 12:33:19 +00:00
if ( voxels . Contains ( result + postfix ) ) return result + postfix ;
2014-01-03 10:33:45 +00:00
}
// No voxel found
2009-04-19 18:07:22 +00:00
return "" ;
}
#endregion
}
}