Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/CPZ/AmoebaDroid.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: AmoebaDroid Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectAmoebaDroid *AmoebaDroid;
11
12
void AmoebaDroid_Update(void)
13
{
14
RSDK_THIS(AmoebaDroid);
15
16
StateMachine_Run(self->state);
17
}
18
19
void AmoebaDroid_LateUpdate(void) {}
20
21
void AmoebaDroid_StaticUpdate(void) {}
22
23
void AmoebaDroid_Draw(void)
24
{
25
RSDK_THIS(AmoebaDroid);
26
27
if (self->stateDraw) {
28
StateMachine_Run(self->stateDraw);
29
}
30
else {
31
RSDK.DrawSprite(&self->animator, NULL, false);
32
}
33
}
34
35
void AmoebaDroid_Create(void *data)
36
{
37
RSDK_THIS(AmoebaDroid);
38
39
if (!SceneInfo->inEditor) {
40
if (globals->gameMode < MODE_TIMEATTACK) {
41
self->active = ACTIVE_BOUNDS;
42
self->updateRange.x = 0x800000;
43
self->updateRange.y = 0x800000;
44
45
self->type = VOID_TO_INT(data);
46
switch (self->type) {
47
case AMOEBADROID_BOSS:
48
self->visible = false;
49
self->drawGroup = Zone->objectDrawGroup[0];
50
self->drawFX = FX_FLIP;
51
self->health = 6;
52
53
self->hitbox.left = -22;
54
self->hitbox.top = -22;
55
self->hitbox.right = 22;
56
self->hitbox.bottom = 22;
57
58
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 0, &self->animator, true, 0);
59
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 1, &self->attractorTopAnimator, true, 0);
60
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 2, &self->attractorSideAnimator, true, 0);
61
62
self->stateDraw = AmoebaDroid_Draw_AmoebaDroid;
63
self->state = AmoebaDroid_State_SetupArena;
64
break;
65
66
case AMOEBADROID_BLOB_BIG:
67
self->visible = true;
68
self->drawGroup = Zone->objectDrawGroup[1];
69
70
self->hitbox.left = -40;
71
self->hitbox.top = -40;
72
self->hitbox.right = 40;
73
self->hitbox.bottom = 40;
74
75
self->drawFX = FX_SCALE;
76
self->inkEffect = INK_ALPHA;
77
self->scale.y = 0x200;
78
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 3, &self->animator, true, 0);
79
80
self->stateDraw = AmoebaDroid_Draw_BigBlob;
81
self->state = AmoebaDroid_State_BigBlob;
82
break;
83
84
case AMOEBADROID_BLOB_SMALL:
85
self->active = ACTIVE_NORMAL;
86
self->visible = true;
87
self->drawGroup = Zone->objectDrawGroup[1];
88
89
self->hitbox.left = -8;
90
self->hitbox.top = -8;
91
self->hitbox.right = 8;
92
self->hitbox.bottom = 8;
93
94
self->updateRange.x = 0x200000;
95
self->updateRange.y = 0x200000;
96
self->inkEffect = INK_ALPHA;
97
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 4, &self->animator, true, 0);
98
99
self->state = AmoebaDroid_State_SmallBlob;
100
break;
101
102
case AMOEBADROID_POOLSPLASH:
103
self->active = ACTIVE_NORMAL;
104
self->visible = true;
105
self->drawGroup = Zone->hudDrawGroup - 2;
106
107
RSDK.SetSpriteAnimation(AmoebaDroid->waterFrames, 1, &self->animator, true, 0);
108
109
self->state = AmoebaDroid_State_PoolSplash;
110
break;
111
112
case AMOEBADROID_POOLSPLASH_DELAY:
113
self->active = ACTIVE_NORMAL;
114
self->drawGroup = Zone->hudDrawGroup - 2;
115
RSDK.SetSpriteAnimation(AmoebaDroid->waterFrames, 1, &self->animator, true, 0);
116
117
self->state = AmoebaDroid_State_PoolSplash_Delayed;
118
break;
119
120
default: break;
121
}
122
}
123
else {
124
destroyEntity(self);
125
}
126
}
127
}
128
129
void AmoebaDroid_StageLoad(void)
130
{
131
AmoebaDroid->aniFrames = RSDK.LoadSpriteAnimation("CPZ/AmoebaDroid.bin", SCOPE_STAGE);
132
AmoebaDroid->waterFrames = RSDK.LoadSpriteAnimation("Global/Water.bin", SCOPE_STAGE);
133
134
AmoebaDroid->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
135
AmoebaDroid->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
136
AmoebaDroid->sfxGather = RSDK.GetSfx("CPZ/DroidGather.wav");
137
AmoebaDroid->sfxBounce = RSDK.GetSfx("CPZ/DroidBounce.wav");
138
AmoebaDroid->sfxRelease = RSDK.GetSfx("CPZ/DroidRelease.wav");
139
}
140
141
void AmoebaDroid_HandleSmallBlobMovement(void)
142
{
143
RSDK_THIS(AmoebaDroid);
144
145
int32 angle = self->blobAngleX;
146
self->blobRadius = (self->blobAmplitude * RSDK.Cos256(self->blobAngleY)) >> 8;
147
148
for (int32 i = 0; i < AMOEBADROID_BLOB_COUNT; ++i) {
149
EntityAmoebaDroid *smallBlob = self->blobs[i];
150
151
smallBlob->velocity = smallBlob->position;
152
smallBlob->position.x = self->blobAmplitude * RSDK.Cos256(angle) + self->position.x;
153
smallBlob->velocity.x = smallBlob->position.x - smallBlob->velocity.x;
154
smallBlob->position.y = self->blobRadius * RSDK.Sin256(angle) + self->position.y;
155
smallBlob->velocity.y = (self->blobRadius * RSDK.Sin256(angle) + self->position.y) - smallBlob->velocity.y;
156
if ((self->blobAngleX & 0x7F) && angle < 0x80)
157
smallBlob->drawGroup = Zone->objectDrawGroup[0] - 1;
158
else
159
smallBlob->drawGroup = Zone->objectDrawGroup[0];
160
angle = (angle + 32) & 0xFF;
161
}
162
}
163
164
void AmoebaDroid_HandleSmallBlobRelease(bool32 interact)
165
{
166
RSDK_THIS(AmoebaDroid);
167
168
for (int32 i = 0; i < AMOEBADROID_BLOB_COUNT; ++i) {
169
EntityAmoebaDroid *smallBlob = self->blobs[i];
170
if (smallBlob) {
171
smallBlob->velocity.y -= 0x20000;
172
smallBlob->onGround = true;
173
smallBlob->interaction = interact;
174
self->blobs[i] = NULL;
175
}
176
}
177
}
178
179
void AmoebaDroid_Hit(void)
180
{
181
RSDK_THIS(AmoebaDroid);
182
183
if (--self->health <= 0) {
184
AmoebaDroid_HandleSmallBlobRelease(false);
185
self->state = AmoebaDroid_State_Destroyed;
186
self->timer = 0;
187
SceneInfo->timeEnabled = false;
188
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
189
}
190
else {
191
self->invincibleTimer = 48;
192
RSDK.PlaySfx(AmoebaDroid->sfxHit, false, 0xFF);
193
}
194
}
195
196
void AmoebaDroid_Explode(void)
197
{
198
RSDK_THIS(AmoebaDroid);
199
200
if (!(Zone->timer % 3)) {
201
RSDK.PlaySfx(AmoebaDroid->sfxExplosion, false, 255);
202
203
if (Zone->timer & 4) {
204
int32 x = (RSDK.Rand(self->hitbox.left, self->hitbox.right) << 16) + self->position.x;
205
int32 y = (RSDK.Rand(self->hitbox.top, self->hitbox.bottom) << 16) + self->position.y;
206
CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];
207
}
208
}
209
}
210
211
void AmoebaDroid_CheckHit(void)
212
{
213
RSDK_THIS(AmoebaDroid);
214
215
if (self->invincibleTimer > 0)
216
self->invincibleTimer--;
217
218
foreach_active(Player, player)
219
{
220
if (!self->invincibleTimer && Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self))
221
AmoebaDroid_Hit();
222
}
223
}
224
225
void AmoebaDroid_CheckPlayerHit(void)
226
{
227
RSDK_THIS(AmoebaDroid);
228
229
foreach_active(Player, player)
230
{
231
if (!self->invincibleTimer) {
232
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
233
Player_Hurt(player, self);
234
}
235
}
236
}
237
238
void AmoebaDroid_Draw_AmoebaDroid(void)
239
{
240
RSDK_THIS(AmoebaDroid);
241
242
if (self->invincibleTimer & 1) {
243
RSDK.CopyPalette(1, 16, 0, 128, 10);
244
245
self->direction = FLIP_NONE;
246
RSDK.DrawSprite(&self->animator, NULL, false);
247
RSDK.DrawSprite(&self->attractorTopAnimator, NULL, false);
248
RSDK.DrawSprite(&self->attractorSideAnimator, NULL, false);
249
250
self->direction = FLIP_X;
251
RSDK.DrawSprite(&self->attractorSideAnimator, NULL, false);
252
253
RSDK.CopyPalette(1, 0, 0, 128, 10);
254
}
255
else {
256
self->direction = FLIP_NONE;
257
RSDK.DrawSprite(&self->animator, NULL, false);
258
RSDK.DrawSprite(&self->attractorTopAnimator, NULL, false);
259
RSDK.DrawSprite(&self->attractorSideAnimator, NULL, false);
260
261
self->direction = FLIP_X;
262
RSDK.DrawSprite(&self->attractorSideAnimator, NULL, false);
263
}
264
}
265
266
void AmoebaDroid_Draw_BigBlob(void)
267
{
268
RSDK_THIS(AmoebaDroid);
269
Vector2 drawPos = self->position;
270
271
SpriteFrame *frame = RSDK.GetFrame(AmoebaDroid->aniFrames, 3, 0);
272
int32 angle = self->angle;
273
int32 sprY = frame->sprY;
274
275
int32 maxY = (frame->sprY + 96) << 8;
276
frame->height = 1;
277
drawPos.y -= RSDK.Sin256(self->angle) << 10;
278
279
for (int32 y = sprY << 8; y < maxY; angle += 2) {
280
self->scale.x = 0x200 + (RSDK.Sin256(frame->sprY + 2 * angle) >> 2);
281
frame->sprY = y >> 8;
282
283
if ((y >> 8) >= sprY)
284
RSDK.DrawSprite(&self->animator, &drawPos, false);
285
286
drawPos.y += 0x10000;
287
y += 0x100 + (RSDK.Sin256(angle) >> 1);
288
}
289
290
frame->sprY = sprY;
291
}
292
293
void AmoebaDroid_State_SetupArena(void)
294
{
295
RSDK_THIS(AmoebaDroid);
296
297
if (++self->timer >= 8) {
298
self->timer = 0;
299
Zone->playerBoundActiveL[0] = true;
300
Zone->playerBoundActiveR[0] = true;
301
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x;
302
Zone->cameraBoundsR[0] = (self->position.x >> 16) + ScreenInfo->center.x;
303
Zone->cameraBoundsT[0] = (self->position.y >> 16) - ScreenInfo->size.y;
304
Zone->cameraBoundsB[0] = (self->position.y >> 16);
305
AmoebaDroid->boundsL = (Zone->cameraBoundsL[0] + 64) << 16;
306
AmoebaDroid->boundsR = (Zone->cameraBoundsR[0] - 64) << 16;
307
AmoebaDroid->boundsM = self->position.x;
308
AmoebaDroid->boundsT = (Zone->cameraBoundsT[0] + 48) << 16;
309
AmoebaDroid->boundsB = (Zone->cameraBoundsB[0] - 8) << 16;
310
self->state = AmoebaDroid_State_SetupWaterLevel;
311
}
312
}
313
314
void AmoebaDroid_State_SetupWaterLevel(void)
315
{
316
RSDK_THIS(AmoebaDroid);
317
318
if (self->timer) {
319
self->timer++;
320
if (self->timer == 120) {
321
self->timer = 0;
322
self->visible = true;
323
self->position.y += -0x400000 - (ScreenInfo->size.y << 16);
324
self->state = AmoebaDroid_State_DropIn;
325
}
326
}
327
else {
328
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x) {
329
Music_TransitionTrack(TRACK_MINIBOSS, 0.0125);
330
++self->timer;
331
}
332
333
Water->waterLevel = 0x7FFFFFFF;
334
Water->newWaterLevel = 0x7FFFFFFF;
335
Water->targetWaterLevel = 0x7FFFFFFF;
336
Water->waterMoveSpeed = 0;
337
foreach_active(Water, water)
338
{
339
if (water->type == WATER_HEIGHT_TRIGGER)
340
destroyEntity(water);
341
}
342
RSDK.SetDrawGroupProperties(0, false, StateMachine_None);
343
RSDK.SetDrawGroupProperties(Zone->hudDrawGroup, false, StateMachine_None);
344
}
345
}
346
347
void AmoebaDroid_State_DropIn(void)
348
{
349
RSDK_THIS(AmoebaDroid);
350
351
self->position.y += 0x8000;
352
if (++self->timer == 90) {
353
self->timer = 0;
354
self->velocity.y = 0x8000;
355
self->state = AmoebaDroid_State_DropIntoPool;
356
}
357
358
AmoebaDroid_CheckHit();
359
}
360
361
void AmoebaDroid_State_DropIntoPool(void)
362
{
363
RSDK_THIS(AmoebaDroid);
364
365
self->velocity.y += 0x1800;
366
self->position.y += self->velocity.y;
367
368
if (self->position.y > AmoebaDroid->boundsB) {
369
self->velocity.y = self->velocity.y >> 2;
370
self->state = AmoebaDroid_State_SurfaceFromPool;
371
ChemicalPool_SetDeform(self->position.x, 0x100000);
372
CREATE_ENTITY(AmoebaDroid, INT_TO_VOID(AMOEBADROID_POOLSPLASH), self->position.x, self->position.y);
373
RSDK.PlaySfx(Water->sfxSplash, false, 255);
374
}
375
AmoebaDroid_CheckHit();
376
}
377
378
void AmoebaDroid_State_SurfaceFromPool(void)
379
{
380
RSDK_THIS(AmoebaDroid);
381
382
self->velocity.y -= 0x2800;
383
self->position.y += self->velocity.y;
384
385
if (self->position.y <= AmoebaDroid->boundsB) {
386
self->position.y = AmoebaDroid->boundsB;
387
self->offsetPos = self->position;
388
self->velocity.y = 0;
389
self->state = AmoebaDroid_State_ChooseAttack;
390
}
391
AmoebaDroid_CheckHit();
392
}
393
394
void AmoebaDroid_State_ChooseAttack(void)
395
{
396
RSDK_THIS(AmoebaDroid);
397
398
self->position.y = BadnikHelpers_Oscillate(self->offsetPos.y, 2, 10);
399
400
if (++self->timer == 48) {
401
self->timer = 3;
402
if (RSDK.Rand(0, 256) > 128)
403
self->state = AmoebaDroid_State_SwimLeft;
404
else
405
self->state = AmoebaDroid_State_SwimRight;
406
407
EntityAmoebaDroid *part = CREATE_ENTITY(AmoebaDroid, INT_TO_VOID(AMOEBADROID_BLOB_BIG), self->position.x, self->position.y);
408
part->parent = self;
409
self->blobs[0] = part;
410
}
411
AmoebaDroid_CheckHit();
412
}
413
414
void AmoebaDroid_State_SwimLeft(void)
415
{
416
RSDK_THIS(AmoebaDroid);
417
418
RSDK.ProcessAnimation(&self->attractorTopAnimator);
419
RSDK.ProcessAnimation(&self->attractorSideAnimator);
420
421
self->position.y = BadnikHelpers_Oscillate(self->offsetPos.y, 2, 10);
422
423
self->velocity.x -= 0x2000;
424
if (self->velocity.x < -0x40000)
425
self->velocity.x = -0x40000;
426
self->position.x += self->velocity.x;
427
428
if (self->timer <= 0) {
429
if (self->timer <= 0) {
430
self->velocity.x = 0;
431
self->velocity.y = 0;
432
self->state = AmoebaDroid_State_ExitPool;
433
ChemicalPool_SetDeform(self->position.x, -0xC0000);
434
CREATE_ENTITY(AmoebaDroid, INT_TO_VOID(AMOEBADROID_POOLSPLASH_DELAY), self->position.x, self->position.y);
435
RSDK.PlaySfx(Water->sfxSplash, false, 255);
436
}
437
}
438
else if (self->position.x < AmoebaDroid->boundsL + 0x400000) {
439
self->state = AmoebaDroid_State_SwimRight;
440
self->timer--;
441
}
442
443
if (!(Zone->timer & 0xF))
444
RSDK.PlaySfx(AmoebaDroid->sfxGather, false, 255);
445
446
ChemicalPool_SetDeform(self->position.x, -0x8000);
447
}
448
449
void AmoebaDroid_State_SwimRight(void)
450
{
451
RSDK_THIS(AmoebaDroid);
452
453
RSDK.ProcessAnimation(&self->attractorTopAnimator);
454
RSDK.ProcessAnimation(&self->attractorSideAnimator);
455
456
self->position.y = BadnikHelpers_Oscillate(self->offsetPos.y, 2, 10);
457
458
self->velocity.x += 0x2000;
459
if (self->velocity.x > 0x40000)
460
self->velocity.x = 0x40000;
461
self->position.x += self->velocity.x;
462
463
if (self->timer <= 0) {
464
if (self->timer >= 0) {
465
self->velocity.x = 0;
466
self->velocity.y = 0;
467
self->state = AmoebaDroid_State_ExitPool;
468
ChemicalPool_SetDeform(self->position.x, -0xC0000);
469
CREATE_ENTITY(AmoebaDroid, INT_TO_VOID(AMOEBADROID_POOLSPLASH_DELAY), self->position.x, self->position.y);
470
RSDK.PlaySfx(Water->sfxSplash, false, 255);
471
}
472
}
473
else if (self->position.x > AmoebaDroid->boundsR - 0x400000) {
474
self->state = AmoebaDroid_State_SwimLeft;
475
self->timer--;
476
}
477
478
if (!(Zone->timer & 0xF))
479
RSDK.PlaySfx(AmoebaDroid->sfxGather, false, 255);
480
481
ChemicalPool_SetDeform(self->position.x, -0x8000);
482
}
483
484
void AmoebaDroid_State_ExitPool(void)
485
{
486
RSDK_THIS(AmoebaDroid);
487
488
RSDK.ProcessAnimation(&self->attractorTopAnimator);
489
RSDK.ProcessAnimation(&self->attractorSideAnimator);
490
491
self->velocity.y -= 0x1800;
492
self->position.y += self->velocity.y;
493
494
if (self->position.y < AmoebaDroid->boundsT + 0x400000) {
495
self->timer = 3;
496
self->state = AmoebaDroid_State_BounceAttack;
497
}
498
AmoebaDroid_CheckHit();
499
}
500
501
void AmoebaDroid_State_BounceAttack(void)
502
{
503
RSDK_THIS(AmoebaDroid);
504
505
RSDK.ProcessAnimation(&self->attractorTopAnimator);
506
RSDK.ProcessAnimation(&self->attractorSideAnimator);
507
508
self->velocity.y += 0x2000;
509
self->position.y += self->velocity.y;
510
511
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
512
if (self->position.x < player1->position.x)
513
self->position.x = self->position.x + 0x10000;
514
else if (self->position.x > player1->position.x)
515
self->position.x = self->position.x - 0x10000;
516
517
if (self->velocity.y > 0 && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, self->collisionPlane, 0, 0x180000, true)) {
518
if (--self->timer <= 0) {
519
EntityAmoebaDroid *bigBlob = self->blobs[0];
520
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 6, &bigBlob->animator, false, 0);
521
bigBlob->drawFX = FX_NONE;
522
bigBlob->stateDraw = StateMachine_None;
523
bigBlob->state = AmoebaDroid_State_BigBlob_Disappear;
524
525
self->state = StateMachine_None;
526
for (int32 i = 0; i < AMOEBADROID_BLOB_COUNT; ++i) {
527
self->blobs[i] = CREATE_ENTITY(AmoebaDroid, INT_TO_VOID(AMOEBADROID_BLOB_SMALL), self->position.x, self->position.y);
528
}
529
530
self->timer = 0;
531
self->blobAngleY = 196;
532
self->blobAmplitude = 0x2800;
533
self->state = AmoebaDroid_State_GatherBlobs;
534
RSDK.PlaySfx(AmoebaDroid->sfxRelease, false, 255);
535
}
536
else {
537
self->velocity.y = MIN(-self->velocity.y, -0x20000);
538
for (int32 i = 0; i < AMOEBADROID_BLOB_COUNT; ++i) {
539
int32 x = self->position.x + RSDK.Rand(-0x200000, 0x200000);
540
int32 y = self->position.y + RSDK.Rand(0x100000, 0x180000);
541
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, x, y);
542
543
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 5, &debris->animator, false, 0);
544
debris->velocity.x = RSDK.Rand(-0x20000, 0x20000);
545
debris->velocity.y = RSDK.Rand(-0x40000, -0x20000);
546
debris->gravityStrength = 0x3800;
547
debris->inkEffect = INK_BLEND;
548
debris->drawGroup = Zone->objectDrawGroup[0];
549
debris->updateRange.x = 0x200000;
550
debris->updateRange.y = 0x200000;
551
}
552
553
RSDK.PlaySfx(AmoebaDroid->sfxBounce, false, 255);
554
}
555
}
556
557
AmoebaDroid_CheckHit();
558
}
559
560
void AmoebaDroid_State_GatherBlobs(void)
561
{
562
RSDK_THIS(AmoebaDroid);
563
564
RSDK.ProcessAnimation(&self->attractorTopAnimator);
565
RSDK.ProcessAnimation(&self->attractorSideAnimator);
566
567
AmoebaDroid_HandleSmallBlobMovement();
568
self->blobAngleX = (self->blobAngleX + 4) & 0xFF;
569
570
if (self->timer == 60) {
571
self->position.x += self->velocity.x;
572
self->position.y += self->velocity.y;
573
574
if (self->position.y <= AmoebaDroid->boundsT) {
575
self->offsetPos.x = self->position.x;
576
self->offsetPos.y = self->position.y;
577
self->timer = 0;
578
self->angle = 0;
579
self->position.y = AmoebaDroid->boundsT;
580
self->state = AmoebaDroid_State_SpinBlobs;
581
}
582
}
583
else {
584
self->position.y -= 0x8000;
585
586
if (++self->timer == 60) {
587
self->velocity.x = (AmoebaDroid->boundsM - self->position.x + RSDK.Rand(-0x800000, 0x800000)) >> 6;
588
self->velocity.y = (AmoebaDroid->boundsT - self->position.y) >> 6;
589
}
590
}
591
592
AmoebaDroid_CheckHit();
593
}
594
595
void AmoebaDroid_State_SpinBlobs(void)
596
{
597
RSDK_THIS(AmoebaDroid);
598
599
self->position.y = BadnikHelpers_Oscillate(self->offsetPos.y, 2, 10);
600
601
if (self->timer < 120) {
602
AmoebaDroid_HandleSmallBlobMovement();
603
if (self->offsetPos.x <= AmoebaDroid->boundsM)
604
self->blobAngleY -= 2;
605
else
606
self->blobAngleY += 2;
607
self->blobAngleY &= 0xFF;
608
self->blobAngleX = (self->blobAngleX + 4) & 0xFF;
609
RSDK.ProcessAnimation(&self->attractorTopAnimator);
610
RSDK.ProcessAnimation(&self->attractorSideAnimator);
611
}
612
613
AmoebaDroid_CheckHit();
614
615
if (++self->timer == 120) {
616
AmoebaDroid_HandleSmallBlobRelease(true);
617
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 1, &self->attractorTopAnimator, true, 0);
618
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 2, &self->attractorSideAnimator, true, 0);
619
RSDK.PlaySfx(AmoebaDroid->sfxRelease, false, 255);
620
}
621
else if (self->timer == 240) {
622
self->timer = 0;
623
self->angle = 0;
624
self->velocity.y = -0x20000;
625
self->state = AmoebaDroid_State_DropIntoPool;
626
}
627
}
628
629
void AmoebaDroid_State_BigBlob(void)
630
{
631
RSDK_THIS(AmoebaDroid);
632
633
EntityAmoebaDroid *parent = self->parent;
634
self->angle = (self->angle + 4) & 0xFF;
635
if (parent->state == AmoebaDroid_State_DropSignPost || parent->state == AmoebaDroid_State_Destroyed) {
636
if (self->alpha > 0) {
637
self->alpha -= 4;
638
if (self->alpha <= 0)
639
destroyEntity(self);
640
}
641
}
642
else {
643
AmoebaDroid_CheckPlayerHit();
644
645
if (self->alpha < 0x80) {
646
self->alpha++;
647
self->position.x = parent->position.x;
648
self->position.y = parent->position.y;
649
}
650
}
651
652
self->position.x = parent->position.x;
653
self->position.y = parent->position.y;
654
}
655
656
void AmoebaDroid_State_SmallBlob(void)
657
{
658
RSDK_THIS(AmoebaDroid);
659
660
if (self->alpha < 0xC0)
661
self->alpha += 4;
662
663
if (self->onGround) {
664
self->velocity.y += 0x2000;
665
self->position.x += self->velocity.x;
666
self->position.y += self->velocity.y;
667
668
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, self->collisionPlane, 0, 0x60000, true)) {
669
for (int32 i = 0; i < 4; ++i) {
670
int32 x = self->position.x + RSDK.Rand(-0x40000, 0x40000);
671
int32 y = self->position.y + RSDK.Rand(-0x40000, 0x40000);
672
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, x, y);
673
674
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 5, &debris->animator, false, 0);
675
debris->velocity.x = RSDK.Rand(-0x20000, 0x20000);
676
debris->velocity.y = -0x20000;
677
debris->gravityStrength = 0x3800;
678
debris->inkEffect = INK_BLEND;
679
debris->drawGroup = Zone->objectDrawGroup[0];
680
debris->updateRange.x = 0x200000;
681
debris->updateRange.y = 0x200000;
682
}
683
684
destroyEntity(self);
685
}
686
}
687
688
if (self->classID == AmoebaDroid->classID) {
689
if (self->interaction && self->drawGroup == Zone->objectDrawGroup[0])
690
AmoebaDroid_CheckPlayerHit();
691
692
if (!RSDK.CheckOnScreen(self, NULL))
693
destroyEntity(self);
694
}
695
}
696
697
void AmoebaDroid_State_BigBlob_Disappear(void)
698
{
699
RSDK_THIS(AmoebaDroid);
700
701
EntityAmoebaDroid *parent = self->parent;
702
if (parent) {
703
self->position.x = parent->position.x;
704
self->position.y = parent->position.y;
705
}
706
707
RSDK.ProcessAnimation(&self->animator);
708
709
if (self->animator.frameID == self->animator.frameCount - 1)
710
destroyEntity(self);
711
}
712
713
void AmoebaDroid_State_PoolSplash_Delayed(void)
714
{
715
RSDK_THIS(AmoebaDroid);
716
717
if (++self->timer == 16) {
718
self->timer = 0;
719
self->visible = true;
720
self->state = AmoebaDroid_State_PoolSplash;
721
}
722
}
723
724
void AmoebaDroid_State_PoolSplash(void)
725
{
726
RSDK_THIS(AmoebaDroid);
727
728
int32 pos = (self->position.x + 0x80000) >> 20;
729
foreach_active(ChemicalPool, pool)
730
{
731
if (pos >= pool->leftEdge && pos < pool->rightEdge)
732
self->position.y = pool->offsetY + ChemicalPool->surfaceDeformation[pos];
733
}
734
735
RSDK.ProcessAnimation(&self->animator);
736
737
if (self->animator.frameID == self->animator.frameCount - 1)
738
destroyEntity(self);
739
}
740
741
void AmoebaDroid_State_Destroyed(void)
742
{
743
RSDK_THIS(AmoebaDroid);
744
745
AmoebaDroid_Explode();
746
747
++self->timer;
748
if (self->timer == 30) {
749
Debris_CreateFromEntries(AmoebaDroid->aniFrames, AmoebaDroid->debrisInfo2, 7);
750
RSDK.SetSpriteAnimation(-1, 0, &self->attractorTopAnimator, true, 0);
751
RSDK.SetSpriteAnimation(-1, 0, &self->attractorSideAnimator, true, 0);
752
}
753
else if (self->timer == 60) {
754
Debris_CreateFromEntries(AmoebaDroid->aniFrames, AmoebaDroid->debrisInfo1, 7);
755
RSDK.SetSpriteAnimation(-1, 0, &self->animator, true, 0);
756
}
757
else if (self->timer == 90) {
758
Music_TransitionTrack(TRACK_STAGE, 0.0125);
759
self->timer = 0;
760
self->visible = false;
761
self->state = AmoebaDroid_State_DropSignPost;
762
}
763
}
764
765
void AmoebaDroid_State_DropSignPost(void)
766
{
767
RSDK_THIS(AmoebaDroid);
768
769
if (++self->timer == 60) {
770
foreach_all(SignPost, signPost)
771
{
772
signPost->position.x = self->position.x;
773
signPost->active = ACTIVE_NORMAL;
774
signPost->state = SignPost_State_Falling;
775
RSDK.PlaySfx(SignPost->sfxTwinkle, false, 255);
776
}
777
778
destroyEntity(self);
779
}
780
}
781
782
#if GAME_INCLUDE_EDITOR
783
void AmoebaDroid_EditorDraw(void)
784
{
785
RSDK_THIS(AmoebaDroid);
786
787
self->drawFX = FX_FLIP;
788
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 0, &self->animator, true, 0);
789
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 1, &self->attractorTopAnimator, true, 0);
790
RSDK.SetSpriteAnimation(AmoebaDroid->aniFrames, 2, &self->attractorSideAnimator, true, 0);
791
792
AmoebaDroid_Draw_AmoebaDroid();
793
794
if (showGizmos()) {
795
RSDK_DRAWING_OVERLAY(true);
796
797
DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER, -SCREEN_YSIZE, WIDE_SCR_XCENTER, 0, 1 | 2 | 4 | 8, 0x00C0F0);
798
799
RSDK_DRAWING_OVERLAY(false);
800
}
801
}
802
803
void AmoebaDroid_EditorLoad(void)
804
{
805
AmoebaDroid->aniFrames = RSDK.LoadSpriteAnimation("CPZ/AmoebaDroid.bin", SCOPE_STAGE);
806
AmoebaDroid->waterFrames = RSDK.LoadSpriteAnimation("Global/Water.bin", SCOPE_STAGE);
807
808
RSDK_ACTIVE_VAR(AmoebaDroid, type);
809
RSDK_ENUM_VAR("Boss", AMOEBADROID_BOSS);
810
}
811
#endif
812
813
void AmoebaDroid_Serialize(void) { RSDK_EDITABLE_VAR(AmoebaDroid, VAR_ENUM, type); }
814
815