Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/PGZ/HeavyShinobi.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: HeavyShinobi Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectHeavyShinobi *HeavyShinobi;
11
12
void HeavyShinobi_Update(void)
13
{
14
RSDK_THIS(HeavyShinobi);
15
16
StateMachine_Run(self->state);
17
}
18
19
void HeavyShinobi_LateUpdate(void) {}
20
21
void HeavyShinobi_StaticUpdate(void) {}
22
23
void HeavyShinobi_Draw(void)
24
{
25
RSDK_THIS(HeavyShinobi);
26
27
StateMachine_Run(self->stateDraw);
28
}
29
30
void HeavyShinobi_Create(void *data)
31
{
32
RSDK_THIS(HeavyShinobi);
33
34
self->drawFX = FX_ROTATE | FX_FLIP;
35
36
if (!SceneInfo->inEditor) {
37
if (globals->gameMode < MODE_TIMEATTACK) {
38
self->type = VOID_TO_INT(data);
39
40
switch (self->type) {
41
case SHINOBI_MAIN:
42
self->active = ACTIVE_BOUNDS;
43
self->visible = false;
44
self->drawGroup = Zone->objectDrawGroup[0];
45
46
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);
47
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, true, 0);
48
49
self->state = HeavyShinobi_State_Init;
50
self->stateDraw = HeavyShinobi_Draw_Shinobi;
51
self->updateRange.y = 0x800000;
52
self->updateRange.x = 0x800000;
53
break;
54
case SHINOBI_SLASH:
55
56
self->active = ACTIVE_NORMAL;
57
self->visible = true;
58
self->drawGroup = Zone->objectDrawGroup[0];
59
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 8, &self->mainAnimator, true, 0);
60
61
self->inkEffect = INK_ALPHA;
62
self->alpha = 0x100;
63
self->state = HeavyShinobi_StateSlash_Active;
64
self->stateDraw = HeavyShinobi_Draw_Slash;
65
self->updateRange.y = 0x800000;
66
self->updateRange.x = 0x800000;
67
break;
68
case SHINOBI_ASTERON:
69
self->active = ACTIVE_NORMAL;
70
self->visible = true;
71
self->drawGroup = Zone->objectDrawGroup[0] - 1;
72
73
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 9, &self->mainAnimator, true, 0);
74
self->updateRange.x = 0x1000000;
75
self->updateRange.y = 0x1000000;
76
self->alpha = 0xC0;
77
self->state = HeavyShinobi_StateAsteron_Thrown;
78
self->stateDraw = HeavyShinobi_Draw_Asteron;
79
break;
80
case SHINOBI_ASTERONSPIKE:
81
self->active = ACTIVE_NORMAL;
82
self->visible = true;
83
self->drawGroup = Zone->objectDrawGroup[0];
84
85
self->drawFX = FX_ROTATE | FX_FLIP;
86
self->state = HeavyShinobi_State_AsteronSpike;
87
self->stateDraw = HeavyShinobi_Draw_AsteronSpike;
88
self->updateRange.y = 0x800000;
89
self->updateRange.x = 0x800000;
90
break;
91
case SHINOBI_BOUNDS:
92
self->active = ACTIVE_NORMAL;
93
94
// Bug Details: Remember the PGZ2 boss skip?
95
// you guessed it, this is the fix, isPermanent just says "DO NOT OVERWRITE THIS ENTITY"
96
// since all entities spawned via RSDK.CreateEntity use the last 0x100 slots, and it loops around
97
#if GAME_VERSION != VER_100
98
self->isPermanent = true;
99
#endif
100
101
self->visible = true;
102
self->drawGroup = Zone->fgDrawGroup[1] - 1;
103
RSDK.SetSpriteAnimation(WoodChipper->aniFrames, 0, &self->mainAnimator, true, 0);
104
105
self->state = HeavyShinobi_StateBounds_WaitForPlayer;
106
self->stateDraw = HeavyShinobi_Draw_Bounds;
107
self->updateRange.y = 0x800000;
108
self->updateRange.x = 0x800000;
109
break;
110
111
default: break;
112
}
113
}
114
else {
115
destroyEntity(self);
116
}
117
}
118
}
119
120
void HeavyShinobi_StageLoad(void)
121
{
122
HeavyShinobi->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/Shinobi.bin", SCOPE_STAGE);
123
124
HeavyShinobi->hitboxShinobi.left = -16;
125
HeavyShinobi->hitboxShinobi.top = -25;
126
HeavyShinobi->hitboxShinobi.right = 16;
127
HeavyShinobi->hitboxShinobi.bottom = 14;
128
129
HeavyShinobi->hitboxSlashRange.left = -16;
130
HeavyShinobi->hitboxSlashRange.top = -25;
131
HeavyShinobi->hitboxSlashRange.right = 16;
132
HeavyShinobi->hitboxSlashRange.bottom = 25;
133
134
HeavyShinobi->hitboxUnused.left = -16;
135
HeavyShinobi->hitboxUnused.top = -16;
136
HeavyShinobi->hitboxUnused.right = 16;
137
HeavyShinobi->hitboxUnused.bottom = 16;
138
139
HeavyShinobi->hitboxSlash.left = -72;
140
HeavyShinobi->hitboxSlash.top = -64;
141
HeavyShinobi->hitboxSlash.right = 0;
142
HeavyShinobi->hitboxSlash.bottom = 25;
143
144
HeavyShinobi->hitboxAsteron.left = -8;
145
HeavyShinobi->hitboxAsteron.top = -8;
146
HeavyShinobi->hitboxAsteron.right = 8;
147
HeavyShinobi->hitboxAsteron.bottom = 8;
148
149
HeavyShinobi->hitboxBounds.left = -40;
150
HeavyShinobi->hitboxBounds.top = -336;
151
HeavyShinobi->hitboxBounds.right = 40;
152
HeavyShinobi->hitboxBounds.bottom = 336;
153
154
HeavyShinobi->hitboxAsteronSpike.left = -3;
155
HeavyShinobi->hitboxAsteronSpike.top = -3;
156
HeavyShinobi->hitboxAsteronSpike.right = 3;
157
HeavyShinobi->hitboxAsteronSpike.bottom = 3;
158
159
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[0], true, 0);
160
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[1], true, 0);
161
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[2], true, 0);
162
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[3], true, 0);
163
164
HeavyShinobi->activeShurikens = 0;
165
HeavyShinobi->health = 8;
166
HeavyShinobi->invincibilityTimer = 0;
167
168
HeavyShinobi->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
169
HeavyShinobi->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
170
HeavyShinobi->sfxDefeat = RSDK.GetSfx("PSZ/ShinobiDefeat.wav");
171
HeavyShinobi->sfxDropIn = RSDK.GetSfx("PSZ/ShinobiDropIn.wav");
172
HeavyShinobi->sfxExplode = RSDK.GetSfx("PSZ/ShinobiExplode.wav");
173
HeavyShinobi->sfxGlitch = RSDK.GetSfx("PSZ/ShinobiGlitch.wav");
174
HeavyShinobi->sfxJump = RSDK.GetSfx("PSZ/ShinobiJump.wav");
175
HeavyShinobi->sfxParry = RSDK.GetSfx("PSZ/ShinobiParry.wav");
176
HeavyShinobi->sfxSlash = RSDK.GetSfx("PSZ/ShinobiSlash.wav");
177
HeavyShinobi->sfxStick = RSDK.GetSfx("PSZ/ShinobiStick.wav");
178
HeavyShinobi->sfxThrow = RSDK.GetSfx("PSZ/ShinobiThrow.wav");
179
}
180
181
void HeavyShinobi_HandleAfterFX(void)
182
{
183
RSDK_THIS(HeavyShinobi);
184
185
for (int32 i = 15; i > 0; --i) {
186
HeavyShinobi->storePos[i] = HeavyShinobi->storePos[i - 1];
187
}
188
189
HeavyShinobi->storePos[0].x = self->position.x;
190
HeavyShinobi->storePos[0].y = self->position.y;
191
192
for (int32 i = 0; i < 4; ++i) {
193
int32 storeAnim = self->mainAnimator.animationID;
194
if (HeavyShinobi->fxTrailAnimator[i].animationID != storeAnim) {
195
if (HeavyShinobi->storedAnimIDs[i] != storeAnim) {
196
HeavyShinobi->storedAnimIDs[i] = self->mainAnimator.animationID;
197
HeavyShinobi->storedIDs[i] = 4 * (i + 1);
198
}
199
}
200
201
if (HeavyShinobi->storedIDs[i]) {
202
203
--HeavyShinobi->storedIDs[i];
204
if (!HeavyShinobi->storedIDs[i]) {
205
int32 id = HeavyShinobi->storedAnimIDs[i];
206
if (id && (id <= 14 || id > 16)) {
207
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, HeavyShinobi->storedAnimIDs[i], &HeavyShinobi->fxTrailAnimator[i], false, 0);
208
}
209
else {
210
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[i], false, 0);
211
HeavyShinobi->fxTrailAnimator[i].animationID = (uint8)-1;
212
}
213
}
214
}
215
}
216
217
if (HeavyShinobi->invincibilityTimer)
218
HeavyShinobi->invincibilityTimer--;
219
}
220
221
void HeavyShinobi_HandleSlash(EntityPlayer *player)
222
{
223
RSDK_THIS(HeavyShinobi);
224
225
RSDK.PlaySfx(HeavyShinobi->sfxSlash, false, 255);
226
227
self->direction = player->position.x >= self->position.x;
228
RSDK.SetSpriteAnimation(-1, 0, &self->fxAnimator, true, 0);
229
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 4, &self->mainAnimator, true, 1);
230
231
CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_SLASH), self->position.x, self->position.y)->direction = self->direction;
232
233
int32 delay = 4;
234
int32 alpha = 0xC0;
235
for (int32 i = 3; i >= 0; --i) {
236
EntityHeavyShinobi *slash = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_SLASH), self->position.x, self->position.y);
237
slash->mainAnimator.frameDuration += delay;
238
slash->alpha = alpha;
239
slash->direction = self->direction;
240
241
delay += 4;
242
alpha -= 32;
243
}
244
245
self->state = HeavyShinobi_State_Slash;
246
}
247
248
void HeavyShinobi_StartJump(void)
249
{
250
RSDK_THIS(HeavyShinobi);
251
252
RSDK.SetSpriteAnimation(-1, 0, &self->fxAnimator, true, 0);
253
RSDK.PlaySfx(HeavyShinobi->sfxJump, false, 255);
254
self->timer = 28;
255
256
if (self->position.x >= (Zone->cameraBoundsL[0] + 144) << 16) {
257
if (self->position.x <= (Zone->cameraBoundsR[0] - 144) << 16)
258
self->velocity.x = (RSDK.Rand(0, 2) << 18) - 0x20000;
259
else
260
self->velocity.x = -0x20000;
261
}
262
else {
263
self->velocity.x = 0x20000;
264
}
265
266
if (self->velocity.x >= 0)
267
self->velocity.x += 0x1000;
268
else
269
self->velocity.x -= 0x1000;
270
self->velocity.y = -0x70000;
271
272
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 3, &self->mainAnimator, true, 0);
273
self->state = HeavyShinobi_State_Jump;
274
}
275
276
void HeavyShinobi_Hit(void)
277
{
278
RSDK_THIS(HeavyShinobi);
279
280
if (HeavyShinobi->health)
281
HeavyShinobi->health--;
282
283
if (!HeavyShinobi->health) {
284
SceneInfo->timeEnabled = false;
285
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
286
RSDK.PlaySfx(HeavyShinobi->sfxExplosion, false, 255);
287
self->timer = 120;
288
self->state = HeavyShinobi_State_Destroyed;
289
}
290
else {
291
RSDK.PlaySfx(HeavyShinobi->sfxHit, false, 255);
292
self->timer -= 60;
293
HeavyShinobi->invincibilityTimer = 30;
294
}
295
}
296
297
void HeavyShinobi_Explode(void)
298
{
299
RSDK_THIS(HeavyShinobi);
300
301
if (!(Zone->timer & 7)) {
302
RSDK.PlaySfx(HeavyShinobi->sfxExplosion, false, 255);
303
304
if (!(Zone->timer & 0xF)) {
305
int32 x = self->position.x + (RSDK.Rand(-19, 20) << 16);
306
int32 y = self->position.y + (RSDK.Rand(-24, 25) << 16);
307
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);
308
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
309
}
310
}
311
}
312
313
void HeavyShinobi_State_Init(void)
314
{
315
RSDK_THIS(HeavyShinobi);
316
317
if (++self->timer >= 2) {
318
self->timer = 0;
319
320
Zone->playerBoundActiveR[0] = true;
321
Zone->cameraBoundsR[0] = ScreenInfo->center.x + 80 + (self->position.x >> 16);
322
Zone->playerBoundActiveB[0] = true;
323
Zone->cameraBoundsB[0] = (self->position.y >> 16) + 68;
324
325
self->position.y = (ScreenInfo->position.y - 192) << 16;
326
for (int32 i = 0; i < 16; ++i) HeavyShinobi->storePos[i] = self->position;
327
328
self->active = ACTIVE_NORMAL;
329
self->state = HeavyShinobi_State_SetupArena;
330
}
331
}
332
333
void HeavyShinobi_State_SetupArena(void)
334
{
335
RSDK_THIS(HeavyShinobi);
336
337
Zone->playerBoundActiveL[0] = true;
338
Zone->cameraBoundsL[0] = ScreenInfo->position.x;
339
340
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x - 0x500000) {
341
Zone->playerBoundActiveL[0] = true;
342
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x - 80;
343
344
int32 y = (Zone->cameraBoundsB[0] - 376) << 16;
345
346
CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_BOUNDS), (Zone->cameraBoundsL[0] + 40) << 16, y);
347
348
EntityHeavyShinobi *rBounds = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_BOUNDS), (Zone->cameraBoundsR[0] - 40) << 16, y);
349
rBounds->timer = 1;
350
rBounds->position.y = (Zone->cameraBoundsB[0] - 99) << 16;
351
rBounds->state = HeavyShinobi_StateBounds_Active;
352
353
self->state = HeavyShinobi_State_StartFight;
354
}
355
}
356
357
void HeavyShinobi_State_StartFight(void)
358
{
359
RSDK_THIS(HeavyShinobi);
360
361
Zone->playerBoundActiveL[0] = true;
362
Zone->cameraBoundsL[0] = ScreenInfo->position.x;
363
364
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x) {
365
Zone->playerBoundActiveL[0] = true;
366
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x - 80;
367
368
Music_TransitionTrack(TRACK_HBHBOSS, 0.0125);
369
self->visible = true;
370
HeavyShinobi_StartJump();
371
self->velocity.x = 0;
372
self->timer = 0;
373
}
374
}
375
376
void HeavyShinobi_State_Idle(void)
377
{
378
RSDK_THIS(HeavyShinobi);
379
380
HeavyShinobi_HandleAfterFX();
381
382
RSDK.ProcessAnimation(&self->mainAnimator);
383
RSDK.ProcessAnimation(&self->fxAnimator);
384
for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);
385
386
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
387
388
EntityPlayer *player = Player_GetNearestPlayerX();
389
390
if (abs(player->position.x - self->position.x) < 0x500000 && player->state != Ice_PlayerState_Frozen) {
391
HeavyShinobi_HandleSlash(player);
392
}
393
else {
394
if (!HeavyShinobi->activeShurikens && --self->timer <= 0)
395
HeavyShinobi_StartJump();
396
}
397
}
398
399
void HeavyShinobi_State_Slash(void)
400
{
401
RSDK_THIS(HeavyShinobi);
402
403
HeavyShinobi_HandleAfterFX();
404
405
RSDK.ProcessAnimation(&self->mainAnimator);
406
RSDK.ProcessAnimation(&self->fxAnimator);
407
for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);
408
409
if (self->mainAnimator.frameID == 12)
410
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);
411
412
self->position.y += self->velocity.y;
413
self->velocity.y += 0x2800;
414
415
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true))
416
self->velocity.y = 0;
417
418
if (self->mainAnimator.frameID == self->mainAnimator.frameCount - 1) {
419
EntityPlayer *player = Player_GetNearestPlayerX();
420
if (abs(player->position.x - self->position.x) >= 0x500000 || player->state == Ice_PlayerState_Frozen)
421
HeavyShinobi_StartJump();
422
else
423
HeavyShinobi_HandleSlash(player);
424
}
425
426
foreach_active(Player, player)
427
{
428
if (Player_CheckCollisionBox(player, self, &HeavyShinobi->hitboxSlashRange) && Player_CheckValidState(player)) {
429
Ice_FreezePlayer(player);
430
player->timer = 3;
431
player->onGround = false;
432
player->velocity.x = player->position.x < self->position.x ? -0x30000 : 0x30000;
433
player->velocity.y = -0x38000;
434
}
435
}
436
}
437
438
void HeavyShinobi_State_Jump(void)
439
{
440
RSDK_THIS(HeavyShinobi);
441
442
HeavyShinobi_HandleAfterFX();
443
444
for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);
445
RSDK.ProcessAnimation(&self->mainAnimator);
446
447
self->position.x += self->velocity.x;
448
self->position.y += self->velocity.y;
449
self->velocity.y += 0x2800;
450
451
if (self->velocity.x >= 0) {
452
if (self->position.x > (Zone->cameraBoundsR[0] - 104) << 16) {
453
self->velocity.x = 0;
454
self->position.x = (Zone->cameraBoundsR[0] - 104) << 16;
455
}
456
}
457
else {
458
if (self->position.x < (Zone->cameraBoundsL[0] + 104) << 16) {
459
self->velocity.x = 0;
460
self->position.x = (Zone->cameraBoundsL[0] + 104) << 16;
461
}
462
}
463
464
if (!--self->timer) {
465
if (!HeavyShinobi->activeShurikens) {
466
int32 count = 0;
467
switch (HeavyShinobi->health) {
468
case 1:
469
case 2: count = 3; break;
470
471
case 3:
472
case 4:
473
case 5: count = 2; break;
474
475
case 6:
476
case 7: count = 1; break;
477
478
case 8:
479
default: count = 0; break;
480
}
481
482
if (count)
483
RSDK.PlaySfx(HeavyShinobi->sfxThrow, false, 0xFF);
484
485
for (int32 i = 0; i < count; ++i) {
486
EntityHeavyShinobi *asteron = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_ASTERON), self->position.x, self->position.y);
487
asteron->direction = FLIP_NONE;
488
asteron->mainAnimator.frameID = RSDK.Rand(0, 8);
489
asteron->timer = 180;
490
491
switch (count) {
492
case 1: asteron->angle = 64; break;
493
494
case 2:
495
if (!i)
496
asteron->angle = 96;
497
else
498
asteron->angle = 32;
499
break;
500
501
case 3:
502
switch (i) {
503
case 0: asteron->angle = 96; break;
504
case 1: asteron->angle = 64; break;
505
case 2: asteron->angle = 32; break;
506
}
507
break;
508
}
509
510
asteron->angle += RSDK.Rand(-16, 17);
511
asteron->velocity.x = 0x300 * RSDK.Cos256(asteron->angle);
512
asteron->velocity.y = 0x300 * RSDK.Sin256(asteron->angle);
513
++HeavyShinobi->activeShurikens;
514
}
515
}
516
}
517
518
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true)) {
519
self->velocity.x = 0;
520
self->velocity.y = 0;
521
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
522
523
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);
524
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);
525
self->timer = RSDK.Rand(30, 91);
526
self->state = HeavyShinobi_State_Idle;
527
}
528
else {
529
foreach_active(Player, player)
530
{
531
if (player->state != Ice_PlayerState_Frozen && Player_CheckBadnikTouch(player, self, &HeavyShinobi->hitboxShinobi)
532
&& Player_CheckBossHit(player, self)) {
533
if (player->position.x >= self->position.x) {
534
self->direction = FLIP_X;
535
self->velocity.x = -0x20000;
536
}
537
else {
538
self->direction = FLIP_NONE;
539
self->velocity.x = 0x20000;
540
}
541
self->velocity.y = -0x40000;
542
543
int32 angle = RSDK.ATan2(player->position.x - self->position.x, player->position.y - self->position.y);
544
player->velocity.x = 0x380 * RSDK.Cos256(angle);
545
player->velocity.y = 0x380 * RSDK.Sin256(angle);
546
self->timer = 180;
547
self->glitchTimer = 0;
548
549
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 15, &self->mainAnimator, true, 0);
550
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);
551
RSDK.PlaySfx(HeavyShinobi->sfxParry, false, 255);
552
self->state = HeavyShinobi_State_Glitched;
553
}
554
}
555
}
556
}
557
558
void HeavyShinobi_State_Glitched(void)
559
{
560
RSDK_THIS(HeavyShinobi);
561
562
if (!(self->glitchTimer % 40))
563
RSDK.PlaySfx(HeavyShinobi->sfxGlitch, false, 255);
564
++self->glitchTimer;
565
566
HeavyShinobi_HandleAfterFX();
567
568
RSDK.ProcessAnimation(&self->mainAnimator);
569
570
self->position.x += self->velocity.x;
571
self->position.y += self->velocity.y;
572
self->velocity.y += 0x3800;
573
574
if (self->velocity.x >= 0) {
575
if (self->position.x > (Zone->cameraBoundsR[0] - 104) << 16) {
576
self->velocity.x = 0;
577
self->position.x = (Zone->cameraBoundsR[0] - 104) << 16;
578
}
579
}
580
else {
581
if (self->position.x < (Zone->cameraBoundsL[0] + 104) << 16) {
582
self->velocity.x = 0;
583
self->position.x = (Zone->cameraBoundsL[0] + 104) << 16;
584
}
585
}
586
587
if (self->velocity.y > 0x2800 && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true)) {
588
RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
589
self->velocity.x = 0;
590
self->velocity.y = 0;
591
592
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 16, &self->mainAnimator, false, 0);
593
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);
594
595
if (--self->timer <= 0) {
596
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);
597
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);
598
599
self->timer = RSDK.Rand(30, 91);
600
self->state = HeavyShinobi_State_Idle;
601
RSDK.StopSfx(HeavyShinobi->sfxGlitch);
602
}
603
}
604
605
if (!HeavyShinobi->invincibilityTimer) {
606
foreach_active(Player, player)
607
{
608
if (player->state != Ice_PlayerState_Frozen && Player_CheckBadnikTouch(player, self, &HeavyShinobi->hitboxShinobi)
609
&& Player_CheckBossHit(player, self)) {
610
HeavyShinobi_Hit();
611
foreach_break;
612
}
613
}
614
}
615
}
616
617
void HeavyShinobi_State_Destroyed(void)
618
{
619
RSDK_THIS(HeavyShinobi);
620
621
HeavyShinobi_HandleAfterFX();
622
623
RSDK.ProcessAnimation(&self->mainAnimator);
624
RSDK.ProcessAnimation(&self->fxAnimator);
625
626
self->position.x += self->velocity.x;
627
self->position.y += self->velocity.y;
628
self->velocity.y += 0x3800;
629
630
if (self->velocity.x >= 0) {
631
if (self->position.x > (Zone->cameraBoundsR[0] - 104) << 16) {
632
self->velocity.x = 0;
633
self->position.x = (Zone->cameraBoundsR[0] - 104) << 16;
634
}
635
}
636
else {
637
if (self->position.x < (Zone->cameraBoundsL[0] + 104) << 16) {
638
self->velocity.x = 0;
639
self->position.x = (Zone->cameraBoundsL[0] + 104) << 16;
640
}
641
}
642
643
if (self->velocity.y > 0x2800 && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true)) {
644
self->velocity.x = 0;
645
self->velocity.y = 0;
646
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
647
648
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 16, &self->mainAnimator, false, 0);
649
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);
650
}
651
652
HeavyShinobi_Explode();
653
654
if (--self->timer <= 0) {
655
self->velocity.y = -0x70000;
656
657
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 3, &self->mainAnimator, true, 0);
658
RSDK.SetSpriteAnimation(-1, 0, &self->fxAnimator, true, 0);
659
660
RSDK.PlaySfx(HeavyShinobi->sfxDefeat, false, 255);
661
self->state = HeavyShinobi_State_Finished;
662
}
663
}
664
665
void HeavyShinobi_State_Finished(void)
666
{
667
RSDK_THIS(HeavyShinobi);
668
669
HeavyShinobi_HandleAfterFX();
670
671
RSDK.ProcessAnimation(&self->mainAnimator);
672
RSDK.ProcessAnimation(&self->fxAnimator);
673
for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);
674
675
self->position.y += self->velocity.y;
676
self->velocity.y += 0x2800;
677
678
if (self->velocity.y > 0)
679
self->drawGroup = Zone->objectDrawGroup[1];
680
681
if (!RSDK.CheckOnScreen(self, &self->updateRange)) {
682
Music_TransitionTrack(TRACK_STAGE, 0.0125);
683
Zone->cameraBoundsR[0] += WIDE_SCR_XSIZE;
684
HeavyShinobi->health = -1;
685
destroyEntity(self);
686
}
687
}
688
689
void HeavyShinobi_Draw_Shinobi(void)
690
{
691
RSDK_THIS(HeavyShinobi);
692
693
RSDK.DrawSprite(&self->fxAnimator, NULL, false);
694
695
self->inkEffect = INK_ALPHA;
696
self->alpha = 0x60;
697
698
for (int32 i = 3; i >= 0; --i) {
699
RSDK.DrawSprite(&HeavyShinobi->fxTrailAnimator[i], &HeavyShinobi->storePos[i << 2], false);
700
self->alpha += 0x20;
701
}
702
703
self->inkEffect = INK_NONE;
704
if (HeavyShinobi->invincibilityTimer & 1)
705
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
706
707
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
708
709
RSDK.SetPaletteEntry(0, 128, 0x000000);
710
}
711
712
void HeavyShinobi_StateSlash_Active(void)
713
{
714
RSDK_THIS(HeavyShinobi);
715
716
RSDK.ProcessAnimation(&self->mainAnimator);
717
718
if (self->mainAnimator.frameID != self->mainAnimator.frameCount - 1) {
719
foreach_active(Player, player)
720
{
721
if (Player_CheckCollisionTouch(player, self, &HeavyShinobi->hitboxSlash) && Player_CheckValidState(player)) {
722
Ice_FreezePlayer(player);
723
player->timer = 3;
724
player->onGround = false;
725
player->velocity.y = -0x38000;
726
if (player->position.x < self->position.x)
727
player->velocity.x = -0x30000;
728
else
729
player->velocity.x = 0x30000;
730
}
731
}
732
}
733
else {
734
destroyEntity(self);
735
}
736
}
737
738
void HeavyShinobi_Draw_Slash(void)
739
{
740
RSDK_THIS(HeavyShinobi);
741
742
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
743
}
744
745
void HeavyShinobi_StateAsteron_Thrown(void)
746
{
747
RSDK_THIS(HeavyShinobi);
748
749
if (HeavyShinobi->health) {
750
RSDK.ProcessAnimation(&self->mainAnimator);
751
752
self->position.x += self->velocity.x;
753
self->position.y += self->velocity.y;
754
755
bool32 isStuck = false;
756
foreach_active(Player, player)
757
{
758
if (Player_CheckCollisionTouch(player, self, &HeavyShinobi->hitboxAsteron)) {
759
if (player->state == Ice_PlayerState_Frozen) {
760
self->playerPtr = player;
761
self->playerDistance.x = self->position.x - player->position.x;
762
self->playerDistance.y = self->position.y - player->position.y;
763
764
isStuck = true;
765
foreach_break;
766
}
767
768
#if MANIA_USE_PLUS
769
if (Player_CheckMightyShellHit(player, self, -0x400, -0x600)) {
770
self->interaction = false;
771
self->state = HeavyShinobi_StateAsteron_Debris;
772
--HeavyShinobi->activeShurikens;
773
}
774
else {
775
#endif
776
Player_Hurt(player, self);
777
#if MANIA_USE_PLUS
778
}
779
#endif
780
}
781
}
782
783
if (!isStuck && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, true))
784
isStuck = true;
785
786
if (!isStuck) {
787
foreach_active(HeavyShinobi, boss)
788
{
789
if (boss->type == SHINOBI_BOUNDS
790
&& RSDK.CheckObjectCollisionBox(boss, &HeavyShinobi->hitboxBounds, self, &HeavyShinobi->hitboxAsteron, true))
791
isStuck = true;
792
}
793
}
794
795
if (isStuck) {
796
if (self->mainAnimator.frameID & 1)
797
self->mainAnimator.frameID = (self->mainAnimator.frameID + 1) & 7;
798
799
RSDK.PlaySfx(HeavyShinobi->sfxStick, false, 255);
800
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, (self->mainAnimator.frameID >> 1) + 10, &self->fxAnimator, true, 0);
801
self->state = HeavyShinobi_StateAsteron_Explode;
802
}
803
804
if (!RSDK.CheckOnScreen(self, &self->updateRange))
805
destroyEntity(self);
806
}
807
else {
808
RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);
809
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;
810
811
destroyEntity(self);
812
}
813
}
814
815
void HeavyShinobi_StateAsteron_Debris(void)
816
{
817
RSDK_THIS(HeavyShinobi);
818
819
self->position.x += self->velocity.x;
820
self->position.y += self->velocity.y;
821
self->velocity.y += 0x3800;
822
823
self->visible ^= true;
824
825
if (!RSDK.CheckOnScreen(self, &self->updateRange))
826
destroyEntity(self);
827
}
828
829
void HeavyShinobi_StateAsteron_Explode(void)
830
{
831
RSDK_THIS(HeavyShinobi);
832
833
if (!HeavyShinobi->health) {
834
RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);
835
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;
836
837
destroyEntity(self);
838
}
839
else {
840
RSDK.ProcessAnimation(&self->fxAnimator);
841
self->fxAnimator.speed += 2;
842
843
foreach_active(Player, player)
844
{
845
if (self->playerPtr == player) {
846
if (player->state != Ice_PlayerState_Frozen) {
847
RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);
848
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup =
849
Zone->objectDrawGroup[1] + 2;
850
--HeavyShinobi->activeShurikens;
851
852
destroyEntity(self);
853
foreach_break;
854
}
855
}
856
else if ((player->state != Ice_PlayerState_Frozen || !self->playerPtr)
857
&& Player_CheckBadnikTouch(player, self, &HeavyShinobi->hitboxAsteron) && Player_CheckItemBreak(player, self, true)) {
858
RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);
859
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;
860
--HeavyShinobi->activeShurikens;
861
862
destroyEntity(self);
863
foreach_break;
864
}
865
}
866
867
EntityPlayer *playerPtr = self->playerPtr;
868
if (playerPtr) {
869
self->position.x = playerPtr->position.x + self->playerDistance.x;
870
self->position.y = playerPtr->position.y + self->playerDistance.y;
871
}
872
873
if (--self->timer <= 0) {
874
--HeavyShinobi->activeShurikens;
875
RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);
876
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;
877
878
for (int32 i = 0; i < 5; ++i) {
879
EntityHeavyShinobi *child = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_ASTERONSPIKE), self->position.x, self->position.y);
880
child->rotation = (self->mainAnimator.frameID & 0xFFFFFFFE) << 6;
881
child->direction = self->direction;
882
883
switch (i) {
884
case 0:
885
child->angle = (32 * ((self->mainAnimator.frameID & 0xFE) - 2));
886
RSDK.Cos512(child->rotation);
887
child->position.y -= RSDK.Sin512(child->rotation) << 10;
888
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 17, &child->mainAnimator, true, 0);
889
break;
890
891
case 1:
892
child->angle = (32 * (self->mainAnimator.frameID & 0xFE) - 120);
893
child->position.x -= 0x380 * RSDK.Cos512(child->rotation);
894
child->position.y -= 0x180 * RSDK.Sin512(child->rotation);
895
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 18, &child->mainAnimator, true, 0);
896
break;
897
898
case 2:
899
child->angle = (32 * (self->mainAnimator.frameID & 0xFE) - 8);
900
child->position.x += 0x380 * RSDK.Cos512(child->rotation);
901
child->position.y -= 0x180 * RSDK.Sin512(child->rotation);
902
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 18, &child->mainAnimator, true, 0);
903
child->direction ^= FLIP_X;
904
break;
905
906
case 3:
907
child->angle = (32 * ((self->mainAnimator.frameID & 0xFE) + 3));
908
child->position.x -= 0x380 * RSDK.Cos512(child->rotation);
909
child->position.y += 0x380 * RSDK.Sin512(child->rotation);
910
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 19, &child->mainAnimator, true, 0);
911
break;
912
913
case 4:
914
child->angle = (32 * ((self->mainAnimator.frameID & 0xFE) + 1));
915
child->position.x += 0x380 * RSDK.Cos512(child->rotation);
916
child->position.y += 0x380 * RSDK.Sin512(child->rotation);
917
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 19, &child->mainAnimator, true, 0);
918
child->direction ^= FLIP_X;
919
break;
920
921
default: break;
922
}
923
924
child->velocity.x = 0x300 * RSDK.Cos256(child->angle);
925
child->velocity.y = 0x300 * RSDK.Sin256(child->angle);
926
}
927
928
if (playerPtr) {
929
Ice_BreakPlayerBlock(playerPtr);
930
playerPtr->state = Player_State_Air;
931
Player_Hurt(playerPtr, self);
932
}
933
934
destroyEntity(self);
935
}
936
937
if (!RSDK.CheckOnScreen(self, &self->updateRange))
938
destroyEntity(self);
939
}
940
}
941
942
void HeavyShinobi_Draw_Asteron(void)
943
{
944
RSDK_THIS(HeavyShinobi);
945
946
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
947
self->inkEffect = INK_ADD;
948
949
RSDK.DrawSprite(&self->fxAnimator, NULL, false);
950
self->inkEffect = INK_NONE;
951
}
952
953
void HeavyShinobi_State_AsteronSpike(void)
954
{
955
RSDK_THIS(HeavyShinobi);
956
957
if (HeavyShinobi->health) {
958
RSDK.ProcessAnimation(&self->mainAnimator);
959
960
self->position.x += self->velocity.x;
961
self->position.y += self->velocity.y;
962
963
foreach_active(Player, player)
964
{
965
if (Player_CheckCollisionTouch(player, self, &HeavyShinobi->hitboxAsteronSpike) && player->state != Ice_PlayerState_Frozen) {
966
#if MANIA_USE_PLUS
967
if (Player_CheckMightyShellHit(player, self, -0x400, -0x600)) {
968
self->interaction = false;
969
self->state = HeavyShinobi_StateAsteron_Debris;
970
}
971
else {
972
#endif
973
Player_Hurt(player, self);
974
#if MANIA_USE_PLUS
975
}
976
#endif
977
}
978
}
979
980
if (!RSDK.CheckOnScreen(self, &self->updateRange))
981
destroyEntity(self);
982
}
983
else {
984
RSDK.PlaySfx(HeavyShinobi->sfxExplosion, false, 255);
985
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[0];
986
987
destroyEntity(self);
988
}
989
}
990
991
void HeavyShinobi_Draw_AsteronSpike(void)
992
{
993
RSDK_THIS(HeavyShinobi);
994
995
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
996
}
997
998
void HeavyShinobi_StateBounds_WaitForPlayer(void)
999
{
1000
RSDK_THIS(HeavyShinobi);
1001
1002
self->position.y += self->velocity.y;
1003
self->velocity.y += 0x4800;
1004
1005
if (self->position.y >= (Zone->cameraBoundsB[0] - 99) << 16) {
1006
self->position.y = (Zone->cameraBoundsB[0] - 99) << 16;
1007
self->state = HeavyShinobi_StateBounds_Active;
1008
}
1009
1010
foreach_active(Player, player) { Player_CheckCollisionBox(player, self, &HeavyShinobi->hitboxBounds); }
1011
}
1012
1013
void HeavyShinobi_StateBounds_Active(void)
1014
{
1015
RSDK_THIS(HeavyShinobi);
1016
1017
foreach_active(Player, player)
1018
{
1019
if (Player_CheckCollisionBox(player, self, &HeavyShinobi->hitboxBounds)) {
1020
Hitbox *playerHitbox = Player_GetHitbox(player);
1021
1022
int32 left = 0;
1023
int32 right = 0;
1024
if (self->timer) {
1025
left = HeavyShinobi->hitboxBounds.left;
1026
right = playerHitbox->right;
1027
}
1028
else {
1029
left = HeavyShinobi->hitboxBounds.right;
1030
right = playerHitbox->left;
1031
}
1032
1033
player->position.x = self->position.x + ((left - right) << 16);
1034
}
1035
}
1036
1037
if (HeavyShinobi->health == -1) {
1038
RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);
1039
1040
for (int32 i = 0; i < 0x80; ++i) {
1041
int32 x = self->position.x + (RSDK.Rand(-64, 65) << 16);
1042
int32 y = self->position.y + (RSDK.Rand(-80, 81) << 16);
1043
EntityIce *ice = CREATE_ENTITY(Ice, INT_TO_VOID(ICE_CHILD_SHARD), x, y);
1044
1045
RSDK.SetSpriteAnimation(WoodChipper->aniFrames, 1, &ice->blockAnimator, true, 0);
1046
ice->velocity.x = RSDK.Rand(-6, 8) << 15;
1047
ice->velocity.y = RSDK.Rand(-10, 2) << 15;
1048
ice->direction = RSDK.Rand(0, 4);
1049
ice->blockAnimator.speed = RSDK.Rand(1, 4);
1050
ice->active = ACTIVE_NORMAL;
1051
}
1052
1053
destroyEntity(self);
1054
}
1055
}
1056
1057
void HeavyShinobi_Draw_Bounds(void)
1058
{
1059
RSDK_THIS(HeavyShinobi);
1060
Vector2 drawPos;
1061
1062
drawPos.x = self->position.x;
1063
drawPos.y = self->position.y - 0x880000;
1064
self->mainAnimator.frameID = 3;
1065
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1066
1067
self->mainAnimator.frameID = 4;
1068
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1069
1070
drawPos.y += 0x500000;
1071
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1072
1073
drawPos.y += 0x500000;
1074
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1075
1076
self->direction = FLIP_Y;
1077
self->mainAnimator.frameID = 3;
1078
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
1079
1080
self->direction = FLIP_NONE;
1081
}
1082
1083
#if GAME_INCLUDE_EDITOR
1084
void HeavyShinobi_EditorDraw(void)
1085
{
1086
RSDK_THIS(HeavyShinobi);
1087
1088
self->active = ACTIVE_BOUNDS;
1089
self->visible = false;
1090
self->drawGroup = Zone->objectDrawGroup[0];
1091
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);
1092
RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, true, 0);
1093
self->updateRange.x = 0x800000;
1094
self->updateRange.y = 0x800000;
1095
1096
HeavyShinobi_Draw_Shinobi();
1097
1098
if (showGizmos()) {
1099
RSDK_DRAWING_OVERLAY(true);
1100
int32 boundsL = (self->position.x >> 16) - WIDE_SCR_XCENTER - 80;
1101
int32 boundsR = (self->position.x >> 16) + WIDE_SCR_XCENTER + 80;
1102
int32 boundsB = (self->position.y >> 16) + 68;
1103
1104
Vector2 startPos = self->position;
1105
1106
self->active = ACTIVE_NORMAL;
1107
self->visible = true;
1108
self->drawGroup = Zone->fgDrawGroup[1] - 1;
1109
RSDK.SetSpriteAnimation(WoodChipper->aniFrames, 0, &self->mainAnimator, true, 0);
1110
1111
self->position.x = (boundsL + 40) << 16;
1112
self->position.y = (boundsB - 99) << 16;
1113
HeavyShinobi_Draw_Bounds();
1114
1115
self->position.x = (boundsR - 40) << 16;
1116
self->position.y = (boundsB - 99) << 16;
1117
HeavyShinobi_Draw_Bounds();
1118
1119
DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER - 80, -SCREEN_YSIZE, WIDE_SCR_XCENTER + 80, 68, 1 | 0 | 4 | 8, 0x00C0F0);
1120
1121
self->position = startPos;
1122
RSDK_DRAWING_OVERLAY(false);
1123
}
1124
}
1125
1126
void HeavyShinobi_EditorLoad(void)
1127
{
1128
HeavyShinobi->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/Shinobi.bin", SCOPE_STAGE);
1129
1130
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[0], true, 0);
1131
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[1], true, 0);
1132
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[2], true, 0);
1133
RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[3], true, 0);
1134
}
1135
#endif
1136
1137
void HeavyShinobi_Serialize(void) {}
1138
1139