2015-12-31 12:21:44 +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
using System ;
using System.Collections ;
using System.Collections.Generic ;
using CodeImp.DoomBuilder.IO ;
using CodeImp.DoomBuilder.Map ;
#endregion
namespace CodeImp.DoomBuilder.Config
{
//mxd
public class ThingFlagsCompareGroup
{
public readonly string Name ;
public readonly bool IsOptional ; // When set to true, group flags won't be considered as required for a thing to show up ingame by CheckUnusedThings error check and ThingFlagsCompare.CheckThingEditFormFlags() method.
public readonly Dictionary < string , ThingFlagsCompare > Flags ;
public ThingFlagsCompareGroup ( Configuration cfg , string name )
{
Name = name ;
Flags = new Dictionary < string , ThingFlagsCompare > ( ) ;
IsOptional = cfg . ReadSetting ( "thingflagscompare." + name + ".optional" , false ) ;
IDictionary dic = cfg . ReadSetting ( "thingflagscompare." + name , new Hashtable ( ) ) ;
foreach ( DictionaryEntry de in dic )
{
if ( de . Value ! = null & & ! ( de . Value is IDictionary ) ) continue ; // flag either has no value, or is defined as block
string flag = de . Key . ToString ( ) ;
// Duplicate flags check
if ( Flags . ContainsKey ( flag ) )
2022-11-25 17:14:35 +00:00
General . ErrorLogger . Add ( ErrorType . Warning , "ThingFlagsCompare flag \"" + flag + "\" is double defined in the \"" + name + "\" group" ) ;
2015-12-31 12:21:44 +00:00
Flags [ flag ] = new ThingFlagsCompare ( cfg , name , flag ) ;
}
}
// Compares flags group of the two things.
public ThingFlagsCompareResult Compare ( Thing t1 , Thing t2 )
{
ThingFlagsCompareResult result = new ThingFlagsCompareResult ( ) ;
foreach ( ThingFlagsCompare tfc in Flags . Values )
{
// Current flag doesn't overlap when required flag does not overlap
if ( ! string . IsNullOrEmpty ( tfc . RequiredFlag ) & & ! GetFlag ( tfc . RequiredFlag ) . Compare ( t1 , t2 ) )
{
result . Result = - 1 ;
continue ;
}
// Compare current flag
bool flagoverlaps = tfc . Compare ( t1 , t2 ) ;
// Ignore this group when whole group doens't match or required flag is not set
if ( ! flagoverlaps & & tfc . IgnoreGroupWhenUnset ) return new ThingFlagsCompareResult { Result = 0 } ;
// If current flag overlaps, check IgnoredGroup and RequiredGroup settings
if ( flagoverlaps )
{
result . Result = 1 ;
foreach ( string s in tfc . IgnoredGroups )
{
if ( ! result . IgnoredGroups . Contains ( s ) ) result . IgnoredGroups . Add ( s ) ;
}
if ( tfc . RequiredGroups . Count > 0 )
{
foreach ( string s in tfc . RequiredGroups )
{
if ( result . IgnoredGroups . Contains ( s ) ) result . IgnoredGroups . Remove ( s ) ;
if ( ! result . RequiredGroups . Contains ( s ) ) result . RequiredGroups . Add ( s ) ;
}
}
}
}
return result ;
}
public ThingFlagsCompare GetFlag ( string flag )
{
// Check our flags
if ( Flags . ContainsKey ( flag ) ) return Flags [ flag ] ;
// Check other groups
foreach ( ThingFlagsCompareGroup group in General . Map . Config . ThingFlagsCompare . Values )
{
if ( group ! = this & & group . Flags . ContainsKey ( flag ) ) return group . Flags [ flag ] ;
}
// Fial...
return null ;
}
}
//mxd
public class ThingFlagsCompareResult
{
public readonly HashSet < string > IgnoredGroups ;
public readonly HashSet < string > RequiredGroups ;
// -1 if group does not overlap
// 0 if group should be ignored
// 1 if group overlaps
public int Result ;
public ThingFlagsCompareResult ( )
{
Result = - 1 ;
IgnoredGroups = new HashSet < string > ( ) ;
RequiredGroups = new HashSet < string > ( ) ;
}
}
public class ThingFlagsCompare
{
private enum CompareMethod
{
Equal ,
And
} ;
#region = = = = = = = = = = = = = = = = = = Constants
#endregion
#region = = = = = = = = = = = = = = = = = = Variables
private readonly string flag ;
private readonly HashSet < string > requiredgroups ; //mxd. This flag only works if at least one flag is set in the "requiredgroup"
private readonly HashSet < string > ignoredgroups ; //mxd. If this flag is set, flags from ignoredgroup can be... well... ignored!
private string requiredflag ; //mxd. This flag only works if requiredflag is set.
private readonly bool ingnorethisgroupwhenunset ; //mxd
private readonly CompareMethod comparemethod ;
private readonly bool invert ;
private readonly char [ ] comma = new [ ] { ',' } ;
#endregion
#region = = = = = = = = = = = = = = = = = = Properties
public string Flag { get { return flag ; } }
public HashSet < string > RequiredGroups { get { return requiredgroups ; } } //mxd
public HashSet < string > IgnoredGroups { get { return ignoredgroups ; } } //mxd
public string RequiredFlag { get { return requiredflag ; } internal set { requiredflag = value ; } } //mxd
public bool IgnoreGroupWhenUnset { get { return ingnorethisgroupwhenunset ; } } //mxd
#endregion
#region = = = = = = = = = = = = = = = = = = Constructor / Disposer
// Constructor
public ThingFlagsCompare ( Configuration cfg , string group , string flag )
{
string cfgpath = "thingflagscompare." + group + "." + flag ;
this . flag = flag ;
string cm = cfg . ReadSetting ( cfgpath + ".comparemethod" , "and" ) ;
switch ( cm )
{
default :
General . ErrorLogger . Add ( ErrorType . Warning , "Unrecognized value \"" + cm + "\" for comparemethod in " + cfgpath + " in game configuration " + cfg . ReadSetting ( "game" , "<unnamed game>" ) + ". Defaulting to \"and\"." ) ;
goto case "and" ;
case "and" :
comparemethod = CompareMethod . And ;
break ;
case "equal" :
comparemethod = CompareMethod . Equal ;
break ;
}
invert = cfg . ReadSetting ( cfgpath + ".invert" , false ) ;
//mxd
requiredgroups = new HashSet < string > ( ) ;
string [ ] requiredgroupsarr = cfg . ReadSetting ( cfgpath + ".requiredgroups" , string . Empty ) . Split ( comma , StringSplitOptions . RemoveEmptyEntries ) ;
foreach ( string s in requiredgroupsarr )
if ( ! requiredgroups . Contains ( s ) ) requiredgroups . Add ( s ) ;
//mxd
ignoredgroups = new HashSet < string > ( ) ;
string [ ] ignoredgroupsarr = cfg . ReadSetting ( cfgpath + ".ignoredgroups" , string . Empty ) . Split ( comma , StringSplitOptions . RemoveEmptyEntries ) ;
foreach ( string s in ignoredgroupsarr )
if ( ! ignoredgroups . Contains ( s ) ) ignoredgroups . Add ( s ) ;
requiredflag = cfg . ReadSetting ( cfgpath + ".requiredflag" , string . Empty ) ; //mxd
ingnorethisgroupwhenunset = cfg . ReadSetting ( cfgpath + ".ingnorethisgroupwhenunset" , false ) ; //mxd
// We have no destructor
GC . SuppressFinalize ( this ) ;
}
#endregion
#region = = = = = = = = = = = = = = = = = = Methods
// Compares the flag of the two things.
public bool Compare ( Thing t1 , Thing t2 )
{
//mxd. Get flags
bool t1flag = ( invert ? ! t1 . IsFlagSet ( flag ) : t1 . IsFlagSet ( flag ) ) ;
bool t2flag = ( invert ? ! t2 . IsFlagSet ( flag ) : t2 . IsFlagSet ( flag ) ) ;
//mxd. Ignore the flag when ingnorethisgroupwhenunset is set and both flags are unset
if ( ! t1flag & & ! t2flag & & ingnorethisgroupwhenunset ) return false ;
//mxd. Compare them
switch ( comparemethod )
{
case CompareMethod . And : return t1flag & & t2flag ;
case CompareMethod . Equal : return t1flag = = t2flag ;
default : throw new NotImplementedException ( "Unknown compare method!" ) ;
}
}
//mxd
public static List < string > CheckFlags ( HashSet < string > flags )
{
Dictionary < string , HashSet < string > > flagspergroup = new Dictionary < string , HashSet < string > > ( General . Map . Config . ThingFlagsCompare . Count ) ;
HashSet < string > ignoredgroups = new HashSet < string > ( ) ;
// Gather flags per group
foreach ( KeyValuePair < string , ThingFlagsCompareGroup > group in General . Map . Config . ThingFlagsCompare )
{
flagspergroup . Add ( group . Key , new HashSet < string > ( ) ) ;
foreach ( ThingFlagsCompare flag in group . Value . Flags . Values )
{
if ( IsFlagSet ( flags , flag . flag , flag . invert ) & &
( string . IsNullOrEmpty ( flag . requiredflag ) | | IsFlagSet ( flags , flag . requiredflag , group . Value . GetFlag ( flag . requiredflag ) . invert ) ) )
{
flagspergroup [ group . Key ] . Add ( flag . Flag ) ;
foreach ( string s in flag . ignoredgroups )
if ( ! ignoredgroups . Contains ( s ) ) ignoredgroups . Add ( s ) ;
}
else if ( flag . ingnorethisgroupwhenunset )
{
flagspergroup . Remove ( group . Key ) ;
break ;
}
}
}
// Check required dependancies
foreach ( KeyValuePair < string , HashSet < string > > group in flagspergroup )
{
foreach ( string flag in group . Value )
{
foreach ( string s in General . Map . Config . ThingFlagsCompare [ group . Key ] . Flags [ flag ] . requiredgroups )
{
if ( ignoredgroups . Contains ( s ) ) ignoredgroups . Remove ( s ) ;
}
}
}
// Get rid of ignoredgroups
foreach ( string s in ignoredgroups ) flagspergroup . Remove ( s ) ;
// Return message
List < string > result = new List < string > ( ) ;
foreach ( KeyValuePair < string , HashSet < string > > group in flagspergroup )
{
if ( group . Value . Count = = 0 & & ! General . Map . Config . ThingFlagsCompare [ group . Key ] . IsOptional )
{
switch ( group . Key )
{
case "skills" :
result . Add ( "Thing is not used in any skill level." ) ;
break ;
case "gamemodes" :
result . Add ( "Thing is not used in any game mode." ) ;
break ;
case "classes" :
result . Add ( "Thing is not used by any class." ) ;
break ;
default :
2022-11-25 17:14:35 +00:00
result . Add ( "At least one \"" + group . Key + "\" flag should be set." ) ;
2015-12-31 12:21:44 +00:00
break ;
}
}
}
return result ;
}
//mxd
private static bool IsFlagSet ( HashSet < string > flags , string flag , bool invert )
{
bool result = flags . Contains ( flag ) ;
return ( invert ? ! result : result ) ;
}
#endregion
}
}