Terraria v1.4.4.9
Terraria source code documentation
Loading...
Searching...
No Matches
BallCollision.cs
Go to the documentation of this file.
1using System;
5
7
8public static class BallCollision
9{
10 [Flags]
11 private enum TileEdges : uint
12 {
13 None = 0u,
14 Top = 1u,
15 Bottom = 2u,
16 Left = 4u,
17 Right = 8u,
18 TopLeftSlope = 0x10u,
19 TopRightSlope = 0x20u,
20 BottomLeftSlope = 0x40u,
21 BottomRightSlope = 0x80u
22 }
23
24 public static BallStepResult Step(PhysicsProperties physicsProperties, Entity entity, ref float entityAngularVelocity, IBallContactListener listener)
25 {
26 Vector2 position = entity.position;
27 Vector2 velocity = entity.velocity;
28 Vector2 size = entity.Size;
29 float num = entityAngularVelocity;
30 float num2 = size.X * 0.5f;
31 num *= physicsProperties.Drag;
32 velocity *= physicsProperties.Drag;
33 float num3 = velocity.Length();
34 if (num3 > 1000f)
35 {
36 velocity = 1000f * Vector2.Normalize(velocity);
37 num3 = 1000f;
38 }
39 int num4 = Math.Max(1, (int)Math.Ceiling(num3 / 2f));
40 float num5 = 1f / (float)num4;
41 velocity *= num5;
42 num *= num5;
43 float num6 = physicsProperties.Gravity / (float)(num4 * num4);
44 bool flag = false;
45 for (int i = 0; i < num4; i++)
46 {
47 velocity.Y += num6;
48 if (CheckForPassThrough(position + size * 0.5f, out var type, out var contactTile))
49 {
50 if (type == BallPassThroughType.Tile && Main.tileSolid[contactTile.type] && !Main.tileSolidTop[contactTile.type])
51 {
52 velocity *= 0f;
53 num *= 0f;
54 flag = true;
55 }
56 else
57 {
58 BallPassThroughEvent passThrough = new BallPassThroughEvent(num5, contactTile, entity, type);
59 listener.OnPassThrough(physicsProperties, ref position, ref velocity, ref num, ref passThrough);
60 }
61 }
62 position += velocity;
63 if (!IsBallInWorld(position, size))
64 {
66 }
67 if (GetClosestEdgeToCircle(position, size, velocity, out var collisionPoint, out contactTile))
68 {
69 Vector2 vector = Vector2.Normalize(position + size * 0.5f - collisionPoint);
70 position = collisionPoint + vector * (num2 + 0.0001f) - size * 0.5f;
71 BallCollisionEvent collision = new BallCollisionEvent(num5, vector, collisionPoint, contactTile, entity);
72 flag = true;
73 velocity = Vector2.Reflect(velocity, collision.Normal);
74 listener.OnCollision(physicsProperties, ref position, ref velocity, ref collision);
75 num = (collision.Normal.X * velocity.Y - collision.Normal.Y * velocity.X) / num2;
76 }
77 }
78 velocity /= num5;
79 num /= num5;
81 if (flag && velocity.X > -0.01f && velocity.X < 0.01f && velocity.Y <= 0f && velocity.Y > 0f - physicsProperties.Gravity)
82 {
83 result = BallStepResult.Resting();
84 }
85 entity.position = position;
86 entity.velocity = velocity;
87 entityAngularVelocity = num;
88 return result;
89 }
90
91 private static bool CheckForPassThrough(Vector2 center, out BallPassThroughType type, out Tile contactTile)
92 {
93 Point tileCoordinates = center.ToTileCoordinates();
94 Tile tile = (contactTile = Main.tile[tileCoordinates.X, tileCoordinates.Y]);
96 if (tile == null)
97 {
98 return false;
99 }
100 if (tile.nactive())
101 {
103 return IsPositionInsideTile(center, tileCoordinates, tile);
104 }
105 if (tile.liquid > 0)
106 {
107 float num = (float)(tileCoordinates.Y + 1) * 16f - (float)(int)tile.liquid / 255f * 16f;
108 switch (tile.liquidType())
109 {
110 case 1:
112 break;
113 case 2:
115 break;
116 default:
118 break;
119 }
120 return num < center.Y;
121 }
122 return false;
123 }
124
125 private static bool IsPositionInsideTile(Vector2 position, Point tileCoordinates, Tile tile)
126 {
127 if (tile.slope() == 0 && !tile.halfBrick())
128 {
129 return true;
130 }
131 Vector2 vector = position / 16f - new Vector2(tileCoordinates.X, tileCoordinates.Y);
132 return tile.slope() switch
133 {
134 0 => vector.Y > 0.5f,
135 1 => vector.Y > vector.X,
136 2 => vector.Y > 1f - vector.X,
137 3 => vector.Y < 1f - vector.X,
138 4 => vector.Y < vector.X,
139 _ => false,
140 };
141 }
142
143 private static bool IsBallInWorld(Vector2 position, Vector2 size)
144 {
145 if (position.X > 32f && position.Y > 32f && position.X + size.X < (float)Main.maxTilesX * 16f - 32f)
146 {
147 return position.Y + size.Y < (float)Main.maxTilesY * 16f - 32f;
148 }
149 return false;
150 }
151
152 private static bool GetClosestEdgeToCircle(Vector2 position, Vector2 size, Vector2 velocity, out Vector2 collisionPoint, out Tile collisionTile)
153 {
154 Rectangle tileBounds = GetTileBounds(position, size);
155 Vector2 vector = position + size * 0.5f;
156 TileEdges tileEdges = TileEdges.None;
157 tileEdges = ((!(velocity.Y < 0f)) ? (tileEdges | TileEdges.Top) : (tileEdges | TileEdges.Bottom));
158 tileEdges = ((!(velocity.X < 0f)) ? (tileEdges | TileEdges.Left) : (tileEdges | TileEdges.Right));
159 tileEdges = ((!(velocity.Y > velocity.X)) ? (tileEdges | TileEdges.TopRightSlope) : (tileEdges | TileEdges.BottomLeftSlope));
160 tileEdges = ((!(velocity.Y > 0f - velocity.X)) ? (tileEdges | TileEdges.TopLeftSlope) : (tileEdges | TileEdges.BottomRightSlope));
161 collisionPoint = Vector2.Zero;
162 collisionTile = null;
163 float num = float.MaxValue;
164 Vector2 closestPointOut = default(Vector2);
165 float distanceSquaredOut = 0f;
166 for (int i = tileBounds.Left; i < tileBounds.Right; i++)
167 {
168 for (int j = tileBounds.Top; j < tileBounds.Bottom; j++)
169 {
170 if (GetCollisionPointForTile(tileEdges, i, j, vector, ref closestPointOut, ref distanceSquaredOut) && !(distanceSquaredOut >= num) && !(Vector2.Dot(velocity, vector - closestPointOut) > 0f))
171 {
172 num = distanceSquaredOut;
173 collisionPoint = closestPointOut;
174 collisionTile = Main.tile[i, j];
175 }
176 }
177 }
178 float num2 = size.X / 2f;
179 return num < num2 * num2;
180 }
181
182 private static bool GetCollisionPointForTile(TileEdges edgesToTest, int x, int y, Vector2 center, ref Vector2 closestPointOut, ref float distanceSquaredOut)
183 {
184 Tile tile = Main.tile[x, y];
185 if (tile == null || !tile.nactive() || (!Main.tileSolid[tile.type] && !Main.tileSolidTop[tile.type]))
186 {
187 return false;
188 }
189 if (!Main.tileSolid[tile.type] && Main.tileSolidTop[tile.type] && tile.frameY != 0)
190 {
191 return false;
192 }
193 if (Main.tileSolidTop[tile.type])
194 {
195 edgesToTest &= TileEdges.Top | TileEdges.BottomLeftSlope | TileEdges.BottomRightSlope;
196 }
197 Vector2 tilePosition = new Vector2((float)x * 16f, (float)y * 16f);
198 bool flag = false;
199 LineSegment edge = default(LineSegment);
200 if (GetSlopeEdge(ref edgesToTest, tile, tilePosition, ref edge))
201 {
202 closestPointOut = ClosestPointOnLineSegment(center, edge);
203 distanceSquaredOut = Vector2.DistanceSquared(closestPointOut, center);
204 flag = true;
205 }
206 if (GetTopOrBottomEdge(edgesToTest, x, y, tilePosition, ref edge))
207 {
208 Vector2 vector = ClosestPointOnLineSegment(center, edge);
209 float num = Vector2.DistanceSquared(vector, center);
210 if (!flag || num < distanceSquaredOut)
211 {
212 distanceSquaredOut = num;
213 closestPointOut = vector;
214 }
215 flag = true;
216 }
217 if (GetLeftOrRightEdge(edgesToTest, x, y, tilePosition, ref edge))
218 {
219 Vector2 vector2 = ClosestPointOnLineSegment(center, edge);
220 float num2 = Vector2.DistanceSquared(vector2, center);
221 if (!flag || num2 < distanceSquaredOut)
222 {
223 distanceSquaredOut = num2;
224 closestPointOut = vector2;
225 }
226 flag = true;
227 }
228 return flag;
229 }
230
231 private static bool GetSlopeEdge(ref TileEdges edgesToTest, Tile tile, Vector2 tilePosition, ref LineSegment edge)
232 {
233 switch (tile.slope())
234 {
235 case 0:
236 return false;
237 case 1:
238 edgesToTest &= TileEdges.Bottom | TileEdges.Left | TileEdges.BottomLeftSlope;
239 if ((edgesToTest & TileEdges.BottomLeftSlope) == 0)
240 {
241 return false;
242 }
243 edge.Start = tilePosition;
244 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
245 return true;
246 case 2:
247 edgesToTest &= TileEdges.Bottom | TileEdges.Right | TileEdges.BottomRightSlope;
248 if ((edgesToTest & TileEdges.BottomRightSlope) == 0)
249 {
250 return false;
251 }
252 edge.Start = new Vector2(tilePosition.X, tilePosition.Y + 16f);
253 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y);
254 return true;
255 case 3:
256 edgesToTest &= TileEdges.Top | TileEdges.Left | TileEdges.TopLeftSlope;
257 if ((edgesToTest & TileEdges.TopLeftSlope) == 0)
258 {
259 return false;
260 }
261 edge.Start = new Vector2(tilePosition.X, tilePosition.Y + 16f);
262 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y);
263 return true;
264 case 4:
265 edgesToTest &= TileEdges.Top | TileEdges.Right | TileEdges.TopRightSlope;
266 if ((edgesToTest & TileEdges.TopRightSlope) == 0)
267 {
268 return false;
269 }
270 edge.Start = tilePosition;
271 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
272 return true;
273 default:
274 return false;
275 }
276 }
277
278 private static bool GetTopOrBottomEdge(TileEdges edgesToTest, int x, int y, Vector2 tilePosition, ref LineSegment edge)
279 {
280 if ((edgesToTest & TileEdges.Bottom) != 0)
281 {
282 Tile tile = Main.tile[x, y + 1];
283 if (IsNeighborSolid(tile) && tile.slope() != 1 && tile.slope() != 2 && !tile.halfBrick())
284 {
285 return false;
286 }
287 edge.Start = new Vector2(tilePosition.X, tilePosition.Y + 16f);
288 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
289 return true;
290 }
291 if ((edgesToTest & TileEdges.Top) != 0)
292 {
293 Tile tile2 = Main.tile[x, y - 1];
294 if (!Main.tile[x, y].halfBrick() && IsNeighborSolid(tile2) && tile2.slope() != 3 && tile2.slope() != 4)
295 {
296 return false;
297 }
298 if (Main.tile[x, y].halfBrick())
299 {
300 tilePosition.Y += 8f;
301 }
302 edge.Start = new Vector2(tilePosition.X, tilePosition.Y);
303 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y);
304 return true;
305 }
306 return false;
307 }
308
309 private static bool GetLeftOrRightEdge(TileEdges edgesToTest, int x, int y, Vector2 tilePosition, ref LineSegment edge)
310 {
311 if ((edgesToTest & TileEdges.Left) != 0)
312 {
313 Tile tile = Main.tile[x, y];
314 Tile tile2 = Main.tile[x - 1, y];
315 if (IsNeighborSolid(tile2) && tile2.slope() != 1 && tile2.slope() != 3 && (!tile2.halfBrick() || tile.halfBrick()))
316 {
317 return false;
318 }
319 edge.Start = new Vector2(tilePosition.X, tilePosition.Y);
320 edge.End = new Vector2(tilePosition.X, tilePosition.Y + 16f);
321 if (tile.halfBrick())
322 {
323 edge.Start.Y += 8f;
324 }
325 return true;
326 }
327 if ((edgesToTest & TileEdges.Right) != 0)
328 {
329 Tile tile3 = Main.tile[x, y];
330 Tile tile4 = Main.tile[x + 1, y];
331 if (IsNeighborSolid(tile4) && tile4.slope() != 2 && tile4.slope() != 4 && (!tile4.halfBrick() || tile3.halfBrick()))
332 {
333 return false;
334 }
335 edge.Start = new Vector2(tilePosition.X + 16f, tilePosition.Y);
336 edge.End = new Vector2(tilePosition.X + 16f, tilePosition.Y + 16f);
337 if (tile3.halfBrick())
338 {
339 edge.Start.Y += 8f;
340 }
341 return true;
342 }
343 return false;
344 }
345
346 private static Rectangle GetTileBounds(Vector2 position, Vector2 size)
347 {
348 int num = (int)Math.Floor(position.X / 16f);
349 int num2 = (int)Math.Floor(position.Y / 16f);
350 int num3 = (int)Math.Floor((position.X + size.X) / 16f);
351 int num4 = (int)Math.Floor((position.Y + size.Y) / 16f);
352 return new Rectangle(num, num2, num3 - num + 1, num4 - num2 + 1);
353 }
354
355 private static bool IsNeighborSolid(Tile tile)
356 {
357 if (tile != null && tile.nactive() && Main.tileSolid[tile.type])
358 {
359 return !Main.tileSolidTop[tile.type];
360 }
361 return false;
362 }
363
364 private static Vector2 ClosestPointOnLineSegment(Vector2 point, LineSegment lineSegment)
365 {
366 Vector2 value = point - lineSegment.Start;
367 Vector2 vector = lineSegment.End - lineSegment.Start;
368 float num = vector.LengthSquared();
369 float num2 = Vector2.Dot(value, vector) / num;
370 if (num2 < 0f)
371 {
372 return lineSegment.Start;
373 }
374 if (num2 > 1f)
375 {
376 return lineSegment.End;
377 }
378 return lineSegment.Start + vector * num2;
379 }
380
381 [Conditional("DEBUG")]
382 private static void DrawEdge(LineSegment edge)
383 {
384 }
385}
static double Ceiling(double a)
static double Floor(double d)
static byte Max(byte val1, byte val2)
Definition Math.cs:738
Vector2 Size
Definition Entity.cs:151
Vector2 velocity
Definition Entity.cs:16
Vector2 position
Definition Entity.cs:14
static int maxTilesY
Definition Main.cs:1116
static bool[] tileSolidTop
Definition Main.cs:1469
static int maxTilesX
Definition Main.cs:1114
static bool[] tileSolid
Definition Main.cs:1471
static Tile[,] tile
Definition Main.cs:1675
static Rectangle GetTileBounds(Vector2 position, Vector2 size)
static bool GetLeftOrRightEdge(TileEdges edgesToTest, int x, int y, Vector2 tilePosition, ref LineSegment edge)
static Vector2 ClosestPointOnLineSegment(Vector2 point, LineSegment lineSegment)
static bool IsBallInWorld(Vector2 position, Vector2 size)
static BallStepResult Step(PhysicsProperties physicsProperties, Entity entity, ref float entityAngularVelocity, IBallContactListener listener)
static bool IsNeighborSolid(Tile tile)
static bool GetCollisionPointForTile(TileEdges edgesToTest, int x, int y, Vector2 center, ref Vector2 closestPointOut, ref float distanceSquaredOut)
static bool CheckForPassThrough(Vector2 center, out BallPassThroughType type, out Tile contactTile)
static void DrawEdge(LineSegment edge)
static bool GetClosestEdgeToCircle(Vector2 position, Vector2 size, Vector2 velocity, out Vector2 collisionPoint, out Tile collisionTile)
static bool GetSlopeEdge(ref TileEdges edgesToTest, Tile tile, Vector2 tilePosition, ref LineSegment edge)
static bool IsPositionInsideTile(Vector2 position, Point tileCoordinates, Tile tile)
static bool GetTopOrBottomEdge(TileEdges edgesToTest, int x, int y, Vector2 tilePosition, ref LineSegment edge)
void liquidType(int liquidType)
Definition Tile.cs:233
bool nactive()
Definition Tile.cs:257
byte liquid
Definition Tile.cs:12
short frameY
Definition Tile.cs:24
ushort type
Definition Tile.cs:8
byte slope()
Definition Tile.cs:684
bool halfBrick()
Definition Tile.cs:650
void OnPassThrough(PhysicsProperties properties, ref Vector2 position, ref Vector2 velocity, ref float angularVelocity, ref BallPassThroughEvent passThrough)
void OnCollision(PhysicsProperties properties, ref Vector2 position, ref Vector2 velocity, ref BallCollisionEvent collision)
static float Dot(Vector2 value1, Vector2 value2)
Definition Vector2.cs:121
static Vector2 Reflect(Vector2 vector, Vector2 normal)
Definition Vector2.cs:157
static float DistanceSquared(Vector2 value1, Vector2 value2)
Definition Vector2.cs:107
static BallStepResult Moving()
static BallStepResult OutOfBounds()
static BallStepResult Resting()