Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/MMZ/RPlaneShifter.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: RPlaneShifter Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectRPlaneShifter *RPlaneShifter;
11
12
void RPlaneShifter_Update(void)
13
{
14
RSDK_THIS(RPlaneShifter);
15
16
self->prevActivePlayers = self->activePlayers;
17
18
if (self->baseAnimator.animationID == 1)
19
RSDK.ProcessAnimation(&self->baseAnimator);
20
21
StateMachine_Run(self->state);
22
}
23
24
void RPlaneShifter_LateUpdate(void) {}
25
26
void RPlaneShifter_StaticUpdate(void)
27
{
28
foreach_active(RPlaneShifter, shifter) { RSDK.AddDrawListRef(Zone->playerDrawGroup[1] + 1, RSDK.GetEntitySlot(shifter)); }
29
}
30
31
void RPlaneShifter_Draw(void) { RPlaneShifter_DrawSprites(); }
32
33
void RPlaneShifter_Create(void *data)
34
{
35
RSDK_THIS(RPlaneShifter);
36
37
self->visible = true;
38
self->drawGroup = Zone->objectDrawGroup[0];
39
self->active = ACTIVE_BOUNDS;
40
self->angle = 0;
41
42
if (SceneInfo->inEditor && !self->height)
43
self->height = 10;
44
45
self->updateRange.x = 0x800000;
46
self->updateRange.y = 0x800000;
47
48
RSDK.SetSpriteAnimation(RPlaneShifter->aniFrames, 1, &self->baseAnimator, true, 0);
49
RSDK.SetSpriteAnimation(RPlaneShifter->aniFrames, 2, &RPlaneShifter->barAnimator, true, 0);
50
self->state = RPlaneShifter_State_Init;
51
}
52
53
void RPlaneShifter_StageLoad(void)
54
{
55
RPlaneShifter->active = ACTIVE_ALWAYS;
56
57
RPlaneShifter->aniFrames = RSDK.LoadSpriteAnimation("MMZ/RPlaneShifter.bin", SCOPE_STAGE);
58
59
RPlaneShifter->sfxTicTock = RSDK.GetSfx("MMZ/TicTock.wav");
60
}
61
62
void RPlaneShifter_DrawSprites(void)
63
{
64
RSDK_THIS(RPlaneShifter);
65
66
Vector2 drawPos = self->position;
67
if (SceneInfo->inEditor || SceneInfo->currentDrawGroup == Zone->playerDrawGroup[1] + 1)
68
RSDK.DrawSprite(&self->baseAnimator, &drawPos, false);
69
70
drawPos.y -= 0x80000;
71
72
uint8 poleAngles[3];
73
poleAngles[0] = self->angle + 21;
74
poleAngles[1] = self->angle + 106;
75
poleAngles[2] = self->angle - 64;
76
77
for (uint32 i = 0; i < self->height; ++i) {
78
drawPos.y -= 0x100000;
79
for (int32 p = 0; p < 3; ++p) {
80
drawPos.x = self->position.x;
81
82
if (SceneInfo->inEditor) {
83
drawPos.x += 0x1C00 * RSDK.Cos256(poleAngles[p]);
84
RSDK.DrawSprite(&RPlaneShifter->barAnimator, &drawPos, false);
85
}
86
else if (SceneInfo->currentDrawGroup != Zone->playerDrawGroup[1] + 1) {
87
if ((poleAngles[p] >= 0 && poleAngles[p] < 0x80) || SceneInfo->currentDrawGroup == Zone->playerDrawGroup[1] + 1) {
88
drawPos.x += 0x1C00 * RSDK.Cos256(poleAngles[p]);
89
RSDK.DrawSprite(&RPlaneShifter->barAnimator, &drawPos, false);
90
}
91
}
92
else if (poleAngles[p] >= 0x80) {
93
drawPos.x += 0x1C00 * RSDK.Cos256(poleAngles[p]);
94
RSDK.DrawSprite(&RPlaneShifter->barAnimator, &drawPos, false);
95
}
96
}
97
}
98
99
drawPos.x = self->position.x;
100
drawPos.y -= 0x80000;
101
if (SceneInfo->inEditor || SceneInfo->currentDrawGroup == Zone->playerDrawGroup[1] + 1)
102
RSDK.DrawSprite(&self->baseAnimator, &drawPos, false);
103
}
104
105
void RPlaneShifter_HandlePlaneShift(EntityPlayer *player)
106
{
107
RSDK_THIS(RPlaneShifter);
108
109
player->state = Player_State_Ground;
110
player->onGround = true;
111
player->nextGroundState = StateMachine_None;
112
player->nextAirState = StateMachine_None;
113
player->groundVel = 0;
114
115
RSDK.SetSpriteAnimation(player->aniFrames, ANI_IDLE, &player->animator, false, 0);
116
117
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
118
if (player->sidekick && (!player->sidekick || (self->prevActivePlayers & 1))) {
119
player->collisionPlane = player1->collisionPlane;
120
player->drawGroup = player1->drawGroup - 1;
121
player->direction = player->direction == FLIP_NONE;
122
}
123
else {
124
if (self->flags) {
125
player->drawGroup = player->collisionPlane ? Zone->playerDrawGroup[0] : Zone->playerDrawGroup[1];
126
player->collisionPlane = player->collisionPlane ? 0 : 1;
127
}
128
else {
129
player->drawGroup = player->collisionPlane ? Zone->playerDrawGroup[1] : Zone->playerDrawGroup[0];
130
player->collisionPlane = player->collisionPlane ? 0 : 1;
131
}
132
133
player->direction = player->direction == FLIP_NONE;
134
}
135
}
136
137
void RPlaneShifter_State_Init(void)
138
{
139
RSDK_THIS(RPlaneShifter);
140
141
self->active = ACTIVE_BOUNDS;
142
self->visible = true;
143
144
self->hitbox.left = -2;
145
self->hitbox.top = -8 - (16 * self->height);
146
self->hitbox.right = 2;
147
self->hitbox.bottom = -8;
148
149
self->unused = 0;
150
RSDK.SetSpriteAnimation(RPlaneShifter->aniFrames, 0, &self->baseAnimator, true, 0);
151
152
self->state = RPlaneShifter_State_AwaitPlayer;
153
RPlaneShifter_State_AwaitPlayer();
154
}
155
156
void RPlaneShifter_State_AwaitPlayer(void)
157
{
158
RSDK_THIS(RPlaneShifter);
159
160
foreach_active(Player, player)
161
{
162
int32 playerID = RSDK.GetEntitySlot(player);
163
164
if (!((1 << playerID) & self->activePlayers) && !((1 << playerID) & self->stoodPlayers)) {
165
if (Player_CheckCollisionTouch(player, self, &self->hitbox) && player->onGround && !player->isChibi) {
166
self->activePlayers |= 1 << playerID;
167
self->stoodPlayers |= 1 << playerID;
168
169
player->groundVel = 0;
170
player->velocity.x = 0;
171
player->velocity.y = 0;
172
player->position.x = self->position.x;
173
174
RSDK.SetSpriteAnimation(player->aniFrames, ANI_IDLE, &player->animator, false, 0);
175
player->state = Player_State_Static;
176
player->nextGroundState = StateMachine_None;
177
player->nextAirState = StateMachine_None;
178
player->onGround = true;
179
180
if (!player->sidekick) {
181
self->startAngle = self->angle << 16;
182
self->spinAngle = 0;
183
RSDK.SetSpriteAnimation(RPlaneShifter->aniFrames, 1, &self->baseAnimator, true, 0);
184
RSDK.PlaySfx(RPlaneShifter->sfxTicTock, false, 255);
185
self->state = RPlaneShifter_State_Spinning;
186
}
187
else {
188
player->abilityValues[0] = player->left;
189
player->abilityValues[1] = player->right;
190
}
191
}
192
}
193
194
if (player->sidekick) {
195
if (self->activePlayers & (1 << playerID)) {
196
if (player->jumpPress) {
197
self->activePlayers &= ~(1 << playerID);
198
self->stoodPlayers &= ~(1 << playerID);
199
Player_Action_Jump(player);
200
}
201
202
if (!player->abilityValues[0] && player->left) {
203
self->activePlayers &= ~(1 << playerID);
204
self->stoodPlayers &= ~(1 << playerID);
205
player->state = Player_State_Ground;
206
}
207
208
if (!player->abilityValues[1] && player->right) {
209
self->activePlayers &= ~(1 << playerID);
210
self->stoodPlayers &= ~(1 << playerID);
211
player->state = Player_State_Ground;
212
}
213
214
player->abilityValues[0] = player->left;
215
player->abilityValues[1] = player->right;
216
}
217
}
218
219
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
220
self->stoodPlayers |= 1 << playerID;
221
else
222
self->stoodPlayers &= ~(1 << playerID);
223
}
224
}
225
226
void RPlaneShifter_State_Spinning(void)
227
{
228
RSDK_THIS(RPlaneShifter);
229
230
if (abs(self->spinAngle >> 16) < 128) {
231
uint8 updateSpinSpeed = 1;
232
if (self->rotateDir && self->spinAngle >> 16 > -65)
233
updateSpinSpeed = 0;
234
else if (!self->rotateDir && self->spinAngle >> 16 < 65)
235
updateSpinSpeed = 0;
236
237
int32 speed = self->rotateDir ? -0x3800 : 0x3800;
238
239
if (updateSpinSpeed) {
240
self->spinSpeed -= speed;
241
if (self->rotateDir && self->spinSpeed > -0x3800) {
242
self->spinSpeed = speed;
243
updateSpinSpeed = 2;
244
}
245
else if (!self->rotateDir && self->spinSpeed < 0x3800) {
246
self->spinSpeed = speed;
247
updateSpinSpeed = 2;
248
}
249
}
250
else {
251
self->spinSpeed += speed;
252
}
253
254
if (self->rotateDir && self->spinSpeed > -0x3800 && updateSpinSpeed != 2) {
255
self->spinSpeed = speed;
256
}
257
258
self->spinAngle += self->spinSpeed;
259
self->angle = (self->startAngle + self->spinAngle) >> 16;
260
261
if (self->angle < 0)
262
263
self->angle += 0x100;
264
self->angle &= 0xFF;
265
}
266
267
if (abs(self->spinAngle >> 16) >= 128) {
268
self->spinSpeed = 0;
269
self->spinAngle = 128 << 16;
270
if (!self->rotateDir)
271
self->spinAngle = -self->spinAngle;
272
273
self->angle = (self->startAngle + self->spinAngle) >> 16;
274
if (self->angle < 0)
275
self->angle += 0x100;
276
self->angle &= 0xFF;
277
278
RSDK.SetSpriteAnimation(RPlaneShifter->aniFrames, 0, &self->baseAnimator, true, 0);
279
self->state = RPlaneShifter_State_FinishSpin;
280
}
281
282
foreach_active(Player, player)
283
{
284
int32 playerID = RSDK.GetEntitySlot(player);
285
286
if (!((1 << playerID) & self->activePlayers) && !((1 << playerID) & self->stoodPlayers)
287
&& Player_CheckCollisionTouch(player, self, &self->hitbox) && player->onGround && !player->isChibi) {
288
self->activePlayers |= 1 << playerID;
289
player->groundVel = 0;
290
player->velocity.x = 0;
291
player->velocity.y = 0;
292
player->position.x = self->position.x;
293
294
RSDK.SetSpriteAnimation(player->aniFrames, ANI_IDLE, &player->animator, false, 0);
295
player->state = Player_State_Static;
296
player->nextGroundState = StateMachine_None;
297
player->nextAirState = StateMachine_None;
298
player->onGround = true;
299
}
300
301
if ((1 << playerID) & self->activePlayers) {
302
RSDK.SetSpriteAnimation(player->aniFrames, ANI_TWISTER, &player->animator, false, 0);
303
304
int32 angle = (self->spinAngle >> 16) & 0xFF;
305
int32 negAngle = 0xFF - angle;
306
if (player->direction)
307
negAngle = angle;
308
309
int32 frame = 0;
310
switch (negAngle >> 6) {
311
case 0:
312
case 1: frame = 9 * negAngle / 128; break;
313
case 2:
314
case 3: frame = 16 * (negAngle - 128) / 128 + 9; break;
315
default: break;
316
}
317
318
player->animator.frameID = frame % 24;
319
player->animator.speed = 0;
320
}
321
322
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
323
self->stoodPlayers |= 1 << playerID;
324
else
325
self->stoodPlayers &= ~(1 << playerID);
326
}
327
}
328
329
void RPlaneShifter_State_FinishSpin(void)
330
{
331
RSDK_THIS(RPlaneShifter);
332
foreach_active(Player, player)
333
{
334
int32 playerID = RSDK.GetEntitySlot(player);
335
336
if ((1 << playerID) & self->activePlayers) {
337
self->activePlayers &= ~(1 << playerID);
338
RPlaneShifter_HandlePlaneShift(player);
339
}
340
341
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
342
self->stoodPlayers |= 1 << playerID;
343
else
344
self->stoodPlayers &= ~(1 << playerID);
345
346
// wait for P1 to get out of the spinner
347
if (!player->sidekick && !((1 << playerID) & self->activePlayers) && !((1 << playerID) & self->stoodPlayers)) {
348
self->activePlayers = 0;
349
self->spinAngle = 0;
350
self->startAngle = 0;
351
self->rotateDir = !self->rotateDir;
352
self->state = RPlaneShifter_State_AwaitPlayer;
353
}
354
}
355
}
356
357
#if GAME_INCLUDE_EDITOR
358
void RPlaneShifter_EditorDraw(void) { RPlaneShifter_DrawSprites(); }
359
360
void RPlaneShifter_EditorLoad(void)
361
{
362
RPlaneShifter->aniFrames = RSDK.LoadSpriteAnimation("MMZ/RPlaneShifter.bin", SCOPE_STAGE);
363
364
RSDK_ACTIVE_VAR(RPlaneShifter, flags);
365
RSDK_ENUM_VAR("Plane A -> Low Draw Group, Plane B -> High Draw Group", 0);
366
RSDK_ENUM_VAR("Plane A -> High Draw Group, Plane B -> Low Draw Group", 1);
367
}
368
#endif
369
370
void RPlaneShifter_Serialize(void)
371
{
372
RSDK_EDITABLE_VAR(RPlaneShifter, VAR_UINT32, height);
373
RSDK_EDITABLE_VAR(RPlaneShifter, VAR_UINT8, flags);
374
}
375
376