Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/PGZ/FrostThrower.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: FrostThrower Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectFrostThrower *FrostThrower;
11
12
void FrostThrower_Update(void)
13
{
14
RSDK_THIS(FrostThrower);
15
16
StateMachine_Run(self->state);
17
}
18
19
void FrostThrower_LateUpdate(void) {}
20
21
void FrostThrower_StaticUpdate(void) {}
22
23
void FrostThrower_Draw(void)
24
{
25
RSDK_THIS(FrostThrower);
26
27
RSDK.DrawSprite(&self->dispenseAnimator, NULL, false);
28
29
if (self->isActive)
30
FrostThrower_DrawGustFX();
31
}
32
33
void FrostThrower_Create(void *data)
34
{
35
RSDK_THIS(FrostThrower);
36
37
self->active = ACTIVE_BOUNDS;
38
self->visible = true;
39
self->drawFX = FX_FLIP;
40
self->drawGroup = Zone->playerDrawGroup[0] + 1;
41
self->updateRange.x = 0x100000;
42
self->updateRange.y = 0x100000;
43
44
self->hitbox.left = -14;
45
self->hitbox.right = 15;
46
self->maxGustCount[0] = 2;
47
self->maxGustCount[1] = 2;
48
self->maxGustCount[2] = 3;
49
self->maxGustCount[3] = 3;
50
51
FrostThrower_HandleGustCount();
52
FrostThrower_HandleGustPos();
53
54
RSDK.SetSpriteAnimation(FrostThrower->aniFrames, 0, &self->dispenseAnimator, true, 0);
55
RSDK.SetSpriteAnimation(FrostThrower->aniFrames, 1, &self->gustAnimator, true, 0);
56
57
self->state = FrostThrower_State_AwaitInterval;
58
}
59
60
void FrostThrower_StageLoad(void)
61
{
62
if (RSDK.CheckSceneFolder("PSZ1"))
63
FrostThrower->aniFrames = RSDK.LoadSpriteAnimation("PSZ1/FrostThrower.bin", SCOPE_STAGE); // this doesn't actually exist in the final game...
64
else if (RSDK.CheckSceneFolder("PSZ2"))
65
FrostThrower->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/FrostThrower.bin", SCOPE_STAGE);
66
67
FrostThrower->sfxFrostThrower = RSDK.GetSfx("PSZ/FrostThrower.wav");
68
FrostThrower->sfxFreeze = RSDK.GetSfx("PSZ/Freeze.wav");
69
}
70
71
void FrostThrower_DrawGustFX(void)
72
{
73
RSDK_THIS(FrostThrower);
74
75
int32 pos = 0;
76
for (int32 i = 0; i < 4; ++i) {
77
int32 count = MIN(self->gustCount[i], 3);
78
79
RSDK.SetSpriteAnimation(FrostThrower->aniFrames, 1, &self->gustAnimator, true, i);
80
for (int32 p = 0; p < count; ++p) {
81
Vector2 drawPos;
82
drawPos.x = self->position.x + self->gustPos[pos + p].x;
83
drawPos.y = self->position.y + self->gustPos[pos + p].y;
84
RSDK.DrawSprite(&self->gustAnimator, &drawPos, false);
85
}
86
87
pos += 3;
88
}
89
}
90
91
void FrostThrower_CheckPlayerCollisions(void)
92
{
93
RSDK_THIS(FrostThrower);
94
95
foreach_active(Player, player)
96
{
97
int32 playerID = RSDK.GetEntitySlot(player);
98
99
if (Player_CheckCollisionTouch(player, self, &self->hitbox) && !Ice->playerTimers[playerID])
100
Ice_FreezePlayer(player);
101
}
102
}
103
104
void FrostThrower_HandleGustCount(void)
105
{
106
RSDK_THIS(FrostThrower);
107
108
for (int32 i = 0; i < 4; ++i) {
109
self->gustCount[i] = self->maxGustCount[i];
110
if (self->maxGustCount[i] > 1) {
111
if (RSDK.Rand(0, 10) <= 6)
112
self->gustCount[i] = RSDK.Rand(1, self->maxGustCount[i]);
113
}
114
}
115
}
116
117
void FrostThrower_HandleGustPos(void)
118
{
119
RSDK_THIS(FrostThrower);
120
int32 pos = 0;
121
122
int32 yMin[] = { 2, 20, 45, 55 };
123
int32 yMax[] = { 22, 45, 62, 78 };
124
int32 xMin[] = { -3, -5, -7, -9 };
125
int32 xMax[] = { 3, 5, 7, 9 };
126
for (int32 i = 0; i < 4; ++i) {
127
for (int32 p = 0; p < self->gustCount[i]; ++p) {
128
self->gustPos[pos + p].x = RSDK.Rand(xMin[i], xMax[i]) << 16;
129
self->gustPos[pos + p].y = RSDK.Rand(yMin[i], yMax[i]) << 16;
130
}
131
pos += 3;
132
}
133
}
134
135
void FrostThrower_State_AwaitInterval(void)
136
{
137
RSDK_THIS(FrostThrower);
138
139
if (!((Zone->timer + self->intervalOffset) % self->interval)) {
140
self->active = ACTIVE_NORMAL;
141
self->timer = 0;
142
self->isActive = true;
143
self->state = FrostThrower_State_Dispensing;
144
RSDK.PlaySfx(FrostThrower->sfxFrostThrower, false, 255);
145
}
146
}
147
148
void FrostThrower_State_Dispensing(void)
149
{
150
RSDK_THIS(FrostThrower);
151
152
self->hitbox.top = 0;
153
if (self->timer >= 20)
154
self->hitbox.bottom = 80;
155
else
156
self->hitbox.bottom = 4 * self->timer;
157
158
FrostThrower_CheckPlayerCollisions();
159
160
if (self->timer > 3)
161
self->maxGustCount[0] = 2;
162
else
163
self->maxGustCount[0] = 1;
164
165
if (self->timer > 8)
166
self->maxGustCount[1] = 2;
167
else if (self->timer > 5)
168
self->maxGustCount[1] = 1;
169
else
170
self->maxGustCount[1] = 0;
171
172
if (self->timer > 16)
173
self->maxGustCount[2] = 3;
174
else if (self->timer > 14)
175
self->maxGustCount[2] = 2;
176
else if (self->timer > 12)
177
self->maxGustCount[2] = 1;
178
else
179
self->maxGustCount[2] = 0;
180
181
if (self->timer > 19)
182
self->maxGustCount[3] = 3;
183
else if (self->timer > 17)
184
self->maxGustCount[3] = 2;
185
else if (self->timer > 15)
186
self->maxGustCount[3] = 1;
187
else
188
self->maxGustCount[3] = 0;
189
190
FrostThrower_HandleGustCount();
191
FrostThrower_HandleGustPos();
192
193
if (++self->timer >= self->duration) {
194
self->state = FrostThrower_State_StopDispensing;
195
self->timer = 0;
196
}
197
}
198
199
void FrostThrower_State_StopDispensing(void)
200
{
201
RSDK_THIS(FrostThrower);
202
203
self->hitbox.top = 4 * self->timer;
204
self->hitbox.bottom = 80;
205
206
FrostThrower_CheckPlayerCollisions();
207
208
if (self->timer > 8)
209
self->maxGustCount[0] = 0;
210
else if (self->timer > 5)
211
self->maxGustCount[0] = 1;
212
else
213
self->maxGustCount[1] = 2;
214
215
if (self->timer > 8)
216
self->maxGustCount[1] = 0;
217
else if (self->timer > 5)
218
self->maxGustCount[1] = 1;
219
else
220
self->maxGustCount[1] = 2;
221
222
if (self->timer > 19)
223
self->maxGustCount[2] = 0;
224
else if (self->timer > 17)
225
self->maxGustCount[2] = 1;
226
else if (self->timer > 15)
227
self->maxGustCount[2] = 2;
228
else
229
self->maxGustCount[2] = 3;
230
231
if (self->timer > 16)
232
self->maxGustCount[3] = 0;
233
else if (self->timer > 14)
234
self->maxGustCount[3] = 1;
235
else if (self->timer > 12)
236
self->maxGustCount[3] = 2;
237
else
238
self->maxGustCount[3] = 3;
239
240
FrostThrower_HandleGustCount();
241
FrostThrower_HandleGustPos();
242
243
if (self->timer++ >= 20) {
244
self->active = ACTIVE_BOUNDS;
245
self->isActive = false;
246
self->state = FrostThrower_State_AwaitInterval;
247
self->timer = 0;
248
}
249
}
250
251
#if GAME_INCLUDE_EDITOR
252
void FrostThrower_EditorDraw(void)
253
{
254
RSDK_THIS(FrostThrower);
255
256
RSDK.SetSpriteAnimation(FrostThrower->aniFrames, 0, &self->dispenseAnimator, true, 0);
257
RSDK.SetSpriteAnimation(FrostThrower->aniFrames, 1, &self->gustAnimator, true, 0);
258
259
FrostThrower_Draw();
260
}
261
262
void FrostThrower_EditorLoad(void)
263
{
264
if (RSDK.CheckSceneFolder("PSZ1"))
265
FrostThrower->aniFrames = RSDK.LoadSpriteAnimation("PSZ1/FrostThrower.bin", SCOPE_STAGE);
266
else if (RSDK.CheckSceneFolder("PSZ2"))
267
FrostThrower->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/FrostThrower.bin", SCOPE_STAGE);
268
}
269
#endif
270
271
void FrostThrower_Serialize(void)
272
{
273
RSDK_EDITABLE_VAR(FrostThrower, VAR_UINT16, interval);
274
RSDK_EDITABLE_VAR(FrostThrower, VAR_UINT16, intervalOffset);
275
RSDK_EDITABLE_VAR(FrostThrower, VAR_UINT16, duration);
276
}
277
278