/*************************************************************************** * Utility.cs * ------------------- * begin : May 1, 2002 * copyright : (C) The RunUO Software Team * email : info@runuo.com * * $Id$ * ***************************************************************************/ /*************************************************************************** * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * ***************************************************************************/ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Text; using System.Xml; using Microsoft.Win32; using Server.Network; namespace Server { public static class Utility { private static Encoding m_UTF8, m_UTF8WithEncoding; public static Encoding UTF8 { get { if ( m_UTF8 == null ) m_UTF8 = new UTF8Encoding( false, false ); return m_UTF8; } } public static Encoding UTF8WithEncoding { get { if ( m_UTF8WithEncoding == null ) m_UTF8WithEncoding = new UTF8Encoding( true, false ); return m_UTF8WithEncoding; } } public static void Separate( StringBuilder sb, string value, string separator ) { if ( sb.Length > 0 ) sb.Append( separator ); sb.Append( value ); } public static string Intern( string str ) { if ( str == null ) return null; else if ( str.Length == 0 ) return String.Empty; return String.Intern( str ); } public static void Intern( ref string str ) { str = Intern( str ); } private static Dictionary _ipAddressTable; public static IPAddress Intern( IPAddress ipAddress ) { if ( _ipAddressTable == null ) { _ipAddressTable = new Dictionary(); } IPAddress interned; if ( !_ipAddressTable.TryGetValue( ipAddress, out interned ) ) { interned = ipAddress; _ipAddressTable[ipAddress] = interned; } return interned; } public static void Intern( ref IPAddress ipAddress ) { ipAddress = Intern( ipAddress ); } public static bool IsValidIP( string text ) { bool valid = true; IPMatch( text, IPAddress.None, ref valid ); return valid; } public static bool IPMatch( string val, IPAddress ip ) { bool valid = true; return IPMatch( val, ip, ref valid ); } public static string FixHtml( string str ) { if( str == null ) return ""; bool hasOpen = ( str.IndexOf( '<' ) >= 0 ); bool hasClose = ( str.IndexOf( '>' ) >= 0 ); bool hasPound = ( str.IndexOf( '#' ) >= 0 ); if ( !hasOpen && !hasClose && !hasPound ) return str; StringBuilder sb = new StringBuilder( str ); if ( hasOpen ) sb.Replace( '<', '(' ); if ( hasClose ) sb.Replace( '>', ')' ); if ( hasPound ) sb.Replace( '#', '-' ); return sb.ToString(); } public static bool IPMatchCIDR( string cidr, IPAddress ip ) { if ( ip == null || ip.AddressFamily == AddressFamily.InterNetworkV6 ) return false; //Just worry about IPv4 for now /* string[] str = cidr.Split( '/' ); if ( str.Length != 2 ) return false; /* ************************************************** IPAddress cidrPrefix; if ( !IPAddress.TryParse( str[0], out cidrPrefix ) ) return false; * */ /* string[] dotSplit = str[0].Split( '.' ); if ( dotSplit.Length != 4 ) //At this point and time, and for speed sake, we'll only worry about IPv4 return false; byte[] bytes = new byte[4]; for ( int i = 0; i < 4; i++ ) { byte.TryParse( dotSplit[i], out bytes[i] ); } uint cidrPrefix = OrderedAddressValue( bytes ); int cidrLength = Utility.ToInt32( str[1] ); //The below solution is the fastest solution of the three */ byte[] bytes = new byte[4]; string[] split = cidr.Split( '.' ); bool cidrBits = false; int cidrLength = 0; for ( int i = 0; i < 4; i++ ) { int part = 0; int partBase = 10; string pattern = split[i]; for ( int j = 0; j < pattern.Length; j++ ) { char c = (char)pattern[j]; if ( c == 'x' || c == 'X' ) { partBase = 16; } else if ( c >= '0' && c <= '9' ) { int offset = c - '0'; if ( cidrBits ) { cidrLength *= partBase; cidrLength += offset; } else { part *= partBase; part += offset; } } else if ( c >= 'a' && c <= 'f' ) { int offset = 10 + ( c - 'a' ); if ( cidrBits ) { cidrLength *= partBase; cidrLength += offset; } else { part *= partBase; part += offset; } } else if ( c >= 'A' && c <= 'F' ) { int offset = 10 + ( c - 'A' ); if ( cidrBits ) { cidrLength *= partBase; cidrLength += offset; } else { part *= partBase; part += offset; } } else if ( c == '/' ) { if ( cidrBits || i != 3 ) //If there's two '/' or the '/' isn't in the last byte { return false; } partBase = 10; cidrBits = true; } else { return false; } } bytes[i] = (byte)part; } uint cidrPrefix = OrderedAddressValue( bytes ); return IPMatchCIDR( cidrPrefix, ip, cidrLength ); } public static bool IPMatchCIDR( IPAddress cidrPrefix, IPAddress ip, int cidrLength ) { if ( cidrPrefix == null || ip == null || cidrPrefix.AddressFamily == AddressFamily.InterNetworkV6 ) //Ignore IPv6 for now return false; uint cidrValue = SwapUnsignedInt( (uint)GetLongAddressValue( cidrPrefix ) ); uint ipValue = SwapUnsignedInt( (uint)GetLongAddressValue( ip ) ); return IPMatchCIDR( cidrValue, ipValue, cidrLength ); } public static bool IPMatchCIDR( uint cidrPrefixValue, IPAddress ip, int cidrLength ) { if ( ip == null || ip.AddressFamily == AddressFamily.InterNetworkV6) return false; uint ipValue = SwapUnsignedInt( (uint)GetLongAddressValue( ip ) ); return IPMatchCIDR( cidrPrefixValue, ipValue, cidrLength ); } public static bool IPMatchCIDR( uint cidrPrefixValue, uint ipValue, int cidrLength ) { if ( cidrLength <= 0 || cidrLength >= 32 ) //if invalid cidr Length, just compare IPs return cidrPrefixValue == ipValue; uint mask = uint.MaxValue << 32-cidrLength; return ( ( cidrPrefixValue & mask ) == ( ipValue & mask ) ); } private static uint OrderedAddressValue( byte[] bytes ) { if ( bytes.Length != 4 ) return 0; return (uint)(((( bytes[0] << 0x18 ) | (bytes[1] << 0x10)) | (bytes[2] << 8)) | bytes[3]) & ((uint)0xffffffff); } private static uint SwapUnsignedInt( uint source ) { return (uint)( ( ( ( source & 0x000000FF ) << 0x18 ) | ( ( source & 0x0000FF00 ) << 8 ) | ( ( source & 0x00FF0000 ) >> 8 ) | ( ( source & 0xFF000000 ) >> 0x18 ) ) ); } public static bool TryConvertIPv6toIPv4( ref IPAddress address ) { if ( !Socket.OSSupportsIPv6 || address.AddressFamily == AddressFamily.InterNetwork ) return true; byte[] addr = address.GetAddressBytes(); if ( addr.Length == 16 ) //sanity 0 - 15 //10 11 //12 13 14 15 { if ( addr[10] != 0xFF || addr[11] != 0xFF ) return false; for ( int i = 0; i < 10; i++ ) { if ( addr[i] != 0 ) return false; } byte[] v4Addr = new byte[4]; for ( int i = 0; i < 4; i++ ) { v4Addr[i] = addr[12 + i]; } address = new IPAddress( v4Addr ); return true; } return false; } public static bool IPMatch( string val, IPAddress ip, ref bool valid ) { valid = true; string[] split = val.Split( '.' ); for ( int i = 0; i < 4; ++i ) { int lowPart, highPart; if ( i >= split.Length ) { lowPart = 0; highPart = 255; } else { string pattern = split[i]; if ( pattern == "*" ) { lowPart = 0; highPart = 255; } else { lowPart = 0; highPart = 0; bool highOnly = false; int lowBase = 10; int highBase = 10; for ( int j = 0; j < pattern.Length; ++j ) { char c = (char)pattern[j]; if ( c == '?' ) { if ( !highOnly ) { lowPart *= lowBase; lowPart += 0; } highPart *= highBase; highPart += highBase - 1; } else if ( c == '-' ) { highOnly = true; highPart = 0; } else if ( c == 'x' || c == 'X' ) { lowBase = 16; highBase = 16; } else if ( c >= '0' && c <= '9' ) { int offset = c - '0'; if ( !highOnly ) { lowPart *= lowBase; lowPart += offset; } highPart *= highBase; highPart += offset; } else if ( c >= 'a' && c <= 'f' ) { int offset = 10 + (c - 'a'); if ( !highOnly ) { lowPart *= lowBase; lowPart += offset; } highPart *= highBase; highPart += offset; } else if ( c >= 'A' && c <= 'F' ) { int offset = 10 + (c - 'A'); if ( !highOnly ) { lowPart *= lowBase; lowPart += offset; } highPart *= highBase; highPart += offset; } else { valid = false; //high & lowpart would be 0 if it got to here. } } } } int b = (byte)(Utility.GetAddressValue( ip ) >> (i * 8)); if ( b < lowPart || b > highPart ) return false; } return true; } public static bool IPMatchClassC( IPAddress ip1, IPAddress ip2 ) { return ( (Utility.GetAddressValue( ip1 ) & 0xFFFFFF) == (Utility.GetAddressValue( ip2 ) & 0xFFFFFF) ); } public static int InsensitiveCompare( string first, string second ) { return Insensitive.Compare( first, second ); } public static bool InsensitiveStartsWith( string first, string second ) { return Insensitive.StartsWith( first, second ); } #region To[Something] public static bool ToBoolean( string value ) { bool b; bool.TryParse( value, out b ); return b; } public static double ToDouble( string value ) { double d; double.TryParse( value, out d ); return d; } public static TimeSpan ToTimeSpan( string value ) { TimeSpan t; TimeSpan.TryParse( value, out t ); return t; } public static int ToInt32( string value ) { int i; if( value.StartsWith( "0x" ) ) int.TryParse( value.Substring( 2 ), NumberStyles.HexNumber, null, out i ); else int.TryParse( value, out i ); return i; } #endregion #region Get[Something] public static double GetXMLDouble(string doubleString, double defaultValue) { try { return XmlConvert.ToDouble(doubleString); } catch { double val; if (double.TryParse(doubleString, out val)) return val; return defaultValue; } } public static int GetXMLInt32( string intString, int defaultValue ) { try { return XmlConvert.ToInt32( intString ); } catch { int val; if ( int.TryParse( intString, out val ) ) return val; return defaultValue; } } public static DateTime GetXMLDateTime( string dateTimeString, DateTime defaultValue ) { try { return XmlConvert.ToDateTime( dateTimeString, XmlDateTimeSerializationMode.Utc ); } catch { DateTime d; if( DateTime.TryParse( dateTimeString, out d ) ) return d; return defaultValue; } } public static DateTimeOffset GetXMLDateTimeOffset( string dateTimeOffsetString, DateTimeOffset defaultValue ) { try { return XmlConvert.ToDateTimeOffset( dateTimeOffsetString ); } catch { DateTimeOffset d; if( DateTimeOffset.TryParse( dateTimeOffsetString, out d ) ) return d; return defaultValue; } } public static TimeSpan GetXMLTimeSpan( string timeSpanString, TimeSpan defaultValue ) { try { return XmlConvert.ToTimeSpan( timeSpanString ); } catch { return defaultValue; } } public static string GetAttribute( XmlElement node, string attributeName ) { return GetAttribute( node, attributeName, null ); } public static string GetAttribute( XmlElement node, string attributeName, string defaultValue ) { if ( node == null ) return defaultValue; XmlAttribute attr = node.Attributes[attributeName]; if ( attr == null ) return defaultValue; return attr.Value; } public static string GetText( XmlElement node, string defaultValue ) { if ( node == null ) return defaultValue; return node.InnerText; } public static int GetAddressValue( IPAddress address ) { #pragma warning disable 618 return (int)address.Address; #pragma warning restore 618 } public static long GetLongAddressValue( IPAddress address ) { #pragma warning disable 618 return address.Address; #pragma warning restore 618 } #endregion #region In[...]Range public static bool InRange( Point3D p1, Point3D p2, int range ) { return ( p1.m_X >= (p2.m_X - range) ) && ( p1.m_X <= (p2.m_X + range) ) && ( p1.m_Y >= (p2.m_Y - range) ) && ( p1.m_Y <= (p2.m_Y + range) ); } public static bool InUpdateRange( Point3D p1, Point3D p2 ) { return ( p1.m_X >= (p2.m_X - 18) ) && ( p1.m_X <= (p2.m_X + 18) ) && ( p1.m_Y >= (p2.m_Y - 18) ) && ( p1.m_Y <= (p2.m_Y + 18) ); } public static bool InUpdateRange( Point2D p1, Point2D p2 ) { return ( p1.m_X >= (p2.m_X - 18) ) && ( p1.m_X <= (p2.m_X + 18) ) && ( p1.m_Y >= (p2.m_Y - 18) ) && ( p1.m_Y <= (p2.m_Y + 18) ); } public static bool InUpdateRange( IPoint2D p1, IPoint2D p2 ) { return ( p1.X >= (p2.X - 18) ) && ( p1.X <= (p2.X + 18) ) && ( p1.Y >= (p2.Y - 18) ) && ( p1.Y <= (p2.Y + 18) ); } #endregion public static Direction GetDirection( IPoint2D from, IPoint2D to ) { int dx = to.X - from.X; int dy = to.Y - from.Y; int adx = Math.Abs( dx ); int ady = Math.Abs( dy ); if ( adx >= ady * 3 ) { if ( dx > 0 ) return Direction.East; else return Direction.West; } else if ( ady >= adx * 3 ) { if ( dy > 0 ) return Direction.South; else return Direction.North; } else if ( dx > 0 ) { if ( dy > 0 ) return Direction.Down; else return Direction.Right; } else { if ( dy > 0 ) return Direction.Left; else return Direction.Up; } } /* Should probably be rewritten to use an ITile interface public static bool CanMobileFit( int z, StaticTile[] tiles ) { int checkHeight = 15; int checkZ = z; for ( int i = 0; i < tiles.Length; ++i ) { StaticTile tile = tiles[i]; if ( ((checkZ + checkHeight) > tile.Z && checkZ < (tile.Z + tile.Height))*//* || (tile.Z < (checkZ + checkHeight) && (tile.Z + tile.Height) > checkZ)*//* ) { return false; } else if ( checkHeight == 0 && tile.Height == 0 && checkZ == tile.Z ) { return false; } } return true; } public static bool IsInContact( StaticTile check, StaticTile[] tiles ) { int checkHeight = check.Height; int checkZ = check.Z; for ( int i = 0; i < tiles.Length; ++i ) { StaticTile tile = tiles[i]; if ( ((checkZ + checkHeight) > tile.Z && checkZ < (tile.Z + tile.Height))*//* || (tile.Z < (checkZ + checkHeight) && (tile.Z + tile.Height) > checkZ)*//* ) { return true; } else if ( checkHeight == 0 && tile.Height == 0 && checkZ == tile.Z ) { return true; } } return false; } */ public static object GetArrayCap( Array array, int index ) { return GetArrayCap( array, index, null ); } public static object GetArrayCap( Array array, int index, object emptyValue ) { if ( array.Length > 0 ) { if ( index < 0 ) { index = 0; } else if ( index >= array.Length ) { index = array.Length - 1; } return array.GetValue( index ); } else { return emptyValue; } } #region Random //4d6+8 would be: Utility.Dice( 4, 6, 8 ) public static int Dice( int numDice, int numSides, int bonus ) { int total = 0; for (int i = 0; i < numDice; ++i) total += RandomImpl.Next(numSides) + 1; total += bonus; return total; } public static int RandomList( params int[] list ) { return list[RandomImpl.Next(list.Length)]; } public static bool RandomBool() { return RandomImpl.NextBool(); } public static int RandomMinMax( int min, int max ) { if ( min > max ) { int copy = min; min = max; max = copy; } else if ( min == max ) { return min; } return min + RandomImpl.Next((max - min) + 1); } public static int Random( int from, int count ) { if ( count == 0 ) { return from; } else if ( count > 0 ) { return from + RandomImpl.Next(count); } else { return from - RandomImpl.Next(-count); } } public static int Random( int count ) { return RandomImpl.Next(count); } public static void RandomBytes( byte[] buffer ) { RandomImpl.NextBytes(buffer); } public static double RandomDouble() { return RandomImpl.NextDouble(); } #endregion #region Random Hues /// /// Random pink, blue, green, orange, red or yellow hue /// public static int RandomNondyedHue() { switch ( Random( 6 ) ) { case 0: return RandomPinkHue(); case 1: return RandomBlueHue(); case 2: return RandomGreenHue(); case 3: return RandomOrangeHue(); case 4: return RandomRedHue(); case 5: return RandomYellowHue(); } return 0; } /// /// Random hue in the range 1201-1254 /// public static int RandomPinkHue() { return Random( 1201, 54 ); } /// /// Random hue in the range 1301-1354 /// public static int RandomBlueHue() { return Random( 1301, 54 ); } /// /// Random hue in the range 1401-1454 /// public static int RandomGreenHue() { return Random( 1401, 54 ); } /// /// Random hue in the range 1501-1554 /// public static int RandomOrangeHue() { return Random( 1501, 54 ); } /// /// Random hue in the range 1601-1654 /// public static int RandomRedHue() { return Random( 1601, 54 ); } /// /// Random hue in the range 1701-1754 /// public static int RandomYellowHue() { return Random( 1701, 54 ); } /// /// Random hue in the range 1801-1908 /// public static int RandomNeutralHue() { return Random( 1801, 108 ); } /// /// Random hue in the range 2001-2018 /// public static int RandomSnakeHue() { return Random( 2001, 18 ); } /// /// Random hue in the range 2101-2130 /// public static int RandomBirdHue() { return Random( 2101, 30 ); } /// /// Random hue in the range 2201-2224 /// public static int RandomSlimeHue() { return Random( 2201, 24 ); } /// /// Random hue in the range 2301-2318 /// public static int RandomAnimalHue() { return Random( 2301, 18 ); } /// /// Random hue in the range 2401-2430 /// public static int RandomMetalHue() { return Random( 2401, 30 ); } public static int ClipDyedHue( int hue ) { if ( hue < 2 ) return 2; else if ( hue > 1001 ) return 1001; else return hue; } /// /// Random hue in the range 2-1001 /// public static int RandomDyedHue() { return Random( 2, 1000 ); } /// /// Random hue from 0x62, 0x71, 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 /// public static int RandomBrightHue() { if ( Utility.RandomDouble() < 0.1 ) return Utility.RandomList( 0x62, 0x71 ); return Utility.RandomList( 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 ); } //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] public static int ClipSkinHue( int hue ) { if ( hue < 1002 ) return 1002; else if ( hue > 1058 ) return 1058; else return hue; } //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] public static int RandomSkinHue() { return Random( 1002, 57 ) | 0x8000; } //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] public static int ClipHairHue( int hue ) { if ( hue < 1102 ) return 1102; else if ( hue > 1149 ) return 1149; else return hue; } //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] public static int RandomHairHue() { return Random( 1102, 48 ); } #endregion private static SkillName[] m_AllSkills = new SkillName[] { SkillName.Alchemy, SkillName.Anatomy, SkillName.AnimalLore, SkillName.ItemID, SkillName.ArmsLore, SkillName.Parry, SkillName.Begging, SkillName.Blacksmith, SkillName.Fletching, SkillName.Peacemaking, SkillName.Camping, SkillName.Carpentry, SkillName.Cartography, SkillName.Cooking, SkillName.DetectHidden, SkillName.Discordance, SkillName.EvalInt, SkillName.Healing, SkillName.Fishing, SkillName.Forensics, SkillName.Herding, SkillName.Hiding, SkillName.Provocation, SkillName.Inscribe, SkillName.Lockpicking, SkillName.Magery, SkillName.MagicResist, SkillName.Tactics, SkillName.Snooping, SkillName.Musicianship, SkillName.Poisoning, SkillName.Archery, SkillName.SpiritSpeak, SkillName.Stealing, SkillName.Tailoring, SkillName.AnimalTaming, SkillName.TasteID, SkillName.Tinkering, SkillName.Tracking, SkillName.Veterinary, SkillName.Swords, SkillName.Macing, SkillName.Fencing, SkillName.Wrestling, SkillName.Lumberjacking, SkillName.Mining, SkillName.Meditation, SkillName.Stealth, SkillName.RemoveTrap, SkillName.Necromancy, SkillName.Focus, SkillName.Chivalry, SkillName.Bushido, SkillName.Ninjitsu, SkillName.Spellweaving }; private static SkillName[] m_CombatSkills = new SkillName[] { SkillName.Archery, SkillName.Swords, SkillName.Macing, SkillName.Fencing, SkillName.Wrestling }; private static SkillName[] m_CraftSkills = new SkillName[] { SkillName.Alchemy, SkillName.Blacksmith, SkillName.Fletching, SkillName.Carpentry, SkillName.Cartography, SkillName.Cooking, SkillName.Inscribe, SkillName.Tailoring, SkillName.Tinkering }; public static SkillName RandomSkill() { return m_AllSkills[Utility.Random(m_AllSkills.Length - ( Core.ML ? 0 : Core.SE ? 1 : Core.AOS ? 3 : 6 ) )]; } public static SkillName RandomCombatSkill() { return m_CombatSkills[Utility.Random(m_CombatSkills.Length)]; } public static SkillName RandomCraftSkill() { return m_CraftSkills[Utility.Random(m_CraftSkills.Length)]; } public static void FixPoints( ref Point3D top, ref Point3D bottom ) { if ( bottom.m_X < top.m_X ) { int swap = top.m_X; top.m_X = bottom.m_X; bottom.m_X = swap; } if ( bottom.m_Y < top.m_Y ) { int swap = top.m_Y; top.m_Y = bottom.m_Y; bottom.m_Y = swap; } if ( bottom.m_Z < top.m_Z ) { int swap = top.m_Z; top.m_Z = bottom.m_Z; bottom.m_Z = swap; } } public static ArrayList BuildArrayList( IEnumerable enumerable ) { IEnumerator e = enumerable.GetEnumerator(); ArrayList list = new ArrayList(); while ( e.MoveNext() ) { list.Add( e.Current ); } return list; } public static bool RangeCheck( IPoint2D p1, IPoint2D p2, int range ) { return ( p1.X >= (p2.X - range) ) && ( p1.X <= (p2.X + range) ) && ( p1.Y >= (p2.Y - range) ) && ( p2.Y <= (p2.Y + range) ); } public static void FormatBuffer( TextWriter output, Stream input, int length ) { output.WriteLine( " 0 1 2 3 4 5 6 7 8 9 A B C D E F" ); output.WriteLine( " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --" ); int byteIndex = 0; int whole = length >> 4; int rem = length & 0xF; for ( int i = 0; i < whole; ++i, byteIndex += 16 ) { StringBuilder bytes = new StringBuilder( 49 ); StringBuilder chars = new StringBuilder( 16 ); for ( int j = 0; j < 16; ++j ) { int c = input.ReadByte(); bytes.Append( c.ToString( "X2" ) ); if ( j != 7 ) { bytes.Append( ' ' ); } else { bytes.Append( " " ); } if ( c >= 0x20 && c < 0x7F ) { chars.Append( (char)c ); } else { chars.Append( '.' ); } } output.Write( byteIndex.ToString( "X4" ) ); output.Write( " " ); output.Write( bytes.ToString() ); output.Write( " " ); output.WriteLine( chars.ToString() ); } if ( rem != 0 ) { StringBuilder bytes = new StringBuilder( 49 ); StringBuilder chars = new StringBuilder( rem ); for ( int j = 0; j < 16; ++j ) { if ( j < rem ) { int c = input.ReadByte(); bytes.Append( c.ToString( "X2" ) ); if ( j != 7 ) { bytes.Append( ' ' ); } else { bytes.Append( " " ); } if ( c >= 0x20 && c < 0x7F ) { chars.Append( (char)c ); } else { chars.Append( '.' ); } } else { bytes.Append( " " ); } } output.Write( byteIndex.ToString( "X4" ) ); output.Write( " " ); output.Write( bytes.ToString() ); output.Write( " " ); output.WriteLine( chars.ToString() ); } } private static Stack m_ConsoleColors = new Stack(); public static void PushColor( ConsoleColor color ) { try { m_ConsoleColors.Push( Console.ForegroundColor ); Console.ForegroundColor = color; } catch { } } public static void PopColor() { try { Console.ForegroundColor = m_ConsoleColors.Pop(); } catch { } } public static bool NumberBetween( double num, int bound1, int bound2, double allowance ) { if ( bound1 > bound2 ) { int i = bound1; bound1 = bound2; bound2 = i; } return ( numbound1-allowance ); } public static void AssignRandomHair( Mobile m ) { AssignRandomHair( m, true ); } public static void AssignRandomHair( Mobile m, int hue ) { m.HairItemID = m.Race.RandomHair( m ); m.HairHue = hue; } public static void AssignRandomHair( Mobile m, bool randomHue ) { m.HairItemID = m.Race.RandomHair( m ); if( randomHue ) m.HairHue = m.Race.RandomHairHue(); } public static void AssignRandomFacialHair( Mobile m ) { AssignRandomFacialHair( m, true ); } public static void AssignRandomFacialHair( Mobile m, int hue ) { m.FacialHairItemID = m.Race.RandomFacialHair( m ); m.FacialHairHue = hue; } public static void AssignRandomFacialHair( Mobile m, bool randomHue ) { m.FacialHairItemID = m.Race.RandomFacialHair( m ); if( randomHue ) m.FacialHairHue = m.Race.RandomHairHue(); } public static List CastConvertList( List list ) where TOutput : TInput { return list.ConvertAll( new Converter( delegate( TInput value ) { return (TOutput)value; } ) ); } public static List SafeConvertList( List list ) where TOutput : class { List output = new List( list.Capacity ); for( int i = 0; i < list.Count; i++ ) { TOutput t = list[i] as TOutput; if( t != null ) output.Add( t ); } return output; } } }