Path: blob/master/SonicMania/Objects/PGZ/HeavyShinobi.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: HeavyShinobi Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectHeavyShinobi *HeavyShinobi;1011void HeavyShinobi_Update(void)12{13RSDK_THIS(HeavyShinobi);1415StateMachine_Run(self->state);16}1718void HeavyShinobi_LateUpdate(void) {}1920void HeavyShinobi_StaticUpdate(void) {}2122void HeavyShinobi_Draw(void)23{24RSDK_THIS(HeavyShinobi);2526StateMachine_Run(self->stateDraw);27}2829void HeavyShinobi_Create(void *data)30{31RSDK_THIS(HeavyShinobi);3233self->drawFX = FX_ROTATE | FX_FLIP;3435if (!SceneInfo->inEditor) {36if (globals->gameMode < MODE_TIMEATTACK) {37self->type = VOID_TO_INT(data);3839switch (self->type) {40case SHINOBI_MAIN:41self->active = ACTIVE_BOUNDS;42self->visible = false;43self->drawGroup = Zone->objectDrawGroup[0];4445RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);46RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, true, 0);4748self->state = HeavyShinobi_State_Init;49self->stateDraw = HeavyShinobi_Draw_Shinobi;50self->updateRange.y = 0x800000;51self->updateRange.x = 0x800000;52break;53case SHINOBI_SLASH:5455self->active = ACTIVE_NORMAL;56self->visible = true;57self->drawGroup = Zone->objectDrawGroup[0];58RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 8, &self->mainAnimator, true, 0);5960self->inkEffect = INK_ALPHA;61self->alpha = 0x100;62self->state = HeavyShinobi_StateSlash_Active;63self->stateDraw = HeavyShinobi_Draw_Slash;64self->updateRange.y = 0x800000;65self->updateRange.x = 0x800000;66break;67case SHINOBI_ASTERON:68self->active = ACTIVE_NORMAL;69self->visible = true;70self->drawGroup = Zone->objectDrawGroup[0] - 1;7172RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 9, &self->mainAnimator, true, 0);73self->updateRange.x = 0x1000000;74self->updateRange.y = 0x1000000;75self->alpha = 0xC0;76self->state = HeavyShinobi_StateAsteron_Thrown;77self->stateDraw = HeavyShinobi_Draw_Asteron;78break;79case SHINOBI_ASTERONSPIKE:80self->active = ACTIVE_NORMAL;81self->visible = true;82self->drawGroup = Zone->objectDrawGroup[0];8384self->drawFX = FX_ROTATE | FX_FLIP;85self->state = HeavyShinobi_State_AsteronSpike;86self->stateDraw = HeavyShinobi_Draw_AsteronSpike;87self->updateRange.y = 0x800000;88self->updateRange.x = 0x800000;89break;90case SHINOBI_BOUNDS:91self->active = ACTIVE_NORMAL;9293// Bug Details: Remember the PGZ2 boss skip?94// you guessed it, this is the fix, isPermanent just says "DO NOT OVERWRITE THIS ENTITY"95// since all entities spawned via RSDK.CreateEntity use the last 0x100 slots, and it loops around96#if GAME_VERSION != VER_10097self->isPermanent = true;98#endif99100self->visible = true;101self->drawGroup = Zone->fgDrawGroup[1] - 1;102RSDK.SetSpriteAnimation(WoodChipper->aniFrames, 0, &self->mainAnimator, true, 0);103104self->state = HeavyShinobi_StateBounds_WaitForPlayer;105self->stateDraw = HeavyShinobi_Draw_Bounds;106self->updateRange.y = 0x800000;107self->updateRange.x = 0x800000;108break;109110default: break;111}112}113else {114destroyEntity(self);115}116}117}118119void HeavyShinobi_StageLoad(void)120{121HeavyShinobi->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/Shinobi.bin", SCOPE_STAGE);122123HeavyShinobi->hitboxShinobi.left = -16;124HeavyShinobi->hitboxShinobi.top = -25;125HeavyShinobi->hitboxShinobi.right = 16;126HeavyShinobi->hitboxShinobi.bottom = 14;127128HeavyShinobi->hitboxSlashRange.left = -16;129HeavyShinobi->hitboxSlashRange.top = -25;130HeavyShinobi->hitboxSlashRange.right = 16;131HeavyShinobi->hitboxSlashRange.bottom = 25;132133HeavyShinobi->hitboxUnused.left = -16;134HeavyShinobi->hitboxUnused.top = -16;135HeavyShinobi->hitboxUnused.right = 16;136HeavyShinobi->hitboxUnused.bottom = 16;137138HeavyShinobi->hitboxSlash.left = -72;139HeavyShinobi->hitboxSlash.top = -64;140HeavyShinobi->hitboxSlash.right = 0;141HeavyShinobi->hitboxSlash.bottom = 25;142143HeavyShinobi->hitboxAsteron.left = -8;144HeavyShinobi->hitboxAsteron.top = -8;145HeavyShinobi->hitboxAsteron.right = 8;146HeavyShinobi->hitboxAsteron.bottom = 8;147148HeavyShinobi->hitboxBounds.left = -40;149HeavyShinobi->hitboxBounds.top = -336;150HeavyShinobi->hitboxBounds.right = 40;151HeavyShinobi->hitboxBounds.bottom = 336;152153HeavyShinobi->hitboxAsteronSpike.left = -3;154HeavyShinobi->hitboxAsteronSpike.top = -3;155HeavyShinobi->hitboxAsteronSpike.right = 3;156HeavyShinobi->hitboxAsteronSpike.bottom = 3;157158RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[0], true, 0);159RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[1], true, 0);160RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[2], true, 0);161RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[3], true, 0);162163HeavyShinobi->activeShurikens = 0;164HeavyShinobi->health = 8;165HeavyShinobi->invincibilityTimer = 0;166167HeavyShinobi->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");168HeavyShinobi->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");169HeavyShinobi->sfxDefeat = RSDK.GetSfx("PSZ/ShinobiDefeat.wav");170HeavyShinobi->sfxDropIn = RSDK.GetSfx("PSZ/ShinobiDropIn.wav");171HeavyShinobi->sfxExplode = RSDK.GetSfx("PSZ/ShinobiExplode.wav");172HeavyShinobi->sfxGlitch = RSDK.GetSfx("PSZ/ShinobiGlitch.wav");173HeavyShinobi->sfxJump = RSDK.GetSfx("PSZ/ShinobiJump.wav");174HeavyShinobi->sfxParry = RSDK.GetSfx("PSZ/ShinobiParry.wav");175HeavyShinobi->sfxSlash = RSDK.GetSfx("PSZ/ShinobiSlash.wav");176HeavyShinobi->sfxStick = RSDK.GetSfx("PSZ/ShinobiStick.wav");177HeavyShinobi->sfxThrow = RSDK.GetSfx("PSZ/ShinobiThrow.wav");178}179180void HeavyShinobi_HandleAfterFX(void)181{182RSDK_THIS(HeavyShinobi);183184for (int32 i = 15; i > 0; --i) {185HeavyShinobi->storePos[i] = HeavyShinobi->storePos[i - 1];186}187188HeavyShinobi->storePos[0].x = self->position.x;189HeavyShinobi->storePos[0].y = self->position.y;190191for (int32 i = 0; i < 4; ++i) {192int32 storeAnim = self->mainAnimator.animationID;193if (HeavyShinobi->fxTrailAnimator[i].animationID != storeAnim) {194if (HeavyShinobi->storedAnimIDs[i] != storeAnim) {195HeavyShinobi->storedAnimIDs[i] = self->mainAnimator.animationID;196HeavyShinobi->storedIDs[i] = 4 * (i + 1);197}198}199200if (HeavyShinobi->storedIDs[i]) {201202--HeavyShinobi->storedIDs[i];203if (!HeavyShinobi->storedIDs[i]) {204int32 id = HeavyShinobi->storedAnimIDs[i];205if (id && (id <= 14 || id > 16)) {206RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, HeavyShinobi->storedAnimIDs[i], &HeavyShinobi->fxTrailAnimator[i], false, 0);207}208else {209RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[i], false, 0);210HeavyShinobi->fxTrailAnimator[i].animationID = (uint8)-1;211}212}213}214}215216if (HeavyShinobi->invincibilityTimer)217HeavyShinobi->invincibilityTimer--;218}219220void HeavyShinobi_HandleSlash(EntityPlayer *player)221{222RSDK_THIS(HeavyShinobi);223224RSDK.PlaySfx(HeavyShinobi->sfxSlash, false, 255);225226self->direction = player->position.x >= self->position.x;227RSDK.SetSpriteAnimation(-1, 0, &self->fxAnimator, true, 0);228RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 4, &self->mainAnimator, true, 1);229230CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_SLASH), self->position.x, self->position.y)->direction = self->direction;231232int32 delay = 4;233int32 alpha = 0xC0;234for (int32 i = 3; i >= 0; --i) {235EntityHeavyShinobi *slash = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_SLASH), self->position.x, self->position.y);236slash->mainAnimator.frameDuration += delay;237slash->alpha = alpha;238slash->direction = self->direction;239240delay += 4;241alpha -= 32;242}243244self->state = HeavyShinobi_State_Slash;245}246247void HeavyShinobi_StartJump(void)248{249RSDK_THIS(HeavyShinobi);250251RSDK.SetSpriteAnimation(-1, 0, &self->fxAnimator, true, 0);252RSDK.PlaySfx(HeavyShinobi->sfxJump, false, 255);253self->timer = 28;254255if (self->position.x >= (Zone->cameraBoundsL[0] + 144) << 16) {256if (self->position.x <= (Zone->cameraBoundsR[0] - 144) << 16)257self->velocity.x = (RSDK.Rand(0, 2) << 18) - 0x20000;258else259self->velocity.x = -0x20000;260}261else {262self->velocity.x = 0x20000;263}264265if (self->velocity.x >= 0)266self->velocity.x += 0x1000;267else268self->velocity.x -= 0x1000;269self->velocity.y = -0x70000;270271RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 3, &self->mainAnimator, true, 0);272self->state = HeavyShinobi_State_Jump;273}274275void HeavyShinobi_Hit(void)276{277RSDK_THIS(HeavyShinobi);278279if (HeavyShinobi->health)280HeavyShinobi->health--;281282if (!HeavyShinobi->health) {283SceneInfo->timeEnabled = false;284Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);285RSDK.PlaySfx(HeavyShinobi->sfxExplosion, false, 255);286self->timer = 120;287self->state = HeavyShinobi_State_Destroyed;288}289else {290RSDK.PlaySfx(HeavyShinobi->sfxHit, false, 255);291self->timer -= 60;292HeavyShinobi->invincibilityTimer = 30;293}294}295296void HeavyShinobi_Explode(void)297{298RSDK_THIS(HeavyShinobi);299300if (!(Zone->timer & 7)) {301RSDK.PlaySfx(HeavyShinobi->sfxExplosion, false, 255);302303if (!(Zone->timer & 0xF)) {304int32 x = self->position.x + (RSDK.Rand(-19, 20) << 16);305int32 y = self->position.y + (RSDK.Rand(-24, 25) << 16);306EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);307explosion->drawGroup = Zone->objectDrawGroup[1] + 2;308}309}310}311312void HeavyShinobi_State_Init(void)313{314RSDK_THIS(HeavyShinobi);315316if (++self->timer >= 2) {317self->timer = 0;318319Zone->playerBoundActiveR[0] = true;320Zone->cameraBoundsR[0] = ScreenInfo->center.x + 80 + (self->position.x >> 16);321Zone->playerBoundActiveB[0] = true;322Zone->cameraBoundsB[0] = (self->position.y >> 16) + 68;323324self->position.y = (ScreenInfo->position.y - 192) << 16;325for (int32 i = 0; i < 16; ++i) HeavyShinobi->storePos[i] = self->position;326327self->active = ACTIVE_NORMAL;328self->state = HeavyShinobi_State_SetupArena;329}330}331332void HeavyShinobi_State_SetupArena(void)333{334RSDK_THIS(HeavyShinobi);335336Zone->playerBoundActiveL[0] = true;337Zone->cameraBoundsL[0] = ScreenInfo->position.x;338339if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x - 0x500000) {340Zone->playerBoundActiveL[0] = true;341Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x - 80;342343int32 y = (Zone->cameraBoundsB[0] - 376) << 16;344345CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_BOUNDS), (Zone->cameraBoundsL[0] + 40) << 16, y);346347EntityHeavyShinobi *rBounds = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_BOUNDS), (Zone->cameraBoundsR[0] - 40) << 16, y);348rBounds->timer = 1;349rBounds->position.y = (Zone->cameraBoundsB[0] - 99) << 16;350rBounds->state = HeavyShinobi_StateBounds_Active;351352self->state = HeavyShinobi_State_StartFight;353}354}355356void HeavyShinobi_State_StartFight(void)357{358RSDK_THIS(HeavyShinobi);359360Zone->playerBoundActiveL[0] = true;361Zone->cameraBoundsL[0] = ScreenInfo->position.x;362363if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x) {364Zone->playerBoundActiveL[0] = true;365Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x - 80;366367Music_TransitionTrack(TRACK_HBHBOSS, 0.0125);368self->visible = true;369HeavyShinobi_StartJump();370self->velocity.x = 0;371self->timer = 0;372}373}374375void HeavyShinobi_State_Idle(void)376{377RSDK_THIS(HeavyShinobi);378379HeavyShinobi_HandleAfterFX();380381RSDK.ProcessAnimation(&self->mainAnimator);382RSDK.ProcessAnimation(&self->fxAnimator);383for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);384385self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;386387EntityPlayer *player = Player_GetNearestPlayerX();388389if (abs(player->position.x - self->position.x) < 0x500000 && player->state != Ice_PlayerState_Frozen) {390HeavyShinobi_HandleSlash(player);391}392else {393if (!HeavyShinobi->activeShurikens && --self->timer <= 0)394HeavyShinobi_StartJump();395}396}397398void HeavyShinobi_State_Slash(void)399{400RSDK_THIS(HeavyShinobi);401402HeavyShinobi_HandleAfterFX();403404RSDK.ProcessAnimation(&self->mainAnimator);405RSDK.ProcessAnimation(&self->fxAnimator);406for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);407408if (self->mainAnimator.frameID == 12)409RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);410411self->position.y += self->velocity.y;412self->velocity.y += 0x2800;413414if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true))415self->velocity.y = 0;416417if (self->mainAnimator.frameID == self->mainAnimator.frameCount - 1) {418EntityPlayer *player = Player_GetNearestPlayerX();419if (abs(player->position.x - self->position.x) >= 0x500000 || player->state == Ice_PlayerState_Frozen)420HeavyShinobi_StartJump();421else422HeavyShinobi_HandleSlash(player);423}424425foreach_active(Player, player)426{427if (Player_CheckCollisionBox(player, self, &HeavyShinobi->hitboxSlashRange) && Player_CheckValidState(player)) {428Ice_FreezePlayer(player);429player->timer = 3;430player->onGround = false;431player->velocity.x = player->position.x < self->position.x ? -0x30000 : 0x30000;432player->velocity.y = -0x38000;433}434}435}436437void HeavyShinobi_State_Jump(void)438{439RSDK_THIS(HeavyShinobi);440441HeavyShinobi_HandleAfterFX();442443for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);444RSDK.ProcessAnimation(&self->mainAnimator);445446self->position.x += self->velocity.x;447self->position.y += self->velocity.y;448self->velocity.y += 0x2800;449450if (self->velocity.x >= 0) {451if (self->position.x > (Zone->cameraBoundsR[0] - 104) << 16) {452self->velocity.x = 0;453self->position.x = (Zone->cameraBoundsR[0] - 104) << 16;454}455}456else {457if (self->position.x < (Zone->cameraBoundsL[0] + 104) << 16) {458self->velocity.x = 0;459self->position.x = (Zone->cameraBoundsL[0] + 104) << 16;460}461}462463if (!--self->timer) {464if (!HeavyShinobi->activeShurikens) {465int32 count = 0;466switch (HeavyShinobi->health) {467case 1:468case 2: count = 3; break;469470case 3:471case 4:472case 5: count = 2; break;473474case 6:475case 7: count = 1; break;476477case 8:478default: count = 0; break;479}480481if (count)482RSDK.PlaySfx(HeavyShinobi->sfxThrow, false, 0xFF);483484for (int32 i = 0; i < count; ++i) {485EntityHeavyShinobi *asteron = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_ASTERON), self->position.x, self->position.y);486asteron->direction = FLIP_NONE;487asteron->mainAnimator.frameID = RSDK.Rand(0, 8);488asteron->timer = 180;489490switch (count) {491case 1: asteron->angle = 64; break;492493case 2:494if (!i)495asteron->angle = 96;496else497asteron->angle = 32;498break;499500case 3:501switch (i) {502case 0: asteron->angle = 96; break;503case 1: asteron->angle = 64; break;504case 2: asteron->angle = 32; break;505}506break;507}508509asteron->angle += RSDK.Rand(-16, 17);510asteron->velocity.x = 0x300 * RSDK.Cos256(asteron->angle);511asteron->velocity.y = 0x300 * RSDK.Sin256(asteron->angle);512++HeavyShinobi->activeShurikens;513}514}515}516517if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true)) {518self->velocity.x = 0;519self->velocity.y = 0;520self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;521522RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);523RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);524self->timer = RSDK.Rand(30, 91);525self->state = HeavyShinobi_State_Idle;526}527else {528foreach_active(Player, player)529{530if (player->state != Ice_PlayerState_Frozen && Player_CheckBadnikTouch(player, self, &HeavyShinobi->hitboxShinobi)531&& Player_CheckBossHit(player, self)) {532if (player->position.x >= self->position.x) {533self->direction = FLIP_X;534self->velocity.x = -0x20000;535}536else {537self->direction = FLIP_NONE;538self->velocity.x = 0x20000;539}540self->velocity.y = -0x40000;541542int32 angle = RSDK.ATan2(player->position.x - self->position.x, player->position.y - self->position.y);543player->velocity.x = 0x380 * RSDK.Cos256(angle);544player->velocity.y = 0x380 * RSDK.Sin256(angle);545self->timer = 180;546self->glitchTimer = 0;547548RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 15, &self->mainAnimator, true, 0);549RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);550RSDK.PlaySfx(HeavyShinobi->sfxParry, false, 255);551self->state = HeavyShinobi_State_Glitched;552}553}554}555}556557void HeavyShinobi_State_Glitched(void)558{559RSDK_THIS(HeavyShinobi);560561if (!(self->glitchTimer % 40))562RSDK.PlaySfx(HeavyShinobi->sfxGlitch, false, 255);563++self->glitchTimer;564565HeavyShinobi_HandleAfterFX();566567RSDK.ProcessAnimation(&self->mainAnimator);568569self->position.x += self->velocity.x;570self->position.y += self->velocity.y;571self->velocity.y += 0x3800;572573if (self->velocity.x >= 0) {574if (self->position.x > (Zone->cameraBoundsR[0] - 104) << 16) {575self->velocity.x = 0;576self->position.x = (Zone->cameraBoundsR[0] - 104) << 16;577}578}579else {580if (self->position.x < (Zone->cameraBoundsL[0] + 104) << 16) {581self->velocity.x = 0;582self->position.x = (Zone->cameraBoundsL[0] + 104) << 16;583}584}585586if (self->velocity.y > 0x2800 && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true)) {587RSDK_GET_ENTITY(SLOT_PLAYER1, Player);588self->velocity.x = 0;589self->velocity.y = 0;590591RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 16, &self->mainAnimator, false, 0);592RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);593594if (--self->timer <= 0) {595RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);596RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);597598self->timer = RSDK.Rand(30, 91);599self->state = HeavyShinobi_State_Idle;600RSDK.StopSfx(HeavyShinobi->sfxGlitch);601}602}603604if (!HeavyShinobi->invincibilityTimer) {605foreach_active(Player, player)606{607if (player->state != Ice_PlayerState_Frozen && Player_CheckBadnikTouch(player, self, &HeavyShinobi->hitboxShinobi)608&& Player_CheckBossHit(player, self)) {609HeavyShinobi_Hit();610foreach_break;611}612}613}614}615616void HeavyShinobi_State_Destroyed(void)617{618RSDK_THIS(HeavyShinobi);619620HeavyShinobi_HandleAfterFX();621622RSDK.ProcessAnimation(&self->mainAnimator);623RSDK.ProcessAnimation(&self->fxAnimator);624625self->position.x += self->velocity.x;626self->position.y += self->velocity.y;627self->velocity.y += 0x3800;628629if (self->velocity.x >= 0) {630if (self->position.x > (Zone->cameraBoundsR[0] - 104) << 16) {631self->velocity.x = 0;632self->position.x = (Zone->cameraBoundsR[0] - 104) << 16;633}634}635else {636if (self->position.x < (Zone->cameraBoundsL[0] + 104) << 16) {637self->velocity.x = 0;638self->position.x = (Zone->cameraBoundsL[0] + 104) << 16;639}640}641642if (self->velocity.y > 0x2800 && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x2A0000, true)) {643self->velocity.x = 0;644self->velocity.y = 0;645self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;646647RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 16, &self->mainAnimator, false, 0);648RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, false, 0);649}650651HeavyShinobi_Explode();652653if (--self->timer <= 0) {654self->velocity.y = -0x70000;655656RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 3, &self->mainAnimator, true, 0);657RSDK.SetSpriteAnimation(-1, 0, &self->fxAnimator, true, 0);658659RSDK.PlaySfx(HeavyShinobi->sfxDefeat, false, 255);660self->state = HeavyShinobi_State_Finished;661}662}663664void HeavyShinobi_State_Finished(void)665{666RSDK_THIS(HeavyShinobi);667668HeavyShinobi_HandleAfterFX();669670RSDK.ProcessAnimation(&self->mainAnimator);671RSDK.ProcessAnimation(&self->fxAnimator);672for (int32 i = 0; i < 4; ++i) RSDK.ProcessAnimation(&HeavyShinobi->fxTrailAnimator[i]);673674self->position.y += self->velocity.y;675self->velocity.y += 0x2800;676677if (self->velocity.y > 0)678self->drawGroup = Zone->objectDrawGroup[1];679680if (!RSDK.CheckOnScreen(self, &self->updateRange)) {681Music_TransitionTrack(TRACK_STAGE, 0.0125);682Zone->cameraBoundsR[0] += WIDE_SCR_XSIZE;683HeavyShinobi->health = -1;684destroyEntity(self);685}686}687688void HeavyShinobi_Draw_Shinobi(void)689{690RSDK_THIS(HeavyShinobi);691692RSDK.DrawSprite(&self->fxAnimator, NULL, false);693694self->inkEffect = INK_ALPHA;695self->alpha = 0x60;696697for (int32 i = 3; i >= 0; --i) {698RSDK.DrawSprite(&HeavyShinobi->fxTrailAnimator[i], &HeavyShinobi->storePos[i << 2], false);699self->alpha += 0x20;700}701702self->inkEffect = INK_NONE;703if (HeavyShinobi->invincibilityTimer & 1)704RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);705706RSDK.DrawSprite(&self->mainAnimator, NULL, false);707708RSDK.SetPaletteEntry(0, 128, 0x000000);709}710711void HeavyShinobi_StateSlash_Active(void)712{713RSDK_THIS(HeavyShinobi);714715RSDK.ProcessAnimation(&self->mainAnimator);716717if (self->mainAnimator.frameID != self->mainAnimator.frameCount - 1) {718foreach_active(Player, player)719{720if (Player_CheckCollisionTouch(player, self, &HeavyShinobi->hitboxSlash) && Player_CheckValidState(player)) {721Ice_FreezePlayer(player);722player->timer = 3;723player->onGround = false;724player->velocity.y = -0x38000;725if (player->position.x < self->position.x)726player->velocity.x = -0x30000;727else728player->velocity.x = 0x30000;729}730}731}732else {733destroyEntity(self);734}735}736737void HeavyShinobi_Draw_Slash(void)738{739RSDK_THIS(HeavyShinobi);740741RSDK.DrawSprite(&self->mainAnimator, NULL, false);742}743744void HeavyShinobi_StateAsteron_Thrown(void)745{746RSDK_THIS(HeavyShinobi);747748if (HeavyShinobi->health) {749RSDK.ProcessAnimation(&self->mainAnimator);750751self->position.x += self->velocity.x;752self->position.y += self->velocity.y;753754bool32 isStuck = false;755foreach_active(Player, player)756{757if (Player_CheckCollisionTouch(player, self, &HeavyShinobi->hitboxAsteron)) {758if (player->state == Ice_PlayerState_Frozen) {759self->playerPtr = player;760self->playerDistance.x = self->position.x - player->position.x;761self->playerDistance.y = self->position.y - player->position.y;762763isStuck = true;764foreach_break;765}766767#if MANIA_USE_PLUS768if (Player_CheckMightyShellHit(player, self, -0x400, -0x600)) {769self->interaction = false;770self->state = HeavyShinobi_StateAsteron_Debris;771--HeavyShinobi->activeShurikens;772}773else {774#endif775Player_Hurt(player, self);776#if MANIA_USE_PLUS777}778#endif779}780}781782if (!isStuck && RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, true))783isStuck = true;784785if (!isStuck) {786foreach_active(HeavyShinobi, boss)787{788if (boss->type == SHINOBI_BOUNDS789&& RSDK.CheckObjectCollisionBox(boss, &HeavyShinobi->hitboxBounds, self, &HeavyShinobi->hitboxAsteron, true))790isStuck = true;791}792}793794if (isStuck) {795if (self->mainAnimator.frameID & 1)796self->mainAnimator.frameID = (self->mainAnimator.frameID + 1) & 7;797798RSDK.PlaySfx(HeavyShinobi->sfxStick, false, 255);799RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, (self->mainAnimator.frameID >> 1) + 10, &self->fxAnimator, true, 0);800self->state = HeavyShinobi_StateAsteron_Explode;801}802803if (!RSDK.CheckOnScreen(self, &self->updateRange))804destroyEntity(self);805}806else {807RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);808CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;809810destroyEntity(self);811}812}813814void HeavyShinobi_StateAsteron_Debris(void)815{816RSDK_THIS(HeavyShinobi);817818self->position.x += self->velocity.x;819self->position.y += self->velocity.y;820self->velocity.y += 0x3800;821822self->visible ^= true;823824if (!RSDK.CheckOnScreen(self, &self->updateRange))825destroyEntity(self);826}827828void HeavyShinobi_StateAsteron_Explode(void)829{830RSDK_THIS(HeavyShinobi);831832if (!HeavyShinobi->health) {833RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);834CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;835836destroyEntity(self);837}838else {839RSDK.ProcessAnimation(&self->fxAnimator);840self->fxAnimator.speed += 2;841842foreach_active(Player, player)843{844if (self->playerPtr == player) {845if (player->state != Ice_PlayerState_Frozen) {846RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);847CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup =848Zone->objectDrawGroup[1] + 2;849--HeavyShinobi->activeShurikens;850851destroyEntity(self);852foreach_break;853}854}855else if ((player->state != Ice_PlayerState_Frozen || !self->playerPtr)856&& Player_CheckBadnikTouch(player, self, &HeavyShinobi->hitboxAsteron) && Player_CheckItemBreak(player, self, true)) {857RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);858CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;859--HeavyShinobi->activeShurikens;860861destroyEntity(self);862foreach_break;863}864}865866EntityPlayer *playerPtr = self->playerPtr;867if (playerPtr) {868self->position.x = playerPtr->position.x + self->playerDistance.x;869self->position.y = playerPtr->position.y + self->playerDistance.y;870}871872if (--self->timer <= 0) {873--HeavyShinobi->activeShurikens;874RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);875CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;876877for (int32 i = 0; i < 5; ++i) {878EntityHeavyShinobi *child = CREATE_ENTITY(HeavyShinobi, INT_TO_VOID(SHINOBI_ASTERONSPIKE), self->position.x, self->position.y);879child->rotation = (self->mainAnimator.frameID & 0xFFFFFFFE) << 6;880child->direction = self->direction;881882switch (i) {883case 0:884child->angle = (32 * ((self->mainAnimator.frameID & 0xFE) - 2));885RSDK.Cos512(child->rotation);886child->position.y -= RSDK.Sin512(child->rotation) << 10;887RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 17, &child->mainAnimator, true, 0);888break;889890case 1:891child->angle = (32 * (self->mainAnimator.frameID & 0xFE) - 120);892child->position.x -= 0x380 * RSDK.Cos512(child->rotation);893child->position.y -= 0x180 * RSDK.Sin512(child->rotation);894RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 18, &child->mainAnimator, true, 0);895break;896897case 2:898child->angle = (32 * (self->mainAnimator.frameID & 0xFE) - 8);899child->position.x += 0x380 * RSDK.Cos512(child->rotation);900child->position.y -= 0x180 * RSDK.Sin512(child->rotation);901RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 18, &child->mainAnimator, true, 0);902child->direction ^= FLIP_X;903break;904905case 3:906child->angle = (32 * ((self->mainAnimator.frameID & 0xFE) + 3));907child->position.x -= 0x380 * RSDK.Cos512(child->rotation);908child->position.y += 0x380 * RSDK.Sin512(child->rotation);909RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 19, &child->mainAnimator, true, 0);910break;911912case 4:913child->angle = (32 * ((self->mainAnimator.frameID & 0xFE) + 1));914child->position.x += 0x380 * RSDK.Cos512(child->rotation);915child->position.y += 0x380 * RSDK.Sin512(child->rotation);916RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 19, &child->mainAnimator, true, 0);917child->direction ^= FLIP_X;918break;919920default: break;921}922923child->velocity.x = 0x300 * RSDK.Cos256(child->angle);924child->velocity.y = 0x300 * RSDK.Sin256(child->angle);925}926927if (playerPtr) {928Ice_BreakPlayerBlock(playerPtr);929playerPtr->state = Player_State_Air;930Player_Hurt(playerPtr, self);931}932933destroyEntity(self);934}935936if (!RSDK.CheckOnScreen(self, &self->updateRange))937destroyEntity(self);938}939}940941void HeavyShinobi_Draw_Asteron(void)942{943RSDK_THIS(HeavyShinobi);944945RSDK.DrawSprite(&self->mainAnimator, NULL, false);946self->inkEffect = INK_ADD;947948RSDK.DrawSprite(&self->fxAnimator, NULL, false);949self->inkEffect = INK_NONE;950}951952void HeavyShinobi_State_AsteronSpike(void)953{954RSDK_THIS(HeavyShinobi);955956if (HeavyShinobi->health) {957RSDK.ProcessAnimation(&self->mainAnimator);958959self->position.x += self->velocity.x;960self->position.y += self->velocity.y;961962foreach_active(Player, player)963{964if (Player_CheckCollisionTouch(player, self, &HeavyShinobi->hitboxAsteronSpike) && player->state != Ice_PlayerState_Frozen) {965#if MANIA_USE_PLUS966if (Player_CheckMightyShellHit(player, self, -0x400, -0x600)) {967self->interaction = false;968self->state = HeavyShinobi_StateAsteron_Debris;969}970else {971#endif972Player_Hurt(player, self);973#if MANIA_USE_PLUS974}975#endif976}977}978979if (!RSDK.CheckOnScreen(self, &self->updateRange))980destroyEntity(self);981}982else {983RSDK.PlaySfx(HeavyShinobi->sfxExplosion, false, 255);984CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[0];985986destroyEntity(self);987}988}989990void HeavyShinobi_Draw_AsteronSpike(void)991{992RSDK_THIS(HeavyShinobi);993994RSDK.DrawSprite(&self->mainAnimator, NULL, false);995}996997void HeavyShinobi_StateBounds_WaitForPlayer(void)998{999RSDK_THIS(HeavyShinobi);10001001self->position.y += self->velocity.y;1002self->velocity.y += 0x4800;10031004if (self->position.y >= (Zone->cameraBoundsB[0] - 99) << 16) {1005self->position.y = (Zone->cameraBoundsB[0] - 99) << 16;1006self->state = HeavyShinobi_StateBounds_Active;1007}10081009foreach_active(Player, player) { Player_CheckCollisionBox(player, self, &HeavyShinobi->hitboxBounds); }1010}10111012void HeavyShinobi_StateBounds_Active(void)1013{1014RSDK_THIS(HeavyShinobi);10151016foreach_active(Player, player)1017{1018if (Player_CheckCollisionBox(player, self, &HeavyShinobi->hitboxBounds)) {1019Hitbox *playerHitbox = Player_GetHitbox(player);10201021int32 left = 0;1022int32 right = 0;1023if (self->timer) {1024left = HeavyShinobi->hitboxBounds.left;1025right = playerHitbox->right;1026}1027else {1028left = HeavyShinobi->hitboxBounds.right;1029right = playerHitbox->left;1030}10311032player->position.x = self->position.x + ((left - right) << 16);1033}1034}10351036if (HeavyShinobi->health == -1) {1037RSDK.PlaySfx(HeavyShinobi->sfxExplode, false, 255);10381039for (int32 i = 0; i < 0x80; ++i) {1040int32 x = self->position.x + (RSDK.Rand(-64, 65) << 16);1041int32 y = self->position.y + (RSDK.Rand(-80, 81) << 16);1042EntityIce *ice = CREATE_ENTITY(Ice, INT_TO_VOID(ICE_CHILD_SHARD), x, y);10431044RSDK.SetSpriteAnimation(WoodChipper->aniFrames, 1, &ice->blockAnimator, true, 0);1045ice->velocity.x = RSDK.Rand(-6, 8) << 15;1046ice->velocity.y = RSDK.Rand(-10, 2) << 15;1047ice->direction = RSDK.Rand(0, 4);1048ice->blockAnimator.speed = RSDK.Rand(1, 4);1049ice->active = ACTIVE_NORMAL;1050}10511052destroyEntity(self);1053}1054}10551056void HeavyShinobi_Draw_Bounds(void)1057{1058RSDK_THIS(HeavyShinobi);1059Vector2 drawPos;10601061drawPos.x = self->position.x;1062drawPos.y = self->position.y - 0x880000;1063self->mainAnimator.frameID = 3;1064RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);10651066self->mainAnimator.frameID = 4;1067RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);10681069drawPos.y += 0x500000;1070RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);10711072drawPos.y += 0x500000;1073RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);10741075self->direction = FLIP_Y;1076self->mainAnimator.frameID = 3;1077RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);10781079self->direction = FLIP_NONE;1080}10811082#if GAME_INCLUDE_EDITOR1083void HeavyShinobi_EditorDraw(void)1084{1085RSDK_THIS(HeavyShinobi);10861087self->active = ACTIVE_BOUNDS;1088self->visible = false;1089self->drawGroup = Zone->objectDrawGroup[0];1090RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 0, &self->mainAnimator, true, 0);1091RSDK.SetSpriteAnimation(HeavyShinobi->aniFrames, 5, &self->fxAnimator, true, 0);1092self->updateRange.x = 0x800000;1093self->updateRange.y = 0x800000;10941095HeavyShinobi_Draw_Shinobi();10961097if (showGizmos()) {1098RSDK_DRAWING_OVERLAY(true);1099int32 boundsL = (self->position.x >> 16) - WIDE_SCR_XCENTER - 80;1100int32 boundsR = (self->position.x >> 16) + WIDE_SCR_XCENTER + 80;1101int32 boundsB = (self->position.y >> 16) + 68;11021103Vector2 startPos = self->position;11041105self->active = ACTIVE_NORMAL;1106self->visible = true;1107self->drawGroup = Zone->fgDrawGroup[1] - 1;1108RSDK.SetSpriteAnimation(WoodChipper->aniFrames, 0, &self->mainAnimator, true, 0);11091110self->position.x = (boundsL + 40) << 16;1111self->position.y = (boundsB - 99) << 16;1112HeavyShinobi_Draw_Bounds();11131114self->position.x = (boundsR - 40) << 16;1115self->position.y = (boundsB - 99) << 16;1116HeavyShinobi_Draw_Bounds();11171118DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER - 80, -SCREEN_YSIZE, WIDE_SCR_XCENTER + 80, 68, 1 | 0 | 4 | 8, 0x00C0F0);11191120self->position = startPos;1121RSDK_DRAWING_OVERLAY(false);1122}1123}11241125void HeavyShinobi_EditorLoad(void)1126{1127HeavyShinobi->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/Shinobi.bin", SCOPE_STAGE);11281129RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[0], true, 0);1130RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[1], true, 0);1131RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[2], true, 0);1132RSDK.SetSpriteAnimation(-1, 0, &HeavyShinobi->fxTrailAnimator[3], true, 0);1133}1134#endif11351136void HeavyShinobi_Serialize(void) {}113711381139