Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/HCZ/LaundroMobile.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: LaundroMobile Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectLaundroMobile *LaundroMobile;
11
12
void LaundroMobile_Update(void)
13
{
14
RSDK_THIS(LaundroMobile);
15
16
StateMachine_Run(self->state);
17
}
18
19
void LaundroMobile_LateUpdate(void) {}
20
21
void LaundroMobile_StaticUpdate(void)
22
{
23
if (LaundroMobile->loopSfxTimer) {
24
if (!LaundroMobile->playingLoopSfx) {
25
if (LaundroMobile->health <= 8)
26
RSDK.PlaySfx(LaundroMobile->sfxWash, true, 0xFF);
27
else
28
RSDK.PlaySfx(LaundroMobile->sfxFan, 47208, 0xFF);
29
30
LaundroMobile->playingLoopSfx = true;
31
}
32
}
33
else {
34
if (LaundroMobile->playingLoopSfx) {
35
RSDK.StopSfx(LaundroMobile->sfxFan);
36
RSDK.StopSfx(LaundroMobile->sfxWash);
37
38
LaundroMobile->playingLoopSfx = false;
39
}
40
}
41
42
LaundroMobile->loopSfxTimer = false;
43
44
if (SceneInfo->state == ENGINESTATE_REGULAR && LaundroMobile->useStageWrap)
45
LaundroMobile_HandleStageWrap();
46
}
47
48
void LaundroMobile_Draw(void)
49
{
50
RSDK_THIS(LaundroMobile);
51
52
StateMachine_Run(self->stateDraw);
53
}
54
55
void LaundroMobile_Create(void *data)
56
{
57
RSDK_THIS(LaundroMobile);
58
59
if (globals->gameMode == MODE_TIMEATTACK) {
60
destroyEntity(self);
61
}
62
else {
63
self->drawFX = FX_FLIP;
64
if (!SceneInfo->inEditor) {
65
if (!self->type)
66
self->type = VOID_TO_INT(data);
67
68
switch (self->type) {
69
case LAUNDROMOBILE_BOSS:
70
self->active = ACTIVE_BOUNDS;
71
self->visible = false;
72
self->drawGroup = Zone->objectDrawGroup[0] + 1;
73
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 0, &self->mainAnimator, true, 0);
74
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 3, &self->propellerAnimator, true, 0);
75
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 0, &self->eggmanAnimator, true, 0);
76
self->startY = self->position.y;
77
self->updateRange.x = 0x800000;
78
self->updateRange.y = 0x800000;
79
80
LaundroMobile->health = 5 + 8; // phase1 health + phase 2 health
81
LaundroMobile->invincibilityTimer = 0;
82
LaundroMobile->nextLoopPoint = 0;
83
LaundroMobile->attackDir = FLIP_NONE;
84
LaundroMobile->currentVelocity = 0x38000;
85
LaundroMobile->rocketActive = false;
86
LaundroMobile->animSpeed = 0;
87
LaundroMobile->travelledPaths = 0;
88
LaundroMobile->useStageWrap = false;
89
LaundroMobile->loopSfxTimer = 0;
90
LaundroMobile->playingLoopSfx = false;
91
RSDK.StopSfx(LaundroMobile->sfxFan);
92
RSDK.StopSfx(LaundroMobile->sfxWash);
93
LaundroMobile->rocketAngles[0] = 0;
94
LaundroMobile->rocketAngles[1] = 0x8000;
95
LaundroMobile->rocketAngles[2] = 0;
96
LaundroMobile->rocketAngles[3] = 0x8000;
97
LaundroMobile->rocketSpeeds[0] = 0;
98
LaundroMobile->rocketSpeeds[1] = 0;
99
LaundroMobile->isUnderwater = false;
100
101
self->tileCollisions = TILECOLLISION_DOWN;
102
self->collisionLayers = Zone->collisionLayers;
103
self->collisionPlane = 0;
104
self->state = LaundroMobile_StateBoss_AwaitPlayer_Phase1;
105
self->stateDraw = LaundroMobile_Draw_Boss;
106
break;
107
108
case LAUNDROMOBILE_BOMB:
109
self->active = ACTIVE_BOUNDS;
110
self->visible = true;
111
self->drawGroup = Zone->objectDrawGroup[0];
112
self->originPos = self->position;
113
114
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 7, &self->mainAnimator, true, 0);
115
116
self->updateRange.x = 0x1800000;
117
self->updateRange.y = 0x1800000;
118
self->state = LaundroMobile_StateBomb_Spawner;
119
self->stateDraw = LaundroMobile_Draw_Simple;
120
break;
121
122
case LAUNDROMOBILE_LAUNDRY:
123
self->active = ACTIVE_XBOUNDS;
124
self->visible = true;
125
self->drawGroup = Zone->objectDrawGroup[0] + 1;
126
127
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 0, &self->mainAnimator, true, 0);
128
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 1, &self->propellerAnimator, true, 0);
129
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 2, &self->eggmanAnimator, true, 0);
130
131
self->updateRange.x = 0x2000000;
132
self->updateRange.y = 0x800000;
133
self->state = LaundroMobile_State_Laundry;
134
self->stateDraw = LaundroMobile_Draw_Laundry;
135
break;
136
137
case LAUNDROMOBILE_LOOPPOINT: LaundroMobile->loopPoints[LaundroMobile->nextLoopPoint++] = (Entity *)self; break;
138
139
case LAUNDROMOBILE_BLOCK:
140
self->active = ACTIVE_BOUNDS;
141
self->visible = true;
142
self->drawGroup = Zone->objectDrawGroup[0];
143
self->originPos = self->position;
144
145
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &self->mainAnimator, true, RSDK.Rand(0, 3));
146
147
self->updateRange.x = 0x1800000;
148
self->updateRange.y = 0x1800000;
149
self->velocity.x = LaundroMobile->currentVelocity - 0x18000;
150
self->state = LaundroMobile_StateBlock_Spawner;
151
self->stateDraw = LaundroMobile_Draw_Simple;
152
break;
153
154
case LAUNDROMOBILE_SPIKES:
155
self->active = ACTIVE_BOUNDS;
156
self->visible = true;
157
self->drawGroup = Zone->objectDrawGroup[0];
158
self->originPos = self->position;
159
160
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &self->mainAnimator, true, RSDK.Rand(0, 3) + 3);
161
162
self->updateRange.x = 0x1800000;
163
self->updateRange.y = 0x1800000;
164
self->velocity.x = LaundroMobile->currentVelocity - 0x18000;
165
self->state = LaundroMobile_StateBlock_Spawner;
166
self->stateDraw = LaundroMobile_Draw_Simple;
167
break;
168
169
case LAUNDROMOBILE_DELAYEDSPLASH:
170
self->active = ACTIVE_NORMAL;
171
self->timer = 8;
172
self->state = LaundroMobile_State_DelayedSplash;
173
break;
174
175
default: break;
176
}
177
}
178
}
179
}
180
181
void LaundroMobile_StageLoad(void)
182
{
183
LaundroMobile->aniFrames = RSDK.LoadSpriteAnimation("HCZ/LaundroMobile.bin", SCOPE_STAGE);
184
LaundroMobile->eggmanFrames = RSDK.LoadSpriteAnimation("Eggman/EggmanHCZ2.bin", SCOPE_STAGE);
185
186
LaundroMobile->hitboxBoss.left = -32;
187
LaundroMobile->hitboxBoss.top = -32;
188
LaundroMobile->hitboxBoss.right = 32;
189
LaundroMobile->hitboxBoss.bottom = 32;
190
191
LaundroMobile->hitboxMissile.left = -24;
192
LaundroMobile->hitboxMissile.top = -24;
193
LaundroMobile->hitboxMissile.right = 24;
194
LaundroMobile->hitboxMissile.bottom = 24;
195
196
LaundroMobile->hitboxBox.left = -16;
197
LaundroMobile->hitboxBox.top = -16;
198
LaundroMobile->hitboxBox.right = 16;
199
LaundroMobile->hitboxBox.bottom = 16;
200
201
LaundroMobile->hitboxBomb.left = -32;
202
LaundroMobile->hitboxBomb.top = -16;
203
LaundroMobile->hitboxBomb.right = 32;
204
LaundroMobile->hitboxBomb.bottom = 16;
205
206
LaundroMobile->innerBox.left = -39;
207
LaundroMobile->innerBox.top = -40;
208
LaundroMobile->innerBox.right = 39;
209
LaundroMobile->innerBox.bottom = 40;
210
211
LaundroMobile->outerBox.left = -40;
212
LaundroMobile->outerBox.top = -40;
213
LaundroMobile->outerBox.right = 40;
214
LaundroMobile->outerBox.bottom = 40;
215
216
LaundroMobile->active = ACTIVE_ALWAYS;
217
LaundroMobile->playingLoopSfx = false;
218
LaundroMobile->loopSfxTimer = 0;
219
220
LaundroMobile->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
221
LaundroMobile->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
222
LaundroMobile->sfxButton2 = RSDK.GetSfx("Stage/Button2.wav");
223
LaundroMobile->sfxFan = RSDK.GetSfx("HCZ/BigFan.wav");
224
LaundroMobile->sfxRoll = RSDK.GetSfx("Global/Roll.wav");
225
LaundroMobile->sfxWash = RSDK.GetSfx("HCZ/Wash.wav");
226
LaundroMobile->sfxHullClose = RSDK.GetSfx("Stage/HullClose.wav");
227
LaundroMobile->sfxPush = RSDK.GetSfx("Stage/Push.wav");
228
LaundroMobile->sfxFireball = RSDK.GetSfx("Stage/Fireball.wav");
229
LaundroMobile->sfxButton = RSDK.GetSfx("Stage/Button.wav");
230
LaundroMobile->sfxLedgeBreak = RSDK.GetSfx("Stage/LedgeBreak3.wav");
231
LaundroMobile->sfxPimPom = RSDK.GetSfx("Stage/PimPom.wav");
232
#if MANIA_USE_PLUS
233
LaundroMobile->sfxRumble = RSDK.GetSfx("Stage/Rumble.wav");
234
LaundroMobile->sfxImpact = RSDK.GetSfx("Stage/Impact6.wav");
235
#endif
236
}
237
238
void LaundroMobile_CheckPlayerCollisions(void)
239
{
240
RSDK_THIS(LaundroMobile);
241
242
if (LaundroMobile->invincibilityTimer) {
243
LaundroMobile->invincibilityTimer--;
244
}
245
else {
246
int32 storeX = self->position.x;
247
int32 storeY = self->position.y;
248
foreach_active(Player, player)
249
{
250
for (int32 i = 0; i < 4 && LaundroMobile->rocketActive; ++i) {
251
self->position.x = LaundroMobile->rocketPositions[i].x;
252
self->position.y = LaundroMobile->rocketPositions[i].y;
253
254
if (Player_CheckCollisionTouch(player, self, &LaundroMobile->hitboxMissile)) {
255
#if MANIA_USE_PLUS
256
if (!Player_CheckMightyUnspin(player, 0x400, 2, &player->uncurlTimer))
257
#endif
258
Player_Hurt(player, self);
259
break;
260
}
261
}
262
self->position.x = storeX;
263
self->position.y = storeY;
264
265
int32 velX = player->velocity.x;
266
int32 velY = player->velocity.y;
267
if (Player_CheckBadnikTouch(player, self, &LaundroMobile->hitboxBoss) && Player_CheckBossHit(player, self)) {
268
if (LaundroMobile->health)
269
LaundroMobile->health--;
270
271
if (LaundroMobile->health) {
272
if (LaundroMobile->health == 8) {
273
RSDK.StopSfx(LaundroMobile->sfxFan);
274
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 4, &self->propellerAnimator, true, 0);
275
self->timer = 60;
276
self->state = LaundroMobile_StateBoss_Destroyed_Phase1;
277
}
278
else {
279
if (LaundroMobile->health < 8) {
280
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 3, &self->eggmanAnimator, true, 0);
281
}
282
else {
283
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 2, &self->eggmanAnimator, true, 0);
284
player->groundVel = velX;
285
player->velocity.x = velX;
286
player->velocity.y = velY;
287
}
288
RSDK.PlaySfx(LaundroMobile->sfxHit, false, 255);
289
LaundroMobile->invincibilityTimer = 30;
290
}
291
}
292
else {
293
SceneInfo->timeEnabled = false;
294
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
295
RSDK.PlaySfx(LaundroMobile->sfxExplosion, false, 255);
296
LaundroMobile->invincibilityTimer = 60;
297
298
EntityWhirlpool *whirlpool = self->whirlpool;
299
self->state = LaundroMobile_StateBoss_Destroyed_Phase2;
300
if (whirlpool) {
301
if (whirlpool->classID == Whirlpool->classID)
302
whirlpool->activePlayers = -3;
303
self->whirlpool = NULL;
304
}
305
}
306
foreach_break;
307
}
308
}
309
}
310
}
311
312
void LaundroMobile_Explode(void)
313
{
314
RSDK_THIS(LaundroMobile);
315
316
int32 interval = LaundroMobile->health > 8 ? 7 : 3;
317
318
if (!(Zone->timer % interval)) {
319
RSDK.PlaySfx(LaundroMobile->sfxExplosion, false, 255);
320
if (Zone->timer & 4) {
321
int32 x = self->position.x + (RSDK.Rand(-19, 20) << 16);
322
int32 y = self->position.y + (RSDK.Rand(-24, 25) << 16);
323
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);
324
325
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
326
if (LaundroMobile->health > 8)
327
explosion->velocity.x = 0x24000;
328
}
329
}
330
}
331
332
void LaundroMobile_HandleStageWrap(void)
333
{
334
EntityLaundroMobile *boss = LaundroMobile->laundroMobile;
335
336
if (!(Zone->timer & 3)) {
337
EntityCurrent *current = CREATE_ENTITY(Current, INT_TO_VOID(CURRENT_CHILD_BUBBLE), ScreenInfo->position.x << 16,
338
(8 * RSDK.Rand(0, ScreenInfo->size.y >> 3) + ScreenInfo->position.y) << 16);
339
340
current->drawGroup = Zone->playerDrawGroup[0];
341
current->strength = 6;
342
current->type = CURRENT_C_RIGHT;
343
current->alpha = 0xF0;
344
current->size.x = (ScreenInfo->position.x + ScreenInfo->size.x + 0x1000) << 16;
345
}
346
347
foreach_active(Player, player)
348
{
349
if (player->state != Player_State_Static) {
350
if (player->position.x < boss->position.x)
351
player->position.x = boss->position.x;
352
353
if (Player_CheckValidState(player)) {
354
if ((abs(player->velocity.y) <= 0x18000 || player->onGround) && player->position.x < 0x6D800000) {
355
player->collisionMode = CMODE_FLOOR;
356
player->onGround = false;
357
player->nextGroundState = StateMachine_None;
358
player->nextAirState = StateMachine_None;
359
player->state = Current_PlayerState_Right;
360
if (player->animator.animationID != ANI_CLING && player->animator.animationID != ANI_SHAFT_SWING) {
361
if (player->position.x >= boss->position.x + 0xC00000) {
362
player->velocity.x = LaundroMobile->currentVelocity;
363
player->groundVel = player->velocity.x;
364
}
365
else {
366
player->velocity.x = LaundroMobile->currentVelocity + ((boss->position.x - player->position.x + 0xC00000) >> 6);
367
player->groundVel = player->velocity.x;
368
}
369
RSDK.SetSpriteAnimation(player->aniFrames, ANI_FAN, &player->animator, false, 0);
370
}
371
372
player->velocity.y = 0;
373
if (player->up)
374
player->velocity.y = -0x18000;
375
else if (player->down)
376
player->velocity.y = 0x18000;
377
}
378
}
379
}
380
}
381
382
if (LaundroMobile->currentVelocity < 0x80000)
383
LaundroMobile->currentVelocity += 0x400;
384
385
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
386
387
if (Player_CheckValidState(player1)) {
388
if (LaundroMobile->nextLoopPoint == 5 && boss->timer <= 0) {
389
if (LaundroMobile->currentVelocity < 0xC0000)
390
LaundroMobile->currentVelocity += 0x1000;
391
}
392
else {
393
Entity *loopPoint = LaundroMobile->loopPoints[LaundroMobile->nextLoopPoint];
394
if (player1->position.x >= loopPoint->position.x + 0xE000000) {
395
int32 startOffsetX = player1->position.x - loopPoint->position.x - 0xE000000;
396
int32 startOffsetY = player1->position.y - loopPoint->position.y;
397
if (LaundroMobile->health <= 8) {
398
LaundroMobile->nextLoopPoint = 5;
399
boss->state = LaundroMobile_StateBoss_WaitForLastStageWrap;
400
}
401
else {
402
if (LaundroMobile->travelledPaths == 0x1E) {
403
LaundroMobile->travelledPaths = 0;
404
}
405
406
int32 prevLoopPoint = LaundroMobile->nextLoopPoint;
407
while (LaundroMobile->nextLoopPoint == prevLoopPoint) {
408
// LoopPoints 1-4 are valid to use as attacks, 5 is reserved for the final stage wrap
409
LaundroMobile->nextLoopPoint = RSDK.Rand(1, 5);
410
411
while ((1 << LaundroMobile->nextLoopPoint) & LaundroMobile->travelledPaths) {
412
LaundroMobile->nextLoopPoint = RSDK.Rand(1, 5);
413
}
414
}
415
416
LaundroMobile->travelledPaths |= 1 << LaundroMobile->nextLoopPoint;
417
}
418
loopPoint = LaundroMobile->loopPoints[LaundroMobile->nextLoopPoint];
419
420
int32 offsetX = player1->position.x - startOffsetX - loopPoint->position.x;
421
int32 offsetY = player1->position.y - loopPoint->position.y - startOffsetY;
422
player1->position.x -= offsetX;
423
player1->position.y -= offsetY;
424
425
boss->position.y -= offsetY;
426
boss->originPos.y -= offsetY;
427
boss->unusedPos.y -= offsetY;
428
429
boss->position.x -= offsetX;
430
boss->originPos.x -= offsetX;
431
boss->unusedPos.x -= offsetX;
432
433
ScreenInfo->position.x -= offsetX >> 16;
434
ScreenInfo->position.y -= offsetY >> 16;
435
436
EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);
437
camera->position.x -= offsetX;
438
camera->position.y -= offsetY;
439
camera->center.x -= offsetX >> 16;
440
camera->center.y -= offsetY >> 16;
441
if (Player->playerCount >= 2) {
442
EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);
443
player2->position.x -= offsetX;
444
player2->position.y -= offsetY;
445
}
446
447
for (int32 i = 0; i < 0x1000; ++i) {
448
Entity *entPtr = RSDK_GET_ENTITY_GEN(i);
449
450
if (entPtr->classID == LaundroMobile->classID) {
451
EntityLaundroMobile *laundroMobile = (EntityLaundroMobile *)entPtr;
452
if ((laundroMobile->type == LAUNDROMOBILE_BOMB && laundroMobile->state != LaundroMobile_StateBomb_Spawner)
453
|| laundroMobile->state == LaundroMobile_StateBlock_Block) {
454
laundroMobile->position.x -= offsetX;
455
laundroMobile->position.y -= offsetY;
456
}
457
}
458
else if (entPtr->classID == Ring->classID) {
459
EntityRing *ring = (EntityRing *)entPtr;
460
if (ring->state != Ring_State_Normal) {
461
entPtr->position.x -= offsetX;
462
entPtr->position.y -= offsetY;
463
}
464
}
465
else if (entPtr->classID == Debris->classID) {
466
entPtr->position.x -= offsetX;
467
entPtr->position.y -= offsetY;
468
}
469
else if (entPtr->classID == Water->classID) {
470
EntityWater *water = (EntityWater *)entPtr;
471
if (water->type == WATER_BUBBLE) {
472
water->position.x -= offsetX;
473
water->position.y -= offsetY;
474
water->bubbleX = water->position.x;
475
}
476
}
477
else if (entPtr->classID == ImageTrail->classID) {
478
EntityImageTrail *trail = (EntityImageTrail *)entPtr;
479
trail->position.x -= offsetX;
480
trail->position.y -= offsetY;
481
trail->currentPos.x -= offsetX;
482
trail->currentPos.y -= offsetY;
483
for (int32 t = 0; t < IMAGETRAIL_TRACK_COUNT; ++t) {
484
trail->statePos[t].x -= offsetX;
485
trail->statePos[t].y -= offsetY;
486
}
487
}
488
else if (entPtr->classID == Current->classID) {
489
EntityCurrent *current = (EntityCurrent *)entPtr;
490
if (current->state == Current_State_Child) {
491
current->position.x -= offsetX;
492
current->position.y -= offsetY;
493
current->size.x -= offsetX;
494
}
495
}
496
}
497
}
498
}
499
}
500
}
501
502
void LaundroMobile_HandleRocketMovement(void)
503
{
504
RSDK_THIS(LaundroMobile);
505
506
for (int32 r = 0; r < 2; ++r) {
507
LaundroMobile->rocketPositions[r].x = self->position.x + 0x1400 * RSDK.Cos256(LaundroMobile->rocketAngles[r] >> 8);
508
LaundroMobile->rocketPositions[r].y = self->position.y + 0x1400 * RSDK.Cos256(LaundroMobile->rocketAngles[r] >> 8);
509
LaundroMobile->rocketAngles[r] += LaundroMobile->rocketSpeeds[0];
510
}
511
512
for (int32 r = 2; r < 4; ++r) {
513
LaundroMobile->rocketPositions[r].x = self->position.x - 0x1400 * RSDK.Cos256(LaundroMobile->rocketAngles[r] >> 8);
514
LaundroMobile->rocketPositions[r].y = self->position.y + 0x1400 * RSDK.Cos256(LaundroMobile->rocketAngles[r] >> 8);
515
LaundroMobile->rocketAngles[r] += LaundroMobile->rocketSpeeds[1];
516
}
517
}
518
519
void LaundroMobile_HandleEggmanAnimations(void)
520
{
521
RSDK_THIS(LaundroMobile);
522
523
RSDK.ProcessAnimation(&self->eggmanAnimator);
524
525
bool32 playerHurt = false;
526
foreach_active(Player, player)
527
{
528
if (player->state == Player_State_Hurt || player->state == Player_State_Death || player->state == Player_State_Drown)
529
playerHurt = true;
530
}
531
532
switch (self->eggmanAnimator.animationID) {
533
case 0:
534
if (playerHurt)
535
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 4, &self->eggmanAnimator, true, 0);
536
break;
537
538
case 1:
539
if (playerHurt)
540
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 5, &self->eggmanAnimator, true, 0);
541
break;
542
543
case 2:
544
if (!LaundroMobile->invincibilityTimer)
545
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 0, &self->eggmanAnimator, true, 0);
546
break;
547
548
case 3:
549
if (!LaundroMobile->invincibilityTimer)
550
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 1, &self->eggmanAnimator, true, 0);
551
break;
552
553
case 4:
554
if (self->eggmanAnimator.frameID >= self->eggmanAnimator.frameCount - 1) {
555
if (playerHurt)
556
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 4, &self->eggmanAnimator, true, 6);
557
else
558
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 0, &self->eggmanAnimator, true, 0);
559
}
560
break;
561
562
case 5:
563
if (self->eggmanAnimator.frameID >= self->eggmanAnimator.frameCount - 1) {
564
if (playerHurt)
565
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 5, &self->eggmanAnimator, true, 6);
566
else
567
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 1, &self->eggmanAnimator, true, 0);
568
}
569
break;
570
571
default: break;
572
}
573
}
574
575
void LaundroMobile_HandleTileCollisions(void)
576
{
577
RSDK_THIS(LaundroMobile);
578
579
uint8 collisionLevel = 0xFF;
580
while (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0x200000, 0x200000, false) && collisionLevel >= 0) {
581
self->position.y -= 0x10000;
582
collisionLevel = 0;
583
}
584
585
while (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, 0x200000, -0x200000, false) && collisionLevel >= 1) {
586
self->position.y += 0x10000;
587
collisionLevel = 1;
588
}
589
590
while (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, -0x200000, 0x200000, false) && collisionLevel >= 2) {
591
self->position.y -= 0x10000;
592
collisionLevel = 2;
593
}
594
595
while (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, -0x200000, -0x200000, false) && collisionLevel >= 3) {
596
self->position.y += 0x10000;
597
collisionLevel = 3;
598
}
599
}
600
601
void LaundroMobile_StateBoss_AwaitPlayer_Phase1(void)
602
{
603
RSDK_THIS(LaundroMobile);
604
605
if (++self->timer >= 2) {
606
self->position.y += 0x500000;
607
self->originPos.y = self->position.y;
608
self->position.x -= 0x100000;
609
self->timer = 0;
610
self->active = ACTIVE_NORMAL;
611
RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
612
613
if (self->position.x >= (24704 << 16)) {
614
// did this use to only have 12 health at some point during dev?
615
if (LaundroMobile->health == (MANIA_USE_PLUS ? (5 + 8) : (4 + 8))) {
616
LaundroMobile->health = 8;
617
Music_TransitionTrack(TRACK_EGGMAN1, 0.0125);
618
self->visible = true;
619
self->state = LaundroMobile_StateBoss_AwaitPlayer_Phase2;
620
}
621
else {
622
destroyEntity(self);
623
}
624
}
625
else {
626
LaundroMobile->laundroMobile = self;
627
self->state = LaundroMobile_StateBoss_SetupArena_Phase1;
628
RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, BreakBar)->releaseTimer = 0;
629
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
630
player1->jumpPress = false;
631
}
632
}
633
}
634
635
void LaundroMobile_StateBoss_SetupArena_Phase1(void)
636
{
637
RSDK_THIS(LaundroMobile);
638
639
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
640
641
RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, BreakBar)->releaseTimer = 0;
642
player1->jumpPress = false;
643
644
if (player1->position.x > self->position.x + 0x1700000) {
645
Music_TransitionTrack(TRACK_EGGMAN1, 0.0125);
646
RSDK.GetTileLayer(4)->drawGroup[0] = DRAWGROUP_COUNT;
647
LaundroMobile->nextLoopPoint = 0;
648
Water->waterLevel = 0;
649
Water->targetWaterLevel = 0;
650
self->timer = 120;
651
self->propellerAnimator.speed = 0;
652
self->visible = true;
653
self->state = LaundroMobile_StateBoss_EnterEggman_Phase1;
654
}
655
}
656
657
void LaundroMobile_StateBoss_EnterEggman_Phase1(void)
658
{
659
RSDK_THIS(LaundroMobile);
660
661
RSDK.ProcessAnimation(&self->propellerAnimator);
662
663
self->position.x += 0x18000;
664
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 2, 10);
665
666
RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, BreakBar)->releaseTimer = 0;
667
RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->jumpPress = false;
668
669
if (!--self->timer) {
670
RSDK.PlaySfx(LaundroMobile->sfxButton2, false, 255);
671
self->state = LaundroMobile_StateBoss_StartupPropellers;
672
LaundroMobile->useStageWrap = true;
673
}
674
675
LaundroMobile_CheckPlayerCollisions();
676
}
677
678
void LaundroMobile_StateBoss_StartupPropellers(void)
679
{
680
RSDK_THIS(LaundroMobile);
681
682
RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, BreakBar)->releaseTimer = 0;
683
RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->jumpPress = false;
684
685
RSDK.ProcessAnimation(&self->propellerAnimator);
686
687
if (self->propellerAnimator.speed >= 0x200) {
688
++LaundroMobile->loopSfxTimer;
689
RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, BreakBar)->releaseTimer = 240;
690
self->state = LaundroMobile_StateBoss_HandlePhase1;
691
}
692
else {
693
self->propellerAnimator.speed += 4;
694
if (self->propellerAnimator.speed >= 0x20)
695
++LaundroMobile->loopSfxTimer;
696
}
697
698
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 2, 10);
699
LaundroMobile_CheckPlayerCollisions();
700
}
701
702
void LaundroMobile_StateBoss_HandlePhase1(void)
703
{
704
RSDK_THIS(LaundroMobile);
705
706
LaundroMobile_HandleEggmanAnimations();
707
708
RSDK.ProcessAnimation(&self->propellerAnimator);
709
710
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
711
712
self->position.y = self->originPos.y;
713
714
if (abs(self->originPos.y - player1->position.y) >= 0x100000) {
715
if (player1->position.y >= self->originPos.y) {
716
if (self->velocity.y < 0x18000)
717
self->velocity.y += 0x1000;
718
}
719
else {
720
if (self->velocity.y > -0x18000)
721
self->velocity.y -= 0x1000;
722
}
723
}
724
else if (self->velocity.y) {
725
if (self->velocity.y <= 0) {
726
self->velocity.y += 0x1000;
727
728
if (self->velocity.y > 0)
729
self->velocity.y = 0;
730
}
731
else {
732
self->velocity.y -= 0x1000;
733
734
if (self->velocity.y < 0)
735
self->velocity.y = 0;
736
}
737
}
738
739
self->position.y += self->velocity.y;
740
LaundroMobile_HandleTileCollisions();
741
742
if (self->position.y == self->originPos.y) {
743
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 2, 10);
744
}
745
else {
746
self->originPos.y = self->position.y;
747
}
748
749
self->position.x += LaundroMobile->currentVelocity;
750
751
++LaundroMobile->loopSfxTimer;
752
LaundroMobile_CheckPlayerCollisions();
753
}
754
755
void LaundroMobile_StateBoss_Destroyed_Phase1(void)
756
{
757
RSDK_THIS(LaundroMobile);
758
759
LaundroMobile_Explode();
760
761
RSDK.ProcessAnimation(&self->propellerAnimator);
762
763
self->position.y = self->originPos.y;
764
LaundroMobile_HandleTileCollisions();
765
766
self->position.x += LaundroMobile->currentVelocity - 0x8000;
767
768
if (self->position.y == self->originPos.y)
769
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 2, 10);
770
else
771
self->originPos.y = self->position.y;
772
773
LaundroMobile_CheckPlayerCollisions();
774
if (--self->timer <= 0) {
775
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
776
777
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 4, &debris->animator, true, 0);
778
debris->velocity.x = 0x50000;
779
debris->velocity.y = -0x28000;
780
debris->gravityStrength = 0x3800;
781
debris->drawGroup = Zone->objectDrawGroup[1];
782
debris->updateRange.x = 0x400000;
783
debris->updateRange.y = 0x400000;
784
RSDK.SetSpriteAnimation(-1, 0, &self->propellerAnimator, true, 0);
785
self->state = LaundroMobile_StateBoss_Explode_Phase1;
786
}
787
}
788
789
void LaundroMobile_StateBoss_Explode_Phase1(void)
790
{
791
RSDK_THIS(LaundroMobile);
792
793
LaundroMobile_Explode();
794
795
RSDK.ProcessAnimation(&self->propellerAnimator);
796
797
self->position.y = self->originPos.y;
798
LaundroMobile_HandleTileCollisions();
799
800
self->position.x += LaundroMobile->currentVelocity - 0x10000;
801
802
if (self->position.y == self->originPos.y)
803
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 2, 10);
804
else
805
self->originPos.y = self->position.y;
806
807
LaundroMobile_CheckPlayerCollisions();
808
}
809
810
void LaundroMobile_StateBoss_WaitForLastStageWrap(void)
811
{
812
RSDK_THIS(LaundroMobile);
813
814
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > (28032 << 16)) {
815
LaundroMobile->useStageWrap = false;
816
foreach_active(Player, player) { player->state = Player_State_Air; }
817
self->state = LaundroMobile_StateBoss_AwaitPlayer_Phase2;
818
}
819
820
LaundroMobile_CheckPlayerCollisions();
821
}
822
823
void LaundroMobile_StateBoss_AwaitPlayer_Phase2(void)
824
{
825
RSDK_THIS(LaundroMobile);
826
827
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
828
829
if (player1->onGround && !player1->angle) { // wait till we're on flat ground
830
self->timer = 60;
831
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 1, &self->eggmanAnimator, true, 0);
832
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 5, &self->propellerAnimator, true, 0);
833
self->state = LaundroMobile_StateBoss_SetupArena_Phase2;
834
}
835
836
LaundroMobile_CheckPlayerCollisions();
837
}
838
839
void LaundroMobile_StateBoss_SetupArena_Phase2(void)
840
{
841
RSDK_THIS(LaundroMobile);
842
843
if (--self->timer <= 0) {
844
self->position.x = 28160 << 16;
845
self->timer = 104;
846
self->position.y = (ScreenInfo->position.y - 64) << 16;
847
848
LaundroMobile_HandleRocketMovement();
849
850
self->stateDraw = LaundroMobile_Draw_Boss_Destroyed;
851
self->state = LaundroMobile_StateBoss_EnterEggman_Phase2;
852
}
853
854
LaundroMobile_CheckPlayerCollisions();
855
}
856
857
void LaundroMobile_StateBoss_EnterEggman_Phase2(void)
858
{
859
RSDK_THIS(LaundroMobile);
860
861
self->position.y += 0x10000;
862
863
LaundroMobile_HandleRocketMovement();
864
865
if (--self->timer <= 0) {
866
self->timer = 32;
867
self->state = LaundroMobile_StateBoss_StartupRockets;
868
}
869
870
LaundroMobile_CheckPlayerCollisions();
871
LaundroMobile_HandleEggmanAnimations();
872
}
873
874
void LaundroMobile_StateBoss_StartupRockets(void)
875
{
876
RSDK_THIS(LaundroMobile);
877
878
LaundroMobile_HandleRocketMovement();
879
880
if (LaundroMobile->rocketSpeeds[0] < 0x200)
881
LaundroMobile->rocketSpeeds[0] += 8;
882
883
if (self->timer <= 0) {
884
if (LaundroMobile->rocketSpeeds[1] < 0x200)
885
LaundroMobile->rocketSpeeds[1] += 8;
886
}
887
else {
888
self->timer--;
889
}
890
891
if (LaundroMobile->rocketSpeeds[0] + LaundroMobile->rocketSpeeds[1] == 0x400) {
892
self->timer = 96;
893
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 6, &self->flameAnimator, true, 0);
894
LaundroMobile->rocketActive = true;
895
RSDK.PlaySfx(LaundroMobile->sfxPush, false, 255);
896
self->state = LaundroMobile_StateBoss_SpeedUpRockets;
897
}
898
899
LaundroMobile_CheckPlayerCollisions();
900
LaundroMobile_HandleEggmanAnimations();
901
}
902
903
void LaundroMobile_StateBoss_SpeedUpRockets(void)
904
{
905
RSDK_THIS(LaundroMobile);
906
907
LaundroMobile_HandleRocketMovement();
908
909
if (LaundroMobile->rocketSpeeds[0] < 0x400) {
910
LaundroMobile->rocketSpeeds[1] += 8;
911
LaundroMobile->rocketSpeeds[0] = LaundroMobile->rocketSpeeds[1];
912
}
913
914
if (--self->timer <= 0) {
915
self->timer = 60;
916
RSDK.PlaySfx(LaundroMobile->sfxFireball, false, 255);
917
self->state = LaundroMobile_StateBoss_RiseUpToAttack;
918
}
919
920
LaundroMobile_CheckPlayerCollisions();
921
LaundroMobile_HandleEggmanAnimations();
922
}
923
924
void LaundroMobile_StateBoss_RiseUpToAttack(void)
925
{
926
RSDK_THIS(LaundroMobile);
927
928
self->position.y += self->velocity.y;
929
self->velocity.y -= 0x3800;
930
931
LaundroMobile_HandleRocketMovement();
932
933
if (--self->timer <= 0) {
934
self->position.x += LaundroMobile->attackDir ? 0x580000 : -0x580000;
935
self->velocity.y = 0x50000;
936
RSDK.PlaySfx(LaundroMobile->sfxRoll, false, 255);
937
LaundroMobile->attackCount = LaundroMobile->attackCounts[RSDK.Rand(0, 8)];
938
self->state = LaundroMobile_StateBoss_Attacking;
939
}
940
941
LaundroMobile_CheckPlayerCollisions();
942
LaundroMobile_HandleEggmanAnimations();
943
}
944
945
void LaundroMobile_StateBoss_Attacking(void)
946
{
947
RSDK_THIS(LaundroMobile);
948
949
RSDK.ProcessObjectMovement(self, &LaundroMobile->outerBox, &LaundroMobile->innerBox);
950
951
if (self->velocity.y < 0 && self->position.y < (ScreenInfo->position.y - 256) << 16) {
952
self->onGround = false;
953
self->velocity.y = 0x50000;
954
LaundroMobile->attackDir ^= FLIP_X;
955
RSDK.PlaySfx(LaundroMobile->sfxRoll, false, 255);
956
}
957
958
if (self->position.y <= Water->waterLevel) {
959
if (LaundroMobile->isUnderwater) {
960
LaundroMobile->isUnderwater = false;
961
962
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);
963
CREATE_ENTITY(LaundroMobile, INT_TO_VOID(LAUNDROMOBILE_DELAYEDSPLASH), self->position.x - 0x100000, Water->waterLevel);
964
CREATE_ENTITY(LaundroMobile, INT_TO_VOID(LAUNDROMOBILE_DELAYEDSPLASH), self->position.x + 0x100000, Water->waterLevel);
965
966
RSDK.PlaySfx(Water->sfxSplash, false, 255);
967
if (--LaundroMobile->attackCount <= 0) {
968
self->onGround = false;
969
self->state = LaundroMobile_StateBoss_ReturnToLaundry;
970
self->velocity.x = LaundroMobile->attackDir == FLIP_NONE ? -0x18000 : 0x18000;
971
}
972
}
973
}
974
else if (!LaundroMobile->isUnderwater) {
975
LaundroMobile->isUnderwater = true;
976
977
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);
978
CREATE_ENTITY(LaundroMobile, INT_TO_VOID(LAUNDROMOBILE_DELAYEDSPLASH), self->position.x - 0x100000, Water->waterLevel);
979
CREATE_ENTITY(LaundroMobile, INT_TO_VOID(LAUNDROMOBILE_DELAYEDSPLASH), self->position.x + 0x100000, Water->waterLevel);
980
RSDK.PlaySfx(Water->sfxSplash, false, 255);
981
}
982
983
LaundroMobile_HandleRocketMovement();
984
LaundroMobile_CheckPlayerCollisions();
985
LaundroMobile_HandleEggmanAnimations();
986
}
987
988
void LaundroMobile_StateBoss_ReturnToLaundry(void)
989
{
990
RSDK_THIS(LaundroMobile);
991
992
self->position.x += self->velocity.x;
993
self->position.y += self->velocity.y;
994
self->velocity.y += 0x2800;
995
996
if ((LaundroMobile->attackDir && self->position.x >= (28160 << 16)) || (!LaundroMobile->attackDir && self->position.x <= (28160 << 16))) {
997
self->position.x = 28160 << 16;
998
RSDK.SetSpriteAnimation(-1, 0, &self->flameAnimator, true, 0);
999
LaundroMobile->rocketActive = false;
1000
RSDK.PlaySfx(LaundroMobile->sfxHullClose, false, 255);
1001
self->state = LaundroMobile_StateBoss_PrepareWhirlpool;
1002
}
1003
1004
LaundroMobile_HandleRocketMovement();
1005
LaundroMobile_CheckPlayerCollisions();
1006
LaundroMobile_HandleEggmanAnimations();
1007
}
1008
1009
void LaundroMobile_StateBoss_PrepareWhirlpool(void)
1010
{
1011
RSDK_THIS(LaundroMobile);
1012
1013
if (LaundroMobile->rocketSpeeds[0] <= 0x180) {
1014
if (!(LaundroMobile->rocketAngles[0] & 0x7E00)) {
1015
LaundroMobile->rocketSpeeds[0] = 0;
1016
LaundroMobile->rocketAngles[0] &= 0xFE00;
1017
LaundroMobile->rocketAngles[1] &= 0xFE00;
1018
}
1019
}
1020
else {
1021
LaundroMobile->rocketSpeeds[0] -= 8;
1022
}
1023
1024
if (LaundroMobile->rocketSpeeds[1] <= 0x180) {
1025
if (!(LaundroMobile->rocketAngles[2] & 0x7E00)) {
1026
LaundroMobile->rocketSpeeds[1] = 0;
1027
LaundroMobile->rocketAngles[2] &= 0xFE00;
1028
LaundroMobile->rocketAngles[3] &= 0xFE00;
1029
}
1030
}
1031
else {
1032
LaundroMobile->rocketSpeeds[1] -= 8;
1033
}
1034
1035
if (!(LaundroMobile->rocketSpeeds[0] + LaundroMobile->rocketSpeeds[1])) {
1036
RSDK.PlaySfx(LaundroMobile->sfxFan, false, 255);
1037
self->state = LaundroMobile_StateBoss_StartupWhirlpool;
1038
}
1039
1040
LaundroMobile_HandleRocketMovement();
1041
LaundroMobile_CheckPlayerCollisions();
1042
LaundroMobile_HandleEggmanAnimations();
1043
}
1044
1045
void LaundroMobile_StateBoss_StartupWhirlpool(void)
1046
{
1047
RSDK_THIS(LaundroMobile);
1048
1049
LaundroMobile->animSpeed += 4;
1050
if (LaundroMobile->animSpeed < 0x100) {
1051
LaundroMobile_CheckPlayerCollisions();
1052
LaundroMobile_HandleEggmanAnimations();
1053
}
1054
else {
1055
EntityWhirlpool *whirlpool = self->whirlpool;
1056
if (whirlpool) {
1057
if (whirlpool->classID == Whirlpool->classID) {
1058
whirlpool->position.x = self->position.x;
1059
}
1060
else {
1061
self->whirlpool = NULL;
1062
}
1063
1064
self->timer = 480;
1065
self->state = LaundroMobile_StateBoss_WhirlpoolActive;
1066
}
1067
else {
1068
Vector2 pos = { 0x1000000, 0x1000000 };
1069
whirlpool = CREATE_ENTITY(Whirlpool, &pos, self->position.x, self->originPos.y - 0x200000);
1070
1071
whirlpool->activePlayers = 0xFF;
1072
whirlpool->angVel = 10;
1073
whirlpool->alpha = 0;
1074
whirlpool->drawGroup = Zone->objectDrawGroup[0] + 1;
1075
whirlpool->isPermanent = true;
1076
self->whirlpool = whirlpool;
1077
1078
self->timer = 480;
1079
self->state = LaundroMobile_StateBoss_WhirlpoolActive;
1080
}
1081
}
1082
}
1083
1084
void LaundroMobile_StateBoss_WhirlpoolActive(void)
1085
{
1086
RSDK_THIS(LaundroMobile);
1087
1088
++LaundroMobile->loopSfxTimer;
1089
1090
if (--self->timer > 0) {
1091
foreach_active(Player, player)
1092
{
1093
int32 playerID = RSDK.GetEntitySlot(player);
1094
if (player->position.y > Water->waterLevel) {
1095
if (player->state == Player_State_Static) {
1096
player->onGround = false;
1097
player->nextGroundState = StateMachine_None;
1098
player->nextAirState = StateMachine_None;
1099
1100
if ((player->position.y & 0xFFFF0000) != (self->position.y & 0xFFFF0000) + 0x580000) {
1101
if (player->position.y >= self->position.y + 0x580000)
1102
player->position.y -= 0x10000;
1103
else
1104
player->position.y += 0x10000;
1105
}
1106
1107
if (LaundroMobile->playerRadius[playerID] != 0x5000) {
1108
if (LaundroMobile->playerRadius[playerID] >= 0x5000)
1109
LaundroMobile->playerRadius[playerID] -= 0x100;
1110
else
1111
LaundroMobile->playerRadius[playerID] += 0x100;
1112
}
1113
1114
if ((LaundroMobile->playerAngles[playerID] & 0xFF) >= 0x80)
1115
player->drawGroup = Zone->playerDrawGroup[0];
1116
else
1117
player->drawGroup = self->drawGroup - 1;
1118
1119
LaundroMobile->playerAngles[playerID] += 3;
1120
player->position.x =
1121
LaundroMobile->playerRadius[playerID] * RSDK.Cos256(LaundroMobile->playerAngles[playerID]) + self->position.x;
1122
}
1123
else if (Player_CheckValidState(player)) {
1124
player->onGround = false;
1125
player->velocity.x = 0;
1126
player->velocity.y = 0;
1127
player->groundVel = 0;
1128
player->nextGroundState = StateMachine_None;
1129
player->nextAirState = StateMachine_None;
1130
LaundroMobile->playerRadius[playerID] = ((player->position.x - self->position.x) & 0xFFFF0000);
1131
1132
if (LaundroMobile->playerRadius[playerID] <= 0) {
1133
LaundroMobile->playerRadius[playerID] = -LaundroMobile->playerRadius[playerID];
1134
LaundroMobile->playerAngles[playerID] = 128;
1135
}
1136
else {
1137
LaundroMobile->playerAngles[playerID] = 0;
1138
}
1139
LaundroMobile->playerRadius[playerID] >>= 8;
1140
RSDK.SetSpriteAnimation(player->aniFrames, ANI_FAN, &player->animator, false, 0);
1141
1142
player->state = Player_State_Static;
1143
}
1144
}
1145
}
1146
}
1147
else {
1148
foreach_active(Player, player)
1149
{
1150
int32 playerID = RSDK.GetEntitySlot(player);
1151
if (player->state == Player_State_Static) {
1152
player->velocity.x = player->position.x
1153
- LaundroMobile->playerRadius[playerID] * RSDK.Cos256(LaundroMobile->playerAngles[playerID] - 3)
1154
- self->position.x;
1155
player->drawGroup = Zone->playerDrawGroup[0];
1156
player->state = Player_State_Air;
1157
}
1158
}
1159
1160
EntityWhirlpool *whirlpool = self->whirlpool;
1161
if (whirlpool) {
1162
if (whirlpool->classID == Whirlpool->classID)
1163
whirlpool->activePlayers = -3;
1164
1165
self->whirlpool = NULL;
1166
}
1167
1168
self->state = LaundroMobile_StateBoss_PrepareRockets;
1169
}
1170
1171
LaundroMobile_CheckPlayerCollisions();
1172
LaundroMobile_HandleEggmanAnimations();
1173
}
1174
1175
void LaundroMobile_StateBoss_PrepareRockets(void)
1176
{
1177
RSDK_THIS(LaundroMobile);
1178
1179
LaundroMobile->animSpeed -= 4;
1180
if (LaundroMobile->animSpeed <= 0) {
1181
self->velocity.y = 0;
1182
self->timer = 32;
1183
self->state = LaundroMobile_StateBoss_StartupRockets;
1184
}
1185
1186
LaundroMobile_CheckPlayerCollisions();
1187
LaundroMobile_HandleEggmanAnimations();
1188
}
1189
1190
void LaundroMobile_StateBoss_Destroyed_Phase2(void)
1191
{
1192
RSDK_THIS(LaundroMobile);
1193
1194
foreach_active(Player, player)
1195
{
1196
int32 playerID = RSDK.GetEntitySlot(player);
1197
1198
if (player->state == Player_State_Static) {
1199
player->velocity.x = player->position.x - LaundroMobile->playerRadius[playerID] * RSDK.Cos256(LaundroMobile->playerAngles[playerID] - 3)
1200
- self->position.x;
1201
player->drawGroup = Zone->playerDrawGroup[0];
1202
player->state = Player_State_Air;
1203
}
1204
}
1205
1206
LaundroMobile_Explode();
1207
1208
if (LaundroMobile->animSpeed > 0)
1209
LaundroMobile->animSpeed -= 4;
1210
1211
if (--LaundroMobile->invincibilityTimer) {
1212
if (LaundroMobile->invincibilityTimer == 30) {
1213
for (int32 i = 0; i < 4; ++i) {
1214
EntityDebris *debris =
1215
CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, LaundroMobile->rocketPositions[i].x, LaundroMobile->rocketPositions[i].y);
1216
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 5, &debris->animator, true, (LaundroMobile->rocketAngles[i] >> 12) & 0xF);
1217
debris->animator.speed = 0;
1218
1219
if (debris->position.y >= self->position.y) {
1220
debris->velocity.x = debris->position.x < self->position.x ? -0x20000 : 0x20000;
1221
debris->velocity.y = -0x10000;
1222
}
1223
else {
1224
debris->velocity.x = debris->position.x < self->position.x ? -0x40000 : 0x40000;
1225
debris->velocity.y = -0x20000;
1226
}
1227
1228
debris->gravityStrength = 0x3800;
1229
debris->drawGroup = Zone->objectDrawGroup[1];
1230
debris->updateRange.x = 0x400000;
1231
debris->updateRange.y = 0x400000;
1232
LaundroMobile->rocketPositions[i].x = 0;
1233
LaundroMobile->rocketPositions[i].y = 0;
1234
}
1235
}
1236
}
1237
else {
1238
self->timer = 60;
1239
self->velocity.y = 0x20000;
1240
self->state = LaundroMobile_StateBoss_Explode_Phase2;
1241
}
1242
}
1243
1244
void LaundroMobile_StateBoss_Explode_Phase2(void)
1245
{
1246
RSDK_THIS(LaundroMobile);
1247
1248
self->position.y += self->velocity.y;
1249
self->velocity.y -= 0x3800;
1250
1251
LaundroMobile_Explode();
1252
1253
if (--self->timer <= 0) {
1254
Water->moveWaterLevel = true;
1255
Water->targetWaterLevel += 0x780000;
1256
Water->waterMoveSpeed = 0x10000;
1257
self->timer = 0;
1258
1259
Music_TransitionTrack(TRACK_STAGE, 0.0125);
1260
1261
CREATE_ENTITY(EggPrison, INT_TO_VOID(EGGPRISON_FLYING), (ScreenInfo->position.x + ScreenInfo->center.x) << 16,
1262
(ScreenInfo->position.y - 48) << 16);
1263
1264
#if MANIA_USE_PLUS
1265
Zone->stageFinishCallback = LaundroMobile_StageFinish_Wait;
1266
self->state = LaundroMobile_StateOutro_StartCutscene;
1267
#else
1268
destroyEntity(self);
1269
#endif
1270
}
1271
}
1272
1273
#if MANIA_USE_PLUS
1274
void LaundroMobile_StageFinish_Wait(void) {}
1275
1276
void LaundroMobile_StateOutro_StartCutscene(void)
1277
{
1278
RSDK_THIS(LaundroMobile);
1279
1280
EntityActClear *actClear = RSDK_GET_ENTITY(SLOT_ACTCLEAR, ActClear);
1281
1282
if (self->timer) {
1283
if (actClear->classID != ActClear->classID) {
1284
self->timer = 0;
1285
1286
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1287
player1->drawGroup = Zone->playerDrawGroup[0];
1288
player1->state = Player_State_Ground;
1289
player1->direction = FLIP_NONE;
1290
player1->stateInput = StateMachine_None;
1291
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);
1292
1293
EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);
1294
if (player2->classID == Player->classID) {
1295
player2->drawGroup = Zone->playerDrawGroup[0];
1296
player2->state = Player_State_Ground;
1297
player2->direction = FLIP_NONE;
1298
player2->stateInput = StateMachine_None;
1299
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);
1300
}
1301
1302
self->state = LaundroMobile_StateOutro_Rumble;
1303
}
1304
}
1305
else if (actClear->classID == ActClear->classID) {
1306
self->timer = 1;
1307
}
1308
}
1309
1310
void LaundroMobile_StateOutro_Rumble(void)
1311
{
1312
RSDK_THIS(LaundroMobile);
1313
1314
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1315
EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);
1316
1317
player1->timer = 0;
1318
if (player2->classID == Player->classID)
1319
player2->timer = 0;
1320
1321
if (!(Zone->timer & 3)) {
1322
Camera_ShakeScreen(0, 0, 2);
1323
}
1324
1325
if (!(Zone->timer & 7))
1326
RSDK.PlaySfx(LaundroMobile->sfxRumble, false, 255);
1327
1328
if (++self->timer == 90) {
1329
self->timer = 0;
1330
foreach_active(WaterGush, gush)
1331
{
1332
if (gush->position.x > self->position.x) {
1333
gush->activated = true;
1334
gush->inkEffect = INK_ALPHA;
1335
gush->alpha = 256;
1336
gush->drawGroup = Zone->playerDrawGroup[0];
1337
}
1338
}
1339
1340
for (int32 i = 0; i < 0x20; ++i) {
1341
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, 28336 << 16, 2784 << 16);
1342
1343
RSDK.SetSpriteAnimation(WaterGush->aniFrames, 4, &debris->animator, true, 0);
1344
debris->position.x += 0x60000 * RSDK.Rand(-8, 8);
1345
debris->position.y += 0x60000 * RSDK.Rand(-8, 8);
1346
debris->velocity.x = RSDK.Rand(-8, 8) << 16;
1347
debris->velocity.y = RSDK.Rand(-8, 8) << 16;
1348
debris->velocity.x = RSDK.Rand(-8, 9) << 15;
1349
debris->velocity.y = RSDK.Rand(-8, 5) << 16;
1350
debris->direction = RSDK.Rand(0, 4);
1351
debris->drawFX = FX_FLIP;
1352
debris->drawGroup = Zone->objectDrawGroup[1];
1353
debris->gravityStrength = 0x3800;
1354
}
1355
1356
RSDK.CopyTileLayer(Zone->fgLayer[1], 1763, 172, Zone->fgLayer[1], 1919, 172, 11, 4);
1357
1358
RSDK.PlaySfx(LaundroMobile->sfxImpact, false, 255);
1359
RSDK.PlaySfx(WaterGush->sfxGush, false, 255);
1360
RSDK.PlaySfx(Water->sfxSplash, false, 255);
1361
self->state = LaundroMobile_StateOutro_WaterGush;
1362
}
1363
}
1364
1365
void LaundroMobile_StateOutro_WaterGush(void)
1366
{
1367
RSDK_THIS(LaundroMobile);
1368
1369
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1370
EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);
1371
1372
player1->timer = 0;
1373
if (player2->classID == Player->classID)
1374
player2->timer = 0;
1375
1376
if (++self->timer < 75) {
1377
foreach_active(WaterGush, gush)
1378
{
1379
gush->position.y += 0x8000;
1380
gush->alpha -= 2;
1381
1382
if (gush->position.x > self->position.x) {
1383
if (gush->position.x - gush->gushPos < player1->position.x) {
1384
player1->velocity.x = -0x30000;
1385
player1->groundVel = -0x30000;
1386
}
1387
1388
if (player2->classID == Player->classID && gush->position.x - gush->gushPos < player2->position.x) {
1389
player2->velocity.x = -0x30000;
1390
player2->groundVel = -0x30000;
1391
}
1392
}
1393
}
1394
}
1395
else {
1396
foreach_active(WaterGush, gush)
1397
{
1398
gush->position.y += 0x8000;
1399
gush->alpha -= 2;
1400
1401
if (gush->position.x > self->position.x && gush->alpha <= 0) {
1402
destroyEntity(gush);
1403
1404
for (int32 p = 0; p < PLAYER_COUNT; ++p) Zone->playerBoundActiveR[p] = false;
1405
1406
self->timer = 0;
1407
self->state = LaundroMobile_StateOutro_ExitHCZ;
1408
}
1409
}
1410
}
1411
}
1412
1413
void LaundroMobile_StateOutro_ExitHCZ(void)
1414
{
1415
RSDK_THIS(LaundroMobile);
1416
1417
if (++self->timer > 120) {
1418
RSDK_GET_ENTITY(SLOT_PLAYER2, Player)->right = true;
1419
1420
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1421
if (player1->classID == Player->classID)
1422
player1->right = true;
1423
1424
if (player1->position.x > (Zone->cameraBoundsR[0] + 64) << 16) {
1425
HCZSetup_StageFinish_EndAct2();
1426
destroyEntity(self);
1427
}
1428
}
1429
}
1430
#endif
1431
1432
void LaundroMobile_Draw_Boss(void)
1433
{
1434
RSDK_THIS(LaundroMobile);
1435
1436
if (LaundroMobile->invincibilityTimer & 1) {
1437
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
1438
RSDK.SetPaletteEntry(1, 128, 0xE0E0E0);
1439
}
1440
1441
Vector2 drawPos;
1442
drawPos.x = self->position.x - 0x20000;
1443
drawPos.y = self->position.y - 0x1B0000;
1444
1445
self->mainAnimator.frameID = 4;
1446
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1447
1448
drawPos.y += 0x300000;
1449
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1450
1451
drawPos = self->position;
1452
drawPos.x += 0x100000;
1453
RSDK.DrawSprite(&self->eggmanAnimator, &drawPos, false);
1454
1455
self->mainAnimator.frameID = 0;
1456
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
1457
RSDK.DrawSprite(&self->propellerAnimator, NULL, false);
1458
1459
drawPos.x = self->position.x - 0x120000;
1460
drawPos.y = self->position.y - 0x1B0000;
1461
self->mainAnimator.frameID = 4;
1462
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1463
1464
drawPos.y += 0x300000;
1465
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1466
1467
RSDK.SetPaletteEntry(0, 128, 0x000000);
1468
RSDK.SetPaletteEntry(1, 128, 0x000000);
1469
}
1470
1471
void LaundroMobile_Draw_Boss_Destroyed(void)
1472
{
1473
RSDK_THIS(LaundroMobile);
1474
1475
if (LaundroMobile->invincibilityTimer & 1) {
1476
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
1477
RSDK.SetPaletteEntry(1, 128, 0xE0E0E0);
1478
}
1479
1480
for (int32 r = 0; r < 4; ++r) {
1481
self->direction = r >= 2 ? FLIP_X : FLIP_NONE;
1482
uint8 angle = LaundroMobile->rocketAngles[r] >> 8;
1483
if (angle >= 0x80) {
1484
self->propellerAnimator.frameID = angle >> 4;
1485
1486
self->flameAnimator.frameID = 12;
1487
if (!(Zone->timer & 1))
1488
self->flameAnimator.frameID = self->propellerAnimator.frameID;
1489
1490
RSDK.DrawSprite(&self->propellerAnimator, &LaundroMobile->rocketPositions[r], false);
1491
RSDK.DrawSprite(&self->flameAnimator, &LaundroMobile->rocketPositions[r], false);
1492
}
1493
}
1494
1495
self->direction = FLIP_NONE;
1496
self->mainAnimator.frameID = 5;
1497
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
1498
RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);
1499
1500
self->mainAnimator.frameID = 1;
1501
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
1502
1503
for (int32 r = 0; r < 4; ++r) {
1504
self->direction = r >= 2 ? FLIP_X : FLIP_NONE;
1505
uint8 angle = LaundroMobile->rocketAngles[r] >> 8;
1506
if (angle < 0x80) {
1507
self->propellerAnimator.frameID = angle >> 4;
1508
1509
self->flameAnimator.frameID = 12;
1510
if (!(Zone->timer & 1))
1511
self->flameAnimator.frameID = self->propellerAnimator.frameID;
1512
1513
angle = (angle + 64) & 0xFF;
1514
if (angle <= 0x80)
1515
RSDK.DrawSprite(&self->flameAnimator, &LaundroMobile->rocketPositions[r], false);
1516
1517
RSDK.DrawSprite(&self->propellerAnimator, &LaundroMobile->rocketPositions[r], false);
1518
1519
if (angle > 0x80)
1520
RSDK.DrawSprite(&self->flameAnimator, &LaundroMobile->rocketPositions[r], false);
1521
}
1522
}
1523
1524
self->direction = FLIP_NONE;
1525
RSDK.SetPaletteEntry(0, 128, 0x000000);
1526
RSDK.SetPaletteEntry(1, 128, 0x000000);
1527
}
1528
1529
void LaundroMobile_StateBomb_Spawner(void)
1530
{
1531
RSDK_THIS(LaundroMobile);
1532
1533
if (self->active == ACTIVE_BOUNDS) {
1534
if (self->position.x + 0x200000 > ScreenInfo->position.x << 16) {
1535
if (self->position.x - 0x200000 <= (ScreenInfo->position.x + ScreenInfo->size.x) << 16) {
1536
self->visible = false;
1537
EntityLaundroMobile *bomb = CREATE_ENTITY(LaundroMobile, INT_TO_VOID(LAUNDROMOBILE_BOMB), self->position.x, self->position.y);
1538
bomb->velocity.x = LaundroMobile->currentVelocity - 0x20000;
1539
bomb->active = ACTIVE_NORMAL;
1540
bomb->state = LaundroMobile_StateBomb_Bomb_Idle;
1541
bomb->isPermanent = true;
1542
self->active = ACTIVE_NORMAL;
1543
}
1544
}
1545
}
1546
else {
1547
if (self->position.x + 0x200000 < ScreenInfo->position.x << 16) {
1548
self->visible = true;
1549
self->active = ACTIVE_BOUNDS;
1550
}
1551
}
1552
}
1553
1554
void LaundroMobile_StateBomb_Bomb_Idle(void)
1555
{
1556
RSDK_THIS(LaundroMobile);
1557
1558
self->position.x += self->velocity.x;
1559
1560
foreach_active(Player, player)
1561
{
1562
int32 velX = player->velocity.x;
1563
1564
if (Player_CheckCollisionBox(player, self, &LaundroMobile->hitboxBox) == C_LEFT) {
1565
RSDK.PlaySfx(LaundroMobile->sfxButton, false, 255);
1566
self->velocity.x = LaundroMobile->currentVelocity + 0x18000;
1567
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 8, &self->mainAnimator, true, 0);
1568
self->state = LaundroMobile_StateBomb_Bomb_Activated;
1569
}
1570
1571
player->velocity.x = velX;
1572
}
1573
1574
if (self->position.x + 0x200000 < ScreenInfo->position.x << 16) {
1575
destroyEntity(self);
1576
}
1577
else {
1578
foreach_active(LaundroMobile, boss)
1579
{
1580
if (boss->type == LAUNDROMOBILE_BOSS
1581
&& RSDK.CheckObjectCollisionTouchBox(boss, &LaundroMobile->hitboxBoss, self, &LaundroMobile->hitboxBox)
1582
&& boss->state == LaundroMobile_StateBoss_Explode_Phase1) {
1583
RSDK.PlaySfx(LaundroMobile->sfxPimPom, false, 255);
1584
1585
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
1586
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 7, &debris->animator, true, 0);
1587
debris->velocity.y = -0x28000;
1588
debris->velocity.x = LaundroMobile->currentVelocity + 0x28000;
1589
debris->gravityStrength = 0x3800;
1590
debris->drawGroup = Zone->objectDrawGroup[1];
1591
debris->updateRange.x = 0x400000;
1592
debris->updateRange.y = 0x400000;
1593
1594
destroyEntity(self);
1595
foreach_break;
1596
}
1597
}
1598
}
1599
}
1600
1601
void LaundroMobile_StateBomb_Bomb_Activated(void)
1602
{
1603
RSDK_THIS(LaundroMobile);
1604
1605
self->position.x += self->velocity.x;
1606
self->velocity.x -= 0x800;
1607
1608
RSDK.ProcessAnimation(&self->mainAnimator);
1609
1610
if (self->position.x + 0x200000 < ScreenInfo->position.x << 16) {
1611
destroyEntity(self);
1612
}
1613
else {
1614
foreach_active(Player, player)
1615
{
1616
int32 velX = player->velocity.x;
1617
Player_CheckCollisionBox(player, self, &LaundroMobile->hitboxBox);
1618
player->velocity.x = velX;
1619
}
1620
1621
EntityLaundroMobile *boss = LaundroMobile->laundroMobile;
1622
if (RSDK.CheckObjectCollisionTouchBox(boss, &LaundroMobile->hitboxBoss, self, &LaundroMobile->hitboxBox)) {
1623
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y);
1624
explosion->drawGroup = Zone->objectDrawGroup[1];
1625
explosion->velocity.x = LaundroMobile->currentVelocity - 0x10000;
1626
RSDK.PlaySfx(LaundroMobile->sfxExplosion, false, 255);
1627
1628
EntityWater *water = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y);
1629
water->drawGroup = Zone->objectDrawGroup[0] + 1;
1630
water->angle = 2 * RSDK.Rand(0, 256);
1631
water->speed = -0x1400;
1632
water->velocity.x = (LaundroMobile->currentVelocity + (LaundroMobile->currentVelocity >> 2)) + (LaundroMobile->currentVelocity >> 1);
1633
water->bubbleX = water->position.x;
1634
water->childPtr = 0;
1635
RSDK.SetSpriteAnimation(Water->aniFrames, 3, &water->animator, false, 5);
1636
water->tileCollisions = TILECOLLISION_DOWN;
1637
1638
if (!LaundroMobile->invincibilityTimer) {
1639
--LaundroMobile->health;
1640
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 2, &boss->eggmanAnimator, true, 0);
1641
RSDK.PlaySfx(LaundroMobile->sfxHit, false, 255);
1642
LaundroMobile->invincibilityTimer = 30;
1643
if (LaundroMobile->health <= 8) {
1644
RSDK.StopSfx(LaundroMobile->sfxFan);
1645
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 4, &boss->propellerAnimator, true, 0);
1646
boss->timer = 60;
1647
boss->state = LaundroMobile_StateBoss_Destroyed_Phase1;
1648
}
1649
}
1650
destroyEntity(self);
1651
}
1652
}
1653
}
1654
1655
void LaundroMobile_Draw_Simple(void)
1656
{
1657
RSDK_THIS(LaundroMobile);
1658
1659
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
1660
}
1661
1662
void LaundroMobile_StateBlock_Spawner(void)
1663
{
1664
RSDK_THIS(LaundroMobile);
1665
1666
if (self->active == ACTIVE_BOUNDS) {
1667
if (self->position.x + 0x200000 > ScreenInfo->position.x << 16) {
1668
if (self->position.x - 0x200000 <= (ScreenInfo->position.x + ScreenInfo->size.x) << 16) {
1669
self->visible = false;
1670
1671
EntityLaundroMobile *block = CREATE_ENTITY(LaundroMobile, INT_TO_VOID(self->type), self->position.x, self->position.y);
1672
block->velocity.x = LaundroMobile->currentVelocity - 0x20000;
1673
block->active = ACTIVE_NORMAL;
1674
block->state = LaundroMobile_StateBlock_Block;
1675
block->isPermanent = true;
1676
1677
self->active = ACTIVE_NORMAL;
1678
}
1679
}
1680
}
1681
else {
1682
if (self->position.x + 0x200000 < ScreenInfo->position.x << 16) {
1683
self->visible = true;
1684
self->active = ACTIVE_BOUNDS;
1685
}
1686
}
1687
}
1688
1689
void LaundroMobile_StateBlock_Block(void)
1690
{
1691
RSDK_THIS(LaundroMobile);
1692
1693
self->position.x += self->velocity.x;
1694
1695
foreach_active(Player, player)
1696
{
1697
if (self->type == LAUNDROMOBILE_SPIKES) {
1698
if (Player_CheckCollisionBox(player, self, &LaundroMobile->hitboxBomb) == C_LEFT) {
1699
Player_Hurt(player, self);
1700
}
1701
}
1702
else {
1703
Player_CheckCollisionBox(player, self, &LaundroMobile->hitboxBox);
1704
}
1705
}
1706
1707
if (self->position.x + 0x200000 >= ScreenInfo->position.x << 16) {
1708
foreach_active(LaundroMobile, laundroMobile)
1709
{
1710
if (laundroMobile->type == LAUNDROMOBILE_BOSS) {
1711
Hitbox *hitbox = &LaundroMobile->hitboxBomb;
1712
if (self->type != LAUNDROMOBILE_SPIKES)
1713
hitbox = &LaundroMobile->hitboxBox;
1714
1715
if (RSDK.CheckObjectCollisionTouchBox(laundroMobile, &LaundroMobile->hitboxBoss, self, hitbox)
1716
|| laundroMobile->state == LaundroMobile_StateBoss_Explode_Phase1) {
1717
RSDK.PlaySfx(LaundroMobile->sfxLedgeBreak, false, 255);
1718
1719
EntityDebris *debris = NULL;
1720
if (self->type == LAUNDROMOBILE_SPIKES) {
1721
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y - 0x80000);
1722
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &debris->animator, true, 18);
1723
debris->drawFX |= FX_ROTATE;
1724
debris->velocity.x = LaundroMobile->currentVelocity + 0x18000;
1725
debris->velocity.y = -0x28000;
1726
debris->gravityStrength = 0x3800;
1727
debris->rotSpeed = RSDK.Rand(-8, 8);
1728
debris->drawGroup = Zone->objectDrawGroup[1];
1729
debris->updateRange.x = 0x400000;
1730
debris->updateRange.y = 0x400000;
1731
1732
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y + 0x80000);
1733
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &debris->animator, true, 18);
1734
debris->drawFX |= FX_ROTATE;
1735
debris->velocity.x = LaundroMobile->currentVelocity + 0x18000;
1736
debris->velocity.y = -0x20000;
1737
debris->gravityStrength = 0x3800;
1738
debris->rotSpeed = RSDK.Rand(-8, 8);
1739
debris->drawGroup = Zone->objectDrawGroup[1];
1740
debris->updateRange.x = 0x400000;
1741
debris->updateRange.y = 0x400000;
1742
self->position.x += 0x100000;
1743
}
1744
1745
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
1746
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &debris->animator, true, 4 * (self->mainAnimator.frameID % 3) + 6);
1747
debris->velocity.y = -0x28000;
1748
debris->velocity.x = LaundroMobile->currentVelocity + 0x20000;
1749
debris->gravityStrength = 0x3800;
1750
debris->drawGroup = Zone->objectDrawGroup[1];
1751
debris->updateRange.x = 0x400000;
1752
debris->updateRange.y = 0x400000;
1753
1754
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
1755
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &debris->animator, true, 4 * (self->mainAnimator.frameID % 3) + 7);
1756
debris->velocity.y = -0x28000;
1757
debris->velocity.x = LaundroMobile->currentVelocity + 0x28000;
1758
debris->gravityStrength = 0x3800;
1759
debris->drawGroup = Zone->objectDrawGroup[1];
1760
debris->updateRange.x = 0x400000;
1761
debris->updateRange.y = 0x400000;
1762
1763
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
1764
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &debris->animator, true, 4 * (self->mainAnimator.frameID % 3) + 8);
1765
debris->velocity.y = -0x20000;
1766
debris->velocity.x = LaundroMobile->currentVelocity + 0x20000;
1767
debris->gravityStrength = 0x3800;
1768
debris->drawGroup = Zone->objectDrawGroup[1];
1769
debris->updateRange.x = 0x400000;
1770
debris->updateRange.y = 0x400000;
1771
1772
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
1773
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &debris->animator, true, 4 * (self->mainAnimator.frameID % 3) + 9);
1774
debris->velocity.y = -0x20000;
1775
debris->velocity.x = LaundroMobile->currentVelocity + 0x28000;
1776
debris->gravityStrength = 0x3800;
1777
debris->drawGroup = Zone->objectDrawGroup[1];
1778
debris->updateRange.x = 0x400000;
1779
debris->updateRange.y = 0x400000;
1780
1781
destroyEntity(self);
1782
foreach_break;
1783
}
1784
}
1785
}
1786
}
1787
else {
1788
destroyEntity(self);
1789
}
1790
}
1791
1792
void LaundroMobile_State_Laundry(void)
1793
{
1794
RSDK_THIS(LaundroMobile);
1795
1796
self->propellerAnimator.speed = LaundroMobile->animSpeed;
1797
self->eggmanAnimator.speed = LaundroMobile->animSpeed;
1798
1799
RSDK.ProcessAnimation(&self->propellerAnimator);
1800
RSDK.ProcessAnimation(&self->eggmanAnimator);
1801
1802
Zone->playerBoundActiveL[0] = true;
1803
Zone->playerBoundActiveR[0] = true;
1804
Zone->cameraBoundsL[0] = ScreenInfo->position.x;
1805
Zone->cameraBoundsR[0] = ScreenInfo->center.x + (self->position.x >> 16);
1806
1807
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x - 0xC00000)
1808
Zone->cameraBoundsT[0] = ScreenInfo->position.y;
1809
1810
if (!LaundroMobile->health && !LaundroMobile->invincibilityTimer) {
1811
Debris_CreateFromEntries_UseOffset(LaundroMobile->aniFrames, LaundroMobile->debrisInfo);
1812
destroyEntity(self);
1813
}
1814
}
1815
1816
void LaundroMobile_Draw_Laundry(void)
1817
{
1818
RSDK_THIS(LaundroMobile);
1819
1820
Vector2 drawPos = self->position;
1821
self->mainAnimator.frameID = 2;
1822
RSDK.DrawSprite(&self->mainAnimator, 0, false);
1823
RSDK.DrawSprite(&self->propellerAnimator, 0, false);
1824
1825
drawPos.y += 0x200000;
1826
RSDK.DrawSprite(&self->propellerAnimator, &drawPos, false);
1827
1828
drawPos.y += 0x200000;
1829
RSDK.DrawSprite(&self->propellerAnimator, &drawPos, false);
1830
1831
self->mainAnimator.frameID = 3;
1832
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1833
RSDK.DrawSprite(&self->eggmanAnimator, &drawPos, false);
1834
}
1835
1836
void LaundroMobile_State_DelayedSplash(void)
1837
{
1838
RSDK_THIS(LaundroMobile);
1839
1840
if (--self->timer <= 0) {
1841
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);
1842
destroyEntity(self);
1843
}
1844
}
1845
1846
#if GAME_INCLUDE_EDITOR
1847
void LaundroMobile_EditorDraw(void)
1848
{
1849
RSDK_THIS(LaundroMobile);
1850
1851
switch (self->type) {
1852
case LAUNDROMOBILE_BOSS:
1853
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 0, &self->mainAnimator, true, 0);
1854
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 3, &self->propellerAnimator, true, 0);
1855
RSDK.SetSpriteAnimation(LaundroMobile->eggmanFrames, 0, &self->eggmanAnimator, true, 0);
1856
1857
self->startY = self->position.y;
1858
self->updateRange.x = 0x800000;
1859
self->updateRange.y = 0x800000;
1860
self->stateDraw = LaundroMobile_Draw_Boss;
1861
break;
1862
1863
case LAUNDROMOBILE_BOMB:
1864
self->originPos.x = self->position.x;
1865
self->originPos.y = self->position.y;
1866
1867
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 7, &self->mainAnimator, true, 0);
1868
1869
self->updateRange.x = 0x1800000;
1870
self->updateRange.y = 0x1800000;
1871
self->stateDraw = LaundroMobile_Draw_Simple;
1872
break;
1873
1874
case LAUNDROMOBILE_LAUNDRY:
1875
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 0, &self->mainAnimator, true, 0);
1876
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 1, &self->propellerAnimator, true, 0);
1877
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 2, &self->eggmanAnimator, true, 0);
1878
1879
self->updateRange.x = 0x2000000;
1880
self->updateRange.y = 0x800000;
1881
self->stateDraw = LaundroMobile_Draw_Laundry;
1882
break;
1883
1884
case LAUNDROMOBILE_LOOPPOINT:
1885
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 0, &self->mainAnimator, true, 0);
1886
1887
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
1888
self->stateDraw = StateMachine_None;
1889
break;
1890
1891
case LAUNDROMOBILE_BLOCK:
1892
self->originPos = self->position;
1893
1894
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &self->mainAnimator, false, 0);
1895
1896
self->updateRange.x = 0x1800000;
1897
self->updateRange.y = 0x1800000;
1898
self->stateDraw = LaundroMobile_Draw_Simple;
1899
break;
1900
1901
case LAUNDROMOBILE_SPIKES:
1902
self->originPos = self->position;
1903
1904
RSDK.SetSpriteAnimation(LaundroMobile->aniFrames, 9, &self->mainAnimator, false, 3);
1905
1906
self->updateRange.x = 0x1800000;
1907
self->updateRange.y = 0x1800000;
1908
self->stateDraw = LaundroMobile_Draw_Simple;
1909
break;
1910
1911
default: break;
1912
}
1913
1914
StateMachine_Run(self->stateDraw);
1915
}
1916
1917
void LaundroMobile_EditorLoad(void)
1918
{
1919
LaundroMobile->aniFrames = RSDK.LoadSpriteAnimation("HCZ/LaundroMobile.bin", SCOPE_STAGE);
1920
LaundroMobile->eggmanFrames = RSDK.LoadSpriteAnimation("Eggman/EggmanHCZ2.bin", SCOPE_STAGE);
1921
1922
RSDK_ACTIVE_VAR(LaundroMobile, type);
1923
RSDK_ENUM_VAR("Boss", LAUNDROMOBILE_BOSS);
1924
RSDK_ENUM_VAR("Bomb", LAUNDROMOBILE_BOMB);
1925
RSDK_ENUM_VAR("Laundry", LAUNDROMOBILE_LAUNDRY);
1926
RSDK_ENUM_VAR("Loop Point", LAUNDROMOBILE_LOOPPOINT);
1927
RSDK_ENUM_VAR("Block", LAUNDROMOBILE_BLOCK);
1928
RSDK_ENUM_VAR("Spikes", LAUNDROMOBILE_SPIKES);
1929
}
1930
#endif
1931
1932
void LaundroMobile_Serialize(void) { RSDK_EDITABLE_VAR(LaundroMobile, VAR_UINT8, type); }
1933
1934