371 lines
16 KiB
C#
371 lines
16 KiB
C#
// Decompiled with JetBrains decompiler
|
|
// Type: Terraria.Physics.BallCollision
|
|
// Assembly: Terraria, Version=1.4.0.5, Culture=neutral, PublicKeyToken=null
|
|
// MVID: 67F9E73E-0A81-4937-A22C-5515CD405A83
|
|
// Assembly location: C:\Users\mikeyisbaeyt\Downloads\depotdownloader-2.4.5\depots\105601\6707058\Terraria.exe
|
|
|
|
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using Terraria.DataStructures;
|
|
|
|
namespace Terraria.Physics
|
|
{
|
|
public static class BallCollision
|
|
{
|
|
public static BallStepResult Step(
|
|
PhysicsProperties physicsProperties,
|
|
Entity entity,
|
|
ref float entityAngularVelocity,
|
|
IBallContactListener listener)
|
|
{
|
|
Vector2 position = entity.position;
|
|
Vector2 velocity1 = entity.velocity;
|
|
Vector2 size = entity.Size;
|
|
float num1 = entityAngularVelocity;
|
|
float num2 = size.X * 0.5f;
|
|
float num3 = num1 * physicsProperties.Drag;
|
|
Vector2 vector2_1 = velocity1 * physicsProperties.Drag;
|
|
float num4 = vector2_1.Length();
|
|
if ((double) num4 > 1000.0)
|
|
{
|
|
vector2_1 = 1000f * Vector2.Normalize(vector2_1);
|
|
num4 = 1000f;
|
|
}
|
|
int num5 = Math.Max(1, (int) Math.Ceiling((double) num4 / 2.0));
|
|
float timeScale = 1f / (float) num5;
|
|
Vector2 velocity2 = vector2_1 * timeScale;
|
|
float angularVelocity = num3 * timeScale;
|
|
float num6 = physicsProperties.Gravity / (float) (num5 * num5);
|
|
bool flag = false;
|
|
for (int index = 0; index < num5; ++index)
|
|
{
|
|
velocity2.Y += num6;
|
|
BallPassThroughType type;
|
|
Tile tile;
|
|
if (BallCollision.CheckForPassThrough(position + size * 0.5f, out type, out tile))
|
|
{
|
|
if (type == BallPassThroughType.Tile && Main.tileSolid[(int) tile.type] && !Main.tileSolidTop[(int) tile.type])
|
|
{
|
|
velocity2 *= 0.0f;
|
|
angularVelocity *= 0.0f;
|
|
flag = true;
|
|
}
|
|
else
|
|
{
|
|
BallPassThroughEvent passThrough = new BallPassThroughEvent(timeScale, tile, entity, type);
|
|
listener.OnPassThrough(physicsProperties, ref position, ref velocity2, ref angularVelocity, ref passThrough);
|
|
}
|
|
}
|
|
position += velocity2;
|
|
if (!BallCollision.IsBallInWorld(position, size))
|
|
return BallStepResult.OutOfBounds();
|
|
Vector2 collisionPoint;
|
|
if (BallCollision.GetClosestEdgeToCircle(position, size, velocity2, out collisionPoint, out tile))
|
|
{
|
|
Vector2 normal = Vector2.Normalize(position + size * 0.5f - collisionPoint);
|
|
position = collisionPoint + normal * (num2 + 0.0001f) - size * 0.5f;
|
|
BallCollisionEvent collision = new BallCollisionEvent(timeScale, normal, collisionPoint, tile, entity);
|
|
flag = true;
|
|
velocity2 = Vector2.Reflect(velocity2, collision.Normal);
|
|
listener.OnCollision(physicsProperties, ref position, ref velocity2, ref collision);
|
|
angularVelocity = (float) ((double) collision.Normal.X * (double) velocity2.Y - (double) collision.Normal.Y * (double) velocity2.X) / num2;
|
|
}
|
|
}
|
|
Vector2 vector2_2 = velocity2 / timeScale;
|
|
float num7 = angularVelocity / timeScale;
|
|
BallStepResult ballStepResult = BallStepResult.Moving();
|
|
if (flag && (double) vector2_2.X > -0.00999999977648258 && (double) vector2_2.X < 0.00999999977648258 && (double) vector2_2.Y <= 0.0 && (double) vector2_2.Y > -(double) physicsProperties.Gravity)
|
|
ballStepResult = BallStepResult.Resting();
|
|
entity.position = position;
|
|
entity.velocity = vector2_2;
|
|
entityAngularVelocity = num7;
|
|
return ballStepResult;
|
|
}
|
|
|
|
private static bool CheckForPassThrough(
|
|
Vector2 center,
|
|
out BallPassThroughType type,
|
|
out Tile contactTile)
|
|
{
|
|
Point tileCoordinates = center.ToTileCoordinates();
|
|
Tile tile = Main.tile[tileCoordinates.X, tileCoordinates.Y];
|
|
contactTile = tile;
|
|
type = BallPassThroughType.None;
|
|
if (tile == null)
|
|
return false;
|
|
if (tile.nactive())
|
|
{
|
|
type = BallPassThroughType.Tile;
|
|
return BallCollision.IsPositionInsideTile(center, tileCoordinates, tile);
|
|
}
|
|
if (tile.liquid <= (byte) 0)
|
|
return false;
|
|
float num = (float) ((double) (tileCoordinates.Y + 1) * 16.0 - (double) tile.liquid / (double) byte.MaxValue * 16.0);
|
|
switch (tile.liquidType())
|
|
{
|
|
case 1:
|
|
type = BallPassThroughType.Lava;
|
|
break;
|
|
case 2:
|
|
type = BallPassThroughType.Honey;
|
|
break;
|
|
default:
|
|
type = BallPassThroughType.Water;
|
|
break;
|
|
}
|
|
return (double) num < (double) center.Y;
|
|
}
|
|
|
|
private static bool IsPositionInsideTile(Vector2 position, Point tileCoordinates, Tile tile)
|
|
{
|
|
if (tile.slope() == (byte) 0 && !tile.halfBrick())
|
|
return true;
|
|
Vector2 vector2 = position / 16f - new Vector2((float) tileCoordinates.X, (float) tileCoordinates.Y);
|
|
switch (tile.slope())
|
|
{
|
|
case 0:
|
|
return (double) vector2.Y > 0.5;
|
|
case 1:
|
|
return (double) vector2.Y > (double) vector2.X;
|
|
case 2:
|
|
return (double) vector2.Y > 1.0 - (double) vector2.X;
|
|
case 3:
|
|
return (double) vector2.Y < 1.0 - (double) vector2.X;
|
|
case 4:
|
|
return (double) vector2.Y < (double) vector2.X;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool IsBallInWorld(Vector2 position, Vector2 size) => (double) position.X > 32.0 && (double) position.Y > 32.0 && (double) position.X + (double) size.X < (double) Main.maxTilesX * 16.0 - 32.0 && (double) position.Y + (double) size.Y < (double) Main.maxTilesY * 16.0 - 32.0;
|
|
|
|
private static bool GetClosestEdgeToCircle(
|
|
Vector2 position,
|
|
Vector2 size,
|
|
Vector2 velocity,
|
|
out Vector2 collisionPoint,
|
|
out Tile collisionTile)
|
|
{
|
|
Rectangle tileBounds = BallCollision.GetTileBounds(position, size);
|
|
Vector2 center = position + size * 0.5f;
|
|
BallCollision.TileEdges tileEdges1 = BallCollision.TileEdges.None;
|
|
BallCollision.TileEdges tileEdges2 = (double) velocity.Y >= 0.0 ? tileEdges1 | BallCollision.TileEdges.Top : tileEdges1 | BallCollision.TileEdges.Bottom;
|
|
BallCollision.TileEdges tileEdges3 = (double) velocity.X >= 0.0 ? tileEdges2 | BallCollision.TileEdges.Left : tileEdges2 | BallCollision.TileEdges.Right;
|
|
BallCollision.TileEdges tileEdges4 = (double) velocity.Y <= (double) velocity.X ? tileEdges3 | BallCollision.TileEdges.TopRightSlope : tileEdges3 | BallCollision.TileEdges.BottomLeftSlope;
|
|
BallCollision.TileEdges edgesToTest = (double) velocity.Y <= -(double) velocity.X ? tileEdges4 | BallCollision.TileEdges.TopLeftSlope : tileEdges4 | BallCollision.TileEdges.BottomRightSlope;
|
|
collisionPoint = Vector2.Zero;
|
|
collisionTile = (Tile) null;
|
|
float num1 = float.MaxValue;
|
|
Vector2 closestPointOut = new Vector2();
|
|
float distanceSquaredOut = 0.0f;
|
|
for (int left = tileBounds.Left; left < tileBounds.Right; ++left)
|
|
{
|
|
for (int top = tileBounds.Top; top < tileBounds.Bottom; ++top)
|
|
{
|
|
if (BallCollision.GetCollisionPointForTile(edgesToTest, left, top, center, ref closestPointOut, ref distanceSquaredOut) && (double) distanceSquaredOut < (double) num1 && (double) Vector2.Dot(velocity, center - closestPointOut) <= 0.0)
|
|
{
|
|
num1 = distanceSquaredOut;
|
|
collisionPoint = closestPointOut;
|
|
collisionTile = Main.tile[left, top];
|
|
}
|
|
}
|
|
}
|
|
float num2 = size.X / 2f;
|
|
return (double) num1 < (double) num2 * (double) num2;
|
|
}
|
|
|
|
private static bool GetCollisionPointForTile(
|
|
BallCollision.TileEdges edgesToTest,
|
|
int x,
|
|
int y,
|
|
Vector2 center,
|
|
ref Vector2 closestPointOut,
|
|
ref float distanceSquaredOut)
|
|
{
|
|
Tile tile = Main.tile[x, y];
|
|
if (tile == null || !tile.nactive() || !Main.tileSolid[(int) tile.type] && !Main.tileSolidTop[(int) tile.type] || !Main.tileSolid[(int) tile.type] && Main.tileSolidTop[(int) tile.type] && tile.frameY != (short) 0)
|
|
return false;
|
|
if (Main.tileSolidTop[(int) tile.type])
|
|
edgesToTest &= BallCollision.TileEdges.Top | BallCollision.TileEdges.BottomLeftSlope | BallCollision.TileEdges.BottomRightSlope;
|
|
Vector2 tilePosition = new Vector2((float) x * 16f, (float) y * 16f);
|
|
bool flag = false;
|
|
LineSegment edge = new LineSegment();
|
|
if (BallCollision.GetSlopeEdge(ref edgesToTest, tile, tilePosition, ref edge))
|
|
{
|
|
closestPointOut = BallCollision.ClosestPointOnLineSegment(center, edge);
|
|
distanceSquaredOut = Vector2.DistanceSquared(closestPointOut, center);
|
|
flag = true;
|
|
}
|
|
if (BallCollision.GetTopOrBottomEdge(edgesToTest, x, y, tilePosition, ref edge))
|
|
{
|
|
Vector2 vector2 = BallCollision.ClosestPointOnLineSegment(center, edge);
|
|
float num = Vector2.DistanceSquared(vector2, center);
|
|
if (!flag || (double) num < (double) distanceSquaredOut)
|
|
{
|
|
distanceSquaredOut = num;
|
|
closestPointOut = vector2;
|
|
}
|
|
flag = true;
|
|
}
|
|
if (BallCollision.GetLeftOrRightEdge(edgesToTest, x, y, tilePosition, ref edge))
|
|
{
|
|
Vector2 vector2 = BallCollision.ClosestPointOnLineSegment(center, edge);
|
|
float num = Vector2.DistanceSquared(vector2, center);
|
|
if (!flag || (double) num < (double) distanceSquaredOut)
|
|
{
|
|
distanceSquaredOut = num;
|
|
closestPointOut = vector2;
|
|
}
|
|
flag = true;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
private static bool GetSlopeEdge(
|
|
ref BallCollision.TileEdges edgesToTest,
|
|
Tile tile,
|
|
Vector2 tilePosition,
|
|
ref LineSegment edge)
|
|
{
|
|
switch (tile.slope())
|
|
{
|
|
case 0:
|
|
return false;
|
|
case 1:
|
|
edgesToTest &= BallCollision.TileEdges.Bottom | BallCollision.TileEdges.Left | BallCollision.TileEdges.BottomLeftSlope;
|
|
if ((edgesToTest & BallCollision.TileEdges.BottomLeftSlope) == BallCollision.TileEdges.None)
|
|
return false;
|
|
edge.Start = tilePosition;
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
|
|
return true;
|
|
case 2:
|
|
edgesToTest &= BallCollision.TileEdges.Bottom | BallCollision.TileEdges.Right | BallCollision.TileEdges.BottomRightSlope;
|
|
if ((edgesToTest & BallCollision.TileEdges.BottomRightSlope) == BallCollision.TileEdges.None)
|
|
return false;
|
|
edge.Start = new Vector2(tilePosition.X, tilePosition.Y + 16f);
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y);
|
|
return true;
|
|
case 3:
|
|
edgesToTest &= BallCollision.TileEdges.Top | BallCollision.TileEdges.Left | BallCollision.TileEdges.TopLeftSlope;
|
|
if ((edgesToTest & BallCollision.TileEdges.TopLeftSlope) == BallCollision.TileEdges.None)
|
|
return false;
|
|
edge.Start = new Vector2(tilePosition.X, tilePosition.Y + 16f);
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y);
|
|
return true;
|
|
case 4:
|
|
edgesToTest &= BallCollision.TileEdges.Top | BallCollision.TileEdges.Right | BallCollision.TileEdges.TopRightSlope;
|
|
if ((edgesToTest & BallCollision.TileEdges.TopRightSlope) == BallCollision.TileEdges.None)
|
|
return false;
|
|
edge.Start = tilePosition;
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool GetTopOrBottomEdge(
|
|
BallCollision.TileEdges edgesToTest,
|
|
int x,
|
|
int y,
|
|
Vector2 tilePosition,
|
|
ref LineSegment edge)
|
|
{
|
|
if ((edgesToTest & BallCollision.TileEdges.Bottom) != BallCollision.TileEdges.None)
|
|
{
|
|
Tile tile = Main.tile[x, y + 1];
|
|
if ((!BallCollision.IsNeighborSolid(tile) || tile.slope() == (byte) 1 || tile.slope() == (byte) 2 ? 1 : (tile.halfBrick() ? 1 : 0)) == 0)
|
|
return false;
|
|
edge.Start = new Vector2(tilePosition.X, tilePosition.Y + 16f);
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
|
|
return true;
|
|
}
|
|
if ((edgesToTest & BallCollision.TileEdges.Top) == BallCollision.TileEdges.None)
|
|
return false;
|
|
Tile tile1 = Main.tile[x, y - 1];
|
|
if ((Main.tile[x, y].halfBrick() || !BallCollision.IsNeighborSolid(tile1) || tile1.slope() == (byte) 3 ? 1 : (tile1.slope() == (byte) 4 ? 1 : 0)) == 0)
|
|
return false;
|
|
if (Main.tile[x, y].halfBrick())
|
|
tilePosition.Y += 8f;
|
|
edge.Start = new Vector2(tilePosition.X, tilePosition.Y);
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y);
|
|
return true;
|
|
}
|
|
|
|
private static bool GetLeftOrRightEdge(
|
|
BallCollision.TileEdges edgesToTest,
|
|
int x,
|
|
int y,
|
|
Vector2 tilePosition,
|
|
ref LineSegment edge)
|
|
{
|
|
if ((edgesToTest & BallCollision.TileEdges.Left) != BallCollision.TileEdges.None)
|
|
{
|
|
Tile tile1 = Main.tile[x, y];
|
|
Tile tile2 = Main.tile[x - 1, y];
|
|
if ((!BallCollision.IsNeighborSolid(tile2) || tile2.slope() == (byte) 1 || tile2.slope() == (byte) 3 ? 1 : (!tile2.halfBrick() ? 0 : (!tile1.halfBrick() ? 1 : 0))) == 0)
|
|
return false;
|
|
edge.Start = new Vector2(tilePosition.X, tilePosition.Y);
|
|
edge.End = new Vector2(tilePosition.X, tilePosition.Y + 16f);
|
|
if (tile1.halfBrick())
|
|
edge.Start.Y += 8f;
|
|
return true;
|
|
}
|
|
if ((edgesToTest & BallCollision.TileEdges.Right) == BallCollision.TileEdges.None)
|
|
return false;
|
|
Tile tile3 = Main.tile[x, y];
|
|
Tile tile4 = Main.tile[x + 1, y];
|
|
if ((!BallCollision.IsNeighborSolid(tile4) || tile4.slope() == (byte) 2 || tile4.slope() == (byte) 4 ? 1 : (!tile4.halfBrick() ? 0 : (!tile3.halfBrick() ? 1 : 0))) == 0)
|
|
return false;
|
|
edge.Start = new Vector2(tilePosition.X + 16f, tilePosition.Y);
|
|
edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
|
|
if (tile3.halfBrick())
|
|
edge.Start.Y += 8f;
|
|
return true;
|
|
}
|
|
|
|
private static Rectangle GetTileBounds(Vector2 position, Vector2 size)
|
|
{
|
|
int x = (int) Math.Floor((double) position.X / 16.0);
|
|
int y = (int) Math.Floor((double) position.Y / 16.0);
|
|
int num1 = (int) Math.Floor(((double) position.X + (double) size.X) / 16.0);
|
|
int num2 = (int) Math.Floor(((double) position.Y + (double) size.Y) / 16.0);
|
|
return new Rectangle(x, y, num1 - x + 1, num2 - y + 1);
|
|
}
|
|
|
|
private static bool IsNeighborSolid(Tile tile) => tile != null && tile.nactive() && Main.tileSolid[(int) tile.type] && !Main.tileSolidTop[(int) tile.type];
|
|
|
|
private static Vector2 ClosestPointOnLineSegment(Vector2 point, LineSegment lineSegment)
|
|
{
|
|
Vector2 vector2_1 = point - lineSegment.Start;
|
|
Vector2 vector2_2 = lineSegment.End - lineSegment.Start;
|
|
float num1 = vector2_2.LengthSquared();
|
|
Vector2 vector2_3 = vector2_2;
|
|
float num2 = Vector2.Dot(vector2_1, vector2_3) / num1;
|
|
if ((double) num2 < 0.0)
|
|
return lineSegment.Start;
|
|
return (double) num2 > 1.0 ? lineSegment.End : lineSegment.Start + vector2_2 * num2;
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private static void DrawEdge(LineSegment edge)
|
|
{
|
|
}
|
|
|
|
[Flags]
|
|
private enum TileEdges : uint
|
|
{
|
|
None = 0,
|
|
Top = 1,
|
|
Bottom = 2,
|
|
Left = 4,
|
|
Right = 8,
|
|
TopLeftSlope = 16, // 0x00000010
|
|
TopRightSlope = 32, // 0x00000020
|
|
BottomLeftSlope = 64, // 0x00000040
|
|
BottomRightSlope = 128, // 0x00000080
|
|
}
|
|
}
|
|
}
|