mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-16 11:06:06 -04:00
Modularise LocalPlayer.Physics into separate PhysicsComponent.
This commit is contained in:
parent
dbc2311435
commit
634b3213c3
@ -137,10 +137,10 @@ namespace ClassicalSharp.Gui {
|
|||||||
bool speeding, halfSpeeding, noclip, fly;
|
bool speeding, halfSpeeding, noclip, fly;
|
||||||
int lastZoomFov;
|
int lastZoomFov;
|
||||||
void UpdateHackState( bool force ) {
|
void UpdateHackState( bool force ) {
|
||||||
LocalPlayer p = game.LocalPlayer;
|
HacksComponent hacks = game.LocalPlayer.Hacks;
|
||||||
if( force || p.speeding != speeding || p.halfSpeeding != halfSpeeding || p.noClip != noclip ||
|
if( force || hacks.Speeding != speeding || hacks.HalfSpeeding != halfSpeeding || hacks.Noclip != noclip ||
|
||||||
p.flying != fly || game.ZoomFieldOfView != lastZoomFov ) {
|
hacks.Flying != fly || game.ZoomFieldOfView != lastZoomFov ) {
|
||||||
speeding = p.speeding; halfSpeeding = p.halfSpeeding; noclip = p.noClip; fly = p.flying;
|
speeding = hacks.Speeding; halfSpeeding = hacks.HalfSpeeding; noclip = hacks.Noclip; fly = hacks.Flying;
|
||||||
lastZoomFov = game.ZoomFieldOfView;
|
lastZoomFov = game.ZoomFieldOfView;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
text.Clear();
|
text.Clear();
|
||||||
|
@ -31,7 +31,7 @@ namespace ClassicalSharp.Gui {
|
|||||||
|
|
||||||
Make2( -1, 0, "Jump height", OnWidgetClick,
|
Make2( -1, 0, "Jump height", OnWidgetClick,
|
||||||
g => g.LocalPlayer.JumpHeight.ToString( "F3" ),
|
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,
|
MakeBool( -1, 50, "Double jump", OptionsKey.DoubleJump,
|
||||||
OnWidgetClick, g => g.LocalPlayer.Hacks.DoubleJump,
|
OnWidgetClick, g => g.LocalPlayer.Hacks.DoubleJump,
|
||||||
|
@ -150,13 +150,13 @@
|
|||||||
<Compile Include="Entities\Components\AnimatedComponent.cs" />
|
<Compile Include="Entities\Components\AnimatedComponent.cs" />
|
||||||
<Compile Include="Entities\Components\HacksComponent.cs" />
|
<Compile Include="Entities\Components\HacksComponent.cs" />
|
||||||
<Compile Include="Entities\Components\InterpolatedComponent.cs" />
|
<Compile Include="Entities\Components\InterpolatedComponent.cs" />
|
||||||
|
<Compile Include="Entities\Components\CollisionsComponent.cs" />
|
||||||
<Compile Include="Entities\Components\PhysicsComponent.cs" />
|
<Compile Include="Entities\Components\PhysicsComponent.cs" />
|
||||||
<Compile Include="Entities\Components\ShadowComponent.cs" />
|
<Compile Include="Entities\Components\ShadowComponent.cs" />
|
||||||
<Compile Include="Entities\Entity.Bounds.cs" />
|
<Compile Include="Entities\Entity.Bounds.cs" />
|
||||||
<Compile Include="Entities\Entity.cs" />
|
<Compile Include="Entities\Entity.cs" />
|
||||||
<Compile Include="Entities\EntityList.cs" />
|
<Compile Include="Entities\EntityList.cs" />
|
||||||
<Compile Include="Entities\LocalPlayer.cs" />
|
<Compile Include="Entities\LocalPlayer.cs" />
|
||||||
<Compile Include="Entities\LocalPlayer.Physics.cs" />
|
|
||||||
<Compile Include="Entities\LocationUpdate.cs" />
|
<Compile Include="Entities\LocationUpdate.cs" />
|
||||||
<Compile Include="Entities\NetPlayer.cs" />
|
<Compile Include="Entities\NetPlayer.cs" />
|
||||||
<Compile Include="Events\EntityEvents.cs" />
|
<Compile Include="Events\EntityEvents.cs" />
|
||||||
@ -170,7 +170,7 @@
|
|||||||
<Compile Include="Generator\NotchyGenerator.Utils.cs" />
|
<Compile Include="Generator\NotchyGenerator.Utils.cs" />
|
||||||
<Compile Include="GraphicsAPI\OpenGLESApi.cs" />
|
<Compile Include="GraphicsAPI\OpenGLESApi.cs" />
|
||||||
<Compile Include="Map\Formats\MapCw.Nbt.cs" />
|
<Compile Include="Map\Formats\MapCw.Nbt.cs" />
|
||||||
<Compile Include="Math\RayCaster.cs" />
|
<Compile Include="Math\RayTracer.cs" />
|
||||||
<Compile Include="Model\CustomModel.cs" />
|
<Compile Include="Model\CustomModel.cs" />
|
||||||
<Compile Include="Model\HumanModels.cs" />
|
<Compile Include="Model\HumanModels.cs" />
|
||||||
<Compile Include="Network\NetworkProcessor.CPECustom.cs" />
|
<Compile Include="Network\NetworkProcessor.CPECustom.cs" />
|
||||||
|
331
ClassicalSharp/Entities/Components/CollisionsComponent.cs
Normal file
331
ClassicalSharp/Entities/Components/CollisionsComponent.cs
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
||||||
|
using System;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace ClassicalSharp.Entities {
|
||||||
|
|
||||||
|
/// <summary> Entity component that performs collision detection. </summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary> Constant offset used to avoid floating point roundoff errors. </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,43 +27,45 @@ namespace ClassicalSharp.Entities {
|
|||||||
|
|
||||||
/// <summary> Whether the player has allowed hacks usage as an option.
|
/// <summary> Whether the player has allowed hacks usage as an option.
|
||||||
/// Note that all 'can use X' set by the server override this. </summary>
|
/// Note that all 'can use X' set by the server override this. </summary>
|
||||||
public bool Enabled = true;
|
public bool Enabled = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to use any type of hacks. </summary>
|
/// <summary> Whether the player is allowed to use any type of hacks. </summary>
|
||||||
public bool CanAnyHacks = true;
|
public bool CanAnyHacks = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to use the types of cameras that use third person. </summary>
|
/// <summary> Whether the player is allowed to use the types of cameras that use third person. </summary>
|
||||||
public bool CanUseThirdPersonCamera = true;
|
public bool CanUseThirdPersonCamera = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to increase its speed beyond the normal walking speed. </summary>
|
/// <summary> Whether the player is allowed to increase its speed beyond the normal walking speed. </summary>
|
||||||
public bool CanSpeed = true;
|
public bool CanSpeed = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to fly in the world. </summary>
|
/// <summary> Whether the player is allowed to fly in the world. </summary>
|
||||||
public bool CanFly = true;
|
public bool CanFly = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to teleport to their respawn coordinates. </summary>
|
/// <summary> Whether the player is allowed to teleport to their respawn coordinates. </summary>
|
||||||
public bool CanRespawn = true;
|
public bool CanRespawn = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to pass through all blocks. </summary>
|
/// <summary> Whether the player is allowed to pass through all blocks. </summary>
|
||||||
public bool CanNoclip = true;
|
public bool CanNoclip = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to use pushback block placing. </summary>
|
/// <summary> Whether the player is allowed to use pushback block placing. </summary>
|
||||||
public bool CanPushbackBlocks = true;
|
public bool CanPushbackBlocks = true;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to see all entity name tags. </summary>
|
/// <summary> Whether the player is allowed to see all entity name tags. </summary>
|
||||||
public bool CanSeeAllNames = true;
|
public bool CanSeeAllNames = true;
|
||||||
|
/// <summary> Whether the player is allowed to double jump. </summary>
|
||||||
|
public bool CanDoubleJump = true;
|
||||||
|
/// <summary> Maximum speed the entity can move at horizontally when CanSpeed is false. </summary>
|
||||||
|
public float MaxSpeedMultiplier = 1;
|
||||||
|
|
||||||
/// <summary> Whether the player should slide after letting go of movement buttons in noclip. </summary>
|
/// <summary> Whether the player should slide after letting go of movement buttons in noclip. </summary>
|
||||||
public bool NoclipSlide = true;
|
public bool NoclipSlide = true;
|
||||||
|
|
||||||
/// <summary> Whether the player has allowed the usage of fast double jumping abilities. </summary>
|
/// <summary> Whether the player has allowed the usage of fast double jumping abilities. </summary>
|
||||||
public bool DoubleJump = false;
|
public bool DoubleJump = false;
|
||||||
|
|
||||||
/// <summary> Whether the player is allowed to double jump. </summary>
|
/// <summary> Whether the player currently has noclip on. </summary>
|
||||||
public bool CanDoubleJump = true;
|
public bool Noclip;
|
||||||
|
/// <summary> Whether the player currently has fly mode active. </summary>
|
||||||
/// <summary> Maximum speed the entity can move at horizontally when CanSpeed is false. </summary>
|
public bool Flying;
|
||||||
public float MaxSpeedMultiplier = 1;
|
/// <summary> Whether the player is currently flying upwards. </summary>
|
||||||
|
public bool FlyingUp;
|
||||||
|
/// <summary> Whether the player is currently flying downwards. </summary>
|
||||||
|
public bool FlyingDown;
|
||||||
|
/// <summary> Whether the player is currently walking at base speed * speed multiplier. </summary>
|
||||||
|
public bool Speeding;
|
||||||
|
/// <summary> Whether the player is currently walking at base speed * 0.5 * speed multiplier. </summary>
|
||||||
|
public bool HalfSpeeding;
|
||||||
|
|
||||||
/// <summary> Parses hack flags specified in the motd and/or name of the server. </summary>
|
/// <summary> Parses hack flags specified in the motd and/or name of the server. </summary>
|
||||||
/// <remarks> Recognises +/-hax, +/-fly, +/-noclip, +/-speed, +/-respawn, +/-ophax, and horspeed=xyz </remarks>
|
/// <remarks> Recognises +/-hax, +/-fly, +/-noclip, +/-speed, +/-respawn, +/-ophax, and horspeed=xyz </remarks>
|
||||||
@ -124,5 +126,16 @@ namespace ClassicalSharp.Entities {
|
|||||||
inv.CanPlace[(int)Block.StillLava] = value == 0x64;
|
inv.CanPlace[(int)Block.StillLava] = value == 0x64;
|
||||||
CanSeeAllNames = value == 0x64;
|
CanSeeAllNames = value == 0x64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Disables any hacks if their respective CanHackX value is set to false. </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,325 +7,257 @@ namespace ClassicalSharp.Entities {
|
|||||||
/// <summary> Entity component that performs collision detection. </summary>
|
/// <summary> Entity component that performs collision detection. </summary>
|
||||||
public sealed class PhysicsComponent {
|
public sealed class PhysicsComponent {
|
||||||
|
|
||||||
Game game;
|
bool useLiquidGravity = false; // used by BlockDefinitions.
|
||||||
|
bool canLiquidJump = true;
|
||||||
|
internal bool firstJump, secondJump, jumping;
|
||||||
Entity entity;
|
Entity entity;
|
||||||
|
Game game;
|
||||||
BlockInfo info;
|
BlockInfo info;
|
||||||
|
internal float jumpVel = 0.42f, serverJumpVel = 0.42f;
|
||||||
|
internal HacksComponent hacks;
|
||||||
|
internal CollisionsComponent collisions;
|
||||||
|
|
||||||
public PhysicsComponent( Game game, Entity entity ) {
|
public PhysicsComponent( Game game, Entity entity ) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
info = game.BlockInfo;
|
info = game.BlockInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool hitYMax, collideX, collideY, collideZ;
|
public void UpdateVelocityState( float xMoving, float zMoving ) {
|
||||||
|
if( !hacks.NoclipSlide && (hacks.Noclip && xMoving == 0 && zMoving == 0) )
|
||||||
/// <summary> Constant offset used to avoid floating point roundoff errors. </summary>
|
entity.Velocity = Vector3.Zero;
|
||||||
public const float Adjustment = 0.001f;
|
if( hacks.Flying || hacks.Noclip ) {
|
||||||
|
entity.Velocity.Y = 0; // eliminate the effect of gravity
|
||||||
public byte GetPhysicsBlockId( int x, int y, int z ) {
|
int dir = (hacks.FlyingUp || jumping) ? 1 : (hacks.FlyingDown ? -1 : 0);
|
||||||
if( x < 0 || x >= game.World.Width || z < 0 ||
|
|
||||||
z >= game.World.Length || y < 0 ) return (byte)Block.Bedrock;
|
entity.Velocity.Y += 0.12f * dir;
|
||||||
|
if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += 0.12f * dir;
|
||||||
if( y >= game.World.Height ) return (byte)Block.Air;
|
if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += 0.06f * dir;
|
||||||
return game.World.GetBlock( x, y, z );
|
} else if( jumping && entity.TouchesAnyRope() && entity.Velocity.Y > 0.02f ) {
|
||||||
}
|
entity.Velocity.Y = 0.02f;
|
||||||
|
|
||||||
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 );
|
if( !jumping ) {
|
||||||
// Order loops so that we minimise cache misses
|
canLiquidJump = false; return;
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
bool touchWater = entity.TouchesAnyWater();
|
||||||
void CollideWithReachableBlocks( int count, ref Vector3 size,
|
bool touchLava = entity.TouchesAnyLava();
|
||||||
ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
|
if( touchWater || touchLava ) {
|
||||||
bool wasOn = entity.onGround;
|
BoundingBox bounds = entity.CollisionBounds;
|
||||||
entity.onGround = false;
|
int feetY = Utils.Floor( bounds.Min.Y ), bodyY = feetY + 1;
|
||||||
if( count > 0 )
|
int headY = Utils.Floor( bounds.Max.Y );
|
||||||
QuickSort( stateCache, 0, count - 1 );
|
if( bodyY > headY ) bodyY = headY;
|
||||||
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;
|
bounds.Max.Y = bounds.Min.Y = feetY;
|
||||||
CalcTime( ref entity.Velocity, ref entityBB, ref blockBB, out tx, out ty, out tz );
|
bool liquidFeet = entity.TouchesAny( bounds, StandardLiquid );
|
||||||
if( tx > 1 || ty > 1 || tz > 1 )
|
bounds.Min.Y = Math.Min( bodyY, headY );
|
||||||
Utils.LogDebug( "t > 1 in physics calculation.. this shouldn't have happened." );
|
bounds.Max.Y = Math.Max( bodyY, headY );
|
||||||
BoundingBox finalBB = entityBB.Offset( entity.Velocity * new Vector3( tx, ty, tz ) );
|
bool liquidRest = entity.TouchesAny( bounds, StandardLiquid );
|
||||||
|
|
||||||
// if we have hit the bottom of a block, we need to change the axis we test first.
|
bool pastJumpPoint = liquidFeet && !liquidRest && (entity.Position.Y % 1 >= 0.4);
|
||||||
if( hitYMax ) {
|
if( !pastJumpPoint ) {
|
||||||
if( finalBB.Min.Y + Adjustment >= blockBB.Max.Y )
|
canLiquidJump = true;
|
||||||
ClipYMax( ref blockBB, ref entityBB, ref entityExtentBB, ref size );
|
entity.Velocity.Y += 0.04f;
|
||||||
else if( finalBB.Max.Y - Adjustment <= blockBB.Min.Y )
|
if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += 0.04f;
|
||||||
ClipYMin( ref blockBB, ref entityBB, ref entityExtentBB, ref size );
|
if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += 0.02f;
|
||||||
else if( finalBB.Min.X + Adjustment >= blockBB.Max.X )
|
} else if( pastJumpPoint ) {
|
||||||
ClipXMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size );
|
// either A) jump bob in water B) climb up solid on side
|
||||||
else if( finalBB.Max.X - Adjustment <= blockBB.Min.X )
|
if( canLiquidJump || (collisions.collideX || collisions.collideZ) )
|
||||||
ClipXMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size );
|
entity.Velocity.Y += touchLava ? 0.20f : 0.10f;
|
||||||
else if( finalBB.Min.Z + Adjustment >= blockBB.Max.Z )
|
canLiquidJump = false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
} else if( useLiquidGravity ) {
|
||||||
// if flying or falling, test the horizontal axes first.
|
entity.Velocity.Y += 0.04f;
|
||||||
if( finalBB.Min.X + Adjustment >= blockBB.Max.X )
|
if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += 0.04f;
|
||||||
ClipXMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size );
|
if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += 0.02f;
|
||||||
else if( finalBB.Max.X - Adjustment <= blockBB.Min.X )
|
canLiquidJump = false;
|
||||||
ClipXMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size );
|
} else if( entity.TouchesAnyRope() ) {
|
||||||
else if( finalBB.Min.Z + Adjustment >= blockBB.Max.Z )
|
entity.Velocity.Y += (hacks.Speeding && hacks.CanSpeed) ? 0.15f : 0.10f;
|
||||||
ClipZMax( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size );
|
canLiquidJump = false;
|
||||||
else if( finalBB.Max.Z - Adjustment <= blockBB.Min.Z )
|
} else if( entity.onGround ) {
|
||||||
ClipZMin( ref blockBB, ref entityBB, wasOn, finalBB, ref entityExtentBB, ref size );
|
DoNormalJump();
|
||||||
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,
|
public void DoNormalJump() {
|
||||||
BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) {
|
entity.Velocity.Y = jumpVel;
|
||||||
if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) {
|
if( hacks.Speeding && hacks.CanSpeed ) entity.Velocity.Y += jumpVel;
|
||||||
entity.Position.X = blockBB.Min.X - size.X / 2 - Adjustment;
|
if( hacks.HalfSpeeding && hacks.CanSpeed ) entity.Velocity.Y += jumpVel / 2;
|
||||||
ClipX( ref size, ref entityBB, ref entityExtentBB );
|
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; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if( entity.TouchesAnyWater() && !hacks.Flying && !hacks.Noclip ) {
|
||||||
void ClipXMax( ref BoundingBox blockBB, ref BoundingBox entityBB, bool wasOn,
|
MoveNormal( xMoving, zMoving, 0.02f * horMul, waterDrag, liquidGrav, yMul );
|
||||||
BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) {
|
} else if( entity.TouchesAnyLava() && !hacks.Flying && !hacks.Noclip ) {
|
||||||
if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) {
|
MoveNormal( xMoving, zMoving, 0.02f * horMul, lavaDrag, liquidGrav, yMul );
|
||||||
entity.Position.X = blockBB.Max.X + size.X / 2 + Adjustment;
|
} else if( entity.TouchesAnyRope() && !hacks.Flying && !hacks.Noclip ) {
|
||||||
ClipX( ref size, ref entityBB, ref entityExtentBB );
|
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,
|
void AdjHeadingVelocity( float x, float z, float factor ) {
|
||||||
BoundingBox finalBB, ref BoundingBox entityExtentBB, ref Vector3 size ) {
|
float dist = (float)Math.Sqrt( x * x + z * z );
|
||||||
if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref entityExtentBB ) ) {
|
if( dist < 0.00001f ) return;
|
||||||
entity.Position.Z = blockBB.Max.Z + size.Z / 2 + Adjustment;
|
if( dist < 1 ) dist = 1;
|
||||||
ClipZ( ref size, ref entityBB, ref entityExtentBB );
|
|
||||||
|
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,
|
void MoveNormal( float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul ) {
|
||||||
BoundingBox finalBB, ref BoundingBox extentBB, ref Vector3 size ) {
|
AdjHeadingVelocity( zMoving, xMoving, factor );
|
||||||
if( !wasOn || !DidSlide( blockBB, ref size, finalBB, ref entityBB, ref extentBB ) ) {
|
Move( xMoving, zMoving, factor, drag, gravity, yMul );
|
||||||
entity.Position.Z = blockBB.Min.Z - size.Z / 2 - Adjustment;
|
}
|
||||||
ClipZ( ref size, ref entityBB, ref extentBB );
|
|
||||||
|
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,
|
const float inf = float.PositiveInfinity;
|
||||||
ref BoundingBox extentBB, ref Vector3 size ) {
|
float LowestSpeedModifier() {
|
||||||
entity.Position.Y = blockBB.Min.Y - size.Y - Adjustment;
|
BoundingBox bounds = entity.CollisionBounds;
|
||||||
ClipY( ref size, ref entityBB, ref extentBB );
|
useLiquidGravity = false;
|
||||||
hitYMax = 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,
|
float LowestModifier( BoundingBox bounds, bool checkSolid ) {
|
||||||
ref BoundingBox extentBB, ref Vector3 size ) {
|
Vector3I bbMin = Vector3I.Floor( bounds.Min );
|
||||||
entity.Position.Y = blockBB.Max.Y + Adjustment;
|
Vector3I bbMax = Vector3I.Floor( bounds.Max );
|
||||||
entity.onGround = true;
|
float modifier = inf;
|
||||||
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 y = bbMin.Y; y <= bbMax.Y; y++ )
|
||||||
for( int z = bbMin.Z; z <= bbMax.Z; z++ )
|
for( int z = bbMin.Z; z <= bbMax.Z; z++ )
|
||||||
for( int x = bbMin.X; x <= bbMax.X; x++ )
|
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 min = new Vector3( x, y, z ) + info.MinBB[block];
|
||||||
Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];
|
Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];
|
||||||
|
|
||||||
BoundingBox blockBB = new BoundingBox( min, max );
|
BoundingBox blockBB = new BoundingBox( min, max );
|
||||||
if( !blockBB.Intersects( adjFinalBB ) )
|
if( !blockBB.Intersects( bounds ) ) continue;
|
||||||
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
|
modifier = Math.Min( modifier, info.SpeedMultiplier[block] );
|
||||||
if( j - left <= right - i ) {
|
if( block >= BlockInfo.CpeBlocksCount && type == CollideType.SwimThrough )
|
||||||
if( left < j )
|
useLiquidGravity = true;
|
||||||
QuickSort( keys, left, j );
|
|
||||||
left = i;
|
|
||||||
} else {
|
|
||||||
if( i < right )
|
|
||||||
QuickSort( keys, i, right );
|
|
||||||
right = j;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> 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. </summary>
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -60,7 +60,7 @@ namespace ClassicalSharp.Entities {
|
|||||||
|
|
||||||
/// <summary> Determines whether any of the blocks that intersect the
|
/// <summary> Determines whether any of the blocks that intersect the
|
||||||
/// bounding box of this entity are lava or still lava. </summary>
|
/// bounding box of this entity are lava or still lava. </summary>
|
||||||
protected bool TouchesAnyLava() {
|
public bool TouchesAnyLava() {
|
||||||
BoundingBox bounds = CollisionBounds.Expand( liqExpand );
|
BoundingBox bounds = CollisionBounds.Expand( liqExpand );
|
||||||
AdjustLiquidTestBounds( ref bounds );
|
AdjustLiquidTestBounds( ref bounds );
|
||||||
return TouchesAny( bounds,
|
return TouchesAny( bounds,
|
||||||
@ -69,7 +69,7 @@ namespace ClassicalSharp.Entities {
|
|||||||
|
|
||||||
/// <summary> Determines whether any of the blocks that intersect the
|
/// <summary> Determines whether any of the blocks that intersect the
|
||||||
/// bounding box of this entity are rope. </summary>
|
/// bounding box of this entity are rope. </summary>
|
||||||
protected bool TouchesAnyRope() {
|
public bool TouchesAnyRope() {
|
||||||
BoundingBox bounds = CollisionBounds;
|
BoundingBox bounds = CollisionBounds;
|
||||||
bounds.Max.Y += 0.5f/16f;
|
bounds.Max.Y += 0.5f/16f;
|
||||||
return TouchesAny( bounds, b => b == (byte)Block.Rope );
|
return TouchesAny( bounds, b => b == (byte)Block.Rope );
|
||||||
@ -77,7 +77,7 @@ namespace ClassicalSharp.Entities {
|
|||||||
|
|
||||||
/// <summary> Determines whether any of the blocks that intersect the
|
/// <summary> Determines whether any of the blocks that intersect the
|
||||||
/// bounding box of this entity are water or still water. </summary>
|
/// bounding box of this entity are water or still water. </summary>
|
||||||
protected bool TouchesAnyWater() {
|
public bool TouchesAnyWater() {
|
||||||
BoundingBox bounds = CollisionBounds.Expand( liqExpand );
|
BoundingBox bounds = CollisionBounds.Expand( liqExpand );
|
||||||
AdjustLiquidTestBounds( ref bounds );
|
AdjustLiquidTestBounds( ref bounds );
|
||||||
return TouchesAny( bounds,
|
return TouchesAny( bounds,
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> 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. </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,23 +18,26 @@ namespace ClassicalSharp.Entities {
|
|||||||
/// reach to and interact/modify blocks in. </summary>
|
/// reach to and interact/modify blocks in. </summary>
|
||||||
public float ReachDistance = 5f;
|
public float ReachDistance = 5f;
|
||||||
|
|
||||||
internal float jumpVel = 0.42f, serverJumpVel = 0.42f;
|
|
||||||
/// <summary> Returns the height that the client can currently jump up to.<br/>
|
/// <summary> Returns the height that the client can currently jump up to.<br/>
|
||||||
/// Note that when speeding is enabled the client is able to jump much further. </summary>
|
/// Note that when speeding is enabled the client is able to jump much further. </summary>
|
||||||
public float JumpHeight {
|
public float JumpHeight {
|
||||||
get { return (float)GetMaxHeight( jumpVel ); }
|
get { return (float)PhysicsComponent.GetMaxHeight( physics.jumpVel ); }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal float curWalkTime, curSwing;
|
internal float curWalkTime, curSwing;
|
||||||
internal PhysicsComponent physics;
|
internal CollisionsComponent collisions;
|
||||||
public HacksComponent Hacks;
|
public HacksComponent Hacks;
|
||||||
|
internal PhysicsComponent physics;
|
||||||
|
|
||||||
public LocalPlayer( Game game ) : base( game ) {
|
public LocalPlayer( Game game ) : base( game ) {
|
||||||
DisplayName = game.Username;
|
DisplayName = game.Username;
|
||||||
SkinName = game.Username;
|
SkinName = game.Username;
|
||||||
SkinIdentifier = "skin_255";
|
SkinIdentifier = "skin_255";
|
||||||
physics = new PhysicsComponent( game, this );
|
collisions = new CollisionsComponent( game, this );
|
||||||
Hacks = new HacksComponent( 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.SpeedMultiplier = Options.GetFloat( OptionsKey.Speed, 0.1f, 50, 10 );
|
||||||
Hacks.PushbackPlacing = !game.ClassicMode && Options.GetBool( OptionsKey.PushbackPlacing, false );
|
Hacks.PushbackPlacing = !game.ClassicMode && Options.GetBool( OptionsKey.PushbackPlacing, false );
|
||||||
@ -56,9 +59,8 @@ namespace ClassicalSharp.Entities {
|
|||||||
bool wasOnGround = onGround;
|
bool wasOnGround = onGround;
|
||||||
|
|
||||||
HandleInput( ref xMoving, ref zMoving );
|
HandleInput( ref xMoving, ref zMoving );
|
||||||
UpdateVelocityState( xMoving, zMoving );
|
physics.UpdateVelocityState( xMoving, zMoving );
|
||||||
PhysicsTick( xMoving, zMoving );
|
physics.PhysicsTick( xMoving, zMoving );
|
||||||
if( onGround ) { firstJump = false; secondJump = false; }
|
|
||||||
|
|
||||||
nextPos = Position;
|
nextPos = Position;
|
||||||
Position = lastPos;
|
Position = lastPos;
|
||||||
@ -146,34 +148,26 @@ namespace ClassicalSharp.Entities {
|
|||||||
|
|
||||||
void HandleInput( ref float xMoving, ref float zMoving ) {
|
void HandleInput( ref float xMoving, ref float zMoving ) {
|
||||||
if( game.ScreenLockedInput ) {
|
if( game.ScreenLockedInput ) {
|
||||||
jumping = speeding = flyingUp = flyingDown = false;
|
physics.jumping = Hacks.Speeding = Hacks.FlyingUp = Hacks.FlyingDown = false;
|
||||||
} else {
|
} else {
|
||||||
if( game.IsKeyDown( KeyBinding.Forward ) ) xMoving -= 0.98f;
|
if( game.IsKeyDown( KeyBinding.Forward ) ) xMoving -= 0.98f;
|
||||||
if( game.IsKeyDown( KeyBinding.Back ) ) xMoving += 0.98f;
|
if( game.IsKeyDown( KeyBinding.Back ) ) xMoving += 0.98f;
|
||||||
if( game.IsKeyDown( KeyBinding.Left ) ) zMoving -= 0.98f;
|
if( game.IsKeyDown( KeyBinding.Left ) ) zMoving -= 0.98f;
|
||||||
if( game.IsKeyDown( KeyBinding.Right ) ) zMoving += 0.98f;
|
if( game.IsKeyDown( KeyBinding.Right ) ) zMoving += 0.98f;
|
||||||
|
|
||||||
jumping = game.IsKeyDown( KeyBinding.Jump );
|
physics.jumping = game.IsKeyDown( KeyBinding.Jump );
|
||||||
speeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.Speed );
|
Hacks.Speeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.Speed );
|
||||||
halfSpeeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.HalfSpeed );
|
Hacks.HalfSpeeding = Hacks.Enabled && game.IsKeyDown( KeyBinding.HalfSpeed );
|
||||||
flyingUp = game.IsKeyDown( KeyBinding.FlyUp );
|
Hacks.FlyingUp = game.IsKeyDown( KeyBinding.FlyUp );
|
||||||
flyingDown = game.IsKeyDown( KeyBinding.FlyDown );
|
Hacks.FlyingDown = game.IsKeyDown( KeyBinding.FlyDown );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool jumping, speeding, halfSpeeding, flying, noClip, flyingDown, flyingUp;
|
|
||||||
|
|
||||||
/// <summary> Disables any hacks if their respective CanHackX value is set to false. </summary>
|
/// <summary> Disables any hacks if their respective CanHackX value is set to false. </summary>
|
||||||
public void CheckHacksConsistency() {
|
public void CheckHacksConsistency() {
|
||||||
if( !Hacks.CanFly || !Hacks.Enabled ) { flying = false; flyingDown = false; flyingUp = false; }
|
Hacks.CheckHacksConsistency();
|
||||||
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();
|
|
||||||
if( !Hacks.Enabled || !Hacks.CanAnyHacks || !Hacks.CanSpeed )
|
if( !Hacks.Enabled || !Hacks.CanAnyHacks || !Hacks.CanSpeed )
|
||||||
jumpVel = serverJumpVel;
|
physics.jumpVel = physics.serverJumpVel;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Vector3 lastPos, nextPos;
|
internal Vector3 lastPos, nextPos;
|
||||||
@ -248,17 +242,17 @@ namespace ClassicalSharp.Entities {
|
|||||||
SpawnYaw = HeadYawDegrees;
|
SpawnYaw = HeadYawDegrees;
|
||||||
SpawnPitch = PitchDegrees;
|
SpawnPitch = PitchDegrees;
|
||||||
} else if( key == keys[KeyBinding.Fly] && Hacks.CanFly && Hacks.Enabled ) {
|
} 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 ) {
|
} else if( key == keys[KeyBinding.NoClip] && Hacks.CanNoclip && Hacks.Enabled ) {
|
||||||
if( noClip ) Velocity.Y = 0;
|
if( Hacks.Noclip ) Velocity.Y = 0;
|
||||||
noClip = !noClip;
|
Hacks.Noclip = !Hacks.Noclip;
|
||||||
} else if( key == keys[KeyBinding.Jump] && !onGround && !(flying || noClip) ) {
|
} else if( key == keys[KeyBinding.Jump] && !onGround && !(Hacks.Flying || Hacks.Noclip) ) {
|
||||||
if( !firstJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) {
|
if( !physics.firstJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) {
|
||||||
DoNormalJump();
|
physics.DoNormalJump();
|
||||||
firstJump = true;
|
physics.firstJump = true;
|
||||||
} else if( !secondJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) {
|
} else if( !physics.secondJump && Hacks.CanDoubleJump && Hacks.DoubleJump ) {
|
||||||
DoNormalJump();
|
physics.DoNormalJump();
|
||||||
secondJump = true;
|
physics.secondJump = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -282,10 +276,9 @@ namespace ClassicalSharp.Entities {
|
|||||||
for ( int xx = minX; xx <= maxX; xx++ )
|
for ( int xx = minX; xx <= maxX; xx++ )
|
||||||
{
|
{
|
||||||
Vector3I coords = new Vector3I( P.X + xx, y + yy, P.Z + zz );
|
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 min = info.MinBB[block] + (Vector3)coords;
|
||||||
Vector3 max = info.MaxBB[block] + (Vector3)coords;
|
Vector3 max = info.MaxBB[block] + (Vector3)coords;
|
||||||
Console.WriteLine( min + "_" + max );
|
|
||||||
if( !bb.Intersects( new BoundingBox( min, max ) ) ) continue;
|
if( !bb.Intersects( new BoundingBox( min, max ) ) ) continue;
|
||||||
anyHit |= info.Collide[block] == CollideType.Solid;
|
anyHit |= info.Collide[block] == CollideType.Solid;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ namespace ClassicalSharp {
|
|||||||
pos + game.BlockInfo.MaxBB[newBlock] );
|
pos + game.BlockInfo.MaxBB[newBlock] );
|
||||||
BoundingBox localBB = game.LocalPlayer.CollisionBounds;
|
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;
|
HacksComponent hacks = game.LocalPlayer.Hacks;
|
||||||
if( hacks.CanPushbackBlocks && hacks.PushbackPlacing && hacks.Enabled )
|
if( hacks.CanPushbackBlocks && hacks.PushbackPlacing && hacks.Enabled )
|
||||||
return PushbackPlace( selected, blockBB );
|
return PushbackPlace( selected, blockBB );
|
||||||
@ -163,7 +163,8 @@ namespace ClassicalSharp {
|
|||||||
if( !validPos ) return false;
|
if( !validPos ) return false;
|
||||||
|
|
||||||
game.LocalPlayer.Position = newP;
|
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;
|
game.LocalPlayer.Position = oldP;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,20 @@ namespace ClassicalSharp {
|
|||||||
|
|
||||||
public static class Picking {
|
public static class Picking {
|
||||||
|
|
||||||
static RayCaster ray = new RayCaster();
|
static RayTracer tracer = new RayTracer();
|
||||||
|
|
||||||
/// <summary> Determines the picked block based on the given origin and direction vector.<br/>
|
/// <summary> Determines the picked block based on the given origin and direction vector.<br/>
|
||||||
/// Marks pickedPos as invalid if a block could not be found due to going outside map boundaries
|
/// 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. </summary>
|
/// or not being able to find a suitable candiate within the given reach distance. </summary>
|
||||||
public static void CalculatePickedBlock( Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos ) {
|
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;
|
World map = game.World;
|
||||||
BlockInfo info = game.BlockInfo;
|
BlockInfo info = game.BlockInfo;
|
||||||
float reachSquared = reach * reach;
|
float reachSquared = reach * reach;
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
|
|
||||||
while( iterations < 10000 ) {
|
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 );
|
byte block = GetBlock( map, x, y, z, origin );
|
||||||
Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
|
Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
|
||||||
Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];
|
Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];
|
||||||
@ -43,7 +43,7 @@ namespace ClassicalSharp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray.Step();
|
tracer.Step();
|
||||||
iterations++;
|
iterations++;
|
||||||
}
|
}
|
||||||
throw new InvalidOperationException( "did over 10000 iterations in CalculatePickedBlock(). " +
|
throw new InvalidOperationException( "did over 10000 iterations in CalculatePickedBlock(). " +
|
||||||
@ -56,14 +56,14 @@ namespace ClassicalSharp {
|
|||||||
pickedPos.IntersectPoint = origin + dir * reach;
|
pickedPos.IntersectPoint = origin + dir * reach;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ray.SetRayData( origin, dir );
|
tracer.SetRayData( origin, dir );
|
||||||
World map = game.World;
|
World map = game.World;
|
||||||
BlockInfo info = game.BlockInfo;
|
BlockInfo info = game.BlockInfo;
|
||||||
float reachSquared = reach * reach;
|
float reachSquared = reach * reach;
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
|
|
||||||
while( iterations < 10000 ) {
|
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 );
|
byte block = GetBlock( map, x, y, z, origin );
|
||||||
Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
|
Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
|
||||||
Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];
|
Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];
|
||||||
@ -102,7 +102,7 @@ namespace ClassicalSharp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray.Step();
|
tracer.Step();
|
||||||
iterations++;
|
iterations++;
|
||||||
}
|
}
|
||||||
throw new InvalidOperationException( "did over 10000 iterations in ClipCameraPos(). " +
|
throw new InvalidOperationException( "did over 10000 iterations in ClipCameraPos(). " +
|
||||||
|
@ -12,7 +12,7 @@ namespace ClassicalSharp {
|
|||||||
// John Amanatides, Andrew Woo
|
// John Amanatides, Andrew Woo
|
||||||
// http://www.cse.yorku.ca/~amana/research/grid.pdf
|
// 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
|
// 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;
|
public int X, Y, Z;
|
||||||
Vector3I step, cellBoundary;
|
Vector3I step, cellBoundary;
|
@ -348,9 +348,9 @@ namespace ClassicalSharp.Net {
|
|||||||
p.CheckHacksConsistency();
|
p.CheckHacksConsistency();
|
||||||
|
|
||||||
float jumpHeight = reader.ReadInt16() / 32f;
|
float jumpHeight = reader.ReadInt16() / 32f;
|
||||||
if( jumpHeight < 0 ) p.jumpVel = 0.42f;
|
if( jumpHeight < 0 ) p.physics.jumpVel = 0.42f;
|
||||||
else p.CalculateJumpVelocity( jumpHeight );
|
else p.physics.CalculateJumpVelocity( jumpHeight );
|
||||||
p.serverJumpVel = p.jumpVel;
|
p.physics.serverJumpVel = p.physics.jumpVel;
|
||||||
game.Events.RaiseHackPermissionsChanged();
|
game.Events.RaiseHackPermissionsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ namespace ClassicalSharp.Net {
|
|||||||
if( (DateTime.UtcNow - lastPacket).TotalSeconds >= 20 )
|
if( (DateTime.UtcNow - lastPacket).TotalSeconds >= 20 )
|
||||||
CheckDisconnection( delta );
|
CheckDisconnection( delta );
|
||||||
if( Disconnected ) return;
|
if( Disconnected ) return;
|
||||||
|
LocalPlayer player = game.LocalPlayer;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reader.ReadPendingData();
|
reader.ReadPendingData();
|
||||||
@ -76,15 +77,15 @@ namespace ClassicalSharp.Net {
|
|||||||
return;
|
return;
|
||||||
} catch {
|
} catch {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
while( (reader.size - reader.index) > 0 ) {
|
while( (reader.size - reader.index) > 0 ) {
|
||||||
byte opcode = reader.buffer[reader.index];
|
byte opcode = reader.buffer[reader.index];
|
||||||
// Workaround for older D3 servers which wrote one byte too many for HackControl packets.
|
// Workaround for older D3 servers which wrote one byte too many for HackControl packets.
|
||||||
if( opcode == 0xFF && lastOpcode == PacketId.CpeHackControl ) {
|
if( opcode == 0xFF && lastOpcode == PacketId.CpeHackControl ) {
|
||||||
reader.Skip( 1 );
|
reader.Skip( 1 );
|
||||||
game.LocalPlayer.jumpVel = 0.42f; // assume default jump height
|
player.physics.jumpVel = 0.42f; // assume default jump height
|
||||||
game.LocalPlayer.serverJumpVel = game.LocalPlayer.jumpVel;
|
player.physics.serverJumpVel = player.physics.jumpVel;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +99,8 @@ namespace ClassicalSharp.Net {
|
|||||||
if( (reader.size - reader.index) < packetSizes[opcode] ) break;
|
if( (reader.size - reader.index) < packetSizes[opcode] ) break;
|
||||||
ReadPacket( opcode );
|
ReadPacket( opcode );
|
||||||
}
|
}
|
||||||
reader.RemoveProcessed();
|
|
||||||
|
|
||||||
Player player = game.LocalPlayer;
|
reader.RemoveProcessed();
|
||||||
if( receivedFirstPosition ) {
|
if( receivedFirstPosition ) {
|
||||||
SendPosition( player.Position, player.HeadYawDegrees, player.PitchDegrees );
|
SendPosition( player.Position, player.HeadYawDegrees, player.PitchDegrees );
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user