Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/TMZ/TeeterTotter.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: TeeterTotter Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectTeeterTotter *TeeterTotter;
11
12
void TeeterTotter_Update(void)
13
{
14
RSDK_THIS(TeeterTotter);
15
16
StateMachine_Run(self->state);
17
18
RSDK.ProcessAnimation(&self->animator);
19
}
20
21
void TeeterTotter_LateUpdate(void) {}
22
23
void TeeterTotter_StaticUpdate(void) {}
24
25
void TeeterTotter_Draw(void)
26
{
27
RSDK_THIS(TeeterTotter);
28
29
int32 x = self->position.x;
30
int32 y = self->position.y;
31
int32 offsetX = 0x100000 - (self->length << 21) + x;
32
33
for (uint32 i = 0; i < 2 * self->length; ++i) {
34
if (!((1 << i) & self->inactiveSegments)) {
35
Vector2 drawPos;
36
drawPos.x = (offsetX + (i << 21)) & 0xFFFF0000;
37
drawPos.y = (self->segmentPosition[i] + y) & 0xFFFF0000;
38
39
RSDK.SetSpriteAnimation(TeeterTotter->aniFrames, 0, &self->animator, true, (i + self->color) % 2);
40
41
RSDK.DrawSprite(&self->animator, &drawPos, false);
42
}
43
}
44
}
45
46
void TeeterTotter_Create(void *data)
47
{
48
RSDK_THIS(TeeterTotter);
49
50
self->length = CLAMP(self->length, 1, TEETERTOTTER_SEGMENT_COUNT / 2);
51
self->active = ACTIVE_BOUNDS;
52
self->drawGroup = Zone->objectDrawGroup[0];
53
self->origin = self->position;
54
self->updateRange.x = (self->length + 2) << 22;
55
self->visible = true;
56
self->drawFX = FX_FLIP;
57
self->updateRange.y = (self->length + 4) << 21;
58
59
self->hitbox.left = -16;
60
self->hitbox.top = -16;
61
self->hitbox.right = 16;
62
self->hitbox.bottom = 16;
63
64
self->state = TeeterTotter_State_Init;
65
}
66
67
void TeeterTotter_StageLoad(void)
68
{
69
if (RSDK.CheckSceneFolder("TMZ1") || RSDK.CheckSceneFolder("TMZ2"))
70
TeeterTotter->aniFrames = RSDK.LoadSpriteAnimation("TMZ1/TeeterTotter.bin", SCOPE_STAGE);
71
}
72
73
int32 TeeterTotter_CheckPlayerCollisions(void)
74
{
75
RSDK_THIS(TeeterTotter);
76
77
int32 heaviestSegment = -1;
78
int32 stoodSegmentIDs[] = { -1, -1, -1, -1 };
79
80
int32 storeX = self->position.x;
81
int32 storeY = self->position.y;
82
83
int32 x = 0x100000 - (self->length << 21) + self->position.x;
84
85
for (uint32 s = 0; s < 2 * self->length; ++s) {
86
if (!((1 << heaviestSegment) & self->inactiveSegments)) {
87
self->position.x = (x + (s << 21)) & 0xFFFF0000;
88
self->position.y = (self->segmentPosition[s] + storeY) & 0xFFFF0000;
89
90
foreach_active(Player, player)
91
{
92
int32 playerID = RSDK.GetEntitySlot(player);
93
if (self->playerIDs[playerID] == (int32)s) {
94
player->position.y += self->segmentVelocity[s];
95
player->position.y += 0x10000;
96
}
97
98
if (Player_CheckCollisionBox(player, self, &self->hitbox) == C_TOP) {
99
if (!player->sidekick)
100
heaviestSegment = s;
101
102
stoodSegmentIDs[playerID] = s;
103
}
104
}
105
}
106
}
107
108
self->position.x = storeX;
109
self->position.y = storeY;
110
111
for (int32 i = 0; i < PLAYER_COUNT; ++i) self->playerIDs[i] = stoodSegmentIDs[i];
112
113
return heaviestSegment;
114
}
115
116
void TeeterTotter_ProcessSegmentGravity(void)
117
{
118
RSDK_THIS(TeeterTotter);
119
120
int32 x = self->position.x;
121
int32 y = self->position.y;
122
int32 offsetX = x + (0x100000 - (self->length << 21));
123
124
for (uint32 i = 0; i < 2 * self->length; ++i) {
125
if (!((1 << i) & self->inactiveSegments)) {
126
self->segmentVelocity[i] += 0x3800;
127
self->segmentPosition[i] += self->segmentVelocity[i];
128
129
Vector2 segmentPos;
130
segmentPos.x = (offsetX + (i << 21)) & 0xFFFF0000;
131
segmentPos.y = (y + self->segmentPosition[i]) & 0xFFFF0000;
132
if (!RSDK.CheckPosOnScreen(&segmentPos, &self->updateRange))
133
self->inactiveSegments |= 1 << i;
134
}
135
}
136
}
137
138
void TeeterTotter_HandleSegmentPositions(void)
139
{
140
RSDK_THIS(TeeterTotter);
141
142
uint8 len = self->length;
143
for (uint32 i = 0; i < 2 * self->length; ++i) {
144
int8 pos = i - len;
145
if (pos >= 0)
146
pos++;
147
148
self->segmentPosition[i] = (self->fallPos >> 1) * (int8)(2 * pos + 2 * ((int8)(2 * pos) <= 0) - 1);
149
}
150
}
151
152
void TeeterTotter_State_Init(void)
153
{
154
RSDK_THIS(TeeterTotter);
155
156
self->unused2 = 0;
157
self->fallPos = 0;
158
self->fallVelocity = 0;
159
160
for (uint32 i = 0; i < 2 * self->length; ++i) {
161
self->segmentPosition[i] = 0;
162
self->segmentVelocity[i] = 0;
163
}
164
self->inactiveSegments = 0;
165
166
self->visible = true;
167
self->active = ACTIVE_NORMAL;
168
self->state = TeeterTotter_State_Teeter;
169
}
170
171
void TeeterTotter_State_Teeter(void)
172
{
173
RSDK_THIS(TeeterTotter);
174
175
int32 prevVal[TEETERTOTTER_SEGMENT_COUNT];
176
for (uint32 i = 0; i < 2 * self->length; ++i) {
177
prevVal[i] = self->segmentPosition[i];
178
}
179
180
TeeterTotter_HandleSegmentPositions();
181
182
for (uint32 i = 0; i < 2 * self->length; ++i) {
183
self->segmentVelocity[i] = self->segmentPosition[i] - prevVal[i];
184
}
185
186
int32 id = TeeterTotter_CheckPlayerCollisions();
187
if (id > -1) {
188
int32 segment = id - self->length;
189
if (segment >= 0)
190
++segment;
191
192
self->fallVelocity += 32 * segment;
193
}
194
195
self->fallPos += self->fallVelocity;
196
197
if (abs(self->fallPos) > 0x200000)
198
self->state = TeeterTotter_State_Fall;
199
}
200
201
void TeeterTotter_State_Fall(void)
202
{
203
RSDK_THIS(TeeterTotter);
204
205
TeeterTotter_ProcessSegmentGravity();
206
207
bool32 fullyInactive = true;
208
for (uint32 i = 0; i < 2 * self->length; ++i) fullyInactive &= ((1 << i) & self->inactiveSegments) != 0;
209
210
if (fullyInactive || !(2 * self->length)) {
211
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->origin, &self->updateRange)) {
212
self->position = self->origin;
213
self->visible = false;
214
self->active = ACTIVE_BOUNDS;
215
self->state = TeeterTotter_State_Init;
216
}
217
}
218
}
219
220
#if GAME_INCLUDE_EDITOR
221
void TeeterTotter_EditorDraw(void)
222
{
223
RSDK_THIS(TeeterTotter);
224
225
self->length = CLAMP(self->length, 1, TEETERTOTTER_SEGMENT_COUNT / 2);
226
self->origin = self->position;
227
self->updateRange.x = (self->length + 2) << 22;
228
self->updateRange.y = (self->length + 4) << 21;
229
230
TeeterTotter_Draw();
231
}
232
233
void TeeterTotter_EditorLoad(void)
234
{
235
TeeterTotter->aniFrames = RSDK.LoadSpriteAnimation("TMZ1/TeeterTotter.bin", SCOPE_STAGE);
236
237
RSDK_ACTIVE_VAR(TeeterTotter, color);
238
RSDK_ENUM_VAR("Orange, Blue", TEETERTOTTER_COLOR_ORANGEBLUE);
239
RSDK_ENUM_VAR("Blue, Orange", TEETERTOTTER_COLOR_BLUEORANGE);
240
}
241
#endif
242
243
void TeeterTotter_Serialize(void)
244
{
245
RSDK_EDITABLE_VAR(TeeterTotter, VAR_UINT8, color);
246
RSDK_EDITABLE_VAR(TeeterTotter, VAR_UINT32, length);
247
}
248
249