Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Helpers/MathHelpers.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: MathHelpers Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectMathHelpers *MathHelpers = NULL;
11
12
void MathHelpers_Update(void) {}
13
14
void MathHelpers_LateUpdate(void) {}
15
16
void MathHelpers_StaticUpdate(void) {}
17
18
void MathHelpers_Draw(void) {}
19
20
void MathHelpers_Create(void *data) {}
21
22
void MathHelpers_StageLoad(void) {}
23
24
void MathHelpers_LerpToPos(Vector2 *pos, int32 percent, int32 posX, int32 posY)
25
{
26
if (percent < 0) {
27
pos->x = 0;
28
pos->y = 0;
29
}
30
else if (percent >= 0x100) {
31
pos->x = posX;
32
pos->y = posY;
33
}
34
else {
35
pos->x = percent * (posX >> 8);
36
pos->y = percent * (posY >> 8);
37
}
38
}
39
40
void MathHelpers_Lerp(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)
41
{
42
if (percent < 0) {
43
pos->x = startX;
44
pos->y = startY;
45
}
46
else if (percent >= 0x100) {
47
pos->x = endX;
48
pos->y = endY;
49
}
50
else {
51
pos->x = startX + percent * ((endX - startX) >> 8);
52
pos->y = startY + percent * ((endY - startY) >> 8);
53
}
54
}
55
56
void MathHelpers_LerpSin1024(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)
57
{
58
if (percent < 0) {
59
pos->x = startX;
60
pos->y = startY;
61
}
62
else if (percent >= 0x100) {
63
pos->x = endX;
64
pos->y = endY;
65
}
66
else {
67
int32 lerpPercent = (RSDK.Sin1024(percent + 0x300) >> 2) + 0x100;
68
pos->x = startX + lerpPercent * ((endX - startX) >> 8);
69
pos->y = startY + lerpPercent * ((endY - startY) >> 8);
70
}
71
}
72
73
void MathHelpers_Lerp2Sin1024(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)
74
{
75
if (percent < 0) {
76
pos->x = startX;
77
pos->y = startY;
78
}
79
else if (percent >= 0x100) {
80
pos->x = endX;
81
pos->y = endY;
82
}
83
else {
84
int32 lerpPercent = RSDK.Sin1024(percent) >> 2;
85
pos->x = startX + lerpPercent * ((endX - startX) >> 8);
86
pos->y = startY + lerpPercent * ((endY - startY) >> 8);
87
}
88
}
89
90
void MathHelpers_LerpSin512(Vector2 *pos, int32 percent, int32 startX, int32 startY, int32 endX, int32 endY)
91
{
92
if (percent < 0) {
93
pos->x = startX;
94
pos->y = startY;
95
}
96
else if (percent >= 0x100) {
97
pos->x = endX;
98
pos->y = endY;
99
}
100
else {
101
int32 lerpPercent = (RSDK.Sin512(percent + 0x180) >> 2) + 0x80;
102
pos->x = startX + lerpPercent * ((endX - startX) >> 8);
103
pos->y = startY + lerpPercent * ((endY - startY) >> 8);
104
}
105
}
106
107
Vector2 MathHelpers_GetBezierPoint(int32 percent, int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, int32 x4, int32 y4)
108
{
109
int32 invPercent = 0x10000 - percent;
110
int32 point1 = invPercent * ((uint32)(invPercent * invPercent) >> 16) >> 16;
111
int32 point2 = percent * ((uint32)(invPercent * invPercent) >> 16) >> 16;
112
int32 point3 = invPercent * ((uint32)(percent * percent) >> 16) >> 16;
113
int32 point4 = percent * ((uint32)(percent * percent) >> 16) >> 16;
114
115
Vector2 resultPos;
116
resultPos.x =
117
point4 * (x4 >> 16) + point3 * (x3 >> 16) + point2 * (x2 >> 16) + point1 * (x1 >> 16) + 2 * point2 * (x2 >> 16) + 2 * point3 * (x3 >> 16);
118
resultPos.y =
119
point4 * (y4 >> 16) + point3 * (y3 >> 16) + point2 * (y2 >> 16) + point1 * (y1 >> 16) + 2 * point2 * (y2 >> 16) + 2 * point3 * (y3 >> 16);
120
return resultPos;
121
}
122
123
int32 MathHelpers_SquareRoot(uint32 num)
124
{
125
int32 rem = 1 << 30; // 1 << 31 would result in the value having to be unsigned, so this is the max
126
while (rem > num) rem >>= 2;
127
128
uint32 root = 0;
129
while (rem) {
130
if (num >= rem + root) {
131
num -= rem + root;
132
root += rem << 1;
133
}
134
135
rem >>= 2;
136
root >>= 1;
137
}
138
139
return num <= root ? root : (root + 1);
140
}
141
142
int32 MathHelpers_Distance(int32 x1, int32 y1, int32 x2, int32 y2)
143
{
144
int32 distanceX = abs(x2 - x1) >> 16;
145
int32 distanceY = abs(y2 - y1) >> 16;
146
147
return (MathHelpers_SquareRoot(distanceX * distanceX + distanceY * distanceY) << 16);
148
}
149
150
int32 MathHelpers_GetBezierCurveLength(int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, int32 x4, int32 y4)
151
{
152
int32 lastX = x1;
153
int32 lastY = y1;
154
155
int32 length = 0;
156
// 0x10000 = 1.0
157
// 0xCCC == 0.05
158
for (int32 percent = 0xCCC; percent <= 0x10000; percent += 0xCCC) {
159
Vector2 point = MathHelpers_GetBezierPoint(percent, x1, y1, x2, y2, x3, y3, x4, y4);
160
161
length += MathHelpers_Distance(lastX, lastY, point.x, point.y);
162
lastX = point.x;
163
lastY = point.y;
164
}
165
return length;
166
}
167
168
bool32 MathHelpers_PointInHitbox(int32 thisX, int32 thisY, int32 otherX, int32 otherY, int32 direction, Hitbox *hitbox)
169
{
170
int32 left, top, right, bottom;
171
172
if ((direction & FLIP_X)) {
173
left = -hitbox->left;
174
right = -hitbox->right;
175
}
176
else {
177
right = hitbox->right;
178
left = hitbox->left;
179
}
180
181
if ((direction & FLIP_Y)) {
182
bottom = -hitbox->bottom;
183
top = -hitbox->top;
184
}
185
else {
186
bottom = hitbox->bottom;
187
top = hitbox->top;
188
}
189
int32 hitboxX2 = right;
190
if (left < right)
191
hitboxX2 = left;
192
int32 hitboxX1 = left;
193
if (right > left)
194
hitboxX1 = right;
195
196
int32 hitboxY1 = top;
197
int32 hitboxY2 = bottom;
198
if (top < bottom)
199
hitboxY2 = top;
200
if (bottom > top)
201
hitboxY1 = bottom;
202
return otherX >= thisX + (hitboxX2 << 16) && otherX <= thisX + (hitboxX1 << 16) && otherY >= thisY + (hitboxY2 << 16)
203
&& otherY <= thisY + (hitboxY1 << 16);
204
}
205
206
bool32 MathHelpers_PositionBoxesIntersect(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX1, int32 thisY1, int32 thisX2,
207
int32 thisY2)
208
{
209
int32 left_other = MIN(otherX1, otherX2);
210
int32 top_other = MIN(otherY1, otherY2);
211
int32 right_other = MAX(otherX1, otherX2);
212
int32 bottom_other = MAX(otherY1, otherY2);
213
214
int32 left_this = MIN(thisX1, thisX2);
215
int32 top_this = MIN(thisY1, thisY2);
216
int32 right_this = MAX(thisX1, thisX2);
217
int32 bottom_this = MAX(thisY1, thisY2);
218
219
return left_other <= right_this && right_other >= left_this && top_other <= bottom_this && bottom_other >= top_this;
220
}
221
int32 MathHelpers_GetInteractionDir(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX, int32 thisY)
222
{
223
int32 dir = ((thisY - otherY1) >> 16) * ((otherX2 - otherX1) >> 16) - ((thisX - otherX1) >> 16) * ((otherY2 - otherY1) >> 16);
224
return dir > 0 ? 1 : dir < 0 ? -1 : 0;
225
}
226
bool32 MathHelpers_CheckValidIntersect(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX, int32 thisY)
227
{
228
if (otherX2 > otherX1) {
229
if (thisX < otherX1 || thisX > otherX2)
230
return false;
231
return true;
232
}
233
234
if (otherX2 < otherX1) {
235
if (thisX < otherX1 || thisX > otherX1)
236
return false;
237
return true;
238
}
239
240
if (otherY2 > otherY1) {
241
if (thisY < otherY1 || thisY > otherY2)
242
return false;
243
return true;
244
}
245
246
if (otherY2 < otherY1) {
247
if (thisY < otherY2 || thisY > otherY1)
248
return false;
249
return true;
250
}
251
252
if (thisX < otherX1 || thisY < otherY1)
253
return false;
254
return true;
255
}
256
int32 MathHelpers_CheckPositionOverlap(int32 otherX1, int32 otherY1, int32 otherX2, int32 otherY2, int32 thisX1, int32 thisY1, int32 thisX2,
257
int32 thisY2)
258
{
259
// Creates "hitboxes" from the positions and does a quick check to see if they overlap
260
if (!MathHelpers_PositionBoxesIntersect(otherX1, otherY1, otherX2, otherY2, thisX1, thisY1, thisX2, thisY2))
261
return false;
262
263
if (otherX1 == otherX2 && otherY1 == otherY2) {
264
if (otherX1 != thisX1 || otherY1 != thisY1) {
265
if (otherX1 == thisX2 && otherY1 == thisY2)
266
return true;
267
return false;
268
}
269
return true;
270
}
271
272
if (thisX1 == thisX2 && thisY1 == thisY2) {
273
if (thisX1 == otherX1 && thisY1 == otherY1)
274
return true;
275
276
if (thisX1 == otherX2 && thisY1 == otherY2)
277
return true;
278
279
return false;
280
}
281
282
int32 thisInteractDir1 = MathHelpers_GetInteractionDir(otherX1, otherY1, otherX2, otherY2, thisX1, thisY1);
283
int32 thisInteractDir2 = MathHelpers_GetInteractionDir(otherX1, otherY1, otherX2, otherY2, thisX2, thisY2);
284
285
if (thisInteractDir1) {
286
if (thisInteractDir1 == thisInteractDir2)
287
return false;
288
}
289
else if (!thisInteractDir2) {
290
if (MathHelpers_CheckValidIntersect(otherX1, otherY1, otherX2, otherY2, thisX1, thisY1)
291
|| MathHelpers_CheckValidIntersect(otherX1, otherY1, otherX2, otherY2, thisX2, thisY2)
292
|| MathHelpers_CheckValidIntersect(thisX1, thisY1, thisX2, thisY2, otherX1, otherY1)
293
|| MathHelpers_CheckValidIntersect(thisX1, thisY1, thisX2, thisY2, otherX2, otherY2)) {
294
return true;
295
}
296
297
return false;
298
}
299
300
int32 otherInteractDir1 = MathHelpers_GetInteractionDir(thisX1, thisY1, thisX2, thisY2, otherX1, otherY1);
301
if (!otherInteractDir1)
302
return true;
303
304
int32 otherInteractDir2 = MathHelpers_GetInteractionDir(thisX1, thisY1, thisX2, thisY2, otherX2, otherY2);
305
if (otherInteractDir1 == otherInteractDir2)
306
return false;
307
308
return true;
309
}
310
311
int32 MathHelpers_GetEdgeDistance(int32 distance, int32 radius)
312
{
313
uint32 dist = abs(distance);
314
uint32 rad = abs(radius);
315
316
uint32 result1 = (dist >> 16) * (rad >> 16) << 16;
317
uint32 result2 = (dist >> 16) * (rad & 0xFFFF);
318
uint32 result3 = (dist & 0xFFFF) * (rad >> 16);
319
uint32 result4 = (dist & 0xFFFF) * (rad & 0xFFFF) >> 16;
320
321
uint32 edgeDistance = result1 + result2 + result3 + result4;
322
if ((radius ^ ~distance) >= 0) // if the signs do not match
323
return -(int32)edgeDistance;
324
else
325
return edgeDistance;
326
}
327
328
bool32 MathHelpers_ConstrainToBox(Vector2 *pos, int32 x, int32 y, Vector2 boxPos, Hitbox hitbox)
329
{
330
int32 left = MIN(hitbox.left, hitbox.right);
331
int32 right = MAX(hitbox.right, hitbox.left);
332
int32 top = MIN(hitbox.top, hitbox.bottom);
333
int32 bottom = MAX(hitbox.bottom, hitbox.top);
334
335
int32 boxPosLeft = boxPos.x + (left << 16);
336
int32 boxPosTop = boxPos.y + (top << 16);
337
int32 boxPosRight = boxPos.x + (right << 16);
338
int32 boxPosBottom = boxPos.y + (bottom << 16);
339
340
if (x > boxPosLeft && x < boxPosRight && y > boxPosTop && y < boxPosBottom)
341
return false;
342
343
// Check if they're on the same pixel on x axis (ignores subpixel positions)
344
if (!((x ^ boxPos.x) & 0xFFFF0000)) {
345
if (pos) {
346
pos->x = x & 0xFFFF0000;
347
if (y <= boxPos.y)
348
pos->y = boxPosTop;
349
else
350
pos->y = boxPosBottom;
351
}
352
return true;
353
}
354
355
// Check if they're on the same pixel on y axis (ignores subpixel positions)
356
if (!((y ^ boxPos.y) & 0xFFFF0000)) {
357
if (pos) {
358
if (x <= boxPos.x)
359
pos->x = boxPosLeft;
360
else
361
pos->x = boxPosRight;
362
pos->y = y & 0xFFFF0000;
363
}
364
return true;
365
}
366
367
double div = 1.0f / 65536.0f;
368
int32 radius = (((boxPos.y - y) * div) / ((boxPos.x - x) * div)) * 65536.0f;
369
if (!radius)
370
return false;
371
372
int32 posY = 0;
373
if (x <= boxPos.x) {
374
posY = y + MathHelpers_GetEdgeDistance(boxPosLeft - x, radius);
375
if (boxPosTop <= posY && posY <= boxPosBottom) {
376
if (pos) {
377
pos->x = boxPosLeft;
378
pos->y = posY;
379
}
380
return true;
381
}
382
}
383
384
if (x >= boxPos.x) {
385
posY = y + MathHelpers_GetEdgeDistance(boxPosRight - x, radius);
386
if (boxPosTop <= posY && posY <= boxPosBottom) {
387
if (pos) {
388
pos->x = boxPosRight;
389
pos->y = posY;
390
}
391
return true;
392
}
393
}
394
395
if (y <= boxPos.y) {
396
radius = (((boxPosTop - y) * div) / (radius * div)) * -65536.0;
397
if (boxPosLeft <= x - radius && x - radius <= boxPosRight) {
398
if (pos) {
399
pos->x = x - radius;
400
pos->y = boxPosTop;
401
}
402
return true;
403
}
404
}
405
406
if (y >= boxPos.y) {
407
radius = x - ((((boxPosBottom - y) * div) / (radius * div)) * -65536.0f);
408
if (boxPosLeft <= radius && radius <= boxPosRight) {
409
if (pos) {
410
pos->x = radius;
411
pos->y = boxPosBottom;
412
}
413
return true;
414
}
415
}
416
417
return false;
418
}
419
420
// RSDKv5U changed how the setPos param works, so this is added for compatibility
421
#if RETRO_REV0U
422
uint8 MathHelpers_CheckBoxCollision(void *thisEntity, Hitbox *thisHitbox, void *otherEntity, Hitbox *otherHitbox)
423
{
424
Entity *other = (Entity *)otherEntity;
425
426
Vector2 storePos = other->position;
427
Vector2 storeVel = other->velocity;
428
int32 storeGroundVel = other->groundVel;
429
bool32 storeOnGround = other->onGround;
430
int32 storeAngle = other->angle;
431
432
uint8 side = RSDK.CheckObjectCollisionBox(thisEntity, thisHitbox, otherEntity, otherHitbox, true);
433
Vector2 collidePos = other->position;
434
435
other->position = storePos;
436
other->velocity = storeVel;
437
other->groundVel = storeGroundVel;
438
other->onGround = storeOnGround;
439
other->angle = storeAngle;
440
switch (side) {
441
default:
442
case C_NONE: break;
443
case C_TOP: other->position.y = collidePos.y; break;
444
case C_LEFT: other->position.x = collidePos.x; break;
445
case C_RIGHT: other->position.x = collidePos.x; break;
446
case C_BOTTOM: other->position.y = collidePos.y; break;
447
}
448
449
return side;
450
}
451
#else
452
uint8 MathHelpers_CheckBoxCollision(void *thisEntity, Hitbox *thisHitbox, void *otherEntity, Hitbox *otherHitbox)
453
{
454
return RSDK.CheckObjectCollisionBox(thisEntity, thisHitbox, otherEntity, otherHitbox, false);
455
}
456
#endif
457
458
#if GAME_INCLUDE_EDITOR
459
void MathHelpers_EditorDraw(void) {}
460
461
void MathHelpers_EditorLoad(void) {}
462
#endif
463
464
void MathHelpers_Serialize(void) {}
465
466