Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/HCZ/Fan.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Fan Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectFan *Fan;
11
12
void Fan_Update(void)
13
{
14
RSDK_THIS(Fan);
15
16
StateMachine_Run(self->stateActivate);
17
StateMachine_Run(self->stateDeactivate);
18
19
RSDK.ProcessAnimation(&self->animator);
20
21
StateMachine_Run(self->state);
22
}
23
24
void Fan_LateUpdate(void) {}
25
26
void Fan_StaticUpdate(void)
27
{
28
Fan->activePlayers = 0;
29
30
if (Water) {
31
int32 offsetH = 0;
32
int32 offsetV = 0;
33
34
foreach_active(Water, water)
35
{
36
if (water->state == Water_State_BigBubble && water->activePlayers) {
37
foreach_active(Fan, fan)
38
{
39
Fan->hitboxTop.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;
40
if (fan->state == Fan_HandlePlayerInteractions_Top) {
41
if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,
42
&Fan->hitboxTop)) {
43
offsetV -= 0x20000;
44
}
45
}
46
47
Fan->hitboxBottom.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;
48
if (fan->state == Fan_HandlePlayerInteractions_Bottom) {
49
if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,
50
&Fan->hitboxBottom)) {
51
offsetV += 0x20000;
52
}
53
}
54
55
Fan->hitboxSides.left = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;
56
if (fan->state == Fan_HandlePlayerInteractions_Left) {
57
if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,
58
&Fan->hitboxSides)) {
59
offsetH -= 0x20000;
60
}
61
}
62
if (fan->state == Fan_HandlePlayerInteractions_Right) {
63
if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,
64
&Fan->hitboxSides)) {
65
offsetH += 0x20000;
66
}
67
}
68
}
69
70
offsetH = CLAMP(offsetH, -0x20000, 0x20000);
71
offsetV = CLAMP(offsetV, -0x20000, 0x20000);
72
73
if (water->bubbleOffset.x < offsetH) {
74
water->bubbleOffset.x += 0x800;
75
76
if (water->bubbleOffset.x > offsetH)
77
water->bubbleOffset.x = offsetH;
78
}
79
80
if (water->bubbleOffset.x > offsetH) {
81
water->bubbleOffset.x -= 0x800;
82
83
if (water->bubbleOffset.x < offsetH)
84
water->bubbleOffset.x = offsetH;
85
}
86
87
if (water->bubbleOffset.y < offsetV) {
88
water->bubbleOffset.y += 0x800;
89
90
if (water->bubbleOffset.y > offsetV)
91
water->bubbleOffset.y = offsetV;
92
}
93
94
if (water->bubbleOffset.y > offsetV) {
95
water->bubbleOffset.y -= 0x800;
96
97
if (water->bubbleOffset.y < offsetV)
98
water->bubbleOffset.y = offsetV;
99
}
100
}
101
}
102
}
103
104
if (HangConveyor) {
105
foreach_active(HangConveyor, conveyor)
106
{
107
if (conveyor->movementActivePlayers) {
108
foreach_active(Player, player)
109
{
110
int32 playerID = RSDK.GetEntitySlot(player);
111
112
if (((1 << playerID) & conveyor->movementActivePlayers)) {
113
bool32 fanning = false;
114
115
foreach_active(Fan, fan)
116
{
117
Fan->hitboxTop.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;
118
119
if (fan->state == Fan_HandlePlayerInteractions_Top) {
120
if (Player_CheckCollisionTouch(player, fan, &Fan->hitboxTop) && conveyor->fanTimer[playerID] < 12) {
121
conveyor->fanTimer[playerID]++;
122
fanning = true;
123
foreach_break;
124
}
125
}
126
}
127
128
if (conveyor->fanTimer[playerID] > 0 && !fanning)
129
conveyor->fanTimer[playerID]--;
130
}
131
}
132
}
133
}
134
}
135
136
if (RSDK.CheckSceneFolder("HCZ")) {
137
int32 count = 0;
138
139
if (RSDK_GET_ENTITY(SLOT_PAUSEMENU, PauseMenu)->classID != PauseMenu->classID) {
140
Vector2 range = { 0x400000, 0x400000 };
141
foreach_active(Fan, fan)
142
{
143
uint16 tile = RSDK.GetTile(Zone->fgLayer[1], fan->position.x >> 20, fan->position.y >> 20);
144
if (fan->state && fan->state != Fan_State_Stopped && tile == 0xFFFF && RSDK.CheckOnScreen(fan, &range))
145
++count;
146
}
147
148
if (count) {
149
if (!Fan->playingFanSfx) {
150
RSDK.PlaySfx(Fan->sfxFan, 37404, 0xFF);
151
Fan->playingFanSfx = true;
152
}
153
}
154
}
155
156
if (!count && Fan->playingFanSfx) {
157
RSDK.StopSfx(Fan->sfxFan);
158
Fan->playingFanSfx = false;
159
}
160
}
161
}
162
163
void Fan_Draw(void)
164
{
165
RSDK_THIS(Fan);
166
167
RSDK.DrawSprite(&self->animator, NULL, false);
168
}
169
170
void Fan_Create(void *data)
171
{
172
RSDK_THIS(Fan);
173
174
self->drawFX = FX_FLIP;
175
176
if (!SceneInfo->inEditor) {
177
self->active = ACTIVE_BOUNDS;
178
self->visible = true;
179
self->drawGroup = Zone->objectDrawGroup[0] + 1;
180
self->updateRange.x = 0x800000;
181
self->updateRange.y = 0x800000;
182
183
if (self->type == FAN_V)
184
self->direction *= FLIP_Y;
185
186
if (!self->size)
187
self->size = 5;
188
189
self->size *= 16;
190
RSDK.SetSpriteAnimation(Fan->aniFrames, self->type, &self->animator, true, 0);
191
self->state = Fan_State_Stopped;
192
self->animator.speed = 0;
193
194
switch (self->activation) {
195
case FAN_ACTIVATE_NONE:
196
Fan_Activate();
197
self->animator.speed = 128;
198
break;
199
200
case FAN_ACTIVATE_INTERVAL: self->stateActivate = Fan_Activate_Interval; break;
201
202
case FAN_ACTIVATE_PLATFORM: self->stateActivate = Fan_Activate_Platform; break;
203
204
case FAN_ACTIVATE_BUTTON:
205
Fan_SetupTagLink();
206
self->stateActivate = Fan_Activate_Button;
207
break;
208
209
default: break;
210
}
211
212
if (self->deactivation == FAN_DEACTIVATE_BUTTON) {
213
Fan_SetupTagLink();
214
self->stateDeactivate = Fan_Deactivate_Button;
215
}
216
}
217
}
218
219
void Fan_StageLoad(void)
220
{
221
if (RSDK.CheckSceneFolder("OOZ1") || RSDK.CheckSceneFolder("OOZ2")) {
222
Fan->aniFrames = RSDK.LoadSpriteAnimation("OOZ/Fan.bin", SCOPE_STAGE);
223
224
Fan->hitboxTop.left = -64;
225
Fan->hitboxTop.right = 64;
226
227
Fan->unused = 5;
228
Fan->minVelocity = -0x50000;
229
230
Fan->hitboxBottom.left = -16;
231
Fan->hitboxBottom.right = 16;
232
Fan->hitboxBottom.top = -160;
233
Fan->hitboxBottom.bottom = 32;
234
235
Fan->hitboxSides.top = -160;
236
Fan->hitboxSides.bottom = 112;
237
Fan->hitboxSides.left = -112;
238
Fan->hitboxSides.right = 32;
239
}
240
else if (RSDK.CheckSceneFolder("HCZ")) {
241
Fan->aniFrames = RSDK.LoadSpriteAnimation("HCZ/Fan.bin", SCOPE_STAGE);
242
243
Fan->hitboxTop.left = -16;
244
Fan->hitboxTop.right = 16;
245
246
Fan->unused = 20;
247
Fan->minVelocity = -0x20000;
248
249
Fan->hitboxBottom.left = -16;
250
Fan->hitboxBottom.right = 16;
251
Fan->hitboxBottom.top = -160;
252
Fan->hitboxBottom.bottom = 32;
253
254
Fan->hitboxSides.top = -24;
255
Fan->hitboxSides.bottom = 24;
256
Fan->hitboxSides.left = -112;
257
Fan->hitboxSides.right = 32;
258
259
Fan->sfxFan = RSDK.GetSfx("HCZ/SmallFan.wav");
260
Fan->active = ACTIVE_ALWAYS;
261
}
262
263
Fan->playerHitbox.left = -1;
264
Fan->playerHitbox.top = -1;
265
Fan->playerHitbox.right = 1;
266
Fan->playerHitbox.bottom = 1;
267
}
268
269
void Fan_SetupTagLink(void)
270
{
271
RSDK_THIS(Fan);
272
273
self->taggedButton = NULL;
274
EntityButton *taggedButton = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) - 1, Button);
275
276
if (self->buttonTag > 0) {
277
bool32 matchedTag = false;
278
279
if (Button) {
280
foreach_all(Button, button)
281
{
282
if (button->tag == self->buttonTag) {
283
taggedButton = button;
284
matchedTag = true;
285
}
286
}
287
}
288
289
if (PullChain && !matchedTag) {
290
foreach_all(PullChain, chain)
291
{
292
if (chain->tag == self->buttonTag) {
293
taggedButton = (EntityButton *)chain;
294
matchedTag = true;
295
}
296
}
297
}
298
}
299
300
if (taggedButton) {
301
if ((Button && taggedButton->classID == Button->classID) || (PullChain && taggedButton->classID == PullChain->classID)) {
302
int32 distX = abs(self->position.x - taggedButton->position.x) + 0x800000;
303
int32 distY = abs(self->position.y - taggedButton->position.y) + 0x800000;
304
305
if (self->updateRange.x < distX)
306
self->updateRange.x = distX;
307
308
if (self->updateRange.y < distY)
309
self->updateRange.y = distY;
310
311
self->taggedButton = taggedButton;
312
}
313
}
314
}
315
316
void Fan_HandlePlayerInteractions_Top(void)
317
{
318
RSDK_THIS(Fan);
319
320
Fan->hitboxTop.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - self->size;
321
Fan->hitboxTop.bottom = 48;
322
323
int32 playerID = 1;
324
foreach_active(Player, player)
325
{
326
if (player->state != Player_State_Static) {
327
int32 anim = player->animator.animationID;
328
if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN
329
&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxTop, player, &Fan->playerHitbox)) {
330
if (Water && player->position.y > Water->waterLevel)
331
RSDK.SetSpriteAnimation(player->aniFrames, ANI_FAN, &player->animator, false, 1);
332
else
333
RSDK.SetSpriteAnimation(player->aniFrames, ANI_SPRING_CS, &player->animator, false, 1);
334
335
player->state = Player_State_Air;
336
player->tileCollisions = TILECOLLISION_DOWN;
337
player->onGround = false;
338
339
int32 velocity = MAX((self->position.y + (Fan->hitboxTop.top << 16) - player->position.y) >> 4, Fan->minVelocity);
340
if (player->velocity.y <= velocity) {
341
player->velocity.y = velocity;
342
}
343
else {
344
player->velocity.y = player->velocity.y + (velocity >> 2) + (velocity >> 1);
345
if (player->velocity.y < velocity)
346
player->velocity.y = velocity;
347
}
348
349
if (!(playerID & Fan->activePlayers)) {
350
if (player->velocity.y > -0x40000 && player->velocity.y < 0)
351
player->velocity.x += (32 * player->velocity.x / 31) >> 5;
352
353
Fan->activePlayers |= playerID;
354
}
355
}
356
}
357
358
playerID <<= 1;
359
}
360
361
if (Water) {
362
if (self->position.y > Water->waterLevel && !(Zone->timer & 3)) {
363
EntityWater *water = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y - 0x100000);
364
365
water->isPermanent = false;
366
water->position.x += ZONE_RAND(-6, 7) << 16;
367
water->bubbleX = water->position.x;
368
water->velocity.y = -0x40000;
369
water->childPtr = NULL;
370
}
371
}
372
373
Fan_State_Started();
374
}
375
376
void Fan_HandlePlayerInteractions_Bottom(void)
377
{
378
RSDK_THIS(Fan);
379
380
Fan->hitboxBottom.bottom = self->size - (RSDK.Sin256(2 * Zone->timer) >> 5);
381
382
foreach_active(Player, player)
383
{
384
if (player->state != Player_State_Static) {
385
int32 anim = player->animator.animationID;
386
387
if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN
388
&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxBottom, player, &Fan->playerHitbox)) {
389
int32 max = (self->position.y - player->position.y + 0xA00000) >> 4;
390
if (player->velocity.y < max)
391
player->velocity.y += ((self->position.y - player->position.y + 0xA00000) >> 9);
392
}
393
}
394
}
395
396
Fan_State_Started();
397
}
398
399
void Fan_HandlePlayerInteractions_Left(void)
400
{
401
RSDK_THIS(Fan);
402
403
Fan->hitboxSides.left = (RSDK.Sin256(2 * Zone->timer) >> 5) - self->size;
404
405
foreach_active(Player, player)
406
{
407
if (player->state != Player_State_Static) {
408
int32 anim = player->animator.animationID;
409
410
if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN && player->collisionMode != CMODE_LWALL
411
&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxSides, player, &Fan->playerHitbox)) {
412
player->position.x += (self->position.x - player->position.x - 0xA00000) >> 4;
413
}
414
}
415
}
416
417
Fan_State_Started();
418
}
419
420
void Fan_HandlePlayerInteractions_Right(void)
421
{
422
RSDK_THIS(Fan);
423
424
Fan->hitboxSides.left = (RSDK.Sin256(2 * Zone->timer) >> 5) - self->size;
425
426
foreach_active(Player, player)
427
{
428
if (player->state != Player_State_Static) {
429
int32 anim = player->animator.animationID;
430
431
if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN) {
432
if (player->collisionMode != CMODE_LWALL && player->collisionMode != CMODE_RWALL
433
&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxSides, player, &Fan->playerHitbox)) {
434
player->position.x += (self->position.x - player->position.x + 0xA00000) >> 4;
435
}
436
}
437
}
438
}
439
440
Fan_State_Started();
441
}
442
443
void Fan_State_Started(void)
444
{
445
RSDK_THIS(Fan);
446
447
if (self->animator.speed < 0x80)
448
self->animator.speed += 4;
449
}
450
451
void Fan_State_Stopped(void)
452
{
453
RSDK_THIS(Fan);
454
455
if (self->animator.speed)
456
self->animator.speed -= 2;
457
}
458
459
void Fan_Activate_Interval(void)
460
{
461
RSDK_THIS(Fan);
462
463
if (self->delay) {
464
if (!--self->delay)
465
self->state = Fan_State_Stopped;
466
}
467
else {
468
if (!((Zone->timer + self->intervalOffset) % self->interval) && self->state == Fan_State_Stopped) {
469
self->active = ACTIVE_NORMAL;
470
Fan_Activate();
471
}
472
}
473
}
474
475
void Fan_Activate_Button(void)
476
{
477
RSDK_THIS(Fan);
478
479
EntityButton *button = self->taggedButton;
480
if ((!button || button->activated) && self->state == Fan_State_Stopped) {
481
self->active = ACTIVE_NORMAL;
482
Fan_Activate();
483
}
484
}
485
486
void Fan_Deactivate_Button(void)
487
{
488
RSDK_THIS(Fan);
489
490
EntityButton *button = self->taggedButton;
491
if ((!button || button->activated) && self->state != Fan_State_Stopped) {
492
self->active = ACTIVE_BOUNDS;
493
self->state = Fan_State_Stopped;
494
}
495
}
496
497
void Fan_Activate_Platform(void)
498
{
499
RSDK_THIS(Fan);
500
501
int32 slot = SceneInfo->entitySlot - 1;
502
EntityPlatform *platform = RSDK_GET_ENTITY(slot, Platform);
503
while (platform->classID == Fan->classID) {
504
--slot;
505
platform = RSDK_GET_ENTITY(slot, Platform);
506
}
507
508
if (platform->classID == Platform->classID) {
509
if (platform->amplitude.y == platform->amplitude.x)
510
self->state = Fan_State_Stopped;
511
else if (self->state == Fan_State_Stopped)
512
Fan_Activate();
513
}
514
}
515
516
void Fan_Activate(void)
517
{
518
RSDK_THIS(Fan);
519
520
if (self->type != FAN_V) {
521
if (self->direction == FLIP_NONE)
522
self->state = Fan_HandlePlayerInteractions_Left;
523
else
524
self->state = Fan_HandlePlayerInteractions_Right;
525
526
self->delay = self->duration;
527
}
528
else {
529
if (self->direction == FLIP_NONE)
530
self->state = Fan_HandlePlayerInteractions_Top;
531
else
532
self->state = Fan_HandlePlayerInteractions_Bottom;
533
534
self->delay = self->duration;
535
}
536
}
537
538
#if GAME_INCLUDE_EDITOR
539
void Fan_EditorDraw(void)
540
{
541
RSDK_THIS(Fan);
542
543
RSDK.SetSpriteAnimation(Fan->aniFrames, self->type, &self->animator, true, 0);
544
545
self->updateRange.x = 0x800000;
546
self->updateRange.y = 0x800000;
547
548
int32 dir = self->direction;
549
if (self->type == FAN_V)
550
self->direction *= FLIP_Y;
551
552
Fan_Draw();
553
554
self->direction = dir;
555
556
if (showGizmos()) {
557
if (self->activation == FAN_ACTIVATE_BUTTON || self->deactivation == FAN_DEACTIVATE_BUTTON) {
558
Fan_SetupTagLink();
559
560
RSDK_DRAWING_OVERLAY(true);
561
if (self->taggedButton) {
562
DrawHelpers_DrawArrow(self->taggedButton->position.x, self->taggedButton->position.y, self->position.x, self->position.y, 0xFFFF00,
563
INK_NONE, 0xFF);
564
}
565
RSDK_DRAWING_OVERLAY(false);
566
}
567
}
568
}
569
570
void Fan_EditorLoad(void)
571
{
572
if (RSDK.CheckSceneFolder("OOZ1") || RSDK.CheckSceneFolder("OOZ2"))
573
Fan->aniFrames = RSDK.LoadSpriteAnimation("OOZ/Fan.bin", SCOPE_STAGE);
574
else if (RSDK.CheckSceneFolder("HCZ"))
575
Fan->aniFrames = RSDK.LoadSpriteAnimation("HCZ/Fan.bin", SCOPE_STAGE);
576
577
RSDK_ACTIVE_VAR(Fan, type);
578
RSDK_ENUM_VAR("Vertical", FAN_V);
579
RSDK_ENUM_VAR("Horiozontal", FAN_H);
580
581
RSDK_ACTIVE_VAR(Fan, direction);
582
RSDK_ENUM_VAR("No Flip", FLIP_NONE);
583
RSDK_ENUM_VAR("Flipped", FLIP_X);
584
585
RSDK_ACTIVE_VAR(Fan, activation);
586
RSDK_ENUM_VAR("None", FAN_ACTIVATE_NONE);
587
RSDK_ENUM_VAR("On Interval", FAN_ACTIVATE_INTERVAL);
588
RSDK_ENUM_VAR("On Platform Moved", FAN_ACTIVATE_PLATFORM);
589
RSDK_ENUM_VAR("On Button Press", FAN_ACTIVATE_BUTTON);
590
591
RSDK_ACTIVE_VAR(Fan, deactivation);
592
RSDK_ENUM_VAR("None", FAN_DEACTIVATE_NONE);
593
RSDK_ENUM_VAR("On Button Press", FAN_DEACTIVATE_BUTTON);
594
}
595
#endif
596
597
void Fan_Serialize(void)
598
{
599
RSDK_EDITABLE_VAR(Fan, VAR_UINT8, type);
600
RSDK_EDITABLE_VAR(Fan, VAR_UINT8, direction);
601
RSDK_EDITABLE_VAR(Fan, VAR_UINT8, activation);
602
RSDK_EDITABLE_VAR(Fan, VAR_UINT8, deactivation);
603
RSDK_EDITABLE_VAR(Fan, VAR_ENUM, size);
604
RSDK_EDITABLE_VAR(Fan, VAR_UINT16, interval);
605
RSDK_EDITABLE_VAR(Fan, VAR_UINT16, intervalOffset);
606
RSDK_EDITABLE_VAR(Fan, VAR_UINT16, duration);
607
RSDK_EDITABLE_VAR(Fan, VAR_ENUM, buttonTag);
608
}
609
610