diff --git a/ClassicalSharp/2D/Widgets/ExtPlayerListWidget.cs b/ClassicalSharp/2D/Widgets/ExtPlayerListWidget.cs index cb975a018..46bc2d896 100644 --- a/ClassicalSharp/2D/Widgets/ExtPlayerListWidget.cs +++ b/ClassicalSharp/2D/Widgets/ExtPlayerListWidget.cs @@ -7,25 +7,31 @@ namespace ClassicalSharp { public class ExtPlayerListWidget : PlayerListWidget { public ExtPlayerListWidget( Game game, Font font ) : base( game, font ) { + textures = new Texture[512]; + titleFont = new Font( "Arial", 12, FontStyle.Underline ); } - PlayerInfo[] info = new PlayerInfo[256]; + PlayerInfo[] info = new PlayerInfo[512]; class PlayerInfo { - public string Name; - public string GroupName; - public byte GroupRank; + public string ListName; + public string PlayerName; + public string GroupName; + public byte GroupRank; public byte NameId; + public bool IsGroup = false; public PlayerInfo( CpeListInfo p ) { - Name = p.ListName; + ListName = Utils.StripColours( p.ListName ); + PlayerName = Utils.StripColours( p.PlayerName ); NameId = p.NameId; GroupName = p.GroupName; GroupRank = p.GroupRank; } - public override string ToString() { - return NameId + ":" + Name + "(" + GroupName + "," + GroupRank + ")"; + public PlayerInfo( string groupName ) { + GroupName = groupName; + IsGroup = true; } } @@ -35,23 +41,21 @@ namespace ClassicalSharp { public bool JustComparingGroups = true; public int Compare( PlayerInfo x, PlayerInfo y ) { - if( JustComparingGroups ) { + if( JustComparingGroups ) return x.GroupName.CompareTo( y.GroupName ); - } int rankOrder = x.GroupRank.CompareTo( y.GroupRank ); - if( rankOrder != 0 ) { - return rankOrder; - } - return x.Name.CompareTo( y.Name ); + return rankOrder != 0 ? rankOrder : + x.ListName.CompareTo( y.ListName ); } } + Font titleFont; public override void Init() { base.Init(); game.Events.CpeListInfoAdded += PlayerListInfoAdded; game.Events.CpeListInfoRemoved += PlayerListInfoRemoved; - game.Events.CpeListInfoChanged += PlayerListInfoChanged; + game.Events.CpeListInfoChanged += PlayerListInfoChanged; } public override void Dispose() { @@ -59,12 +63,13 @@ namespace ClassicalSharp { game.Events.CpeListInfoAdded -= PlayerListInfoAdded; game.Events.CpeListInfoChanged -= PlayerListInfoChanged; game.Events.CpeListInfoRemoved -= PlayerListInfoRemoved; + titleFont.Dispose(); } void PlayerListInfoChanged( object sender, IdEventArgs e ) { for( int i = 0; i < namesCount; i++ ) { PlayerInfo pInfo = info[i]; - if( pInfo.NameId == e.Id ) { + if( !pInfo.IsGroup && pInfo.NameId == e.Id ) { Texture tex = textures[i]; graphicsApi.DeleteTexture( ref tex ); AddPlayerInfo( game.CpePlayersList[e.Id], i ); @@ -77,7 +82,7 @@ namespace ClassicalSharp { void PlayerListInfoRemoved( object sender, IdEventArgs e ) { for( int i = 0; i < namesCount; i++ ) { PlayerInfo pInfo = info[i]; - if( pInfo.NameId == e.Id ) { + if( !pInfo.IsGroup && pInfo.NameId == e.Id ) { Texture tex = textures[i]; graphicsApi.DeleteTexture( ref tex ); RemoveItemAt( textures, i ); @@ -119,8 +124,12 @@ namespace ClassicalSharp { } protected override void SortInfoList() { - if( namesCount == 0 ) return; + if( namesCount == 0 ) return; + // Sort the list into groups + for( int i = 0; i < namesCount; i++ ) { + if( info[i].IsGroup ) DeleteGroup( ref i ); + } comparer.JustComparingGroups = true; Array.Sort( info, textures, 0, namesCount, comparer ); @@ -128,12 +137,40 @@ namespace ClassicalSharp { comparer.JustComparingGroups = false; int index = 0; while( index < namesCount ) { + AddGroup( info[index].GroupName, ref index ); int count = GetGroupCount( index ); Array.Sort( info, textures, index, count, comparer ); index += count; } } + void DeleteGroup( ref int i ) { + graphicsApi.DeleteTexture( ref textures[i] ); + RemoveItemAt( info, i ); + RemoveItemAt( textures, i ); + + namesCount--; + i--; + } + + void AddGroup( string group, ref int index ) { + DrawTextArgs args = new DrawTextArgs( group, true ); + Texture tex = game.Drawer2D.MakeTextTexture( titleFont, 0, 0, ref args ); + PlayerInfo pInfo = new PlayerInfo( group ); + + PushDown( info, index, pInfo ); + PushDown( textures, index, tex ); + index++; + namesCount++; + } + + void PushDown( T[] array, int index, T value ) { + for( int i = array.Length - 1; i > index; i-- ) { + array[i] = array[i - 1]; + } + array[index] = value; + } + int GetGroupCount( int startIndex ) { string group = info[startIndex].GroupName; int count = 0; diff --git a/ClassicalSharp/2D/Widgets/NormalPlayerListWidget.cs b/ClassicalSharp/2D/Widgets/NormalPlayerListWidget.cs index a63352b46..545bbd4f9 100644 --- a/ClassicalSharp/2D/Widgets/NormalPlayerListWidget.cs +++ b/ClassicalSharp/2D/Widgets/NormalPlayerListWidget.cs @@ -7,6 +7,7 @@ namespace ClassicalSharp { public sealed class NormalPlayerListWidget : PlayerListWidget { public NormalPlayerListWidget( Game game, Font font ) : base( game, font ) { + textures = new Texture[256]; } PlayerInfo[] info = new PlayerInfo[256]; @@ -16,7 +17,7 @@ namespace ClassicalSharp { public byte PlayerId; public PlayerInfo( Player p ) { - Name = p.DisplayName; + Name = Utils.StripColours( p.DisplayName ); PlayerId = p.ID; } } diff --git a/ClassicalSharp/2D/Widgets/PlayerListWidget.cs b/ClassicalSharp/2D/Widgets/PlayerListWidget.cs index 1b563f20d..d18233bfc 100644 --- a/ClassicalSharp/2D/Widgets/PlayerListWidget.cs +++ b/ClassicalSharp/2D/Widgets/PlayerListWidget.cs @@ -15,7 +15,7 @@ namespace ClassicalSharp { protected const int boundsSize = 10; protected const int namesPerColumn = 20; protected int namesCount = 0; - protected Texture[] textures = new Texture[256]; + protected Texture[] textures; protected int columns; protected int xMin, xMax, yHeight; protected static FastColour tableCol = new FastColour( 20, 20, 20, 220 ); @@ -114,32 +114,31 @@ namespace ClassicalSharp { } protected void SortPlayerInfo() { - SortInfoList(); - int centreX = game.Width / 2; + SortInfoList(); CalcMaxColumnHeight(); - int y = game.Height / 2 - yHeight / 2; - + int y = game.Height / 2 - yHeight / 2; int midCol = columns / 2; + + int centreX = game.Width / 2; int offset = 0; if( columns % 2 != 0 ) { // For an odd number of columns, the middle column is centred. offset = Utils.CeilDiv( GetColumnWidth( midCol ), 2 ); - } + } - int x = centreX - offset; + xMin = centreX - offset; for( int col = midCol - 1; col >= 0; col-- ) { - x -= GetColumnWidth( col ); - SetColumnPos( col, x, y ); - } - xMin = x; - - x = centreX - offset; + xMin -= GetColumnWidth( col ); + SetColumnPos( col, xMin, y ); + } + xMax = centreX - offset; for( int col = midCol; col < columns; col++ ) { - SetColumnPos( col, x, y ); - x += GetColumnWidth( col ); + SetColumnPos( col, xMax, y ); + xMax += GetColumnWidth( col ); } - xMax = x; + UpdateTableDimensions(); + MoveTo( X, game.Height / 4 ); } } } \ No newline at end of file diff --git a/ClassicalSharp/Entities/LocalPlayer.cs b/ClassicalSharp/Entities/LocalPlayer.cs index 9bc745de4..ea1d1f1b4 100644 --- a/ClassicalSharp/Entities/LocalPlayer.cs +++ b/ClassicalSharp/Entities/LocalPlayer.cs @@ -247,8 +247,8 @@ namespace ClassicalSharp { internal float lastYaw, nextYaw, lastPitch, nextPitch; public void SetInterpPosition( float t ) { Position = Vector3.Lerp( lastPos, nextPos, t ); - YawDegrees = Utils.InterpAngle( lastYaw, nextYaw, t ); - PitchDegrees = Utils.InterpAngle( lastPitch, nextPitch, t ); + YawDegrees = Utils.LerpAngle( lastYaw, nextYaw, t ); + PitchDegrees = Utils.LerpAngle( lastPitch, nextPitch, t ); } internal void HandleKeyDown( Key key ) { diff --git a/ClassicalSharp/Entities/NetPlayer.cs b/ClassicalSharp/Entities/NetPlayer.cs index 02d0fe164..f95fbdc03 100644 --- a/ClassicalSharp/Entities/NetPlayer.cs +++ b/ClassicalSharp/Entities/NetPlayer.cs @@ -34,8 +34,8 @@ namespace ClassicalSharp { } else { // Smoother interpolation by also adding midpoint. Vector3 midPos = Vector3.Lerp( lastPos, serverPos, 0.5f ); - float midYaw = Utils.InterpAngle( lastYaw, serverYaw, 0.5f ); - float midPitch = Utils.InterpAngle( lastPitch, serverPitch, 0.5f ); + float midYaw = Utils.LerpAngle( lastYaw, serverYaw, 0.5f ); + float midPitch = Utils.LerpAngle( lastPitch, serverPitch, 0.5f ); AddState( new State( tickCount, midPos, midYaw, midPitch ) ); AddState( new State( tickCount, serverPos, serverYaw, serverPitch ) ); } @@ -88,8 +88,8 @@ namespace ClassicalSharp { public override void Render( double deltaTime, float t ) { Position = Vector3.Lerp( oldState.pos, newState.pos, t ); - YawDegrees = Utils.InterpAngle( oldState.yaw, newState.yaw, t ); - PitchDegrees = Utils.InterpAngle( oldState.pitch, newState.pitch, t ); + YawDegrees = Utils.LerpAngle( oldState.yaw, newState.yaw, t ); + PitchDegrees = Utils.LerpAngle( oldState.pitch, newState.pitch, t ); GetCurrentAnimState( t ); RenderModel( deltaTime ); diff --git a/ClassicalSharp/Network/NetworkProcessor.CPE.cs b/ClassicalSharp/Network/NetworkProcessor.CPE.cs index 2c49e6a45..2de7bb095 100644 --- a/ClassicalSharp/Network/NetworkProcessor.CPE.cs +++ b/ClassicalSharp/Network/NetworkProcessor.CPE.cs @@ -136,6 +136,7 @@ namespace ClassicalSharp { CpeListInfo oldInfo = game.CpePlayersList[nameId]; CpeListInfo info = new CpeListInfo( (byte)nameId, playerName, listName, groupName, groupRank ); game.CpePlayersList[nameId] = info; + //Console.WriteLine( nameId + ": " + groupRank + " , " + groupName + " : " + listName ); if( oldInfo != null ) { game.Events.RaiseCpeListInfoChanged( (byte)nameId ); diff --git a/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs b/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs index 371901c19..f870f3405 100644 --- a/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs +++ b/ClassicalSharp/Rendering/MapRenderer.Occlusion.cs @@ -40,25 +40,24 @@ namespace ClassicalSharp { chunkIn.DistanceFlags |= (mapLoc.Z < 0 || mapLoc.Z >= game.Map.Length) ? flagZ : (byte)0; } + Console.WriteLine( "SRC {0}", cx + "," + cy + "," + cz ); QueueChunk( cx - 1, cy, cz, queue ); QueueChunk( cx + 1, cy, cz, queue ); QueueChunk( cx, cy - 1, cz, queue ); QueueChunk( cx, cy + 1, cz, queue ); QueueChunk( cx, cy, cz - 1, queue ); - QueueChunk( cx, cy, cz + 1, queue ); - ProcessQueue( chunkPos, queue ); + QueueChunk( cx, cy, cz + 1, queue ); + ProcessQueue( queue ); chunkIn.OcclusionFlags = chunkInOcclusionFlags; } - void ProcessQueue( Vector3I p, ChunkQueue queue ) { + void ProcessQueue( ChunkQueue queue ) { + Vector3I p = chunkPos; while( queue.Size > 0 ) { ChunkInfo chunk = queue.Dequeue(); int x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8; int y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8; int z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8; - int cx = chunk.CentreX >> 4; - int cy = chunk.CentreY >> 4; - int cz = chunk.CentreZ >> 4; int xOffset, yOffset, zOffset; int dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) ); @@ -68,33 +67,38 @@ namespace ClassicalSharp { // X axis collisions int dxLeft = Math.Abs( x1 - p.X ), dxRight = Math.Abs( x2 - p.X ); - if( dxLeft < dxRight ) { + if( dxLeft < dxRight ) { distX = dxLeft * dxLeft + dy * dy + dz * dz; xOffset = -1; - } else { + } else { distX = dxRight * dxRight + dy * dy + dz * dz; xOffset = 1; } // Z axis collisions int dxFront = Math.Abs( z1 - p.Z ), dxBack = Math.Abs( z2 - p.Z ); - if( dxFront < dxBack ) { + if( dxFront < dxBack ) { distZ = dx * dx + dy * dy + dxFront * dxFront; zOffset = -1; - } else { + } else { distZ = dx * dx + dy * dy + dxBack * dxBack; zOffset = 1; } // Y axis collisions int dxBottom = Math.Abs( y1 - p.Y ), dxTop = Math.Abs( y2 - p.Y ); - if( dxBottom < dxTop ) { + if( dxBottom < dxTop ) { distY = dx * dx + dxBottom * dxBottom + dz * dz; yOffset = -1; - } else { + } else { distY = dx * dx + dxTop * dxTop + dz * dz; yOffset = 1; } chunk.Occluded = true; int distMin = Math.Min( distX, Math.Min( distY, distZ ) ); - if( distMin == distX ) OccludeX( cx, cy, cz, xOffset, chunk ); - if( distMin == distZ ) OccludeZ( cx, cy, cz, zOffset, chunk ); + int cx = chunk.CentreX >> 4, cy = chunk.CentreY >> 4, cz = chunk.CentreZ >> 4; + + if( !chunk.Empty ) { + Console.WriteLine( chunk.OccludedFlags + " , " + xOffset + " : " + yOffset + " : " + zOffset ); + } + if( distMin == distX ) OccludeX( cx, cy, cz, xOffset, chunk ); if( distMin == distY ) OccludeY( cx, cy, cz, yOffset, chunk ); + if( distMin == distZ ) OccludeZ( cx, cy, cz, zOffset, chunk ); //Console.WriteLine( distMin + " , " + distX + " , " + distY + " , " + distZ ); //Console.WriteLine( chunk.DistanceFlags + " : " + chunk.OccludedFlags + " : " + chunk.OcclusionFlags ); @@ -104,22 +108,77 @@ namespace ClassicalSharp { QueueChunk( cx, cy, cz + 1, queue ); QueueChunk( cx, cy - 1, cz, queue ); QueueChunk( cx, cy + 1, cz, queue ); + if( !chunk.Empty ) { + Console.WriteLine( "{0} D {1}: V {2}, O {3}, {4}", cx + "," + cy + "," + cz, chunk.DistanceFlags, + chunk.OccludedFlags, chunk.OcclusionFlags, chunk.Occluded ); + Console.WriteLine( " M {0} : X {1}, Y {2}, Z {3} ({4}, {5})", distMin, distX, distY, distZ, dxFront, dxBack ); + } } Console.WriteLine( "======================" ); } const byte flagX = 1, flagZ = 2, flagY = 4; public void DebugPickedPos() { + return; if( game.SelectedPos.Valid ) { Vector3I p = game.SelectedPos.BlockPos; - int cx = p.X >> 4; - int cy = p.Y >> 4; - int cz = p.Z >> 4; - ChunkInfo chunk = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; - //Console.WriteLine( chunk.DistanceFlags + " : " + chunk.OccludedFlags + " : " + chunk.OcclusionFlags + " , " + chunk.Occluded ); + int cx = p.X >> 4; int cy = p.Y >> 4; int cz = p.Z >> 4; + Print( "-", cx, cy, cz ); + //if( cx > 0 ) Print( "X-", cx - 1, cy, cz ); + //if( cy > 0 ) Print( "Y-", cx, cy - 1, cz ); + //if( cz > 0 ) Print( "Z-", cx, cy, cz - 1 ); + + //if( cx < chunksX - 1 ) Print( "X+", cx + 1, cy, cz ); + //if( cy < chunksY - 1 ) Print( "Y+", cx, cy + 1, cz ); + if( cz < chunksZ - 1 ) Print( "Z+", cx, cy, cz + 1 ); + if( cz < chunksZ - 2 ) Print( "Z+2", cx, cy, cz + 2 ); + if( cz < chunksZ - 3 ) Print( "Z+3", cx, cy, cz + 3 ); } } + void Print( string prefix, int cx, int cy, int cz ) { + ChunkInfo chunk = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; + Console.WriteLine( "{0} D {1}: V {2}, O {3}, {4}", prefix, chunk.DistanceFlags, + chunk.OccludedFlags, chunk.OcclusionFlags, chunk.Occluded ); + // Console.WriteLine( chunk.DistanceFlags + " : " + chunk.OccludedFlags + " : " + chunk.OcclusionFlags + " , " + chunk.Occluded ); + + Vector3I p = chunkPos; + int x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8; + int y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8; + int z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8; + int dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) ); + int dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) ); + int dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) ); + int distX, distY, distZ; + + // X axis collisions + int dxLeft = Math.Abs( x1 - p.X ), dxRight = Math.Abs( x2 - p.X ); + if( dxLeft < dxRight ) { + distX = dxLeft * dxLeft + dy * dy + dz * dz; + } else { + distX = dxRight * dxRight + dy * dy + dz * dz; + } + + // Z axis collisions + int dxFront = Math.Abs( z1 - p.Z ), dxBack = Math.Abs( z2 - p.Z ); + if( dxFront < dxBack ) { + distZ = dx * dx + dy * dy + dxFront * dxFront; + } else { + distZ = dx * dx + dy * dy + dxBack * dxBack; + } + + // Y axis collisions + int dxBottom = Math.Abs( y1 - p.Y ), dxTop = Math.Abs( y2 - p.Y ); + if( dxBottom < dxTop ) { + distY = dx * dx + dxBottom * dxBottom + dz * dz; + } else { + distY = dx * dx + dxTop * dxTop + dz * dz; + } + + int distMin = Math.Min( distX, Math.Min( distY, distZ ) ); + Console.WriteLine( " M {0} : X {1}, Y {2}, Z {3} ({4}, {5})", distMin, distX, distY, distZ, dxFront, dxBack ); + } + void OccludeX( int cx, int cy, int cz, int xOffset, ChunkInfo info ) { cx += xOffset; if( cx >= 0 && cx < chunksX ) { @@ -134,20 +193,6 @@ namespace ClassicalSharp { info.DistanceFlags |= flagX; } - void OccludeZ( int cx, int cy, int cz, int zOffset, ChunkInfo info ) { - cz += zOffset; - if( cz >= 0 && cz < chunksZ ) { - ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; - if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags ) - info.Occluded = false; - else - info.OccludedFlags |= flagZ; - } else { - info.Occluded = false; - } - info.DistanceFlags |= flagZ; - } - void OccludeY( int cx, int cy, int cz, int yOffset, ChunkInfo info ) { cy += yOffset; if( cy >= 0 && cy < chunksY ) { @@ -161,11 +206,25 @@ namespace ClassicalSharp { } info.DistanceFlags |= flagY; } + + void OccludeZ( int cx, int cy, int cz, int zOffset, ChunkInfo info ) { + cz += zOffset; + if( cz >= 0 && cz < chunksZ ) { + ChunkInfo neighbour = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; + if( (neighbour.OccludedFlags & neighbour.DistanceFlags) != neighbour.DistanceFlags ) + info.Occluded = false; + else + info.OccludedFlags |= flagZ; + } else { + info.Occluded = false; + } + info.DistanceFlags |= flagZ; + } void QueueChunk( int cx, int cy, int cz, ChunkQueue queue ) { if( cx >= 0 && cy >= 0 && cz >= 0 && cx < chunksX && cy < chunksY && cz < chunksZ ) { ChunkInfo info = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; - if( !info.Visited ) + if( !info.Visited ) queue.Enqueue( info ); info.Visited = true; } diff --git a/ClassicalSharp/Rendering/MapRenderer.cs b/ClassicalSharp/Rendering/MapRenderer.cs index a12a3289c..46211c127 100644 --- a/ClassicalSharp/Rendering/MapRenderer.cs +++ b/ClassicalSharp/Rendering/MapRenderer.cs @@ -301,7 +301,7 @@ namespace ClassicalSharp { } api.AlphaTest = false; api.Texturing = false; - //DebugPickedPos(); + DebugPickedPos(); } // Render translucent(liquid) blocks. These 'blend' into other blocks. diff --git a/ClassicalSharp/Utils/Utils.cs b/ClassicalSharp/Utils/Utils.cs index 072690a9c..2b3918e25 100644 --- a/ClassicalSharp/Utils/Utils.cs +++ b/ClassicalSharp/Utils/Utils.cs @@ -21,16 +21,19 @@ namespace ClassicalSharp { public static string AppName = "ClassicalSharp 0.95"; + /// Clamps that specified value such that min ≤ value ≤ max public static void Clamp( ref float value, float min, float max ) { if( value < min ) value = min; if( value > max ) value = max; } + /// Clamps that specified value such that min ≤ value ≤ max public static void Clamp( ref int value, int min, int max ) { if( value < min ) value = min; if( value > max ) value = max; } + /// Returns the next highest power of 2 that is ≥ to the given value. public static int NextPowerOf2( int value ) { int next = 1; while( value > next ) { @@ -39,14 +42,12 @@ namespace ClassicalSharp { return next; } + /// Returns whether the given value is a power of 2. public static bool IsPowerOf2( int value ) { - return value != 0 && ( value & ( value - 1 ) ) == 0; - } - - public static bool IsUrl( string value ) { - return value.StartsWith( "http://" ) || value.StartsWith( "https://" ); + return value != 0 && (value & (value - 1)) == 0; } + /// Returns a string with all the colour codes stripped from it. public static string StripColours( string value ) { if( value.IndexOf( '&' ) == -1 ) { return value; @@ -79,15 +80,18 @@ namespace ClassicalSharp { char[] hexadecimal = new char[len * 2]; for( int i = 0; i < array.Length; i++ ) { int value = array[i]; - int index = i << 1; - int upperNibble = value >> 4; - int lowerNibble = value & 0x0F; - hexadecimal[index] = upperNibble < 10 ? (char)( upperNibble + 48 ) : (char)( upperNibble + 55 ); // 48 = index of 0, 55 = index of (A - 10). - hexadecimal[index + 1] = lowerNibble < 10 ? (char)( lowerNibble + 48 ) : (char)( lowerNibble + 55 ); + 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 ); } + /// Returns the hex code represented by the given character. + /// Throws FormatException if the input character isn't a hex code. public static int ParseHex( char value ) { if( value >= '0' && value <= '9' ) { return (int)( value - '0' ); @@ -100,7 +104,9 @@ namespace ClassicalSharp { } } + /// Multiply a value in degrees by this to get its value in radians. public const float Deg2Rad = (float)(Math.PI / 180); + /// Multiply a value in radians by this to get its value in degrees. public const float Rad2Deg = (float)(180 / Math.PI); public static int DegreesToPacked( double degrees, int period ) { @@ -147,6 +153,7 @@ namespace ClassicalSharp { return new Vector3( cosA * x + sinA * y, -sinA * x + cosA * y, z ); } + /// Returns the square of the euclidean distance between two points. public static float DistanceSquared( Vector3 p1, Vector3 p2 ) { float dx = p2.X - p1.X; float dy = p2.Y - p1.Y; @@ -154,6 +161,7 @@ namespace ClassicalSharp { return dx * dx + dy * dy + dz * dz; } + /// Returns the square of the euclidean distance between two points. public static float DistanceSquared( float x1, float y1, float z1, float x2, float y2, float z2 ) { float dx = x2 - x1; float dy = y2 - y1; @@ -161,6 +169,7 @@ namespace ClassicalSharp { return dx * dx + dy * dy + dz * dz; } + /// Returns the square of the euclidean distance between two points. public static int DistanceSquared( int x1, int y1, int z1, int x2, int y2, int z2 ) { int dx = x2 - x1; int dy = y2 - y1; @@ -221,19 +230,23 @@ namespace ClassicalSharp { return value >= 0 ? (int)value : (int)value - 1; } - public static float Lerp( float a, float b, float t ) { - return a + (b - a) * t; - } - + /// Returns the number of vertices needed to subdivide a quad. internal static int CountVertices( int axis1Len, int axis2Len, int axisSize ) { return CeilDiv( axis1Len, axisSize ) * CeilDiv( axis2Len, axisSize ) * 4; } + /// Performs rounding upwards integer division. public static int CeilDiv( int a, int b ) { return a / b + (a % b != 0 ? 1 : 0); } - public static float InterpAngle( float leftAngle, float rightAngle, float t ) { + /// Performs linear interpolation between two values. + public static float Lerp( float a, float b, float t ) { + return a + (b - a) * t; + } + + /// Linearly interpolates between a given angle range, adjusting if necessary. + 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. @@ -245,6 +258,7 @@ namespace ClassicalSharp { return Lerp( leftAngle, rightAngle, t ); } + /// Determines the skin type of the specified bitmap. public static SkinType GetSkinType( Bitmap bmp ) { if( bmp.Width == bmp.Height * 2 ) { return SkinType.Type64x32; @@ -260,5 +274,9 @@ namespace ClassicalSharp { throw new NotSupportedException( "unsupported skin dimensions: " + bmp.Width + ", " + bmp.Height ); } } + + public static bool IsUrl( string value ) { + return value.StartsWith( "http://" ) || value.StartsWith( "https://" ); + } } } \ No newline at end of file