Path: blob/master/SonicMania/Objects/ERZ/ERZKing.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: ERZKing Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectERZKing *ERZKing;1011void ERZKing_Update(void)12{13RSDK_THIS(ERZKing);1415if (self->invincibilityTimer > 0)16self->invincibilityTimer--;1718StateMachine_Run(self->state);19}2021void ERZKing_LateUpdate(void) {}2223void ERZKing_StaticUpdate(void) {}2425void ERZKing_Draw(void)26{27RSDK_THIS(ERZKing);2829if (self->stateDraw) {30StateMachine_Run(self->stateDraw);31}32else {33RSDK.DrawSprite(&self->basicAnimator, NULL, false);34}35}3637void ERZKing_Create(void *data)38{39RSDK_THIS(ERZKing);4041self->drawFX = FX_FLIP;42if (!SceneInfo->inEditor) {43self->active = ACTIVE_BOUNDS;44self->updateRange.x = 0x800000;45self->updateRange.y = 0x1000000;46self->drawGroup = Zone->objectDrawGroup[0];47self->drawFX = FX_ROTATE | FX_FLIP;48self->type = VOID_TO_INT(data);4950switch (self->type) {51case ERZKING_KING:52self->hitbox.left = -24;53self->hitbox.top = -24;54self->hitbox.right = 24;55self->hitbox.bottom = 24;5657self->visible = false;58self->direction = FLIP_X;59self->health = 8;6061RSDK.SetSpriteAnimation(ERZKing->aniFrames, 0, &self->headAnimator, true, 0);62RSDK.SetSpriteAnimation(ERZKing->aniFrames, 1, &self->bodyAnimator, true, 0);63RSDK.SetSpriteAnimation(ERZKing->aniFrames, 2, &self->beltAnimator, true, 0);64RSDK.SetSpriteAnimation(ERZKing->aniFrames, 7, &self->particleAnimator, true, 0);65RSDK.SetSpriteAnimation(ERZKing->aniFrames, 8, &self->rubyAnimator, true, 0);6667self->originPos = self->position;68self->state = ERZKing_State_SetupArena;69break;7071case ERZKING_ARM_L:72case ERZKING_ARM_R:73self->visible = true;7475RSDK.SetSpriteAnimation(ERZKing->aniFrames, 3, &self->armAnimator, true, 0);76RSDK.SetSpriteAnimation(ERZKing->aniFrames, 4, &self->cuffAnimator, true, 0);7778if (self->type == ERZKING_ARM_L) {79RSDK.SetSpriteAnimation(ERZKing->aniFrames, 6, &self->handAnimator, true, 0);80}81else {82self->drawGroup = Zone->playerDrawGroup[0];83RSDK.SetSpriteAnimation(ERZKing->aniFrames, 5, &self->handAnimator, true, 0);84}8586self->stateDraw = ERZKing_Draw_Arm;87self->state = ERZKing_State_Arm;88break;89}90}91}9293void ERZKing_StageLoad(void)94{95ERZKing->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomKing.bin", SCOPE_STAGE);9697ERZKing->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");98ERZKing->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");99}100101void ERZKing_CheckPlayerCollisions(void)102{103RSDK_THIS(ERZKing);104105foreach_active(Player, player)106{107if (!self->invincibilityTimer && Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self)) {108ERZKing_Hit();109}110}111}112113void ERZKing_Hit(void)114{115RSDK_THIS(ERZKing);116117if (--self->health <= 0) {118self->originPos.x = self->position.x;119self->originPos.y = self->position.y;120self->state = ERZKing_State_Explode;121self->velocity.y = -0x10000;122self->timer = 0;123SceneInfo->timeEnabled = false;124}125else {126self->invincibilityTimer = 48;127RSDK.PlaySfx(ERZKing->sfxHit, false, 255);128}129}130131void ERZKing_Explode(void)132{133RSDK_THIS(ERZKing);134135if (!(Zone->timer % 3)) {136RSDK.PlaySfx(ERZKing->sfxExplosion2, false, 255);137138if (Zone->timer & 4) {139int32 x = self->position.x + (RSDK.Rand(self->hitbox.left, self->hitbox.right) << 16);140int32 y = self->position.y + (RSDK.Rand(self->hitbox.top, self->hitbox.bottom) << 16);141CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];142}143}144}145146void ERZKing_HandleFrames(void)147{148RSDK_THIS(ERZKing);149150RSDK.ProcessAnimation(&self->bodyAnimator);151152self->rotation = RSDK.Sin512(2 * Zone->timer) >> 6;153int32 negAng = -self->rotation;154155self->bodyAngle = (self->bodyAngle + 12) & 0x3FF;156157int32 x = 0x1C00 * RSDK.Sin512(negAng) + self->position.x;158int32 y = 0x1C00 * RSDK.Cos512(negAng) + self->position.y;159160int32 angle = self->bodyAngle;161162for (int32 i = 0; i < 10; i += 2) {163self->armPositions[i].x = x + 2 * RSDK.Cos512(self->rotation) * RSDK.Cos1024(angle);164self->armPositions[i].y = y + 2 * RSDK.Sin512(self->rotation) * RSDK.Cos1024(angle);165self->armAngles[i] = angle & 0x3FF;166167angle += 0x200;168169self->armPositions[i + 1].x = x + 2 * RSDK.Cos512(self->rotation) * RSDK.Cos1024(angle);170self->armPositions[i + 1].y = y + 2 * RSDK.Sin512(self->rotation) * RSDK.Cos1024(angle);171self->armAngles[i + 1] = angle & 0x3FF;172173x += RSDK.Sin512(negAng) << 10;174y += RSDK.Cos512(negAng) << 10;175angle += 0x240;176}177178self->rubyPos.x = self->position.x - 0x1400 * RSDK.Sin512(negAng);179self->rubyPos.y = self->position.y - 0x1400 * RSDK.Cos512(negAng);180if (self->direction) {181self->rubyPos.x -= 0x180 * RSDK.Cos512(negAng);182self->rubyPos.y -= 0x180 * RSDK.Sin512(negAng);183}184else {185self->rubyPos.x += 0x180 * RSDK.Cos512(negAng);186self->rubyPos.y += 0x180 * RSDK.Sin512(negAng);187}188}189190void ERZKing_Draw_Body(void)191{192RSDK_THIS(ERZKing);193194if (self->typeChangeTimer <= 0) {195if (self->invincibilityTimer & 1)196RSDK.CopyPalette(2, 128, 0, 128, 128);197}198else {199RSDK.SetLimitedFade(0, 1, 4, self->typeChangeTimer, 0, 48);200RSDK.SetLimitedFade(0, 1, 4, self->typeChangeTimer, 128, 256);201}202203RSDK.DrawSprite(&self->headAnimator, NULL, false);204RSDK.DrawSprite(&self->bodyAnimator, NULL, false);205206for (int32 i = 0; i < 10; ++i) {207if (self->armAngles[i] < 0x200) {208self->particleAnimator.frameID = self->armAngles[i] / 42 % 6;209RSDK.DrawSprite(&self->particleAnimator, &self->armPositions[i], false);210}211}212213self->drawFX = self->storeDrawFX | FX_ROTATE;214RSDK.DrawSprite(&self->beltAnimator, NULL, false);215216self->drawFX = self->storeDrawFX | FX_ROTATE | FX_FLIP;217for (int32 i = 0; i < 10; ++i) {218if (self->armAngles[i] >= 0x200) {219self->particleAnimator.frameID = self->armAngles[i] / 42 % 6;220RSDK.DrawSprite(&self->particleAnimator, &self->armPositions[i], false);221}222}223224RSDK.DrawSprite(&self->rubyAnimator, &self->rubyPos, false);225226if (self->typeChangeTimer <= 0) {227if (self->invincibilityTimer & 1)228RSDK.CopyPalette(1, 128, 0, 128, 128);229}230else {231RSDK.CopyPalette(1, 0, 0, 0, 48);232RSDK.CopyPalette(1, 128, 0, 128, 128);233}234}235236void ERZKing_Draw_Arm(void)237{238RSDK_THIS(ERZKing);239240EntityERZKing *parent = self->parent;241242if (parent->typeChangeTimer > 0) {243RSDK.SetLimitedFade(0, 1, 4, parent->typeChangeTimer, 0, 48);244RSDK.SetLimitedFade(0, 1, 4, parent->typeChangeTimer, 128, 256);245}246247for (int32 i = 0; i < 6; ++i) {248RSDK.DrawSprite(&self->armAnimator, &self->armPositions[i], false);249}250251RSDK.DrawSprite(&self->cuffAnimator, &self->armPositions[6], false);252RSDK.DrawSprite(&self->handAnimator, &self->armPositions[6], false);253254if (parent->typeChangeTimer > 0) {255RSDK.CopyPalette(1, 0, 0, 0, 48);256RSDK.CopyPalette(1, 128, 0, 128, 128);257}258}259260void ERZKing_State_SetupArena(void)261{262RSDK_THIS(ERZKing);263264if (++self->timer >= 8) {265self->timer = 0;266267Zone->playerBoundActiveL[0] = true;268Zone->playerBoundActiveR[0] = true;269Zone->cameraBoundsL[0] = (self->position.x >> 16) - 320;270Zone->cameraBoundsR[0] = (self->position.x >> 16) + 320;271Zone->cameraBoundsT[0] = Zone->cameraBoundsB[0] - ScreenInfo->size.y;272273ERZKing->boundsL = (Zone->cameraBoundsL[0] + 64) << 16;274ERZKing->boundsR = (Zone->cameraBoundsR[0] - 64) << 16;275ERZKing->boundsM = self->position.x;276ERZKing->boundsT = (Zone->cameraBoundsT[0] + 48) << 16;277ERZKing->boundsB = (Zone->cameraBoundsB[0] - 96) << 16;278279self->position.y += 0x1000000;280self->active = ACTIVE_NORMAL;281self->state = ERZKing_State_SetupBody;282}283}284285void ERZKing_State_SetupBody(void)286{287RSDK_THIS(ERZKing);288289if (self->timer) {290self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x < self->position.x;291292if (++self->timer == 30) {293EntityERZKing *leftArm = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, ERZKing);294RSDK.ResetEntity(leftArm, ERZKing->classID, INT_TO_VOID(ERZKING_ARM_L));295leftArm->position.x = self->position.x;296leftArm->position.y = self->position.y;297leftArm->parent = self;298299EntityERZKing *rightArm = RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, ERZKing);300RSDK.ResetEntity(rightArm, ERZKing->classID, INT_TO_VOID(ERZKING_ARM_R));301rightArm->position.x = self->position.x;302rightArm->position.y = self->position.y;303rightArm->parent = self;304305self->timer = 0;306self->visible = true;307self->stateDraw = ERZKing_Draw_Body;308self->state = ERZKing_State_EnterKing;309}310}311else {312if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x)313++self->timer;314}315}316317void ERZKing_State_EnterKing(void)318{319RSDK_THIS(ERZKing);320321RSDK.ProcessAnimation(&self->beltAnimator);322323self->velocity.y -= 0x1800;324325if (self->position.y <= self->originPos.y - 0x200000) {326self->originPos = self->position;327self->state = ERZKing_State_FlyAround;328}329else {330self->position.y += self->velocity.y;331}332333ERZKing_HandleFrames();334}335336void ERZKing_State_FlyAround(void)337{338RSDK_THIS(ERZKing);339340RSDK.ProcessAnimation(&self->beltAnimator);341342self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 3, 11);343344ERZKing_CheckPlayerCollisions();345346if (self->direction) {347if (self->velocity.x > -0x20000)348self->velocity.x -= 0x800;349350if (self->position.x < ERZKing->boundsL)351self->direction = FLIP_NONE;352}353else {354if (self->velocity.x < 0x20000)355self->velocity.x += 0x800;356357if (self->position.x > ERZKing->boundsR)358self->direction = FLIP_X;359}360self->position.x += self->velocity.x;361++self->timer;362363if (self->timer > 240) {364if (abs(self->position.x - ERZKing->boundsM) < 0x200000) {365self->timer = 0;366self->scale.x = 0x200;367self->scale.y = 0x200;368self->storeDrawFX = FX_SCALE;369self->state = ERZKing_State_ChangeHBH;370371CREATE_ENTITY(FXRuby, FXRuby_State_ShrinkAndDestroy, self->position.x, self->position.y)->radiusSpeed = 0x80000;372}373}374ERZKing_HandleFrames();375}376377void ERZKing_State_ChangeHBH(void)378{379RSDK_THIS(ERZKing);380381self->typeChangeTimer += 16;382383self->scale.x -= self->scale.x >> 4;384self->scale.y = self->scale.x;385386if (self->typeChangeTimer == 0x400) {387self->typeChangeTimer = 0;388389foreach_all(ERZKing, king) { king->active = ACTIVE_NEVER; }390391switch (self->nextType) {392case ERZKING_HEAVY_GUNNER:393CREATE_ENTITY(ERZGunner, NULL, self->position.x, self->position.y);394395self->storeDrawFX = FX_NONE;396self->state = ERZKing_State_FlyAround;397self->nextType--;398self->nextType &= 1;399break;400401case ERZKING_HEAVY_MYSTIC:402CREATE_ENTITY(ERZMystic, NULL, self->position.x, self->position.y);403self->storeDrawFX = FX_NONE;404self->state = ERZKing_State_FlyAround;405self->nextType--;406self->nextType &= 1;407break;408409// Shinobi & Rider never got completed... RIP410}411}412}413414void ERZKing_State_Arm(void)415{416RSDK_THIS(ERZKing);417418EntityERZKing *parent = self->parent;419420int32 moveX = 0;421int32 moveY = ((RSDK.Sin256(2 * (Zone->timer + (self->type << 6)) - 128) + 512) << 12) + parent->position.y;422423self->direction = parent->direction;424int32 negAngle = -parent->rotation;425426int32 x = 0, y = 0;427int32 x2 = 0, y2 = 0;428if (parent->direction) {429moveX = parent->position.x - 0x300000;430x = parent->position.x + 0xD00 * RSDK.Cos512(negAngle) + 0x300 * RSDK.Sin512(negAngle);431y = parent->position.y - 0xD00 * RSDK.Sin512(negAngle) + 0x300 * RSDK.Cos512(negAngle);432433if (self->type == ERZKING_ARM_L) {434x += -0x1800 * RSDK.Cos512(parent->rotation);435y += 0x1800 * RSDK.Sin512(parent->rotation);436moveX -= 0x300000;437}438439x2 = ((self->position.x + x) >> 1) + 0x200000;440y2 = ((self->position.y + y) >> 1) + 0x200000;441}442else {443moveX = parent->position.x + 0x300000;444x = 0x300 * RSDK.Sin512(negAngle) - 0xD00 * RSDK.Cos512(negAngle) + parent->position.x;445y = 0xD00 * RSDK.Sin512(negAngle) + 0x300 * RSDK.Cos512(negAngle) + parent->position.y;446447if (self->type == ERZKING_ARM_L) {448x += 0x1800 * RSDK.Cos512(parent->rotation);449y += -0x1800 * RSDK.Sin512(parent->rotation);450moveX += 0x300000;451}452453x2 = ((self->position.x + x) >> 1) - 0x100000;454y2 = ((self->position.y + y) >> 1) + 0x100000;455}456457self->velocity.x += ((moveX - self->position.x) >> 5) - (self->velocity.x >> 3);458self->velocity.y += ((moveY - self->position.y) >> 5) - (self->velocity.y >> 3);459self->position.x += self->velocity.x;460self->position.y += self->velocity.y;461462int32 percent = 0x1800;463for (int32 i = 0; i < 7; ++i) {464self->armPositions[i] = MathHelpers_GetBezierPoint(percent, x, y, x2, y2, x2, y2, self->position.x, self->position.y);465percent += 0x2000;466}467468RSDK.ProcessAnimation(&self->cuffAnimator);469RSDK.ProcessAnimation(&self->handAnimator);470}471472void ERZKing_State_Explode(void)473{474RSDK_THIS(ERZKing);475476self->velocity.y += 0x2800;477self->position.y += self->velocity.y;478479ERZKing_Explode();480481if (!RSDK.CheckOnScreen(self, NULL)) {482// This boss made it far enough to get the player to the ending... neat!483GameProgress_GiveEnding(GAMEPROGRESS_ENDING_GOOD);484API_UnlockAchievement(&achievementList[ACH_GAME_CLEARED]);485486// It is interesting that the boss doesn't show ending videos, it just takes you to the credits... perhaps they weren't finished yet?487RSDK.SetScene("Presentation", "Credits");488Zone_StartFadeOut(10, 0x000000);489Music_FadeOut(0.025);490491destroyEntity(self);492}493}494495#if GAME_INCLUDE_EDITOR496void ERZKing_EditorDraw(void)497{498RSDK_THIS(ERZKing);499500self->originPos = self->position;501self->bodyAngle = 0;502ERZKing_HandleFrames();503504RSDK.SetSpriteAnimation(ERZKing->aniFrames, 0, &self->headAnimator, true, 0);505RSDK.SetSpriteAnimation(ERZKing->aniFrames, 1, &self->bodyAnimator, true, 0);506RSDK.SetSpriteAnimation(ERZKing->aniFrames, 2, &self->beltAnimator, true, 0);507RSDK.SetSpriteAnimation(ERZKing->aniFrames, 7, &self->particleAnimator, true, 0);508RSDK.SetSpriteAnimation(ERZKing->aniFrames, 8, &self->rubyAnimator, true, 0);509510ERZKing_Draw_Body();511512if (showGizmos()) {513RSDK_DRAWING_OVERLAY(true);514515DrawHelpers_DrawArenaBounds(-320, -SCREEN_YSIZE, 320, 0, 1 | 2 | 4 | 0, 0x00C0F0);516517RSDK_DRAWING_OVERLAY(false);518}519}520521void ERZKing_EditorLoad(void)522{523ERZKing->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomKing.bin", SCOPE_STAGE);524525RSDK_ACTIVE_VAR(ERZKing, type);526RSDK_ENUM_VAR("King", ERZKING_KING);527}528#endif529530void ERZKing_Serialize(void) { RSDK_EDITABLE_VAR(ERZKing, VAR_ENUM, type); }531532533