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 ;
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 ;
2015-03-17 12:28:42 +00:00
private readonly List < string > 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 ; } }
2013-03-18 13:52:27 +00:00
public List < string > UserVars { get { return userVars ; } } //mxd
2009-04-19 18:07:22 +00:00
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
internal ActorStructure ( DecorateParser parser )
{
// Initialize
2014-02-26 14:11:06 +00:00
flags = new Dictionary < string , bool > ( StringComparer . Ordinal ) ;
props = new Dictionary < string , List < string > > ( StringComparer . Ordinal ) ;
states = new Dictionary < string , StateStructure > ( StringComparer . Ordinal ) ;
2013-03-18 13:52:27 +00:00
userVars = new List < string > ( ) ; //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
2014-12-03 23:15:26 +00:00
switch ( token )
{
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 ( ) ) ;
2014-12-03 23:15:26 +00:00
if ( string . IsNullOrEmpty ( inheritclass ) | | parser . IsSpecialToken ( inheritclass ) )
{
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 ) ;
Added, Visual mode, DECORATE: "Alpha" and "DefaultAlpha" properties are now supported.
Added, Visual mode, DECORATE: "RenderStyle" property is now partially supported.
Added, Visual mode, DECORATE: +BRIGHT flag is now supported.
Added, Visual mode, UDMF: "Alpha" thing property is now supported.
Added, Visual mode, UDMF: "RenderStyle" thing property is now partially supported.
Added, Visual mode, Hexen map format and UDMF: "Translucent" and "Invisible" thing flags are now supported.
Added, Game Configurations: added "alpha" and "renderstyle" Thing and Thing Category properties.
Fixed, Visual mode: blockmap was not updated when moving things using "Move Thing Left/Right/Forward/Backward" and "Move Thing to Cursor Location" actions.
DECORATE parser: added a warning when unable to find actor's parent class.
Internal: current map format can now be checked using "General.Map.UDMF", "General.Map.HEXEN" and "General.Map.DOOM" properties.
Updated documentation ("Game Configuration - Things Settings" page).
2015-09-28 14:57:48 +00:00
2015-09-28 19:49:15 +00:00
//mxd. Does it exist? (We can carry on regardless, so add a warning)
if ( baseclass = = null ) parser . ReportWarning ( "Parent class '" + inheritclass + "' does not exist" ) ;
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 ( ) ) ;
2014-12-03 23:15:26 +00:00
if ( string . IsNullOrEmpty ( replaceclass ) | | parser . IsSpecialToken ( replaceclass ) )
{
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 ( ) : "" ) } ;
}
else if ( ! int . TryParse ( token , NumberStyles . Integer , CultureInfo . InvariantCulture , out doomednum ) ) // Check if numeric
{
2013-12-18 09:11:04 +00:00
// Not numeric!
2014-10-23 12:48:31 +00:00
parser . ReportError ( "Expected editor thing number or start of actor scope while parsing '" + classname + "'" ) ;
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
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
2014-12-03 23:15:26 +00:00
switch ( token )
{
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 ( ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( flagname ) )
{
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 ;
2014-12-03 23:15:26 +00:00
while ( parser . SkipWhitespace ( true ) )
{
2013-12-18 09:11:04 +00:00
string t = parser . ReadToken ( ) ;
2015-06-18 19:45:39 +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
2014-12-03 23:15:26 +00:00
while ( parser . SkipWhitespace ( true ) )
{
2013-12-18 09:11:04 +00:00
string statetoken = parser . ReadToken ( ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( statetoken ) )
{
2013-12-18 09:11:04 +00:00
// Start of scope?
2014-12-03 23:15:26 +00:00
if ( statetoken = = "{" )
{
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?
else if ( statetoken = = "}" )
{
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?
else if ( statetoken = = ":" )
{
if ( ! string . IsNullOrEmpty ( previoustoken ) )
{
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 ) ;
2014-12-03 23:15:26 +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
{
2013-12-18 09:11:04 +00:00
parser . ReportError ( "Unexpected end of structure" ) ;
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
2014-12-03 23:15:26 +00:00
while ( parser . SkipWhitespace ( true ) )
{
2013-12-18 09:11:04 +00:00
string t = parser . ReadToken ( ) ;
2015-06-18 19:45:39 +00:00
if ( string . IsNullOrEmpty ( t ) | | t = = ";" ) break ;
2013-12-18 09:11:04 +00:00
if ( t . StartsWith ( "user_" ) & & ! userVars . Contains ( t ) )
userVars . Add ( t ) ;
2009-04-19 18:07:22 +00:00
}
2013-12-18 09:11:04 +00:00
break ;
case "}" :
// Actor scope ends here,
// break out of this parse loop
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 > ( ) ;
2014-12-03 23:15:26 +00:00
while ( parser . SkipWhitespace ( false ) )
{
2009-08-15 08:41:43 +00:00
string v = parser . ReadToken ( ) ;
2015-06-18 19:45:39 +00:00
if ( string . IsNullOrEmpty ( v ) )
2014-12-03 23:15:26 +00:00
{
2009-08-15 08:41:43 +00:00
parser . ReportError ( "Unexpected end of structure" ) ;
return ;
}
2013-12-18 09:11:04 +00:00
if ( v = = "\n" ) break ;
2013-09-11 09:47:53 +00:00
if ( v = = "}" ) return ; //mxd
2013-12-18 09:11:04 +00:00
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
2014-12-03 23:15:26 +00:00
if ( token . StartsWith ( "$" ) )
{
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 > ( ) ;
2014-12-03 23:15:26 +00:00
while ( parser . SkipWhitespace ( false ) )
{
2013-12-18 09:11:04 +00:00
string v = parser . ReadToken ( ) ;
2015-06-18 19:45:39 +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 ;
}
if ( v = = "\n" ) break ;
if ( v = = "}" ) return ; //mxd
if ( v ! = "," ) values . Add ( v ) ;
}
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
if ( done ) break ; //mxd
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" ) )
props [ "height" ] = new List < string > ( ) { ti . Value . Height . ToString ( ) } ;
if ( ! props . ContainsKey ( "radius" ) )
props [ "radius" ] = new List < string > ( ) { ti . Value . Radius . ToString ( ) } ;
return ;
}
}
General . ErrorLogger . Add ( ErrorType . Warning , "Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + ":" + doomednum + "'" ) ;
}
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 )
{
2010-08-17 10:02:07 +00:00
if ( props . ContainsKey ( propname ) )
return true ;
else if ( ! skipsuper & & ( baseclass ! = null ) )
return baseclass . HasProperty ( propname ) ;
else
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 )
{
2010-08-17 10:02:07 +00:00
if ( props . ContainsKey ( propname ) & & ( props [ propname ] . Count > 0 ) )
return true ;
else if ( ! skipsuper & & ( baseclass ! = null ) )
return baseclass . HasPropertyWithValue ( propname ) ;
else
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 ( ) ) ;
else if ( ! skipsuper & & ( baseclass ! = null ) )
return baseclass . GetPropertyAllValues ( propname ) ;
else
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>
public string GetPropertyValueString ( string propname , int valueindex )
{
2010-08-17 10:02:07 +00:00
if ( props . ContainsKey ( propname ) & & ( props [ propname ] . Count > valueindex ) )
2009-08-15 08:41:43 +00:00
return props [ propname ] [ valueindex ] ;
2010-08-17 10:02:07 +00:00
else if ( ! skipsuper & & ( baseclass ! = null ) )
return baseclass . GetPropertyValueString ( propname , valueindex ) ;
2009-08-15 08:41:43 +00:00
else
return "" ;
}
/// <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 )
{
2010-08-17 10:02:07 +00:00
string str = GetPropertyValueString ( propname , valueindex ) ;
2015-01-27 13:27:49 +00:00
//mxd. It can be negative...
if ( str = = "-" & & props . Count > valueindex + 1 )
str + = GetPropertyValueString ( propname , valueindex + 1 ) ;
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 )
{
2010-08-17 10:02:07 +00:00
string str = GetPropertyValueString ( propname , valueindex ) ;
2015-01-27 13:27:49 +00:00
//mxd. It can be negative...
if ( str = = "-" & & props . Count > valueindex + 1 )
str + = GetPropertyValueString ( propname , valueindex + 1 ) ;
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 )
{
2010-08-17 10:02:07 +00:00
if ( flags . ContainsKey ( flag ) )
return true ;
2013-12-23 09:51:52 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
2010-08-17 10:02:07 +00:00
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 )
{
if ( flags . ContainsKey ( flag ) )
return flags [ flag ] ;
2013-12-23 09:51:52 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
2010-08-17 10:02:07 +00:00
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 )
{
if ( states . ContainsKey ( statename ) )
return true ;
2013-12-23 09:51:52 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
2010-08-17 10:02:07 +00:00
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
{
if ( states . ContainsKey ( statename ) )
return states [ statename ] ;
2013-12-23 09:51:52 +00:00
if ( ! skipsuper & & ( baseclass ! = null ) )
2010-08-17 10:02:07 +00:00
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
{
Dictionary < string , StateStructure > list = new Dictionary < string , StateStructure > ( states ) ;
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>
2009-04-19 18:07:22 +00:00
public string FindSuitableSprite ( )
{
2014-01-03 10:33:45 +00:00
string result = string . Empty ;
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
{
2010-08-17 10:02:07 +00:00
return GetPropertyValueString ( "$sprite" , 0 ) ;
2009-04-19 18:07:22 +00:00
}
else
{
// Try the idle state
2010-08-17 10:02:07 +00:00
if ( HasState ( "idle" ) )
2009-04-19 18:07:22 +00:00
{
2010-08-17 10:02:07 +00:00
StateStructure s = GetState ( "idle" ) ;
2010-08-19 15:06:15 +00:00
string spritename = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( spritename ) )
result = spritename ;
2009-04-19 18:07:22 +00:00
}
// Try the see state
2010-08-17 10:02:07 +00:00
if ( string . IsNullOrEmpty ( result ) & & HasState ( "see" ) )
2009-04-19 18:07:22 +00:00
{
2010-08-17 10:02:07 +00:00
StateStructure s = GetState ( "see" ) ;
2010-08-19 15:06:15 +00:00
string spritename = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( spritename ) )
result = spritename ;
2009-04-19 18:07:22 +00:00
}
2009-08-15 08:41:43 +00:00
2009-04-19 18:07:22 +00:00
// Try the inactive state
2010-08-17 10:02:07 +00:00
if ( string . IsNullOrEmpty ( result ) & & HasState ( "inactive" ) )
2009-04-19 18:07:22 +00:00
{
2010-08-17 10:02:07 +00:00
StateStructure s = GetState ( "inactive" ) ;
2010-08-19 15:06:15 +00:00
string spritename = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( spritename ) )
result = spritename ;
2009-04-19 18:07:22 +00:00
}
2010-08-17 10:02:07 +00:00
2010-08-12 05:49:22 +00:00
// Try the spawn state
2010-08-17 10:02:07 +00:00
if ( string . IsNullOrEmpty ( result ) & & HasState ( "spawn" ) )
2010-08-12 05:49:22 +00:00
{
2010-08-17 10:02:07 +00:00
StateStructure s = GetState ( "spawn" ) ;
2010-08-19 15:06:15 +00:00
string spritename = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( spritename ) )
result = spritename ;
2010-08-12 05:49:22 +00:00
}
2010-08-17 10:02:07 +00:00
2009-04-19 18:07:22 +00:00
// Still no sprite found? then just pick the first we can find
if ( string . IsNullOrEmpty ( result ) )
{
2010-08-17 10:02:07 +00:00
Dictionary < string , StateStructure > list = GetAllStates ( ) ;
foreach ( StateStructure s in list . Values )
2009-04-19 18:07:22 +00:00
{
2010-08-19 15:06:15 +00:00
string spritename = s . GetSprite ( 0 ) ;
if ( ! string . IsNullOrEmpty ( spritename ) )
2009-04-19 18:07:22 +00:00
{
2010-08-19 15:06:15 +00:00
result = spritename ;
2009-04-19 18:07:22 +00:00
break ;
}
}
}
if ( ! string . IsNullOrEmpty ( result ) )
{
// 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 )
{
if ( General . Map . Data . GetSpriteExists ( result + postfix ) )
return result + postfix ;
}
}
}
// No sprite found
2014-01-03 10:33:45 +00:00
return string . Empty ;
}
//mxd.
///TODO: rewrite this
2014-12-03 23:15:26 +00:00
public string FindSuitableVoxel ( Dictionary < string , bool > voxels )
{
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" ) ;
string spritename = s . GetSprite ( 0 ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( spritename ) ) result = spritename ;
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" ) ;
string spritename = s . GetSprite ( 0 ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( spritename ) ) result = spritename ;
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" ) ;
string spritename = s . GetSprite ( 0 ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( spritename ) ) result = spritename ;
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" ) ;
string spritename = s . GetSprite ( 0 ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( spritename ) ) result = spritename ;
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 )
{
2014-01-03 10:33:45 +00:00
string spritename = s . GetSprite ( 0 ) ;
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( spritename ) )
{
2014-01-03 10:33:45 +00:00
result = spritename ;
break ;
}
}
}
2014-12-03 23:15:26 +00:00
if ( ! string . IsNullOrEmpty ( result ) )
{
2014-01-03 10:33:45 +00:00
if ( voxels . ContainsKey ( result ) ) return result ;
// 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 )
2014-01-03 10:33:45 +00:00
if ( voxels . ContainsKey ( result + postfix ) ) return result + postfix ;
}
// No voxel found
2009-04-19 18:07:22 +00:00
return "" ;
}
#endregion
}
}