Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/UFO/UFO_Circuit.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: UFO_Circuit Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectUFO_Circuit *UFO_Circuit;
11
12
void UFO_Circuit_Update(void)
13
{
14
RSDK_THIS(UFO_Circuit);
15
16
StateMachine_Run(self->state);
17
18
RSDK.ProcessAnimation(&self->ufoAnimator);
19
}
20
21
void UFO_Circuit_LateUpdate(void)
22
{
23
RSDK_THIS(UFO_Circuit);
24
25
int32 x = self->position.x;
26
int32 y = self->height;
27
int32 z = self->position.y;
28
29
Matrix *m = &UFO_Camera->matWorld;
30
31
self->zdepth = m->values[2][1] * (y >> 16) + m->values[2][2] * (z >> 16) + m->values[2][0] * (x >> 16) + m->values[2][3];
32
33
if (self->zdepth >= 0x400) {
34
int32 depth = (int32)((m->values[0][3] << 8) + (m->values[0][2] * (z >> 8) & 0xFFFFFF00) + (m->values[0][0] * (x >> 8) & 0xFFFFFF00)
35
+ (m->values[0][1] * (y >> 8) & 0xFFFFFF00))
36
/ self->zdepth;
37
38
self->visible = abs(depth) < 0x100;
39
}
40
}
41
42
void UFO_Circuit_StaticUpdate(void) {}
43
44
void UFO_Circuit_Draw(void)
45
{
46
RSDK_THIS(UFO_Circuit);
47
48
if (self->zdepth >= 0x4000) {
49
RSDK.Prepare3DScene(UFO_Circuit->sceneIndex);
50
51
RSDK.MatrixScaleXYZ(&self->matTransform, 0x200, 0x200, 0x200);
52
RSDK.MatrixTranslateXYZ(&self->matTransform, self->position.x, self->height, self->position.y, false);
53
RSDK.MatrixRotateY(&self->matNormal, self->angleY);
54
55
RSDK.MatrixMultiply(&self->matWorld, &self->matNormal, &self->matTransform);
56
RSDK.MatrixMultiply(&self->matWorld, &self->matWorld, &UFO_Camera->matWorld);
57
RSDK.MatrixMultiply(&self->matNormal, &self->matNormal, &UFO_Camera->matView);
58
59
RSDK.AddMeshFrameTo3DScene(self->ufoAnimator.animationID, UFO_Circuit->sceneIndex, &self->ufoAnimator, S3D_SOLIDCOLOR_SHADED_BLENDED_SCREEN,
60
&self->matWorld, &self->matNormal, 0xFFFFFF);
61
RSDK.AddMeshFrameTo3DScene(UFO_Circuit->emeraldModel, UFO_Circuit->sceneIndex, &self->ufoAnimator, S3D_SOLIDCOLOR_SHADED_BLENDED_SCREEN,
62
&self->matWorld, &self->matNormal, 0xFFFFFF);
63
64
RSDK.Draw3DScene(UFO_Circuit->sceneIndex);
65
}
66
}
67
68
void UFO_Circuit_Create(void *data)
69
{
70
RSDK_THIS(UFO_Circuit);
71
72
if (!SceneInfo->inEditor) {
73
self->startPos.x = self->position.x;
74
self->startPos.y = self->position.y;
75
int32 id = RSDK.GetEntitySlot(self);
76
77
EntityUFO_Circuit *next = RSDK_GET_ENTITY(id + 1, UFO_Circuit);
78
if (next->classID == UFO_Circuit->classID) {
79
self->nextNode = next;
80
}
81
else {
82
for (int32 e = id - 1; e > 0; --e) {
83
EntityUFO_Circuit *node = RSDK_GET_ENTITY(e, UFO_Circuit);
84
if (node->classID != UFO_Circuit->classID) {
85
self->nextNode = RSDK_GET_ENTITY(e + 1, UFO_Circuit);
86
break;
87
}
88
}
89
}
90
91
EntityUFO_Circuit *prev = RSDK_GET_ENTITY(id - 1, UFO_Circuit);
92
if (prev->classID == UFO_Circuit->classID) {
93
self->prevNode = prev;
94
}
95
else {
96
for (int32 e = id + 1; e < TEMPENTITY_START; ++e) {
97
EntityUFO_Circuit *node = RSDK_GET_ENTITY(e, UFO_Circuit);
98
if (node->classID != UFO_Circuit->classID) {
99
self->prevNode = RSDK_GET_ENTITY(e - 1, UFO_Circuit);
100
break;
101
}
102
}
103
}
104
105
if (self->startNode) {
106
self->active = ACTIVE_NORMAL;
107
self->visible = true;
108
self->drawGroup = 4;
109
self->curNode = self->reverse ? self->prevNode : self->nextNode;
110
self->groundVel = 0x70000;
111
self->topSpeed = 0x70000;
112
113
RSDK.SetModelAnimation(UFO_Circuit->ufoModel, &self->ufoAnimator, 128, 0, true, 0);
114
115
UFO_Circuit_HandleSpeedSetup();
116
self->state = UFO_Circuit_State_UFO;
117
}
118
}
119
}
120
121
void UFO_Circuit_StageLoad(void)
122
{
123
UFO_Circuit->ufoModel = RSDK.LoadMesh("Special/UFOChase.bin", SCOPE_STAGE);
124
125
switch (UFO_Setup->specialStageID) {
126
case 1: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldYellow.bin", SCOPE_STAGE); break;
127
case 2: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldBlue.bin", SCOPE_STAGE); break;
128
case 3: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldPurple.bin", SCOPE_STAGE); break;
129
case 4: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldGrey.bin", SCOPE_STAGE); break;
130
case 5: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldCyan.bin", SCOPE_STAGE); break;
131
case 6: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldRed.bin", SCOPE_STAGE); break;
132
default: UFO_Circuit->emeraldModel = RSDK.LoadMesh("Special/EmeraldGreen.bin", SCOPE_STAGE); break;
133
}
134
135
UFO_Circuit->sceneIndex = RSDK.Create3DScene("View:Special", 4096, SCOPE_STAGE);
136
137
UFO_Circuit->nodeCount = 0;
138
foreach_all(UFO_Circuit, node) { UFO_Circuit->nodeCount++; }
139
140
UFO_Circuit->decelerationNoMach = 0;
141
UFO_Circuit->decelerationMach = 0;
142
}
143
144
void UFO_Circuit_HandleSpeedSetup(void)
145
{
146
RSDK_THIS(UFO_Circuit);
147
148
self->angle = RSDK.ATan2((self->position.x - self->curNode->position.x) >> 16, (self->position.y - self->curNode->position.y) >> 16);
149
150
switch (self->curNode->throttle) {
151
default:
152
case UFO_CIRCUIT_THRTLE_INVALID:
153
case UFO_CIRCUIT_THRTLE_NONE:
154
UFO_Circuit->decelerationNoMach = 0;
155
UFO_Circuit->decelerationMach = 0;
156
break;
157
158
case UFO_CIRCUIT_THRTLE_SLOW:
159
UFO_Circuit->decelerationNoMach = 0x2000;
160
UFO_Circuit->decelerationMach = 0x8000;
161
break;
162
163
case UFO_CIRCUIT_THRTLE_MED:
164
UFO_Circuit->decelerationNoMach = 0x4000;
165
UFO_Circuit->decelerationMach = 0x10000;
166
break;
167
168
case UFO_CIRCUIT_THRTLE_FAST:
169
UFO_Circuit->decelerationNoMach = 0x8000;
170
UFO_Circuit->decelerationMach = 0x20000;
171
break;
172
}
173
}
174
void UFO_Circuit_HandleNodeSpeeds(void)
175
{
176
RSDK_THIS(UFO_Circuit);
177
178
self->angle = RSDK.ATan2((self->position.x - self->curNode->startPos.x) >> 16, (self->position.y - self->curNode->startPos.y) >> 16);
179
180
switch (self->curNode->throttle) {
181
default:
182
case UFO_CIRCUIT_THRTLE_INVALID: break;
183
184
case UFO_CIRCUIT_THRTLE_NONE:
185
UFO_Circuit->decelerationNoMach = 0;
186
UFO_Circuit->decelerationMach = 0;
187
break;
188
189
case UFO_CIRCUIT_THRTLE_SLOW:
190
UFO_Circuit->decelerationNoMach = 0x2000;
191
UFO_Circuit->decelerationMach = 0x8000;
192
break;
193
194
case UFO_CIRCUIT_THRTLE_MED:
195
UFO_Circuit->decelerationNoMach = 0x4000;
196
UFO_Circuit->decelerationMach = 0x10000;
197
break;
198
199
case UFO_CIRCUIT_THRTLE_FAST:
200
UFO_Circuit->decelerationNoMach = 0x8000;
201
UFO_Circuit->decelerationMach = 0x20000;
202
break;
203
}
204
}
205
bool32 UFO_Circuit_CheckNodeChange(void)
206
{
207
RSDK_THIS(UFO_Circuit);
208
209
foreach_active(UFO_Player, player)
210
{
211
EntityUFO_Circuit *curNode = self->curNode;
212
EntityUFO_Circuit *targetNode = NULL;
213
EntityUFO_Circuit *nextNode = curNode->nextNode;
214
215
int32 x = (player->position.x - self->position.x) >> 16;
216
int32 y = (player->position.y - self->position.y) >> 16;
217
218
if (x * x + y * y >= 0x100000 && nextNode != curNode) {
219
int32 targetDistance = 0x7FFFFFFF;
220
221
while (nextNode && nextNode != curNode) {
222
x = (player->position.x - nextNode->position.x) >> 16;
223
y = (player->position.y - nextNode->position.y) >> 16;
224
225
if (x * x + y * y < targetDistance) {
226
targetNode = nextNode;
227
targetDistance = x * x + y * y;
228
}
229
230
nextNode = nextNode->nextNode;
231
}
232
233
if (targetNode) {
234
int32 id = 0;
235
EntityUFO_Circuit *nodePtr = self->curNode;
236
237
if (self->reverse) {
238
for (; nodePtr != targetNode; ++id) nodePtr = nodePtr->nextNode;
239
}
240
else {
241
for (; nodePtr != targetNode; ++id) nodePtr = nodePtr->prevNode;
242
}
243
244
if (id > UFO_Circuit->nodeCount >> 1) {
245
self->reverse ^= 1;
246
self->curNode = self->reverse ? self->curNode->prevNode : self->curNode->nextNode;
247
248
UFO_Circuit_HandleNodeSpeeds();
249
return true;
250
}
251
}
252
}
253
}
254
255
return false;
256
}
257
void UFO_Circuit_State_UFO(void)
258
{
259
RSDK_THIS(UFO_Circuit);
260
261
if (self->groundVel >= self->topSpeed) {
262
if (self->groundVel > self->topSpeed) {
263
self->groundVel -= 0x8000;
264
265
if (self->groundVel < self->topSpeed)
266
self->groundVel = self->topSpeed;
267
}
268
}
269
else {
270
self->groundVel += 0x8000;
271
272
if (self->groundVel > self->topSpeed)
273
self->groundVel = self->topSpeed;
274
}
275
276
self->topSpeed = UFO_Player->maxSpeed - (UFO_Player->maxSpeed - 0x70000) / 3;
277
self->velocity.x += (((-self->groundVel >> 8) * RSDK.Cos256(self->angle)) - self->velocity.x) >> 3;
278
self->velocity.y += (((-self->groundVel >> 8) * RSDK.Sin256(self->angle)) - self->velocity.y) >> 3;
279
280
self->position.x += self->velocity.x;
281
self->position.y += self->velocity.y;
282
283
self->angleY = 4 * RSDK.ATan2(self->velocity.y, -self->velocity.x);
284
if (!UFO_Circuit_CheckNodeChange()) {
285
EntityUFO_Circuit *curNode = self->curNode;
286
int32 rx = (self->position.x - curNode->startPos.x) >> 16;
287
int32 ry = 0;
288
int32 rz = (self->position.y - curNode->startPos.y) >> 16;
289
290
if (rx * rx + rz * rz < self->groundVel >> 5) {
291
self->curNode = self->reverse ? curNode->prevNode : curNode->nextNode;
292
293
UFO_Circuit_HandleNodeSpeeds();
294
}
295
296
if (!UFO_Setup->timedOut) {
297
foreach_active(UFO_Player, player)
298
{
299
rx = (self->position.x - player->position.x) >> 16;
300
ry = (self->height - player->height - 0xA0000) >> 16;
301
rz = (self->position.y - player->position.y) >> 16;
302
303
int32 dist = rx * rx + ry * ry + rz * rz;
304
if (!UFO_Setup->machLevel && dist < 0xC000)
305
self->topSpeed += (abs(player->velocity.y) + abs(player->velocity.x)) >> 1;
306
307
if (dist >= 0x6000) {
308
if (dist > 0xC0000) {
309
if (UFO_Setup->machLevel) {
310
if (UFO_Setup->machLevel == 1 || abs(player->velocity.y) + abs(player->velocity.x) >= self->topSpeed >> 1
311
|| self->groundVel <= 0xC0 * self->topSpeed >> 8)
312
continue;
313
314
self->groundVel -= UFO_Circuit->decelerationMach;
315
}
316
else {
317
if (self->groundVel > 0xC0 * self->topSpeed >> 8)
318
self->groundVel -= UFO_Circuit->decelerationNoMach;
319
}
320
}
321
}
322
else {
323
player->timer = 0;
324
player->circuitPtr = self;
325
player->stateInput = StateMachine_None;
326
player->state = UFO_Player_State_UFOCaught_Charge;
327
self->state = UFO_Circuit_State_Caught;
328
329
SceneInfo->timeEnabled = false;
330
}
331
}
332
}
333
}
334
}
335
void UFO_Circuit_State_Caught(void)
336
{
337
RSDK_THIS(UFO_Circuit);
338
339
self->velocity.x -= self->velocity.x >> 4;
340
self->velocity.y -= self->velocity.y >> 4;
341
self->position.x += self->velocity.x;
342
self->position.y += self->velocity.y;
343
}
344
345
#if GAME_INCLUDE_EDITOR
346
void UFO_Circuit_EditorDraw(void)
347
{
348
RSDK_THIS(UFO_Circuit);
349
350
RSDK.SetSpriteAnimation(UFO_Circuit->aniFrames, 0, &self->ufoAnimator, true, self->startNode ? 4 : 5);
351
352
RSDK.DrawSprite(&self->ufoAnimator, NULL, false);
353
354
self->active = showGizmos() ? ACTIVE_NORMAL : ACTIVE_BOUNDS;
355
356
if (self->startNode && showGizmos()) {
357
RSDK_DRAWING_OVERLAY(true);
358
359
int32 id = RSDK.GetEntitySlot(self) + 1;
360
361
EntityUFO_Circuit *lastNode = self;
362
EntityUFO_Circuit *nextNode = RSDK_GET_ENTITY(id++, UFO_Circuit);
363
while (nextNode != self) {
364
365
if (nextNode && nextNode->classID == UFO_Circuit->classID) {
366
DrawHelpers_DrawArrow(lastNode->position.x, lastNode->position.y, nextNode->position.x, nextNode->position.y, 0x00FF00, INK_NONE,
367
0xFF);
368
369
lastNode = nextNode;
370
}
371
372
nextNode = RSDK_GET_ENTITY(id++, UFO_Circuit);
373
if (id >= TEMPENTITY_START)
374
id = RESERVE_ENTITY_COUNT;
375
}
376
377
if (lastNode && lastNode->classID == UFO_Circuit->classID) {
378
DrawHelpers_DrawArrow(lastNode->position.x, lastNode->position.y, self->position.x, self->position.y, 0x00FF00, INK_NONE, 0xFF);
379
}
380
381
RSDK_DRAWING_OVERLAY(false);
382
}
383
}
384
385
void UFO_Circuit_EditorLoad(void)
386
{
387
UFO_Circuit->aniFrames = RSDK.LoadSpriteAnimation("Global/PlaneSwitch.bin", SCOPE_STAGE);
388
389
RSDK_ACTIVE_VAR(UFO_Circuit, mode);
390
RSDK_ENUM_VAR("(Unused)", UFO_CIRCUIT_MODE_UNUSED);
391
392
RSDK_ACTIVE_VAR(UFO_Circuit, throttle);
393
RSDK_ENUM_VAR("Invalid", UFO_CIRCUIT_THRTLE_INVALID);
394
RSDK_ENUM_VAR("None", UFO_CIRCUIT_THRTLE_NONE);
395
RSDK_ENUM_VAR("Slow", UFO_CIRCUIT_THRTLE_SLOW);
396
RSDK_ENUM_VAR("Medium", UFO_CIRCUIT_THRTLE_MED);
397
RSDK_ENUM_VAR("Fast", UFO_CIRCUIT_THRTLE_FAST);
398
}
399
#endif
400
401
void UFO_Circuit_Serialize(void)
402
{
403
RSDK_EDITABLE_VAR(UFO_Circuit, VAR_UINT8, mode);
404
RSDK_EDITABLE_VAR(UFO_Circuit, VAR_UINT8, throttle);
405
RSDK_EDITABLE_VAR(UFO_Circuit, VAR_BOOL, startNode);
406
RSDK_EDITABLE_VAR(UFO_Circuit, VAR_BOOL, reverse);
407
}
408
409