diff --git a/ClassicalSharp/2D/Screens/FpsScreen.cs b/ClassicalSharp/2D/Screens/FpsScreen.cs index 19995d693..ce5f25540 100644 --- a/ClassicalSharp/2D/Screens/FpsScreen.cs +++ b/ClassicalSharp/2D/Screens/FpsScreen.cs @@ -137,10 +137,10 @@ namespace ClassicalSharp.Gui { bool speeding, halfSpeeding, noclip, fly; int lastZoomFov; void UpdateHackState( bool force ) { - LocalPlayer p = game.LocalPlayer; - if( force || p.speeding != speeding || p.halfSpeeding != halfSpeeding || p.noClip != noclip || - p.flying != fly || game.ZoomFieldOfView != lastZoomFov ) { - speeding = p.speeding; halfSpeeding = p.halfSpeeding; noclip = p.noClip; fly = p.flying; + HacksComponent hacks = game.LocalPlayer.Hacks; + if( force || hacks.Speeding != speeding || hacks.HalfSpeeding != halfSpeeding || hacks.Noclip != noclip || + hacks.Flying != fly || game.ZoomFieldOfView != lastZoomFov ) { + speeding = hacks.Speeding; halfSpeeding = hacks.HalfSpeeding; noclip = hacks.Noclip; fly = hacks.Flying; lastZoomFov = game.ZoomFieldOfView; int index = 0; text.Clear(); diff --git a/ClassicalSharp/2D/Screens/Menu/HacksSettingsScreen.cs b/ClassicalSharp/2D/Screens/Menu/HacksSettingsScreen.cs index 0ebc49e55..98c9ba017 100644 --- a/ClassicalSharp/2D/Screens/Menu/HacksSettingsScreen.cs +++ b/ClassicalSharp/2D/Screens/Menu/HacksSettingsScreen.cs @@ -31,7 +31,7 @@ namespace ClassicalSharp.Gui { Make2( -1, 0, "Jump height", OnWidgetClick, g => g.LocalPlayer.JumpHeight.ToString( "F3" ), - (g, v) => g.LocalPlayer.CalculateJumpVelocity( Single.Parse( v ) ) ), + (g, v) => g.LocalPlayer.physics.CalculateJumpVelocity( Single.Parse( v ) ) ), MakeBool( -1, 50, "Double jump", OptionsKey.DoubleJump, OnWidgetClick, g => g.LocalPlayer.Hacks.DoubleJump, diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index 27ab04fed..555c6969d 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -150,13 +150,13 @@ + - @@ -170,7 +170,7 @@ - + diff --git a/ClassicalSharp/Entities/Components/CollisionsComponent.cs b/ClassicalSharp/Entities/Components/CollisionsComponent.cs new file mode 100644 index 000000000..d48674c9f --- /dev/null +++ b/ClassicalSharp/Entities/Components/CollisionsComponent.cs @@ -0,0 +1,331 @@ +// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT +using System; +using OpenTK; + +namespace ClassicalSharp.Entities { + + /// Entity component that performs collision detection. + public sealed class CollisionsComponent { + + Game game; + Entity entity; + BlockInfo info; + public CollisionsComponent( Game game, Entity entity ) { + this.game = game; + this.entity = entity; + info = game.BlockInfo; + } + + internal bool hitYMax, collideX, collideY, collideZ; + + /// Constant offset used to avoid floating point roundoff errors. + public const float Adjustment = 0.001f; + + public byte GetPhysicsBlockId( int x, int y, int z ) { + if( x < 0 || x >= game.World.Width || z < 0 || + z >= game.World.Length || y < 0 ) return (byte)Block.Bedrock; + + if( y >= game.World.Height ) return (byte)Block.Air; + return game.World.GetBlock( x, y, z ); + } + + bool GetBoundingBox( byte block, int x, int y, int z, ref BoundingBox box ) { + if( info.Collide[block] != CollideType.Solid ) return false; + Add( x, y, z, ref info.MinBB[block], ref box.Min ); + Add( x, y, z, ref info.MaxBB[block], ref box.Max ); + return true; + } + + static void Add( int x, int y, int z, ref Vector3 offset, ref Vector3 target ) { + target.X = x + offset.X; + target.Y = y + offset.Y; + target.Z = z + offset.Z; + } + + + // TODO: test for corner cases, and refactor this. + internal void MoveAndWallSlide() { + if( entity.Velocity == Vector3.Zero ) return; + Vector3 size = entity.CollisionSize; + BoundingBox entityBB, entityExtentBB; + int count = 0; + FindReachableBlocks( ref count, ref size, out entityBB, out entityExtentBB ); + CollideWithReachableBlocks( count, ref size, ref entityBB, ref entityExtentBB ); + } + + void FindReachableBlocks( ref int count, ref Vector3 size, + out BoundingBox entityBB, out BoundingBox entityExtentBB ) { + Vector3 vel = entity.Velocity; + Vector3 pos = entity.Position; + entityBB = new BoundingBox( + pos.X - size.X / 2, pos.Y, pos.Z - size.Z / 2, + pos.X + size.X / 2, pos.Y + size.Y, pos.Z + size.Z / 2 + ); + + // Exact maximum extent the entity can reach, and the equivalent map coordinates. + entityExtentBB = new BoundingBox( + vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X, + vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y, + vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z, + vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X, + vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y, + vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z + ); + Vector3I min = Vector3I.Floor( entityExtentBB.Min ); + Vector3I max = Vector3I.Floor( entityExtentBB.Max ); + + int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z); + if( elements > stateCache.Length ) { + stateCache = new State[elements]; + } + + BoundingBox blockBB = default( BoundingBox ); + // Order loops so that we minimise cache misses + for( int y = min.Y; y <= max.Y; y++ ) + for( int z = min.Z; z <= max.Z; z++ ) + for( int x = min.X; x <= max.X; x++ ) + { + byte blockId = GetPhysicsBlockId( x, y, z ); + if( !GetBoundingBox( blockId, x, y, z, ref blockBB ) ) continue; + if( !entityExtentBB.Intersects( blockBB ) ) continue; // necessary for non whole blocks. (slabs) + + float tx = 0, ty = 0, tz = 0; + CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz ); + if( tx > 1 || ty > 1 || tz > 1 ) continue; + float tSquared = tx * tx + ty * ty + tz * tz; + stateCache[count++] = new State( x, y, z, blockId, tSquared ); + } + } + + void CollideWithReachableBlocks( int count, ref Vector3 size, + ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { + bool wasOn = entity.onGround; + entity.onGround = false; + if( count > 0 ) + QuickSort( stateCache, 0, count - 1 ); + collideX = false; collideY = false; collideZ = false; + BoundingBox blockBB = default(BoundingBox); + + for( int i = 0; i < count; i++ ) { + State state = stateCache[i]; + Vector3 blockPos = new Vector3( state.X >> 3, state.Y >> 3, state.Z >> 3 ); + int block = (state.X & 0x7) | (state.Y & 0x7) << 3 | (state.Z & 0x7) << 6; + blockBB.Min = blockPos + info.MinBB[block]; + blockBB.Max = blockPos + info.MaxBB[block]; + if( !entityExtentBB.Intersects( blockBB ) ) continue; + + float tx = 0, ty = 0, tz = 0; + CalcTime( ref entity.Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz ); + if( tx > 1 || ty > 1 || tz > 1 ) + Utils.LogDebug( "t > 1 in physics calculation.. this shouldn't have happened." ); + BoundingBox finalBB = entityBB.Offset( entity.Velocity * new Vector3( tx, ty, tz ) ); + + // if we have hit the bottom of a block, we need to change the axis we test first. + if( hitYMax ) { + if( finalBB.Min.Y + Adjustment >= blockBB.Max.Y ) + ClipYMax( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); + else if( finalBB.Max.Y - Adjustment <= blockBB.Min.Y ) + ClipYMin( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); + else if( finalBB.Min.X + Adjustment >= blockBB.Max.X ) + ClipXMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Max.X - Adjustment <= blockBB.Min.X ) + ClipXMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Min.Z + Adjustment >= blockBB.Max.Z ) + ClipZMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Max.Z - Adjustment <= blockBB.Min.Z ) + ClipZMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + continue; + } + + // if flying or falling, test the horizontal axes first. + if( finalBB.Min.X + Adjustment >= blockBB.Max.X ) + ClipXMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Max.X - Adjustment <= blockBB.Min.X ) + ClipXMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Min.Z + Adjustment >= blockBB.Max.Z ) + ClipZMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Max.Z - Adjustment <= blockBB.Min.Z ) + ClipZMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); + else if( finalBB.Min.Y + Adjustment >= blockBB.Max.Y ) + ClipYMax( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); + else if( finalBB.Max.Y - Adjustment <= blockBB.Min.Y ) + ClipYMin( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); + } + } + + void ClipXMin( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, + BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) { + if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) { + entity.Position.X = blockBB.Min.X - size.X / 2 - Adjustment; + ClipX( ref size, ref entityBB, ref entityExtentBB ); + } + } + + void ClipXMax( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, + BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) { + if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) { + entity.Position.X = blockBB.Max.X + size.X / 2 + Adjustment; + ClipX( ref size, ref entityBB, ref entityExtentBB ); + } + } + + void ClipZMax( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, + BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) { + if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) { + entity.Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment; + ClipZ( ref size, ref entityBB, ref entityExtentBB ); + } + } + + void ClipZMin( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, + BoundingBox finalBB, ref BoundingBox extentBB, ref Vector3 size ) { + if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref extentBB ) ) { + entity.Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment; + ClipZ( ref size, ref entityBB, ref extentBB ); + } + } + + void ClipYMin( ref BoundingBox blockBB, ref BoundingBox entityBB, + ref BoundingBox extentBB, ref Vector3 size ) { + entity.Position.Y = blockBB.Min.Y - size.Y - Adjustment; + ClipY( ref size, ref entityBB, ref extentBB ); + hitYMax = false; + } + + void ClipYMax( ref BoundingBox blockBB, ref BoundingBox entityBB, + ref BoundingBox extentBB, ref Vector3 size ) { + entity.Position.Y = blockBB.Max.Y + Adjustment; + entity.onGround = true; + ClipY( ref size, ref entityBB, ref extentBB ); + hitYMax = true; + } + + bool DidSlide( BoundingBox blockBB, ref Vector3 size, BoundingBox finalBB, + ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { + float yDist = blockBB.Max.Y - entityBB.Min.Y; + if( yDist > 0 && yDist <= entity.StepSize + 0.01f ) { + float blockXMin = blockBB.Min.X, blockZMin = blockBB.Min.Z; + blockBB.Min.X = Math.Max( blockBB.Min.X, blockBB.Max.X - size.X / 2 ); + blockBB.Max.X = Math.Min( blockBB.Max.X, blockXMin + size.X / 2 ); + blockBB.Min.Z = Math.Max( blockBB.Min.Z, blockBB.Max.Z - size.Z / 2 ); + blockBB.Max.Z = Math.Min( blockBB.Max.Z, blockZMin + size.Z / 2 ); + + BoundingBox adjBB = finalBB; + adjBB.Min.X = Math.Min( finalBB.Min.X, blockBB.Min.X + Adjustment ); + adjBB.Max.X = Math.Max( finalBB.Max.X, blockBB.Max.X - Adjustment ); + adjBB.Min.Y = blockBB.Max.Y + Adjustment; + adjBB.Max.Y = adjBB.Min.Y + size.Y; + adjBB.Min.Z = Math.Min( finalBB.Min.Z, blockBB.Min.Z + Adjustment ); + adjBB.Max.Z = Math.Max( finalBB.Max.Z, blockBB.Max.Z - Adjustment ); + + if( !CanSlideThrough( ref adjBB ) ) + return false; + entity.Position.Y = blockBB.Max.Y + Adjustment; + entity.onGround = true; + ClipY( ref size, ref entityBB, ref entityExtentBB ); + return true; + } + return false; + } + + bool CanSlideThrough( ref BoundingBox adjFinalBB ) { + Vector3I bbMin = Vector3I.Floor( adjFinalBB.Min ); + Vector3I bbMax = Vector3I.Floor( adjFinalBB.Max ); + + for( int y = bbMin.Y; y <= bbMax.Y; y++ ) + for( int z = bbMin.Z; z <= bbMax.Z; z++ ) + for( int x = bbMin.X; x <= bbMax.X; x++ ) + { + byte block = GetPhysicsBlockId( x, y, z ); + Vector3 min = new Vector3( x, y, z ) + info.MinBB[block]; + Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block]; + + BoundingBox blockBB = new BoundingBox( min, max ); + if( !blockBB.Intersects( adjFinalBB ) ) + continue; + if( info.Collide[GetPhysicsBlockId( x, y, z )] == CollideType.Solid ) + return false; + } + return true; + } + + void ClipX( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { + entity.Velocity.X = 0; + entityBB.Min.X = entityExtentBB.Min.X = entity.Position.X - size.X / 2; + entityBB.Max.X = entityExtentBB.Max.X = entity.Position.X + size.X / 2; + collideX = true; + } + + void ClipY( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { + entity.Velocity.Y = 0; + entityBB.Min.Y = entityExtentBB.Min.Y = entity.Position.Y; + entityBB.Max.Y = entityExtentBB.Max.Y = entity.Position.Y + size.Y; + collideY = true; + } + + void ClipZ( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { + entity.Velocity.Z = 0; + entityBB.Min.Z = entityExtentBB.Min.Z = entity.Position.Z - size.Z / 2; + entityBB.Max.Z = entityExtentBB.Max.Z = entity.Position.Z + size.Z / 2; + collideZ = true; + } + + static void CalcTime( ref Vector3 vel, ref BoundingBox entityBB, ref BoundingBox blockBB, + out float tx, out float ty, out float tz ) { + float dx = vel.X > 0 ? blockBB.Min.X - entityBB.Max.X : entityBB.Min.X - blockBB.Max.X; + float dy = vel.Y > 0 ? blockBB.Min.Y - entityBB.Max.Y : entityBB.Min.Y - blockBB.Max.Y; + float dz = vel.Z > 0 ? blockBB.Min.Z - entityBB.Max.Z : entityBB.Min.Z - blockBB.Max.Z; + + tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs( dx / vel.X ); + ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs( dy / vel.Y ); + tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs( dz / vel.Z ); + + if( entityBB.XIntersects( blockBB ) ) tx = 0; + if( entityBB.YIntersects( blockBB ) ) ty = 0; + if( entityBB.ZIntersects( blockBB ) ) tz = 0; + } + + + struct State { + public int X, Y, Z; + public float tSquared; + + public State( int x, int y, int z, byte block, float tSquared ) { + X = x << 3; Y = y << 3; Z = z << 3; + X |= (block & 0x07); + Y |= (block & 0x38) >> 3; + Z |= (block & 0xC0) >> 6; + this.tSquared = tSquared; + } + } + static State[] stateCache = new State[0]; + + static void QuickSort( State[] keys, int left, int right ) { + while( left < right ) { + int i = left, j = right; + float pivot = keys[(i + j) / 2].tSquared; + // partition the list + while( i <= j ) { + while( pivot > keys[i].tSquared ) i++; + while( pivot < keys[j].tSquared ) j--; + + if( i <= j ) { + State key = keys[i]; keys[i] = keys[j]; keys[j] = key; + i++; j--; + } + } + + // recurse into the smaller subset + if( j - left <= right - i ) { + if( left < j ) + QuickSort( keys, left, j ); + left = i; + } else { + if( i < right ) + QuickSort( keys, i, right ); + right = j; + } + } + } + } +} \ No newline at end of file diff --git a/ClassicalSharp/Entities/Components/HacksComponent.cs b/ClassicalSharp/Entities/Components/HacksComponent.cs index 96ae54d3e..22ad64052 100644 --- a/ClassicalSharp/Entities/Components/HacksComponent.cs +++ b/ClassicalSharp/Entities/Components/HacksComponent.cs @@ -27,43 +27,45 @@ namespace ClassicalSharp.Entities { /// Whether the player has allowed hacks usage as an option. /// Note that all 'can use X' set by the server override this. - public bool Enabled = true; - + public bool Enabled = true; /// Whether the player is allowed to use any type of hacks. - public bool CanAnyHacks = true; - + public bool CanAnyHacks = true; /// Whether the player is allowed to use the types of cameras that use third person. - public bool CanUseThirdPersonCamera = true; - + public bool CanUseThirdPersonCamera = true; /// Whether the player is allowed to increase its speed beyond the normal walking speed. - public bool CanSpeed = true; - + public bool CanSpeed = true; /// Whether the player is allowed to fly in the world. - public bool CanFly = true; - + public bool CanFly = true; /// Whether the player is allowed to teleport to their respawn coordinates. - public bool CanRespawn = true; - + public bool CanRespawn = true; /// Whether the player is allowed to pass through all blocks. - public bool CanNoclip = true; - + public bool CanNoclip = true; /// Whether the player is allowed to use pushback block placing. - public bool CanPushbackBlocks = true; - + public bool CanPushbackBlocks = true; /// Whether the player is allowed to see all entity name tags. - public bool CanSeeAllNames = true; + public bool CanSeeAllNames = true; + /// Whether the player is allowed to double jump. + public bool CanDoubleJump = true; + /// Maximum speed the entity can move at horizontally when CanSpeed is false. + public float MaxSpeedMultiplier = 1; /// Whether the player should slide after letting go of movement buttons in noclip. - public bool NoclipSlide = true; - + public bool NoclipSlide = true; /// Whether the player has allowed the usage of fast double jumping abilities. public bool DoubleJump = false; - /// Whether the player is allowed to double jump. - public bool CanDoubleJump = true; - - /// Maximum speed the entity can move at horizontally when CanSpeed is false. - public float MaxSpeedMultiplier = 1; + /// Whether the player currently has noclip on. + public bool Noclip; + /// Whether the player currently has fly mode active. + public bool Flying; + /// Whether the player is currently flying upwards. + public bool FlyingUp; + /// Whether the player is currently flying downwards. + public bool FlyingDown; + /// Whether the player is currently walking at base speed * speed multiplier. + public bool Speeding; + /// Whether the player is currently walking at base speed * 0.5 * speed multiplier. + public bool HalfSpeeding; /// Parses hack flags specified in the motd and/or name of the server. /// Recognises +/-hax, +/-fly, +/-noclip, +/-speed, +/-respawn, +/-ophax, and horspeed=xyz @@ -124,5 +126,16 @@ namespace ClassicalSharp.Entities { inv.CanPlace[(int)Block.StillLava] = value == 0x64; CanSeeAllNames = value == 0x64; } + + /// Disables any hacks if their respective CanHackX value is set to false. + public void CheckHacksConsistency() { + if( !CanFly || !Enabled ) { Flying = false; FlyingDown = false; FlyingUp = false; } + if( !CanNoclip || !Enabled ) Noclip = false; + if( !CanSpeed || !Enabled ) { Speeding = false; HalfSpeeding = false; } + CanDoubleJump = CanAnyHacks && Enabled && CanSpeed; + + if( !CanUseThirdPersonCamera || !Enabled ) + game.CycleCamera(); + } } } \ No newline at end of file diff --git a/ClassicalSharp/Entities/Components/PhysicsComponent.cs b/ClassicalSharp/Entities/Components/PhysicsComponent.cs index 30bd41ff4..81bbd8840 100644 --- a/ClassicalSharp/Entities/Components/PhysicsComponent.cs +++ b/ClassicalSharp/Entities/Components/PhysicsComponent.cs @@ -7,325 +7,257 @@ namespace ClassicalSharp.Entities { /// Entity component that performs collision detection. public sealed class PhysicsComponent { - Game game; + bool useLiquidGravity = false; // used by BlockDefinitions. + bool canLiquidJump = true; + internal bool firstJump, secondJump, jumping; Entity entity; + Game game; BlockInfo info; + internal float jumpVel = 0.42f, serverJumpVel = 0.42f; + internal HacksComponent hacks; + internal CollisionsComponent collisions; + public PhysicsComponent( Game game, Entity entity ) { this.game = game; this.entity = entity; info = game.BlockInfo; } - internal bool hitYMax, collideX, collideY, collideZ; - - /// Constant offset used to avoid floating point roundoff errors. - public const float Adjustment = 0.001f; - - public byte GetPhysicsBlockId( int x, int y, int z ) { - if( x < 0 || x >= game.World.Width || z < 0 || - z >= game.World.Length || y < 0 ) return (byte)Block.Bedrock; - - if( y >= game.World.Height ) return (byte)Block.Air; - return game.World.GetBlock( x, y, z ); - } - - bool GetBoundingBox( byte block, int x, int y, int z, ref BoundingBox box ) { - if( info.Collide[block] != CollideType.Solid ) return false; - Add( x, y, z, ref info.MinBB[block], ref box.Min ); - Add( x, y, z, ref info.MaxBB[block], ref box.Max ); - return true; - } - - static void Add( int x, int y, int z, ref Vector3 offset, ref Vector3 target ) { - target.X = x + offset.X; - target.Y = y + offset.Y; - target.Z = z + offset.Z; - } - - - // TODO: test for corner cases, and refactor this. - internal void MoveAndWallSlide() { - if( entity.Velocity == Vector3.Zero ) return; - Vector3 size = entity.CollisionSize; - BoundingBox entityBB, entityExtentBB; - int count = 0; - FindReachableBlocks( ref count, ref size, out entityBB, out entityExtentBB ); - CollideWithReachableBlocks( count, ref size, ref entityBB, ref entityExtentBB ); - } - - void FindReachableBlocks( ref int count, ref Vector3 size, - out BoundingBox entityBB, out BoundingBox entityExtentBB ) { - Vector3 vel = entity.Velocity; - Vector3 pos = entity.Position; - entityBB = new BoundingBox( - pos.X - size.X / 2, pos.Y, pos.Z - size.Z / 2, - pos.X + size.X / 2, pos.Y + size.Y, pos.Z + size.Z / 2 - ); - - // Exact maximum extent the entity can reach, and the equivalent map coordinates. - entityExtentBB = new BoundingBox( - vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X, - vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y, - vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z, - vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X, - vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y, - vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z - ); - Vector3I min = Vector3I.Floor( entityExtentBB.Min ); - Vector3I max = Vector3I.Floor( entityExtentBB.Max ); - - int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z); - if( elements > stateCache.Length ) { - stateCache = new State[elements]; + public void UpdateVelocityState( float xMoving, float zMoving ) { + if( !hacks.NoclipSlide && (hacks.Noclip && xMoving == 0 && zMoving == 0) ) + entity.Velocity = Vector3.Zero; + if( hacks.Flying || hacks.Noclip ) { + entity.Velocity.Y = 0; // eliminate the effect of gravity + int dir = (hacks.FlyingUp || jumping) ? 1 : (hacks.FlyingDown ? -1 : 0); + + entity.Velocity.Y += 0.12f * dir; + if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += 0.12f * dir; + if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += 0.06f * dir; + } else if( jumping && entity.TouchesAnyRope() && entity.Velocity.Y > 0.02f ) { + entity.Velocity.Y = 0.02f; } - BoundingBox blockBB = default( BoundingBox ); - // Order loops so that we minimise cache misses - for( int y = min.Y; y <= max.Y; y++ ) - for( int z = min.Z; z <= max.Z; z++ ) - for( int x = min.X; x <= max.X; x++ ) - { - byte blockId = GetPhysicsBlockId( x, y, z ); - if( !GetBoundingBox( blockId, x, y, z, ref blockBB ) ) continue; - if( !entityExtentBB.Intersects( blockBB ) ) continue; // necessary for non whole blocks. (slabs) - - float tx = 0, ty = 0, tz = 0; - CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz ); - if( tx > 1 || ty > 1 || tz > 1 ) continue; - float tSquared = tx * tx + ty * ty + tz * tz; - stateCache[count++] = new State( x, y, z, blockId, tSquared ); + if( !jumping ) { + canLiquidJump = false; return; } - } - - void CollideWithReachableBlocks( int count, ref Vector3 size, - ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { - bool wasOn = entity.onGround; - entity.onGround = false; - if( count > 0 ) - QuickSort( stateCache, 0, count - 1 ); - collideX = false; collideY = false; collideZ = false; - BoundingBox blockBB = default(BoundingBox); - - for( int i = 0; i < count; i++ ) { - State state = stateCache[i]; - Vector3 blockPos = new Vector3( state.X >> 3, state.Y >> 3, state.Z >> 3 ); - int block = (state.X & 0x7) | (state.Y & 0x7) << 3 | (state.Z & 0x7) << 6; - blockBB.Min = blockPos + info.MinBB[block]; - blockBB.Max = blockPos + info.MaxBB[block]; - if( !entityExtentBB.Intersects( blockBB ) ) continue; + + bool touchWater = entity.TouchesAnyWater(); + bool touchLava = entity.TouchesAnyLava(); + if( touchWater || touchLava ) { + BoundingBox bounds = entity.CollisionBounds; + int feetY = Utils.Floor( bounds.Min.Y ), bodyY = feetY + 1; + int headY = Utils.Floor( bounds.Max.Y ); + if( bodyY > headY ) bodyY = headY; - float tx = 0, ty = 0, tz = 0; - CalcTime( ref entity.Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz ); - if( tx > 1 || ty > 1 || tz > 1 ) - Utils.LogDebug( "t > 1 in physics calculation.. this shouldn't have happened." ); - BoundingBox finalBB = entityBB.Offset( entity.Velocity * new Vector3( tx, ty, tz ) ); + bounds.Max.Y = bounds.Min.Y = feetY; + bool liquidFeet = entity.TouchesAny( bounds, StandardLiquid ); + bounds.Min.Y = Math.Min( bodyY, headY ); + bounds.Max.Y = Math.Max( bodyY, headY ); + bool liquidRest = entity.TouchesAny( bounds, StandardLiquid ); - // if we have hit the bottom of a block, we need to change the axis we test first. - if( hitYMax ) { - if( finalBB.Min.Y + Adjustment >= blockBB.Max.Y ) - ClipYMax( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); - else if( finalBB.Max.Y - Adjustment <= blockBB.Min.Y ) - ClipYMin( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); - else if( finalBB.Min.X + Adjustment >= blockBB.Max.X ) - ClipXMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Max.X - Adjustment <= blockBB.Min.X ) - ClipXMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Min.Z + Adjustment >= blockBB.Max.Z ) - ClipZMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Max.Z - Adjustment <= blockBB.Min.Z ) - ClipZMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - continue; + bool pastJumpPoint = liquidFeet && !liquidRest && (entity.Position.Y % 1 >= 0.4); + if( !pastJumpPoint ) { + canLiquidJump = true; + entity.Velocity.Y += 0.04f; + if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += 0.04f; + if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += 0.02f; + } else if( pastJumpPoint ) { + // either A) jump bob in water B) climb up solid on side + if( canLiquidJump || (collisions.collideX || collisions.collideZ) ) + entity.Velocity.Y += touchLava ? 0.20f : 0.10f; + canLiquidJump = false; } - - // if flying or falling, test the horizontal axes first. - if( finalBB.Min.X + Adjustment >= blockBB.Max.X ) - ClipXMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Max.X - Adjustment <= blockBB.Min.X ) - ClipXMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Min.Z + Adjustment >= blockBB.Max.Z ) - ClipZMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Max.Z - Adjustment <= blockBB.Min.Z ) - ClipZMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size ); - else if( finalBB.Min.Y + Adjustment >= blockBB.Max.Y ) - ClipYMax( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); - else if( finalBB.Max.Y - Adjustment <= blockBB.Min.Y ) - ClipYMin( ref blockBB, ref entityBB, ref entityExtentBB, ref size ); + } else if( useLiquidGravity ) { + entity.Velocity.Y += 0.04f; + if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += 0.04f; + if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += 0.02f; + canLiquidJump = false; + } else if( entity.TouchesAnyRope() ) { + entity.Velocity.Y += (hacks.Speeding && hacks.CanSpeed) ? 0.15f : 0.10f; + canLiquidJump = false; + } else if( entity.onGround ) { + DoNormalJump(); } } - void ClipXMin( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, - BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) { - if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) { - entity.Position.X = blockBB.Min.X - size.X / 2 - Adjustment; - ClipX( ref size, ref entityBB, ref entityExtentBB ); + public void DoNormalJump() { + entity.Velocity.Y = jumpVel; + if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += jumpVel; + if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += jumpVel / 2; + canLiquidJump = false; + } + + bool StandardLiquid( byte block ) { + return info.Collide[block] == CollideType.SwimThrough; + } + + static Vector3 waterDrag = new Vector3( 0.8f, 0.8f, 0.8f ), + lavaDrag = new Vector3( 0.5f, 0.5f, 0.5f ), + ropeDrag = new Vector3( 0.5f, 0.85f, 0.5f ), + normalDrag = new Vector3( 0.91f, 0.98f, 0.91f ), + airDrag = new Vector3( 0.6f, 1f, 0.6f ); + const float liquidGrav = 0.02f, ropeGrav = 0.034f, normalGrav = 0.08f; + + public void PhysicsTick( float xMoving, float zMoving ) { + if( hacks.Noclip ) entity.onGround = false; + float multiply = GetBaseMultiply( true ); + float yMultiply = GetBaseMultiply( hacks.CanSpeed ); + float modifier = LowestSpeedModifier(); + + float yMul = Math.Max( 1f, yMultiply / 5 ) * modifier; + float horMul = multiply * modifier; + if( !(hacks.Flying || hacks.Noclip) ) { + if( secondJump ) { horMul *= 93f; yMul *= 10f; } + else if( firstJump ) { horMul *= 46.5f; yMul *= 7.5f; } } - } - - void ClipXMax( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, - BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) { - if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) { - entity.Position.X = blockBB.Max.X + size.X / 2 + Adjustment; - ClipX( ref size, ref entityBB, ref entityExtentBB ); + + if( entity.TouchesAnyWater() && !hacks.Flying && !hacks.Noclip ) { + MoveNormal( xMoving, zMoving, 0.02f * horMul, waterDrag, liquidGrav, yMul ); + } else if( entity.TouchesAnyLava() && !hacks.Flying && !hacks.Noclip ) { + MoveNormal( xMoving, zMoving, 0.02f * horMul, lavaDrag, liquidGrav, yMul ); + } else if( entity.TouchesAnyRope() && !hacks.Flying && !hacks.Noclip ) { + MoveNormal( xMoving, zMoving, 0.02f * 1.7f, ropeDrag, ropeGrav, yMul ); + } else { + float factor = !(hacks.Flying || hacks.Noclip) && entity.onGround ? 0.1f : 0.02f; + float gravity = useLiquidGravity ? liquidGrav : normalGrav; + if( hacks.Flying || hacks.Noclip ) + MoveFlying( xMoving, zMoving, factor * horMul, normalDrag, gravity, yMul ); + else + MoveNormal( xMoving, zMoving, factor * horMul, normalDrag, gravity, yMul ); + + if( entity.BlockUnderFeet == Block.Ice && !(hacks.Flying || hacks.Noclip) ) { + // limit components to +-0.25f by rescaling vector to [-0.25, 0.25] + if( Math.Abs( entity.Velocity.X ) > 0.25f || Math.Abs( entity.Velocity.Z ) > 0.25f ) { + float scale = Math.Min( + Math.Abs( 0.25f / entity.Velocity.X ), Math.Abs( 0.25f / entity.Velocity.Z ) ); + entity.Velocity.X *= scale; + entity.Velocity.Z *= scale; + } + } else if( entity.onGround || hacks.Flying ) { + entity.Velocity *= airDrag; // air drag or ground friction + } } + + if( entity.onGround ) { firstJump = false; secondJump = false; } } - void ClipZMax( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, - BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) { - if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) { - entity.Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment; - ClipZ( ref size, ref entityBB, ref entityExtentBB ); + void AdjHeadingVelocity( float x, float z, float factor ) { + float dist = (float)Math.Sqrt( x * x + z * z ); + if( dist < 0.00001f ) return; + if( dist < 1 ) dist = 1; + + float multiply = factor / dist; + entity.Velocity += Utils.RotateY( x * multiply, 0, z * multiply, entity.HeadYawRadians ); + } + + void MoveFlying( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) { + AdjHeadingVelocity( zMoving, xMoving, factor ); + float yVel = (float)Math.Sqrt( entity.Velocity.X * entity.Velocity.X + entity.Velocity.Z * entity.Velocity.Z ); + // make vertical speed the same as vertical speed. + if( (xMoving != 0 || zMoving != 0) && yVel > 0.001f ) { + entity.Velocity.Y = 0; + yMul = 1; + if( hacks.FlyingUp || jumping ) entity.Velocity.Y += yVel; + if( hacks.FlyingDown ) entity.Velocity.Y -= yVel; } + Move( xMoving, zMoving, factor, drag, gravity, yMul ); } - void ClipZMin( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn, - BoundingBox finalBB, ref BoundingBox extentBB, ref Vector3 size ) { - if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref extentBB ) ) { - entity.Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment; - ClipZ( ref size, ref entityBB, ref extentBB ); + void MoveNormal( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) { + AdjHeadingVelocity( zMoving, xMoving, factor ); + Move( xMoving, zMoving, factor, drag, gravity, yMul ); + } + + void Move( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) { + entity.Velocity.Y *= yMul; + if( !hacks.Noclip ) + collisions.MoveAndWallSlide(); + entity.Position += entity.Velocity; + + entity.Velocity.Y /= yMul; + entity.Velocity *= drag; + entity.Velocity.Y -= gravity; + } + + float GetBaseMultiply( bool canSpeed ) { + float multiply = 0; + if( hacks.Flying || hacks.Noclip ) { + if( hacks.Speeding && canSpeed ) multiply += hacks.SpeedMultiplier * 8; + if( hacks.HalfSpeeding && canSpeed ) multiply += hacks.SpeedMultiplier * 8 / 2; + if( multiply == 0 ) multiply = 8f; + } else { + if( hacks.Speeding && canSpeed ) multiply += hacks.SpeedMultiplier; + if( hacks.HalfSpeeding && canSpeed ) multiply += hacks.SpeedMultiplier / 2; + if( multiply == 0 ) multiply = 1; } + return hacks.CanSpeed ? multiply : Math.Min( multiply, hacks.MaxSpeedMultiplier ); } - void ClipYMin( ref BoundingBox blockBB, ref BoundingBox entityBB, - ref BoundingBox extentBB, ref Vector3 size ) { - entity.Position.Y = blockBB.Min.Y - size.Y - Adjustment; - ClipY( ref size, ref entityBB, ref extentBB ); - hitYMax = false; + const float inf = float.PositiveInfinity; + float LowestSpeedModifier() { + BoundingBox bounds = entity.CollisionBounds; + useLiquidGravity = false; + float baseModifier = LowestModifier( bounds, false ); + bounds.Min.Y -= 0.5f/16f; // also check block standing on + float solidModifier = LowestModifier( bounds, true ); + + if( baseModifier == inf && solidModifier == inf ) return 1; + return baseModifier == inf ? solidModifier : baseModifier; } - void ClipYMax( ref BoundingBox blockBB, ref BoundingBox entityBB, - ref BoundingBox extentBB, ref Vector3 size ) { - entity.Position.Y = blockBB.Max.Y + Adjustment; - entity.onGround = true; - ClipY( ref size, ref entityBB, ref extentBB ); - hitYMax = true; - } - - bool DidSlide( BoundingBox blockBB, ref Vector3 size, BoundingBox finalBB, - ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { - float yDist = blockBB.Max.Y - entityBB.Min.Y; - if( yDist > 0 && yDist <= entity.StepSize + 0.01f ) { - float blockXMin = blockBB.Min.X, blockZMin = blockBB.Min.Z; - blockBB.Min.X = Math.Max( blockBB.Min.X, blockBB.Max.X - size.X / 2 ); - blockBB.Max.X = Math.Min( blockBB.Max.X, blockXMin + size.X / 2 ); - blockBB.Min.Z = Math.Max( blockBB.Min.Z, blockBB.Max.Z - size.Z / 2 ); - blockBB.Max.Z = Math.Min( blockBB.Max.Z, blockZMin + size.Z / 2 ); - - BoundingBox adjBB = finalBB; - adjBB.Min.X = Math.Min( finalBB.Min.X, blockBB.Min.X + Adjustment ); - adjBB.Max.X = Math.Max( finalBB.Max.X, blockBB.Max.X - Adjustment ); - adjBB.Min.Y = blockBB.Max.Y + Adjustment; - adjBB.Max.Y = adjBB.Min.Y + size.Y; - adjBB.Min.Z = Math.Min( finalBB.Min.Z, blockBB.Min.Z + Adjustment ); - adjBB.Max.Z = Math.Max( finalBB.Max.Z, blockBB.Max.Z - Adjustment ); - - if( !CanSlideThrough( ref adjBB ) ) - return false; - entity.Position.Y = blockBB.Max.Y + Adjustment; - entity.onGround = true; - ClipY( ref size, ref entityBB, ref entityExtentBB ); - return true; - } - return false; - } - - bool CanSlideThrough( ref BoundingBox adjFinalBB ) { - Vector3I bbMin = Vector3I.Floor( adjFinalBB.Min ); - Vector3I bbMax = Vector3I.Floor( adjFinalBB.Max ); + float LowestModifier( BoundingBox bounds, bool checkSolid ) { + Vector3I bbMin = Vector3I.Floor( bounds.Min ); + Vector3I bbMax = Vector3I.Floor( bounds.Max ); + float modifier = inf; for( int y = bbMin.Y; y <= bbMax.Y; y++ ) for( int z = bbMin.Z; z <= bbMax.Z; z++ ) for( int x = bbMin.X; x <= bbMax.X; x++ ) { - byte block = GetPhysicsBlockId( x, y, z ); + byte block = game.World.SafeGetBlock( x, y, z ); + if( block == 0 ) continue; + CollideType type = info.Collide[block]; + if( type == CollideType.Solid && !checkSolid ) + continue; + Vector3 min = new Vector3( x, y, z ) + info.MinBB[block]; Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block]; - BoundingBox blockBB = new BoundingBox( min, max ); - if( !blockBB.Intersects( adjFinalBB ) ) - continue; - if( info.Collide[GetPhysicsBlockId( x, y, z )] == CollideType.Solid ) - return false; - } - return true; - } - - void ClipX( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { - entity.Velocity.X = 0; - entityBB.Min.X = entityExtentBB.Min.X = entity.Position.X - size.X / 2; - entityBB.Max.X = entityExtentBB.Max.X = entity.Position.X + size.X / 2; - collideX = true; - } - - void ClipY( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { - entity.Velocity.Y = 0; - entityBB.Min.Y = entityExtentBB.Min.Y = entity.Position.Y; - entityBB.Max.Y = entityExtentBB.Max.Y = entity.Position.Y + size.Y; - collideY = true; - } - - void ClipZ( ref Vector3 size, ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) { - entity.Velocity.Z = 0; - entityBB.Min.Z = entityExtentBB.Min.Z = entity.Position.Z - size.Z / 2; - entityBB.Max.Z = entityExtentBB.Max.Z = entity.Position.Z + size.Z / 2; - collideZ = true; - } - - static void CalcTime( ref Vector3 vel, ref BoundingBox entityBB, ref BoundingBox blockBB, - out float tx, out float ty, out float tz ) { - float dx = vel.X > 0 ? blockBB.Min.X - entityBB.Max.X : entityBB.Min.X - blockBB.Max.X; - float dy = vel.Y > 0 ? blockBB.Min.Y - entityBB.Max.Y : entityBB.Min.Y - blockBB.Max.Y; - float dz = vel.Z > 0 ? blockBB.Min.Z - entityBB.Max.Z : entityBB.Min.Z - blockBB.Max.Z; - - tx = vel.X == 0 ? float.PositiveInfinity : Math.Abs( dx / vel.X ); - ty = vel.Y == 0 ? float.PositiveInfinity : Math.Abs( dy / vel.Y ); - tz = vel.Z == 0 ? float.PositiveInfinity : Math.Abs( dz / vel.Z ); - - if( entityBB.XIntersects( blockBB ) ) tx = 0; - if( entityBB.YIntersects( blockBB ) ) ty = 0; - if( entityBB.ZIntersects( blockBB ) ) tz = 0; - } - - - struct State { - public int X, Y, Z; - public float tSquared; - - public State( int x, int y, int z, byte block, float tSquared ) { - X = x << 3; Y = y << 3; Z = z << 3; - X |= (block & 0x07); - Y |= (block & 0x38) >> 3; - Z |= (block & 0xC0) >> 6; - this.tSquared = tSquared; - } - } - static State[] stateCache = new State[0]; - - static void QuickSort( State[] keys, int left, int right ) { - while( left < right ) { - int i = left, j = right; - float pivot = keys[(i + j) / 2].tSquared; - // partition the list - while( i <= j ) { - while( pivot > keys[i].tSquared ) i++; - while( pivot < keys[j].tSquared ) j--; - - if( i <= j ) { - State key = keys[i]; keys[i] = keys[j]; keys[j] = key; - i++; j--; - } - } + if( !blockBB.Intersects( bounds ) ) continue; - // recurse into the smaller subset - if( j - left <= right - i ) { - if( left < j ) - QuickSort( keys, left, j ); - left = i; - } else { - if( i < right ) - QuickSort( keys, i, right ); - right = j; - } + modifier = Math.Min( modifier, info.SpeedMultiplier[block] ); + if( block >= BlockInfo.CpeBlocksCount && type == CollideType.SwimThrough ) + useLiquidGravity = true; } + return modifier; + } + + /// Calculates the jump velocity required such that when a client presses + /// the jump binding they will be able to jump up to the given height. + internal void CalculateJumpVelocity( float jumpHeight ) { + jumpVel = 0; + if( jumpHeight >= 256 ) jumpVel = 10.0f; + if( jumpHeight >= 512 ) jumpVel = 16.5f; + if( jumpHeight >= 768 ) jumpVel = 22.5f; + + while( GetMaxHeight( jumpVel ) <= jumpHeight ) + jumpVel += 0.001f; + } + + public static double GetMaxHeight( float u ) { + // equation below comes from solving diff(x(t, u))= 0 + // We only work in discrete timesteps, so test both rounded up and down. + double t = 49.49831645 * Math.Log( 0.247483075 * u + 0.9899323 ); + return Math.Max( YPosAt( (int)t, u ), YPosAt( (int)t + 1, u ) ); + } + + static double YPosAt( int t, float u ) { + // v(t, u) = (4 + u) * (0.98^t) - 4, where u = initial velocity + // x(t, u) = Σv(t, u) from 0 to t (since we work in discrete timesteps) + // plugging into Wolfram Alpha gives 1 equation as + // (0.98^t) * (-49u - 196) - 4t + 50u + 196 + double a = Math.Exp( -0.0202027 * t ); //~0.98^t + return a * ( -49 * u - 196 ) - 4 * t + 50 * u + 196; } } } \ No newline at end of file diff --git a/ClassicalSharp/Entities/Entity.Bounds.cs b/ClassicalSharp/Entities/Entity.Bounds.cs index 2a2310a10..ef7b6c553 100644 --- a/ClassicalSharp/Entities/Entity.Bounds.cs +++ b/ClassicalSharp/Entities/Entity.Bounds.cs @@ -60,7 +60,7 @@ namespace ClassicalSharp.Entities { /// Determines whether any of the blocks that intersect the /// bounding box of this entity are lava or still lava. - protected bool TouchesAnyLava() { + public bool TouchesAnyLava() { BoundingBox bounds = CollisionBounds.Expand( liqExpand ); AdjustLiquidTestBounds( ref bounds ); return TouchesAny( bounds, @@ -69,7 +69,7 @@ namespace ClassicalSharp.Entities { /// Determines whether any of the blocks that intersect the /// bounding box of this entity are rope. - protected bool TouchesAnyRope() { + public bool TouchesAnyRope() { BoundingBox bounds = CollisionBounds; bounds.Max.Y += 0.5f/16f; return TouchesAny( bounds, b => b == (byte)Block.Rope ); @@ -77,7 +77,7 @@ namespace ClassicalSharp.Entities { /// Determines whether any of the blocks that intersect the /// bounding box of this entity are water or still water. - protected bool TouchesAnyWater() { + public bool TouchesAnyWater() { BoundingBox bounds = CollisionBounds.Expand( liqExpand ); AdjustLiquidTestBounds( ref bounds ); return TouchesAny( bounds, diff --git a/ClassicalSharp/Entities/LocalPlayer.Physics.cs b/ClassicalSharp/Entities/LocalPlayer.Physics.cs deleted file mode 100644 index 1bbf27951..000000000 --- a/ClassicalSharp/Entities/LocalPlayer.Physics.cs +++ /dev/null @@ -1,251 +0,0 @@ -// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT -using System; -using System.Drawing; -using ClassicalSharp.Renderers; -using OpenTK; -using OpenTK.Input; - -namespace ClassicalSharp.Entities { - - public partial class LocalPlayer : Player { - - bool useLiquidGravity = false; // used by BlockDefinitions. - bool canLiquidJump = true; - bool firstJump = false, secondJump = false; - - void UpdateVelocityState( float xMoving, float zMoving ) { - if( !Hacks.NoclipSlide && (noClip && xMoving == 0 && zMoving == 0) ) - Velocity = Vector3.Zero; - if( flying || noClip ) { - Velocity.Y = 0; // eliminate the effect of gravity - int dir = (flyingUp || jumping) ? 1 : (flyingDown ? -1 : 0); - - Velocity.Y += 0.12f * dir; - if( speeding && Hacks.CanSpeed ) Velocity.Y += 0.12f * dir; - if( halfSpeeding && Hacks.CanSpeed ) Velocity.Y += 0.06f * dir; - } else if( jumping && TouchesAnyRope() && Velocity.Y > 0.02f ) { - Velocity.Y = 0.02f; - } - - if( !jumping ) { - canLiquidJump = false; return; - } - - bool touchWater = TouchesAnyWater(); - bool touchLava = TouchesAnyLava(); - if( touchWater || touchLava ) { - BoundingBox bounds = CollisionBounds; - int feetY = Utils.Floor( bounds.Min.Y ), bodyY = feetY + 1; - int headY = Utils.Floor( bounds.Max.Y ); - if( bodyY > headY ) bodyY = headY; - - bounds.Max.Y = bounds.Min.Y = feetY; - bool liquidFeet = TouchesAny( bounds, StandardLiquid ); - bounds.Min.Y = Math.Min( bodyY, headY ); - bounds.Max.Y = Math.Max( bodyY, headY ); - bool liquidRest = TouchesAny( bounds, StandardLiquid ); - - bool pastJumpPoint = liquidFeet && !liquidRest && (Position.Y % 1 >= 0.4); - if( !pastJumpPoint ) { - canLiquidJump = true; - Velocity.Y += 0.04f; - if( speeding && Hacks.CanSpeed ) Velocity.Y += 0.04f; - if( halfSpeeding && Hacks.CanSpeed ) Velocity.Y += 0.02f; - } else if( pastJumpPoint ) { - // either A) jump bob in water B) climb up solid on side - if( canLiquidJump || (physics.collideX || physics.collideZ) ) - Velocity.Y += touchLava ? 0.20f : 0.10f; - canLiquidJump = false; - } - } else if( useLiquidGravity ) { - Velocity.Y += 0.04f; - if( speeding && Hacks.CanSpeed ) Velocity.Y += 0.04f; - if( halfSpeeding && Hacks.CanSpeed ) Velocity.Y += 0.02f; - canLiquidJump = false; - } else if( TouchesAnyRope() ) { - Velocity.Y += (speeding && Hacks.CanSpeed) ? 0.15f : 0.10f; - canLiquidJump = false; - } else if( onGround ) { - DoNormalJump(); - } - } - - void DoNormalJump() { - Velocity.Y = jumpVel; - if( speeding && Hacks.CanSpeed ) Velocity.Y += jumpVel; - if( halfSpeeding && Hacks.CanSpeed ) Velocity.Y += jumpVel / 2; - canLiquidJump = false; - } - - bool StandardLiquid( byte block ) { - return info.Collide[block] == CollideType.SwimThrough; - } - - static Vector3 waterDrag = new Vector3( 0.8f, 0.8f, 0.8f ), - lavaDrag = new Vector3( 0.5f, 0.5f, 0.5f ), - ropeDrag = new Vector3( 0.5f, 0.85f, 0.5f ), - normalDrag = new Vector3( 0.91f, 0.98f, 0.91f ), - airDrag = new Vector3( 0.6f, 1f, 0.6f ); - const float liquidGrav = 0.02f, ropeGrav = 0.034f, normalGrav = 0.08f; - - void PhysicsTick( float xMoving, float zMoving ) { - if( noClip ) onGround = false; - float multiply = GetBaseMultiply( true ); - float yMultiply = GetBaseMultiply( Hacks.CanSpeed ); - float modifier = LowestSpeedModifier(); - - float yMul = Math.Max( 1f, yMultiply / 5 ) * modifier; - float horMul = multiply * modifier; - if( !(flying || noClip) ) { - if( secondJump ) { horMul *= 93f; yMul *= 10f; } - else if( firstJump ) { horMul *= 46.5f; yMul *= 7.5f; } - } - - if( TouchesAnyWater() && !flying && !noClip ) { - MoveNormal( xMoving, zMoving, 0.02f * horMul, waterDrag, liquidGrav, yMul ); - } else if( TouchesAnyLava() && !flying && !noClip ) { - MoveNormal( xMoving, zMoving, 0.02f * horMul, lavaDrag, liquidGrav, yMul ); - } else if( TouchesAnyRope() && !flying && !noClip ) { - MoveNormal( xMoving, zMoving, 0.02f * 1.7f, ropeDrag, ropeGrav, yMul ); - } else { - float factor = !(flying || noClip) && onGround ? 0.1f : 0.02f; - float gravity = useLiquidGravity ? liquidGrav : normalGrav; - if( flying || noClip ) - MoveFlying( xMoving, zMoving, factor * horMul, normalDrag, gravity, yMul ); - else - MoveNormal( xMoving, zMoving, factor * horMul, normalDrag, gravity, yMul ); - - if( BlockUnderFeet == Block.Ice && !(flying || noClip) ) { - // limit components to +-0.25f by rescaling vector to [-0.25, 0.25] - if( Math.Abs( Velocity.X ) > 0.25f || Math.Abs( Velocity.Z ) > 0.25f ) { - float scale = Math.Min( - Math.Abs( 0.25f / Velocity.X ), Math.Abs( 0.25f / Velocity.Z ) ); - Velocity.X *= scale; - Velocity.Z *= scale; - } - } else if( onGround || flying ) { - Velocity *= airDrag; // air drag or ground friction - } - } - } - - void AdjHeadingVelocity( float x, float z, float factor ) { - float dist = (float)Math.Sqrt( x * x + z * z ); - if( dist < 0.00001f ) return; - if( dist < 1 ) dist = 1; - - float multiply = factor / dist; - Velocity += Utils.RotateY( x * multiply, 0, z * multiply, HeadYawRadians ); - } - - void MoveFlying( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) { - AdjHeadingVelocity( zMoving, xMoving, factor ); - float yVel = (float)Math.Sqrt( Velocity.X * Velocity.X + Velocity.Z * Velocity.Z ); - // make vertical speed the same as vertical speed. - if( (xMoving != 0 || zMoving != 0) && yVel > 0.001f ) { - Velocity.Y = 0; - yMul = 1; - if( flyingUp || jumping ) Velocity.Y += yVel; - if( flyingDown ) Velocity.Y -= yVel; - } - Move( xMoving, zMoving, factor, drag, gravity, yMul ); - } - - void MoveNormal( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) { - AdjHeadingVelocity( zMoving, xMoving, factor ); - Move( xMoving, zMoving, factor, drag, gravity, yMul ); - } - - void Move( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) { - Velocity.Y *= yMul; - if( !noClip ) - physics.MoveAndWallSlide(); - Position += Velocity; - - Velocity.Y /= yMul; - Velocity *= drag; - Velocity.Y -= gravity; - } - - float GetBaseMultiply( bool canSpeed ) { - float multiply = 0; - if( flying || noClip ) { - if( speeding && canSpeed ) multiply += Hacks.SpeedMultiplier * 8; - if( halfSpeeding && canSpeed ) multiply += Hacks.SpeedMultiplier * 8 / 2; - if( multiply == 0 ) multiply = 8f; - } else { - if( speeding && canSpeed ) multiply += Hacks.SpeedMultiplier; - if( halfSpeeding && canSpeed ) multiply += Hacks.SpeedMultiplier / 2; - if( multiply == 0 ) multiply = 1; - } - return Hacks.CanSpeed ? multiply : Math.Min( multiply, Hacks.MaxSpeedMultiplier ); - } - - const float inf = float.PositiveInfinity; - float LowestSpeedModifier() { - BoundingBox bounds = CollisionBounds; - useLiquidGravity = false; - float baseModifier = LowestModifier( bounds, false ); - bounds.Min.Y -= 0.5f/16f; // also check block standing on - float solidModifier = LowestModifier( bounds, true ); - - if( baseModifier == inf && solidModifier == inf ) return 1; - return baseModifier == inf ? solidModifier : baseModifier; - } - - float LowestModifier( BoundingBox bounds, bool checkSolid ) { - Vector3I bbMin = Vector3I.Floor( bounds.Min ); - Vector3I bbMax = Vector3I.Floor( bounds.Max ); - float modifier = inf; - - for( int y = bbMin.Y; y <= bbMax.Y; y++ ) - for( int z = bbMin.Z; z <= bbMax.Z; z++ ) - for( int x = bbMin.X; x <= bbMax.X; x++ ) - { - byte block = game.World.SafeGetBlock( x, y, z ); - if( block == 0 ) continue; - CollideType type = info.Collide[block]; - if( type == CollideType.Solid && !checkSolid ) - continue; - - Vector3 min = new Vector3( x, y, z ) + info.MinBB[block]; - Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block]; - BoundingBox blockBB = new BoundingBox( min, max ); - if( !blockBB.Intersects( bounds ) ) continue; - - modifier = Math.Min( modifier, info.SpeedMultiplier[block] ); - if( block >= BlockInfo.CpeBlocksCount && type == CollideType.SwimThrough ) - useLiquidGravity = true; - } - return modifier; - } - - /// Calculates the jump velocity required such that when a client presses - /// the jump binding they will be able to jump up to the given height. - internal void CalculateJumpVelocity( float jumpHeight ) { - jumpVel = 0; - if( jumpHeight >= 256 ) jumpVel = 10.0f; - if( jumpHeight >= 512 ) jumpVel = 16.5f; - if( jumpHeight >= 768 ) jumpVel = 22.5f; - - while( GetMaxHeight( jumpVel ) <= jumpHeight ) - jumpVel += 0.001f; - } - - static double GetMaxHeight( float u ) { - // equation below comes from solving diff(x(t, u))= 0 - // We only work in discrete timesteps, so test both rounded up and down. - double t = 49.49831645 * Math.Log( 0.247483075 * u + 0.9899323 ); - return Math.Max( YPosAt( (int)t, u ), YPosAt( (int)t + 1, u ) ); - } - - static double YPosAt( int t, float u ) { - // v(t, u) = (4 + u) * (0.98^t) - 4, where u = initial velocity - // x(t, u) = Σv(t, u) from 0 to t (since we work in discrete timesteps) - // plugging into Wolfram Alpha gives 1 equation as - // (0.98^t) * (-49u - 196) - 4t + 50u + 196 - double a = Math.Exp( -0.0202027 * t ); //~0.98^t - return a * ( -49 * u - 196 ) - 4 * t + 50 * u + 196; - } - } -} \ No newline at end of file diff --git a/ClassicalSharp/Entities/LocalPlayer.cs b/ClassicalSharp/Entities/LocalPlayer.cs index ad0ddeaaf..23b94de5e 100644 --- a/ClassicalSharp/Entities/LocalPlayer.cs +++ b/ClassicalSharp/Entities/LocalPlayer.cs @@ -18,23 +18,26 @@ namespace ClassicalSharp.Entities { /// reach to and interact/modify blocks in. public float ReachDistance = 5f; - internal float jumpVel = 0.42f, serverJumpVel = 0.42f; /// Returns the height that the client can currently jump up to.
/// Note that when speeding is enabled the client is able to jump much further.
public float JumpHeight { - get { return (float)GetMaxHeight( jumpVel ); } + get { return (float)PhysicsComponent.GetMaxHeight( physics.jumpVel ); } } internal float curWalkTime, curSwing; - internal PhysicsComponent physics; + internal CollisionsComponent collisions; public HacksComponent Hacks; + internal PhysicsComponent physics; public LocalPlayer( Game game ) : base( game ) { DisplayName = game.Username; SkinName = game.Username; SkinIdentifier = "skin_255"; - physics = new PhysicsComponent( game, this ); + collisions = new CollisionsComponent( game, this ); Hacks = new HacksComponent( game, this ); + physics = new PhysicsComponent( game, this ); + physics.hacks = Hacks; + physics.collisions = collisions; Hacks.SpeedMultiplier = Options.GetFloat( OptionsKey.Speed, 0.1f, 50, 10 ); Hacks.PushbackPlacing = !game.ClassicMode && Options.GetBool( OptionsKey.PushbackPlacing, false ); @@ -56,9 +59,8 @@ namespace ClassicalSharp.Entities { bool wasOnGround = onGround; HandleInput( ref xMoving, ref zMoving ); - UpdateVelocityState( xMoving, zMoving ); - PhysicsTick( xMoving, zMoving ); - if( onGround ) { firstJump = false; secondJump = false; } + physics.UpdateVelocityState( xMoving, zMoving ); + physics.PhysicsTick( xMoving, zMoving ); nextPos = Position; Position = lastPos; @@ -146,34 +148,26 @@ namespace ClassicalSharp.Entities { void HandleInput( ref float xMoving, ref float zMoving ) { if( game.ScreenLockedInput ) { - jumping = speeding = flyingUp = flyingDown = false; + physics.jumping = Hacks.Speeding = Hacks.FlyingUp = Hacks.FlyingDown = false; } else { if( game.IsKeyDown( KeyBinding.Forward ) ) xMoving -= 0.98f; if( game.IsKeyDown( KeyBinding.Back ) ) xMoving += 0.98f; if( game.IsKeyDown( KeyBinding.Left ) ) zMoving -= 0.98f; if( game.IsKeyDown( KeyBinding.Right ) ) zMoving += 0.98f; - jumping = game.IsKeyDown( KeyBinding.Jump ); - speeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.Speed ); - halfSpeeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.HalfSpeed ); - flyingUp = game.IsKeyDown( KeyBinding.FlyUp ); - flyingDown = game.IsKeyDown( KeyBinding.FlyDown ); + physics.jumping = game.IsKeyDown( KeyBinding.Jump ); + Hacks.Speeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.Speed ); + Hacks.HalfSpeeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.HalfSpeed ); + Hacks.FlyingUp = game.IsKeyDown( KeyBinding.FlyUp ); + Hacks.FlyingDown = game.IsKeyDown( KeyBinding.FlyDown ); } } - internal bool jumping, speeding, halfSpeeding, flying, noClip, flyingDown, flyingUp; - /// Disables any hacks if their respective CanHackX value is set to false. public void CheckHacksConsistency() { - if( !Hacks.CanFly || !Hacks.Enabled ) { flying = false; flyingDown = false; flyingUp = false; } - if( !Hacks.CanNoclip || !Hacks.Enabled ) noClip = false; - if( !Hacks.CanSpeed || !Hacks.Enabled ) { speeding = false; halfSpeeding = false; } - Hacks.CanDoubleJump = Hacks.CanAnyHacks && Hacks.Enabled && Hacks.CanSpeed; - - if( !Hacks.CanUseThirdPersonCamera || !Hacks.Enabled ) - game.CycleCamera(); + Hacks.CheckHacksConsistency(); if( !Hacks.Enabled || !Hacks.CanAnyHacks || !Hacks.CanSpeed ) - jumpVel = serverJumpVel; + physics.jumpVel = physics.serverJumpVel; } internal Vector3 lastPos, nextPos; @@ -248,17 +242,17 @@ namespace ClassicalSharp.Entities { SpawnYaw = HeadYawDegrees; SpawnPitch = PitchDegrees; } else if( key == keys[KeyBinding.Fly] && Hacks.CanFly && Hacks.Enabled ) { - flying = !flying; + Hacks.Flying = !Hacks.Flying; } else if( key == keys[KeyBinding.NoClip] && Hacks.CanNoclip && Hacks.Enabled ) { - if( noClip ) Velocity.Y = 0; - noClip = !noClip; - } else if( key == keys[KeyBinding.Jump] && !onGround && !(flying || noClip) ) { - if( !firstJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) { - DoNormalJump(); - firstJump = true; - } else if( !secondJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) { - DoNormalJump(); - secondJump = true; + if( Hacks.Noclip ) Velocity.Y = 0; + Hacks.Noclip = !Hacks.Noclip; + } else if( key == keys[KeyBinding.Jump] && !onGround && !(Hacks.Flying || Hacks.Noclip) ) { + if( !physics.firstJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) { + physics.DoNormalJump(); + physics.firstJump = true; + } else if( !physics.secondJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) { + physics.DoNormalJump(); + physics.secondJump = true; } } else { return false; @@ -282,10 +276,9 @@ namespace ClassicalSharp.Entities { for ( int xx = minX; xx <= maxX; xx++ ) { Vector3I coords = new Vector3I( P.X + xx, y + yy, P.Z + zz ); - byte block = physics.GetPhysicsBlockId( coords.X, coords.Y, coords.Z ); + byte block = collisions.GetPhysicsBlockId( coords.X, coords.Y, coords.Z ); Vector3 min = info.MinBB[block] + (Vector3)coords; Vector3 max = info.MaxBB[block] + (Vector3)coords; - Console.WriteLine( min + "_" + max ); if( !bb.Intersects( new BoundingBox( min, max ) ) ) continue; anyHit |= info.Collide[block] == CollideType.Solid; } diff --git a/ClassicalSharp/Game/InputHandler.cs b/ClassicalSharp/Game/InputHandler.cs index cb7b2e3c5..b00de305b 100644 --- a/ClassicalSharp/Game/InputHandler.cs +++ b/ClassicalSharp/Game/InputHandler.cs @@ -123,7 +123,7 @@ namespace ClassicalSharp { pos + game.BlockInfo.MaxBB[newBlock] ); BoundingBox localBB = game.LocalPlayer.CollisionBounds; - if( game.LocalPlayer.noClip || !localBB.Intersects( blockBB ) ) return true; + if( game.LocalPlayer.Hacks.Noclip || !localBB.Intersects( blockBB ) ) return true; HacksComponent hacks = game.LocalPlayer.Hacks; if( hacks.CanPushbackBlocks && hacks.PushbackPlacing && hacks.Enabled ) return PushbackPlace( selected, blockBB ); @@ -163,7 +163,8 @@ namespace ClassicalSharp { if( !validPos ) return false; game.LocalPlayer.Position = newP; - if( !game.LocalPlayer.noClip && game.LocalPlayer.TouchesAny( CannotPassThrough ) ) { + if( !game.LocalPlayer.Hacks.Noclip + && game.LocalPlayer.TouchesAny( CannotPassThrough ) ) { game.LocalPlayer.Position = oldP; return false; } diff --git a/ClassicalSharp/Math/Picking.cs b/ClassicalSharp/Math/Picking.cs index 96e85de39..159146878 100644 --- a/ClassicalSharp/Math/Picking.cs +++ b/ClassicalSharp/Math/Picking.cs @@ -7,20 +7,20 @@ namespace ClassicalSharp { public static class Picking { - static RayCaster ray = new RayCaster(); + static RayTracer tracer = new RayTracer(); /// Determines the picked block based on the given origin and direction vector.
/// Marks pickedPos as invalid if a block could not be found due to going outside map boundaries /// or not being able to find a suitable candiate within the given reach distance.
public static void CalculatePickedBlock( Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos ) { - ray.SetRayData( origin, dir ); + tracer.SetRayData( origin, dir ); World map = game.World; BlockInfo info = game.BlockInfo; float reachSquared = reach * reach; int iterations = 0; while( iterations < 10000 ) { - int x = ray.X, y = ray.Y, z = ray.Z; + int x = tracer.X, y = tracer.Y, z = tracer.Z; byte block = GetBlock( map, x, y, z, origin ); Vector3 min = new Vector3( x, y, z ) + info.MinBB[block]; Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block]; @@ -43,7 +43,7 @@ namespace ClassicalSharp { return; } } - ray.Step(); + tracer.Step(); iterations++; } throw new InvalidOperationException( "did over 10000 iterations in CalculatePickedBlock(). " + @@ -56,14 +56,14 @@ namespace ClassicalSharp { pickedPos.IntersectPoint = origin + dir * reach; return; } - ray.SetRayData( origin, dir ); + tracer.SetRayData( origin, dir ); World map = game.World; BlockInfo info = game.BlockInfo; float reachSquared = reach * reach; int iterations = 0; while( iterations < 10000 ) { - int x = ray.X, y = ray.Y, z = ray.Z; + int x = tracer.X, y = tracer.Y, z = tracer.Z; byte block = GetBlock( map, x, y, z, origin ); Vector3 min = new Vector3( x, y, z ) + info.MinBB[block]; Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block]; @@ -102,7 +102,7 @@ namespace ClassicalSharp { return; } } - ray.Step(); + tracer.Step(); iterations++; } throw new InvalidOperationException( "did over 10000 iterations in ClipCameraPos(). " + diff --git a/ClassicalSharp/Math/RayCaster.cs b/ClassicalSharp/Math/RayTracer.cs similarity index 96% rename from ClassicalSharp/Math/RayCaster.cs rename to ClassicalSharp/Math/RayTracer.cs index 225978172..e22f23cc5 100644 --- a/ClassicalSharp/Math/RayCaster.cs +++ b/ClassicalSharp/Math/RayTracer.cs @@ -12,7 +12,7 @@ namespace ClassicalSharp { // John Amanatides, Andrew Woo // http://www.cse.yorku.ca/~amana/research/grid.pdf // http://www.devmaster.net/articles/raytracing_series/A%20faster%20voxel%20traversal%20algorithm%20for%20ray%20tracing.pdf - public sealed class RayCaster { + public sealed class RayTracer { public int X, Y, Z; Vector3I step, cellBoundary; diff --git a/ClassicalSharp/Network/NetworkProcessor.CPE.cs b/ClassicalSharp/Network/NetworkProcessor.CPE.cs index 22597d13e..6cfac542a 100644 --- a/ClassicalSharp/Network/NetworkProcessor.CPE.cs +++ b/ClassicalSharp/Network/NetworkProcessor.CPE.cs @@ -348,9 +348,9 @@ namespace ClassicalSharp.Net { p.CheckHacksConsistency(); float jumpHeight = reader.ReadInt16() / 32f; - if( jumpHeight < 0 ) p.jumpVel = 0.42f; - else p.CalculateJumpVelocity( jumpHeight ); - p.serverJumpVel = p.jumpVel; + if( jumpHeight < 0 ) p.physics.jumpVel = 0.42f; + else p.physics.CalculateJumpVelocity( jumpHeight ); + p.physics.serverJumpVel = p.physics.jumpVel; game.Events.RaiseHackPermissionsChanged(); } diff --git a/ClassicalSharp/Network/NetworkProcessor.cs b/ClassicalSharp/Network/NetworkProcessor.cs index 455004dcd..69e76a3e0 100644 --- a/ClassicalSharp/Network/NetworkProcessor.cs +++ b/ClassicalSharp/Network/NetworkProcessor.cs @@ -66,6 +66,7 @@ namespace ClassicalSharp.Net { if( (DateTime.UtcNow - lastPacket).TotalSeconds >= 20 ) CheckDisconnection( delta ); if( Disconnected ) return; + LocalPlayer player = game.LocalPlayer; try { reader.ReadPendingData(); @@ -76,15 +77,15 @@ namespace ClassicalSharp.Net { return; } catch { throw; - } + } while( (reader.size - reader.index) > 0 ) { byte opcode = reader.buffer[reader.index]; // Workaround for older D3 servers which wrote one byte too many for HackControl packets. if( opcode == 0xFF && lastOpcode == PacketId.CpeHackControl ) { reader.Skip( 1 ); - game.LocalPlayer.jumpVel = 0.42f; // assume default jump height - game.LocalPlayer.serverJumpVel = game.LocalPlayer.jumpVel; + player.physics.jumpVel = 0.42f; // assume default jump height + player.physics.serverJumpVel = player.physics.jumpVel; continue; } @@ -98,9 +99,8 @@ namespace ClassicalSharp.Net { if( (reader.size - reader.index) < packetSizes[opcode] ) break; ReadPacket( opcode ); } - reader.RemoveProcessed(); - Player player = game.LocalPlayer; + reader.RemoveProcessed(); if( receivedFirstPosition ) { SendPosition( player.Position, player.HeadYawDegrees, player.PitchDegrees ); }