mirror of
				https://github.com/ClassiCube/ClassiCube.git
				synced 2025-11-04 03:27:49 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			338 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
 | 
						|
using System;
 | 
						|
using System.Drawing;
 | 
						|
using ClassicalSharp.Model;
 | 
						|
using OpenTK;
 | 
						|
using OpenTK.Input;
 | 
						|
#if ANDROID
 | 
						|
using Android.Graphics;
 | 
						|
#endif
 | 
						|
 | 
						|
namespace ClassicalSharp {
 | 
						|
 | 
						|
	// NOTE: These delegates should be removed when using versions later than NET 2.0.
 | 
						|
	// ################################################################
 | 
						|
	public delegate void Action();
 | 
						|
	public delegate void Action<T1, T2>( T1 arg1, T2 arg2 );
 | 
						|
	public delegate void Action<T1, T2, T3>( T1 arg1, T2 arg2, T3 arg3 );
 | 
						|
	public delegate void Action<T1, T2, T3, T4>( T1 arg1, T2 arg2, T3 arg3, T4 arg4 );
 | 
						|
	public delegate TResult Func<TResult>();
 | 
						|
	public delegate TResult Func<T1, TResult>( T1 arg1 );
 | 
						|
	public delegate TResult Func<T1, T2, TResult>( T1 arg1, T2 arg2 );
 | 
						|
	public delegate TResult Func<T1, T2, T3, TResult>( T1 arg1, T2 arg2, T3 arg3 );
 | 
						|
	public delegate bool TryParseFunc<T>( string s, out T value );
 | 
						|
	// ################################################################
 | 
						|
	
 | 
						|
	public static class Utils {
 | 
						|
		
 | 
						|
		/// <summary> Creates a vector with all components at 1E25. </summary>
 | 
						|
		public static Vector3 MaxPos() { return new Vector3( 1E25f, 1E25f, 1E25f ); }
 | 
						|
		
