Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/MSZ/Rattlekiller.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Rattlekiller Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectRattlekiller *Rattlekiller;
11
12
void Rattlekiller_Update(void)
13
{
14
RSDK_THIS(Rattlekiller);
15
16
if (self->timer) {
17
self->timer--;
18
19
if (self->bodyStates[0] == RATTLEKILLER_PLAYERDETECTED) {
20
foreach_active(Player, player)
21
{
22
if (abs(self->bodyPositions[0].x - player->position.x) < 0x600000) {
23
if (abs(self->bodyPositions[0].y - player->position.y) < 0x600000) {
24
self->targetPos = player->position;
25
foreach_break;
26
}
27
}
28
}
29
}
30
}
31
else {
32
foreach_active(Player, player)
33
{
34
if (self->bodyPositions[0].x <= self->topBounds.x) {
35
if (player->position.x >= self->topBounds.x || player->position.x - self->topBounds.x <= -0x600000)
36
continue;
37
}
38
else if (player->position.x <= self->topBounds.x || player->position.x - self->topBounds.x >= 0x600000) {
39
continue;
40
}
41
42
if (abs(self->bodyPositions[0].y - player->position.y) < 0x600000) {
43
int32 delay = 30;
44
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
45
self->bodyDelays[i] = 30;
46
self->bodyOriginPos[i] = self->bodyPositions[i];
47
self->bodyStates[i] = RATTLEKILLER_PLAYERDETECTED;
48
49
delay += 2;
50
}
51
52
self->targetPos.x = player->position.x;
53
self->targetPos.y = player->position.y;
54
self->timer = 480;
55
foreach_break;
56
}
57
}
58
}
59
60
foreach_active(Player, player)
61
{
62
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
63
switch (self->bodyStates[i]) {
64
case RATTLEKILLER_IDLE:
65
if (--self->bodyDelays[i] <= 0)
66
self->bodyStates[i] = RATTLEKILLER_TWIST_DOWN + (self->bodyVelocities[i].y < 0);
67
break;
68
69
case RATTLEKILLER_TWIST_HORIZONTAL: // circle pole horizontally (no vertical movement)
70
self->bodyAngles[i] = (self->bodyAngles[i] + 2) & 0xFF;
71
self->bodyPositions[i].y += self->bodyVelocities[i].y;
72
self->bodyPositions[i].x = self->topBounds.x - 0x1800 * RSDK.Sin256(self->bodyAngles[i]);
73
self->bodyDepth[i] = RSDK.Cos256(self->bodyAngles[i]);
74
if (self->timer < 320) {
75
if (--self->bodyDelays[i] <= 0)
76
self->bodyStates[i] = RATTLEKILLER_TWIST_DOWN + (self->bodyVelocities[i].y < 0);
77
}
78
break;
79
80
case RATTLEKILLER_TWIST_DOWN: // circle pole (moving downwards)
81
self->bodyAngles[i] = (self->bodyAngles[i] + 2) & 0xFF;
82
self->bodyVelocities[i].y += 0x400;
83
if (self->bodyVelocities[i].y > 0x8000)
84
self->bodyVelocities[i].y = 0x8000;
85
86
self->bodyPositions[i].y += self->bodyVelocities[i].y;
87
self->bodyPositions[i].x = self->topBounds.x - 0x1800 * RSDK.Sin256(self->bodyAngles[i]);
88
self->bodyDepth[i] = RSDK.Cos256(self->bodyAngles[i]);
89
90
if (self->bodyPositions[i].y >= self->bottomBounds.y)
91
self->bodyStates[i] = RATTLEKILLER_TWIST_UP;
92
break;
93
94
case RATTLEKILLER_TWIST_UP: // circle pole (moving upwards)
95
self->bodyAngles[i] = (self->bodyAngles[i] + 2) & 0xFF;
96
self->bodyVelocities[i].y -= 0x400;
97
if (self->bodyVelocities[i].y < -0x8000)
98
self->bodyVelocities[i].y = -0x8000;
99
100
self->bodyPositions[i].y += self->bodyVelocities[i].y;
101
self->bodyPositions[i].x = self->topBounds.x - 0x1800 * RSDK.Sin256(self->bodyAngles[i]);
102
self->bodyDepth[i] = RSDK.Cos256(self->bodyAngles[i]);
103
if (self->bodyPositions[i].y <= self->topBounds.y)
104
self->bodyStates[i] = RATTLEKILLER_TWIST_DOWN;
105
break;
106
107
case RATTLEKILLER_PLAYERDETECTED:
108
if (--self->bodyDelays[i] > 0) {
109
if (self->bodyDelays[i] < 15 && i > 4) {
110
self->bodyAngles[i] = (self->bodyAngles[i] + 4) & 0xFF;
111
self->bodyPositions[i].x = self->topBounds.x - 0x1800 * RSDK.Sin256(self->bodyAngles[i]);
112
self->bodyDepth[i] = RSDK.Cos256(self->bodyAngles[i]);
113
}
114
}
115
else if (i) {
116
self->bodyStates[i] = RATTLEKILLER_STRETCHBODY;
117
}
118
else {
119
self->bodyStates[0] = RATTLEKILLER_EXTEND;
120
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, (self->bodyPositions[0].x > self->topBounds.x) + 2, &self->headAnimator,
121
true, 0);
122
RSDK.PlaySfx(Rattlekiller->sfxRocketJet, false, 255);
123
}
124
break;
125
126
case RATTLEKILLER_STRETCHBODY:
127
if (self->bodyStates[0] < RATTLEKILLER_EXTEND) {
128
self->bodyPositions[i].y += (self->bodyPositions[0].y - self->bodyPositions[i].y) >> 3;
129
if (self->bodyStates[i - 1] == RATTLEKILLER_TWIST_HORIZONTAL) {
130
if (abs(self->bodyPositions[0].y - self->bodyPositions[i].y) < 0x20000) {
131
self->bodyPositions[i].y = self->bodyPositions[0].y;
132
self->bodyVelocities[i].x = 0;
133
self->bodyVelocities[i].y = 0;
134
self->bodyStates[i] = RATTLEKILLER_TWIST_HORIZONTAL;
135
self->bodyDelays[i] = 8 * i + self->bodyDelays[0];
136
self->bodyAngles[i] = self->bodyAngles[i - 1] - 16;
137
}
138
}
139
}
140
else if (i < RATTLEKILLER_SEGMENT_COUNT - 1) {
141
self->bodyPositions[i].x = (self->bodyPositions[i - 1].x >> 1) + (self->bodyPositions[i + 1].x >> 1);
142
self->bodyPositions[i].y = (self->bodyPositions[i - 1].y >> 1) + (self->bodyPositions[i + 1].y >> 1);
143
}
144
break;
145
146
case RATTLEKILLER_EXTEND: {
147
int32 rx = (self->targetPos.x - self->bodyPositions[i].x) >> 16;
148
int32 ry = (self->targetPos.y - self->bodyPositions[i].y) >> 16;
149
if (rx * rx + ry * ry >= 0x40) {
150
int32 angle = RSDK.ATan2(rx, ry);
151
self->bodyVelocities[i].x = RSDK.Cos256(angle) << 10;
152
self->bodyPositions[i].x += self->bodyVelocities[i].x;
153
self->bodyPositions[i].y += RSDK.Sin256(angle) << 10;
154
self->bodyVelocities[i].y = RSDK.Sin256(angle) << 10;
155
}
156
else {
157
self->bodyStates[i] = RATTLEKILLER_RETRACT;
158
}
159
RSDK.ProcessAnimation(&self->headAnimator);
160
break;
161
}
162
163
case RATTLEKILLER_RETRACT: {
164
int32 rx = (self->bodyOriginPos[i].x - self->bodyPositions[i].x) >> 16;
165
int32 ry = (self->bodyOriginPos[RATTLEKILLER_SEGMENT_COUNT - 1].y - self->bodyPositions[i].y) >> 16;
166
if (rx * rx + ry * ry >= 0x40) {
167
int32 angle = RSDK.ATan2(rx, ry);
168
self->bodyVelocities[i].x = RSDK.Cos256(angle) << 10;
169
self->bodyPositions[i].x += self->bodyVelocities[i].x;
170
self->bodyPositions[i].y += RSDK.Sin256(angle) << 10;
171
self->bodyVelocities[i].y = RSDK.Sin256(angle) << 10;
172
RSDK.ProcessAnimation(&self->headAnimator);
173
}
174
else {
175
self->bodyStates[i] = RATTLEKILLER_TWIST_HORIZONTAL;
176
if (i > 0)
177
self->bodyAngles[i] = self->bodyAngles[i - 1] - 16;
178
self->bodyPositions[i].x = self->bodyOriginPos[i].x;
179
self->bodyPositions[i].y = self->bodyOriginPos[RATTLEKILLER_SEGMENT_COUNT - 1].y;
180
self->bodyDelays[i] = 8 * i;
181
self->bodyVelocities[i].x = 0;
182
self->bodyVelocities[i].y = 0;
183
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 0, &self->headAnimator, true, 0);
184
}
185
break;
186
}
187
188
default: break;
189
}
190
}
191
192
self->position.x = self->bodyPositions[0].x;
193
self->position.y = self->bodyPositions[0].y;
194
if (Player_CheckBadnikTouch(player, self, &Rattlekiller->hitboxSegment) && Player_CheckBadnikBreak(player, self, false)) {
195
for (int32 i = 1; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
196
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->bodyPositions[i].x, self->bodyPositions[i].y);
197
198
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, self->bodyAnimators[i]->animationID, &debris->animator, true,
199
self->bodyAnimators[i]->frameID);
200
debris->velocity.x = RSDK.Rand(-0x20000, 0x20000);
201
debris->velocity.y = RSDK.Rand(-0x20000, -0x10000);
202
debris->gravityStrength = 0x4800;
203
debris->drawGroup = Zone->objectDrawGroup[1];
204
debris->updateRange.x = 0x400000;
205
debris->updateRange.y = 0x400000;
206
}
207
208
destroyEntity(self);
209
self->active = ACTIVE_DISABLED;
210
foreach_break;
211
}
212
else {
213
for (int32 i = 1; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
214
if (self->bodyDepth[i] > 0) {
215
self->position.x = self->bodyPositions[i].x;
216
self->position.y = self->bodyPositions[i].y;
217
if (Player_CheckCollisionTouch(player, self, &Rattlekiller->hitboxSegment)) {
218
#if MANIA_USE_PLUS
219
if (!Player_CheckMightyUnspin(player, 0x200, 2, &player->uncurlTimer))
220
#endif
221
Player_Hurt(player, self);
222
}
223
}
224
}
225
}
226
}
227
228
self->position.x = self->bodyPositions[0].x;
229
self->position.y = self->bodyPositions[0].y;
230
231
Rattlekiller_HandleSorting();
232
}
233
234
void Rattlekiller_LateUpdate(void) {}
235
236
void Rattlekiller_StaticUpdate(void)
237
{
238
foreach_active(Rattlekiller, rattlekiller) { RSDK.AddDrawListRef(Zone->objectDrawGroup[0] - 1, RSDK.GetEntitySlot(rattlekiller)); }
239
}
240
241
void Rattlekiller_Draw(void)
242
{
243
RSDK_THIS(Rattlekiller);
244
245
if (SceneInfo->currentDrawGroup == self->drawGroup) {
246
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
247
int32 id = self->bodyIDs[i];
248
249
if (self->bodyDepth[id] > 0) {
250
Animator *animator = self->bodyAnimators[id];
251
if (animator->animationID < 2)
252
animator->frameID = self->bodyAngles[id] >> 4;
253
254
RSDK.DrawSprite(animator, &self->bodyPositions[id], false);
255
}
256
}
257
}
258
else {
259
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
260
int32 id = self->bodyIDs[i];
261
262
if (self->bodyDepth[id] <= 0) {
263
Animator *animator = self->bodyAnimators[id];
264
265
if (animator->animationID < 2)
266
animator->frameID = self->bodyAngles[id] >> 4;
267
RSDK.DrawSprite(animator, &self->bodyPositions[id], false);
268
}
269
}
270
}
271
}
272
273
void Rattlekiller_Create(void *data)
274
{
275
RSDK_THIS(Rattlekiller);
276
277
if (!SceneInfo->inEditor) {
278
self->visible = true;
279
self->drawGroup = Zone->objectDrawGroup[0];
280
self->topBounds = self->position;
281
self->bottomBounds = self->position;
282
self->active = ACTIVE_BOUNDS;
283
self->updateRange.x = 0x800000;
284
self->updateRange.y = 0x800000;
285
286
if (data)
287
self->length = VOID_TO_INT(data);
288
else if (!self->length)
289
self->length = 128;
290
291
self->topBounds.y -= self->length << 15;
292
self->bottomBounds.y += self->length << 15;
293
294
int32 delay = 0;
295
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
296
self->bodyIDs[i] = i;
297
self->bodyAnimators[i] = &self->bodyAnimator;
298
self->bodyDelays[i] = delay;
299
self->bodyPositions[i] = self->topBounds;
300
301
delay += 8;
302
}
303
self->bodyAnimators[0] = &self->headAnimator;
304
305
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 0, &self->headAnimator, true, 0);
306
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 1, &self->bodyAnimator, true, 0);
307
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 4, &self->tailAnimator, true, 0);
308
}
309
}
310
311
void Rattlekiller_StageLoad(void)
312
{
313
Rattlekiller->aniFrames = RSDK.LoadSpriteAnimation("MSZ/Rattlekiller.bin", SCOPE_STAGE);
314
315
Rattlekiller->hitboxSegment.left = -8;
316
Rattlekiller->hitboxSegment.top = -8;
317
Rattlekiller->hitboxSegment.right = 8;
318
Rattlekiller->hitboxSegment.bottom = 8;
319
320
Rattlekiller->sfxRocketJet = RSDK.GetSfx("Stage/RocketJet.wav");
321
// Idk where it would've been used but "MSZ/RKillerRattle.wav" was prolly intended for this object
322
323
DEBUGMODE_ADD_OBJ(Rattlekiller);
324
}
325
326
void Rattlekiller_DebugSpawn(void)
327
{
328
RSDK_THIS(DebugMode);
329
330
EntityRattlekiller *rattlekiller = CREATE_ENTITY(Rattlekiller, INT_TO_VOID(0x80), self->position.x, self->position.y);
331
rattlekiller->direction = FLIP_NONE;
332
}
333
334
void Rattlekiller_DebugDraw(void)
335
{
336
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 0, &DebugMode->animator, true, 0);
337
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
338
}
339
340
void Rattlekiller_HandleSorting(void)
341
{
342
RSDK_THIS(Rattlekiller);
343
344
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
345
for (int32 ii = RATTLEKILLER_SEGMENT_COUNT - 1; ii > i; --ii) {
346
int32 id1 = self->bodyIDs[ii - 1];
347
int32 id2 = self->bodyIDs[ii];
348
if (self->bodyDepth[id1] > self->bodyDepth[id2]) {
349
self->bodyIDs[ii - 1] = id2;
350
self->bodyIDs[ii] = id1;
351
}
352
}
353
}
354
}
355
356
#if GAME_INCLUDE_EDITOR
357
void Rattlekiller_EditorDraw(void)
358
{
359
RSDK_THIS(Rattlekiller);
360
361
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 0, &self->headAnimator, false, 0);
362
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 1, &self->bodyAnimator, false, 0);
363
RSDK.SetSpriteAnimation(Rattlekiller->aniFrames, 4, &self->tailAnimator, false, 0);
364
365
self->topBounds = self->position;
366
self->bottomBounds = self->position;
367
368
int32 length = self->length;
369
if (!length)
370
length = 128;
371
372
self->topBounds.y -= length << 15;
373
self->bottomBounds.y += length << 15;
374
375
int32 delay = 0;
376
for (int32 i = 0; i < RATTLEKILLER_SEGMENT_COUNT; ++i) {
377
self->bodyIDs[i] = i;
378
self->bodyAnimators[i] = &self->bodyAnimator;
379
self->bodyDelays[i] = delay;
380
self->bodyPositions[i] = self->position;
381
382
delay += 8;
383
}
384
385
Rattlekiller_HandleSorting();
386
387
int32 drawGroup = self->drawGroup;
388
389
// Draw Lower Depth
390
self->drawGroup = -1;
391
Rattlekiller_Draw();
392
393
// Draw Higher Depth
394
self->drawGroup = SceneInfo->currentDrawGroup;
395
Rattlekiller_Draw();
396
397
self->drawGroup = drawGroup;
398
399
if (showGizmos()) {
400
RSDK_DRAWING_OVERLAY(true);
401
402
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->topBounds.x, self->topBounds.y, 0xFFFF00, INK_NONE, 0xFF);
403
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->bottomBounds.x, self->bottomBounds.y, 0xFFFF00, INK_NONE, 0xFF);
404
405
RSDK_DRAWING_OVERLAY(false);
406
}
407
}
408
409
void Rattlekiller_EditorLoad(void)
410
{
411
Rattlekiller->aniFrames = RSDK.LoadSpriteAnimation("MSZ/Rattlekiller.bin", SCOPE_STAGE);
412
413
RSDK_ACTIVE_VAR(Rattlekiller, direction);
414
RSDK_ENUM_VAR("Left", FLIP_NONE);
415
RSDK_ENUM_VAR("Right", FLIP_X);
416
}
417
#endif
418
419
void Rattlekiller_Serialize(void)
420
{
421
RSDK_EDITABLE_VAR(Rattlekiller, VAR_UINT8, direction);
422
RSDK_EDITABLE_VAR(Rattlekiller, VAR_ENUM, length);
423
}
424
425