 | 
						|
		/// <summary> Clamps that specified value such that min ≤ value ≤ max </summary>
 | 
						|
		public static void Clamp( ref float value, float min, float max ) {
 | 
						|
			if( value < min ) value = min;
 | 
						|
			if( value > max ) value = max;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Clamps that specified value such that min ≤ value ≤ max </summary>
 | 
						|
		public static void Clamp( ref int value, int min, int max ) {
 | 
						|
			if( value < min ) value = min;
 | 
						|
			if( value > max ) value = max;
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static Vector3 Mul( Vector3 a, Vector3 scale ) {
 | 
						|
			a.X *= scale.X; a.Y *= scale.Y; a.Z *= scale.Z;
 | 
						|
			return a;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns the next highest power of 2 that is ≥ to the given value. </summary>
 | 
						|
		public static int NextPowerOf2( int value ) {
 | 
						|
			int next = 1;
 | 
						|
			while( value > next )
 | 
						|
				next <<= 1;
 | 
						|
			return next;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns whether the given value is a power of 2. </summary>
 | 
						|
		public static bool IsPowerOf2( int value ) {
 | 
						|
			return value != 0 && (value & (value - 1)) == 0;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns a string with all the colour codes stripped from it. </summary>
 | 
						|
		public static string StripColours( string value ) {
 | 
						|
			if( value.IndexOf( '&' ) == -1 ) return value;
 | 
						|
			char[] output = new char[value.Length];
 | 
						|
			int usedChars = 0;
 | 
						|
			
 | 
						|
			for( int i = 0; i < value.Length; i++ ) {
 | 
						|
				char token = value[i];
 | 
						|
				if( token == '&' ) {
 | 
						|
					i++; // Skip over the following colour code.
 | 
						|
				} else {
 | 
						|
					output[usedChars++] = token;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return new String( output, 0, usedChars );
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns a string with a + removed if it is the last character in the string. </summary>
 | 
						|
		public static string RemoveEndPlus( string value ) {
 | 
						|
			// Workaround for MCDzienny (and others) use a '+' at the end to distinguish classicube.net accounts
 | 
						|
			// from minecraft.net accounts. Unfortunately they also send this ending + to the client.
 | 
						|
			if( String.IsNullOrEmpty( value ) ) return value;
 | 
						|
			
 | 
						|
			return value[value.Length - 1] == '+' ?
 | 
						|
				value.Substring( 0, value.Length - 1 ) : value;
 | 
						|
		}
 | 
						|
		
 | 
						|
		const StringComparison comp = StringComparison.OrdinalIgnoreCase;
 | 
						|
		/// <summary> Returns whether a equals b, ignoring any case differences. </summary>
 | 
						|
		public static bool CaselessEquals( string a, string b ) { return a.Equals( b, comp ); }
 | 
						|
		
 | 
						|
		/// <summary> Returns whether a starts with b, ignoring any case differences. </summary>
 | 
						|
		public static bool CaselessStarts( string a, string b ) { return a.StartsWith( b, comp ); }
 | 
						|
		
 | 
						|
		/// <summary> Returns whether a ends with b, ignoring any case differences. </summary>
 | 
						|
		public static bool CaselessEnds( string a, string b ) { return a.EndsWith( b, comp ); }
 | 
						|
		
 | 
						|
		/// <summary> Converts the given byte array of length N to a hex string of length 2N. </summary>
 | 
						|
		public static string ToHexString( byte[] array ) {
 | 
						|
			int len = array.Length;
 | 
						|
			char[] hexadecimal = new char[len * 2];
 | 
						|
			for( int i = 0; i < array.Length; i++ ) {
 | 
						|
				int value = array[i];
 | 
						|
				int upper = value >> 4;
 | 
						|
				int lower = value & 0x0F;
 | 
						|
				
 | 
						|
				// 48 = index of 0, 55 = index of (A - 10).
 | 
						|
				hexadecimal[i * 2] = upper < 10 ? (char)(upper + 48) : (char)(upper + 55);
 | 
						|
				hexadecimal[i * 2 + 1] = lower < 10 ? (char)(lower + 48) : (char)(lower + 55);
 | 
						|
			}
 | 
						|
			return new String( hexadecimal );
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns the hex code represented by the given character.
 | 
						|
		/// Throws FormatException if the input character isn't a hex code. </summary>
 | 
						|
		public static int ParseHex( char value ) {
 | 
						|
			int hex;
 | 
						|
			if( !TryParseHex( value, out hex ) )
 | 
						|
				throw new FormatException( "Invalid hex code given: " + value );
 | 
						|
			return hex;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Attempts to return the hex code represented by the given character. </summary>
 | 
						|
		public static bool TryParseHex( char value, out int hex ) {
 | 
						|
			hex = 0;
 | 
						|
			if( value >= '0' && value <= '9') {
 | 
						|
				hex = (int)(value - '0');
 | 
						|
			} else if( value >= 'a' && value <= 'f') {
 | 
						|
				hex = (int)(value - 'a') + 10;
 | 
						|
			} else if( value >= 'A' && value <= 'F') {
 | 
						|
				hex = (int)(value - 'A') + 10;
 | 
						|
			} else {
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
 | 
						|
		/// <summary> Attempts to caselessly parse the given string as a Key enum member,
 | 
						|
		/// returning defValue if there was an error parsing. </summary>
 | 
						|
		public static bool TryParseEnum<T>( string value, T defValue, out T result ) {
 | 
						|
			T mapping;
 | 
						|
			try {
 | 
						|
				mapping = (T)Enum.Parse( typeof(T), value, true );
 | 
						|
			} catch( ArgumentException ) {
 | 
						|
				result = defValue;
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
			result = mapping;
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Multiply a value in degrees by this to get its value in radians. </summary>
 | 
						|
		public const float Deg2Rad = (float)(Math.PI / 180);
 | 
						|
		/// <summary> Multiply a value in radians by this to get its value in degrees. </summary>
 | 
						|
		public const float Rad2Deg = (float)(180 / Math.PI);
 | 
						|
		
 | 
						|
		public static int DegreesToPacked( double degrees, int period ) {
 | 
						|
			return (int)(degrees * period / 360.0) % period;
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static int DegreesToPacked( double degrees ) {
 | 
						|
			return (int)(degrees * 256 / 360.0) & 0xFF;
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static double PackedToDegrees( byte packed ) {
 | 
						|
			return packed * 360.0 / 256.0;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Rotates the given 3D coordinates around the y axis. </summary>
 | 
						|
		public static Vector3 RotateY( Vector3 v, float angle ) {
 | 
						|
			float cosA = (float)Math.Cos( angle );
 | 
						|
			float sinA = (float)Math.Sin( angle );
 | 
						|
			return new Vector3( cosA * v.X - sinA * v.Z, v.Y, sinA * v.X + cosA * v.Z );
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Rotates the given 3D coordinates around the y axis. </summary>
 | 
						|
		public static Vector3 RotateY( float x, float y, float z, float angle ) {
 | 
						|
			float cosA = (float)Math.Cos( angle );
 | 
						|
			float sinA = (float)Math.Sin( angle );
 | 
						|
			return new Vector3( cosA * x - sinA * z, y, sinA * x + cosA * z );
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Rotates the given 3D coordinates around the x axis. </summary>
 | 
						|
		public static void RotateX( ref float y, ref float z, float cosA, float sinA ) {
 | 
						|
			float y2 = cosA * y + sinA * z; z = -sinA * y + cosA * z; y = y2;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Rotates the given 3D coordinates around the y axis. </summary>
 | 
						|
		public static void RotateY( ref float x, ref float z, float cosA, float sinA ) {
 | 
						|
			float x2 = cosA * x - sinA * z; z = sinA * x + cosA * z; x = x2;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Rotates the given 3D coordinates around the z axis. </summary>
 | 
						|
		public static void RotateZ( ref float x, ref float y, float cosA, float sinA ) {
 | 
						|
			float x2 = cosA * x + sinA * y; y = -sinA * x + cosA * y; x = x2;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns the square of the euclidean distance between two points. </summary>
 | 
						|
		public static float DistanceSquared( Vector3 p1, Vector3 p2 ) {
 | 
						|
			float dx = p2.X - p1.X;
 | 
						|
			float dy = p2.Y - p1.Y;
 | 
						|
			float dz = p2.Z - p1.Z;
 | 
						|
			return dx * dx + dy * dy + dz * dz;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns the square of the euclidean distance between two points. </summary>
 | 
						|
		public static float DistanceSquared( float x1, float y1, float z1, float x2, float y2, float z2 ) {
 | 
						|
			float dx = x2 - x1;
 | 
						|
			float dy = y2 - y1;
 | 
						|
			float dz = z2 - z1;
 | 
						|
			return dx * dx + dy * dy + dz * dz;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns the square of the euclidean distance between two points. </summary>
 | 
						|
		public static int DistanceSquared( int x1, int y1, int z1, int x2, int y2, int z2 ) {
 | 
						|
			int dx = x2 - x1;
 | 
						|
			int dy = y2 - y1;
 | 
						|
			int dz = z2 - z1;
 | 
						|
			return dx * dx + dy * dy + dz * dz;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns a normalised vector that faces in the direction
 | 
						|
		/// described by the given yaw and pitch. </summary>
 | 
						|
		public static Vector3 GetDirVector( double yawRad, double pitchRad ) {
 | 
						|
			double x = -Math.Cos( pitchRad ) * -Math.Sin( yawRad );
 | 
						|
			double y = -Math.Sin( pitchRad );
 | 
						|
			double z = -Math.Cos( pitchRad ) * Math.Cos( yawRad );
 | 
						|
			return new Vector3( (float)x, (float)y, (float)z );
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static void GetHeading( Vector3 dir, out double yawRad, out double pitchRad ) {
 | 
						|
			pitchRad = Math.Asin( -dir.Y );
 | 
						|
			yawRad = Math.Atan2( dir.Z, dir.X );
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static void LogDebug( string text ) {
 | 
						|
			Console.WriteLine( text );
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static void LogDebug( string text, params object[] args ) {
 | 
						|
			Console.WriteLine( String.Format( text, args ) );
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static int Floor( float value ) {
 | 
						|
			int valueI = (int)value;
 | 
						|
			return value < valueI ? valueI - 1 : valueI;
 | 
						|
		}
 | 
						|
		
 | 
						|
		public static int AdjViewDist( int value ) {
 | 
						|
			return (int)(1.4142135 * value);
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns the number of vertices needed to subdivide a quad. </summary>
 | 
						|
		internal static int CountVertices( int axis1Len, int axis2Len, int axisSize ) {
 | 
						|
			return CeilDiv( axis1Len, axisSize ) * CeilDiv( axis2Len, axisSize ) * 4;
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Performs rounding upwards integer division. </summary>
 | 
						|
		public static int CeilDiv( int a, int b ) {
 | 
						|
			return a / b + (a % b != 0 ? 1 : 0);
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Performs linear interpolation between two values. </summary>
 | 
						|
		public static float Lerp( float a, float b, float t ) {
 | 
						|
			return a + (b - a) * t;
 | 
						|
		}
 | 
						|
		
 | 
						|
		internal static byte FastByte( string s ) {
 | 
						|
			int sum = 0;
 | 
						|
			switch( s.Length ) {
 | 
						|
					case 1: sum = (s[0] - '0'); break;
 | 
						|
					case 2: sum = (s[0] - '0') * 10 + (s[1] - '0'); break;
 | 
						|
					case 3: sum = (s[0] - '0') * 100 + (s[1] - '0') * 10 + (s[2] - '0'); break;
 | 
						|
			}
 | 
						|
			return (byte)sum;
 | 
						|
		}
 | 
						|
		
 | 
						|
		// http://www.opengl-tutorial.org/intermediate-tutorials/billboards-particles/billboards/
 | 
						|
		public static void CalcBillboardPoints( Vector2 size, Vector3 position, ref Matrix4 view, out Vector3 p111,
 | 
						|
		                                       out Vector3 p121, out Vector3 p212, out Vector3 p222 ) {
 | 
						|
			Vector3 centre = position; centre.Y += size.Y / 2;
 | 
						|
			Vector3 a = new Vector3( view.Row0.X * size.X, view.Row1.X * size.X, view.Row2.X * size.X ); // right * size.X
 | 
						|
			Vector3 b = new Vector3( view.Row0.Y * size.Y, view.Row1.Y * size.Y, view.Row2.Y * size.Y ); // up * size.Y
 | 
						|
			
 | 
						|
			p111 = centre + a * -0.5f + b * -0.5f;
 | 
						|
			p121 = centre + a * -0.5f + b *  0.5f;
 | 
						|
			p212 = centre + a *  0.5f + b * -0.5f;
 | 
						|
			p222 = centre + a *  0.5f + b *  0.5f;
 | 
						|
		}	
 | 
						|
		
 | 
						|
		/// <summary> Linearly interpolates between a given angle range, adjusting if necessary. </summary>
 | 
						|
		public static float LerpAngle( float leftAngle, float rightAngle, float t ) {
 | 
						|
			// we have to cheat a bit for angles here.
 | 
						|
			// Consider 350* --> 0*, we only want to travel 10*,
 | 
						|
			// but without adjusting for this case, we would interpolate back the whole 350* degrees.
 | 
						|
			bool invertLeft = leftAngle > 270 && rightAngle < 90;
 | 
						|
			bool invertRight = rightAngle > 270 && leftAngle < 90;
 | 
						|
			if( invertLeft ) leftAngle = leftAngle - 360;
 | 
						|
			if( invertRight ) rightAngle = rightAngle - 360;
 | 
						|
			
 | 
						|
			return Lerp( leftAngle, rightAngle, t );
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Determines the skin type of the specified bitmap. </summary>
 | 
						|
		public static SkinType GetSkinType( Bitmap bmp ) {
 | 
						|
			if( bmp.Width == bmp.Height * 2 ) {
 | 
						|
				return SkinType.Type64x32;
 | 
						|
			} else if( bmp.Width == bmp.Height ) {
 | 
						|
				// Minecraft alex skins have this particular pixel with alpha of 0.
 | 
						|
				int scale = bmp.Width / 64;
 | 
						|
				
 | 
						|
				#if !ANDROID
 | 
						|
				int alpha = bmp.GetPixel( 54 * scale, 20 * scale ).A;
 | 
						|
				#else
 | 
						|
				int alpha = AndroidColor.GetAlphaComponent( bmp.GetPixel( 54 * scale, 20 * scale ) );
 | 
						|
				#endif
 | 
						|
				return alpha >= 127 ? SkinType.Type64x64 : SkinType.Type64x64Slim;
 | 
						|
			} else {
 | 
						|
				throw new NotSupportedException( "unsupported skin dimensions: " + bmp.Width + ", " + bmp.Height );
 | 
						|
			}
 | 
						|
		}
 | 
						|
		
 | 
						|
		/// <summary> Returns whether the specified string starts with http:// or https:// </summary>
 | 
						|
		public static bool IsUrlPrefix( string value, int index ) {
 | 
						|
			int http = value.IndexOf( "http://", index );
 | 
						|
			int https = value.IndexOf( "https://", index );
 | 
						|
			return http == index || https == index;
 | 
						|
		}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
 | 
						|
		
 | 
						|
		/// <summary> Conversion for code page 437 characters from index 0 to 31 to unicode. </summary>
 | 
						|
		public const string ControlCharReplacements = "\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼";
 | 
						|
		
 | 
						|
		/// <summary> Conversion for code page 437 characters from index 127 to 255 to unicode. </summary>
 | 
						|
		public const string ExtendedCharReplacements = "⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»" +
 | 
						|
			"░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌" +
 | 
						|
			"█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u00a0";
 | 
						|
	}
 | 
						|
} |