Path: blob/master/SonicMania/Objects/ERZ/PhantomEgg.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: PhantomEgg Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectPhantomEgg *PhantomEgg;1011void PhantomEgg_Update(void)12{13RSDK_THIS(PhantomEgg);1415StateMachine_Run(self->state);16}1718void PhantomEgg_LateUpdate(void) {}1920void PhantomEgg_StaticUpdate(void) {}2122void PhantomEgg_Draw(void)23{24RSDK_THIS(PhantomEgg);2526if (self->stateDraw) {27StateMachine_Run(self->stateDraw);28}29else {30RSDK.DrawSprite(&self->crackAnimator, NULL, false);31}32}3334void PhantomEgg_Create(void *data)35{36RSDK_THIS(PhantomEgg);3738self->drawFX = FX_FLIP;3940if (!SceneInfo->inEditor) {41self->active = ACTIVE_BOUNDS;42self->updateRange.x = 0x800000;43self->updateRange.y = 0x1000000;44self->attackStateTable = PhantomEgg->attackStateTable1;45self->type = VOID_TO_INT(data);4647if (!data) {48self->visible = false;49self->drawGroup = Zone->objectDrawGroup[0];5051self->hitbox.left = -24;52self->hitbox.top = -24;53self->hitbox.right = 24;54self->hitbox.bottom = 24;5556self->drawFX = FX_FLIP;57self->active = ACTIVE_NORMAL;58self->health = 16;5960RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 0, &self->coreAnimator, true, 0);61RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 17, &self->eggmanAnimator, true, 0);62RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);63RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 0);64RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 13, &self->rubyAnimator, true, 0);6566self->targetPos.x = self->position.x;67self->targetPos.y = self->position.y;68self->state = PhantomEgg_State_SetupArena;69}70}71}7273void PhantomEgg_StageLoad(void)74{75PhantomEgg->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomEgg.bin", SCOPE_STAGE);7677PhantomEgg->savedGameProgress = false;7879#if MANIA_USE_PLUS80if (SceneInfo->filter & FILTER_ENCORE) {81RSDK.LoadPalette(0, "EncoreTMZ3.act", 0b0000000011111111);82RSDK.LoadPalette(1, "EncoreTMZ3.act", 0b0000000011111111);83}84#endif8586if (!PhantomEgg->setupPalette) {87RSDK.CopyPalette(0, 0, 1, 0, 128);88PhantomEgg->setupPalette = true;89}9091RSDK.CopyPalette(1, 128, 0, 128, 128);92RSDK.CopyPalette(0, 0, 4, 0, 128);9394PhantomEgg->disableSuperForm = false;9596PhantomEgg->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");97PhantomEgg->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");98PhantomEgg->sfxRocketJet = RSDK.GetSfx("Stage/RocketJet.wav");99PhantomEgg->sfxExplosion3 = RSDK.GetSfx("Stage/Explosion3.wav");100PhantomEgg->sfxJump = RSDK.GetSfx("TMZ3/Jump.wav");101PhantomEgg->sfxLand = RSDK.GetSfx("TMZ3/Land.wav");102PhantomEgg->sfxRepel = RSDK.GetSfx("TMZ3/Repel.wav");103PhantomEgg->sfxShield = RSDK.GetSfx("TMZ3/Shield.wav");104PhantomEgg->sfxShock = RSDK.GetSfx("TMZ3/Shock.wav");105PhantomEgg->sfxSummon = RSDK.GetSfx("TMZ3/Summon.wav");106PhantomEgg->sfxMissile = RSDK.GetSfx("TMZ3/Missile.wav");107}108109void PhantomEgg_HandleAnimations(void)110{111RSDK_THIS(PhantomEgg);112113RSDK.ProcessAnimation(&self->eggmanAnimator);114115if (self->rubyAnimator.animationID == 14) {116RSDK.ProcessAnimation(&self->rubyAnimator);117118if (self->rubyAnimator.frameID == self->rubyAnimator.frameCount - 1)119RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 13, &self->rubyAnimator, true, 0);120}121122if (self->invincibilityTimer > 0)123self->invincibilityTimer--;124125if (self->eggmanAnimator.animationID == 17) {126self->eggmanAnimator.speed = 0;127}128else {129if (self->eggmanAnimator.animationID == 18 || self->eggmanAnimator.animationID == 19) {130if (self->eggmanAnimator.frameID == self->eggmanAnimator.frameCount - 1)131RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 17, &self->eggmanAnimator, true, 0);132}133}134}135136void PhantomEgg_CheckPlayerCollisions(void)137{138RSDK_THIS(PhantomEgg);139140if (RSDK.GetEntityCount(PhantomShield->classID, true) <= 0) {141foreach_active(Player, player)142{143if (!self->invincibilityTimer && Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self)) {144PhantomEgg_Hit();145}146}147}148}149150void PhantomEgg_Hit(void)151{152RSDK_THIS(PhantomEgg);153154--self->health;155if (!(self->health & 3)) {156int32 id = (-2 - RSDK.GetEntityCount(TMZCable->classID, true)) & 3;157158foreach_active(TMZCable, cable)159{160if (cable->cableID == id || !self->health) {161RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 9, &cable->animator, true, 0);162cable->state = TMZCable_State_Destroyed;163// Bug Details:164// uncomment to fix a minor visual bug where the start few cable nodes wont be destroyed properly165// cable->timer = 0;166}167}168169if (id == 1)170self->attackStateTable = PhantomEgg->attackStateTable2;171else172self->attackStateTable = PhantomEgg->attackStateTable1;173}174175if (self->health <= 0) {176RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 20, &self->eggmanAnimator, true, 0);177self->targetPos.x = self->position.x;178self->targetPos.y = self->position.y;179self->state = PhantomEgg_State_Destroyed;180self->timer = 0;181PhantomEgg->disableSuperForm = true;182SceneInfo->timeEnabled = false;183Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);184}185else {186self->invincibilityTimer = 48;187RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 19, &self->eggmanAnimator, true, 0);188RSDK.PlaySfx(PhantomEgg->sfxHit, false, 255);189}190}191192void PhantomEgg_Explode(Hitbox *hitbox)193{194RSDK_THIS(PhantomEgg);195196if (!(Zone->timer % 7)) {197RSDK.PlaySfx(PhantomEgg->sfxExplosion2, false, 255);198199if (!(Zone->timer & 8)) {200int32 x = self->position.x + (RSDK.Rand(hitbox->left, hitbox->right) << 16);201int32 y = self->position.y + (RSDK.Rand(hitbox->top, hitbox->bottom) << 16);202CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];203}204}205}206207void PhantomEgg_HandleNextAttack(void)208{209RSDK_THIS(PhantomEgg);210211switch (self->attackStateTable[self->attackTimer]) {212case PHANTOMEGG_ATTACK_JUMP:213if (self->state != PhantomEgg_State_Attack_JumpLand) {214self->groundVel = 0;215self->velocity.x = 0;216self->remainingJumps = 3;217self->state = PhantomEgg_State_Attack_Jumped;218self->attackTimer++;219break;220}221break;222223case PHANTOMEGG_ATTACK_SHOCK:224self->timer = 0;225self->state = PhantomEgg_State_Attack_CableShock;226self->attackTimer++;227break;228229case PHANTOMEGG_ATTACK_MISSILES:230if (self->position.y >= PhantomEgg->boundsB - 0xA00000) {231foreach_active(PhantomMissile, missile) { missile->timer = 16 * missile->id + 8; }232233++self->attackTimer;234}235break;236237case PHANTOMEGG_ATTACK_WARP:238self->timer = 0;239self->state = PhantomEgg_State_Attack_PrepareWarp;240241RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 0);242RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);243RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armRAnimator, true, 0);244245self->attackTimer++;246break;247248default: self->attackTimer++; break;249}250251self->attackTimer %= 32;252}253254void PhantomEgg_SetupWarpFX(void)255{256foreach_all(PhantomEgg, phantomEgg)257{258PhantomEgg->startScanline = ScreenInfo->center.y;259PhantomEgg->endScanline = ScreenInfo->size.y;260261RSDK.GetTileLayer(Zone->fgLayer[0])->scanlineCallback = PhantomEgg_Scanline_WarpFX;262RSDK.GetTileLayer(Zone->fgLayer[1])->scanlineCallback = PhantomEgg_Scanline_WarpFX;263264PhantomRuby_PlaySfx(RUBYSFX_ATTACK1);265266phantomEgg->timer = 0;267phantomEgg->state = PhantomEgg_State_Attack_HandleWarp;268foreach_break;269}270}271272void PhantomEgg_HandlePhantomWarp(uint8 phantomID)273{274PhantomEgg->boundsStoreL1 = Zone->cameraBoundsL[0];275PhantomEgg->boundsStoreR1 = Zone->cameraBoundsR[0];276PhantomEgg->boundsStoreT1 = Zone->cameraBoundsT[0];277PhantomEgg->boundsStoreB1 = Zone->cameraBoundsB[0];278279Entity *targetPhantom = NULL;280281switch (phantomID & 3) {282case 0: {283foreach_all(PhantomGunner, gunner)284{285targetPhantom = (Entity *)gunner;286foreach_break;287}288break;289}290291case 1: {292foreach_all(PhantomShinobi, shinobi)293{294targetPhantom = (Entity *)shinobi;295foreach_break;296}297break;298}299300case 2: {301foreach_all(PhantomMystic, mystic)302{303targetPhantom = (Entity *)mystic;304foreach_break;305}306break;307}308309case 3: {310foreach_all(PhantomRider, rider)311{312targetPhantom = (Entity *)rider;313foreach_break;314}315break;316}317}318319if (targetPhantom) {320int32 phantomSlot = RSDK.GetEntitySlot(targetPhantom);321EntityPlatformNode *nodeTop = RSDK_GET_ENTITY(phantomSlot + 1, PlatformNode);322EntityPlatformNode *nodeBottom = RSDK_GET_ENTITY(phantomSlot + 2, PlatformNode);323EntityPlatformNode *nodeLeft = RSDK_GET_ENTITY(phantomSlot + 3, PlatformNode);324EntityPlatformNode *nodeRight = RSDK_GET_ENTITY(phantomSlot + 4, PlatformNode);325326int32 offsetX = nodeBottom->position.x - PhantomEgg->boundsM;327int32 offsetY = nodeBottom->position.y - (PhantomEgg->boundsStoreB1 << 16);328329for (int32 p = 0; p < Player->playerCount; ++p) {330Zone->cameraBoundsL[p] = nodeLeft->position.x >> 16;331Zone->cameraBoundsR[p] = nodeRight->position.x >> 16;332Zone->cameraBoundsT[p] = nodeTop->position.y >> 16;333Zone->cameraBoundsB[p] = nodeBottom->position.y >> 16;334335Zone->playerBoundsL[p] = nodeLeft->position.x;336Zone->playerBoundsR[p] = nodeRight->position.x;337Zone->playerBoundsT[p] = nodeTop->position.y;338Zone->playerBoundsB[p] = nodeBottom->position.y;339Zone->deathBoundary[p] = nodeBottom->position.y + 0x800000;340}341342targetPhantom->active = ACTIVE_NORMAL;343344EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);345camera->boundsL = Zone->cameraBoundsL[0];346camera->boundsR = Zone->cameraBoundsR[0];347camera->boundsT = Zone->cameraBoundsT[0];348camera->boundsB = Zone->cameraBoundsB[0];349350if ((phantomID & 3) == 3) {351EntityPlatformNode *nodeStart = RSDK_GET_ENTITY(phantomSlot + 5, PlatformNode);352camera->position.x = nodeStart->position.x;353camera->position.y = nodeStart->position.y;354355foreach_active(Player, player)356{357player->position.x = camera->position.x;358player->position.y = camera->position.y;359}360361foreach_all(PhantomHand, hand)362{363hand->position.x = camera->position.x;364hand->position.y = camera->position.y;365}366}367else {368camera->position.x += offsetX;369camera->position.y += offsetY;370foreach_active(Player, player)371{372player->position.x += offsetX;373player->position.y += offsetY;374}375376foreach_all(PhantomHand, hand)377{378hand->position.x += offsetX;379hand->position.y += offsetY;380}381}382383foreach_all(PhantomMissile, missile) { missile->active = ACTIVE_NEVER; }384foreach_all(TMZCable, cable) { cable->active = ACTIVE_NEVER; }385}386}387388void PhantomEgg_HandleReturnWarp(void)389{390for (int32 p = 0; p < Player->playerCount; ++p) {391Zone->cameraBoundsL[p] = PhantomEgg->boundsStoreL1;392Zone->cameraBoundsR[p] = PhantomEgg->boundsStoreR1;393Zone->cameraBoundsT[p] = PhantomEgg->boundsStoreT1;394Zone->cameraBoundsB[p] = PhantomEgg->boundsStoreB1;395396Zone->playerBoundsL[p] = PhantomEgg->boundsStoreL1 << 16;397Zone->playerBoundsR[p] = PhantomEgg->boundsStoreR1 << 16;398Zone->playerBoundsT[p] = PhantomEgg->boundsStoreT1 << 16;399Zone->playerBoundsB[p] = PhantomEgg->boundsStoreB1 << 16;400Zone->deathBoundary[p] = Zone->playerBoundsB[p];401}402403EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);404camera->boundsL = Zone->cameraBoundsL[0];405camera->boundsR = Zone->cameraBoundsR[0];406camera->boundsT = Zone->cameraBoundsT[0];407camera->boundsB = Zone->cameraBoundsB[0];408camera->position.x = PhantomEgg->boundsM;409camera->position.y = PhantomEgg->boundsStoreB1 - (ScreenInfo->center.y << 16);410411foreach_active(Player, player)412{413player->position.x = PhantomEgg->boundsM;414player->position.y = (PhantomEgg->boundsStoreB1 - 40) << 16;415}416417foreach_all(PhantomMissile, missile) { missile->active = ACTIVE_NORMAL; }418foreach_all(TMZCable, cable) { cable->active = ACTIVE_NORMAL; }419}420421void PhantomEgg_Scanline_WarpFX(ScanlineInfo *scanlines)422{423TileLayer *fgLow = RSDK.GetTileLayer(Zone->fgLayer[0]);424RSDK.ProcessParallax(fgLow);425426int32 line = 0;427if (PhantomEgg->startScanline - PhantomEgg->endScanline >= 0)428line = PhantomEgg->startScanline - PhantomEgg->endScanline;429430int32 lineY = scanlines[line].position.y;431for (int32 l = 0; l < line; ++l) scanlines[l].position.y = lineY;432433line = PhantomEgg->startScanline + PhantomEgg->endScanline;434if (line > ScreenInfo->size.y)435line = ScreenInfo->size.y;436437lineY = scanlines[line].position.y;438for (int32 l = line; l < ScreenInfo->size.y; ++l) scanlines[l].position.y = lineY;439}440441void PhantomEgg_Draw_Normal(void)442{443RSDK_THIS(PhantomEgg);444445if (self->invincibilityTimer & 1) {446RSDK.CopyPalette(3, 32, 0, 32, 10);447RSDK.CopyPalette(3, 128, 0, 128, 16);448RSDK.SetPaletteEntry(0, 128, 0xF0F0F0);449450self->direction = FLIP_NONE;451self->coreAnimator.frameID = 1;452RSDK.DrawSprite(&self->coreAnimator, NULL, false);453RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);454455self->coreAnimator.frameID = 0;456RSDK.DrawSprite(&self->coreAnimator, NULL, false);457RSDK.DrawSprite(&self->armLAnimator, NULL, false);458RSDK.DrawSprite(&self->legAnimator, NULL, false);459RSDK.DrawSprite(&self->rubyAnimator, NULL, false);460461self->direction = FLIP_X;462RSDK.DrawSprite(&self->armLAnimator, NULL, false);463RSDK.DrawSprite(&self->legAnimator, NULL, false);464465RSDK.CopyPalette(1, 32, 0, 32, 10);466RSDK.CopyPalette(1, 128, 0, 128, 16);467RSDK.SetPaletteEntry(0, 128, 0x000000);468}469else {470self->direction = FLIP_NONE;471self->coreAnimator.frameID = 1;472RSDK.DrawSprite(&self->coreAnimator, NULL, false);473RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);474475self->coreAnimator.frameID = 0;476RSDK.DrawSprite(&self->coreAnimator, NULL, false);477RSDK.DrawSprite(&self->armLAnimator, NULL, false);478RSDK.DrawSprite(&self->legAnimator, NULL, false);479RSDK.DrawSprite(&self->rubyAnimator, NULL, false);480481self->direction = FLIP_X;482RSDK.DrawSprite(&self->armLAnimator, NULL, false);483RSDK.DrawSprite(&self->legAnimator, NULL, false);484}485}486487void PhantomEgg_Draw_Cracked(void)488{489RSDK_THIS(PhantomEgg);490491self->direction = FLIP_NONE;492self->coreAnimator.frameID = 1;493RSDK.DrawSprite(&self->coreAnimator, NULL, false);494RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);495496self->coreAnimator.frameID = 0;497RSDK.DrawSprite(&self->coreAnimator, NULL, false);498RSDK.DrawSprite(&self->crackAnimator, NULL, false);499RSDK.DrawSprite(&self->armLAnimator, NULL, false);500RSDK.DrawSprite(&self->legAnimator, NULL, false);501502self->direction = FLIP_X;503RSDK.DrawSprite(&self->armLAnimator, NULL, false);504RSDK.DrawSprite(&self->legAnimator, NULL, false);505}506507void PhantomEgg_State_SetupArena(void)508{509RSDK_THIS(PhantomEgg);510511if (++self->timer >= 8) {512self->timer = 0;513514Zone->playerBoundActiveL[0] = true;515Zone->playerBoundActiveR[0] = true;516Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x;517Zone->cameraBoundsR[0] = (self->position.x >> 16) + ScreenInfo->center.x;518Zone->cameraBoundsT[0] = Zone->cameraBoundsB[0] - ScreenInfo->size.y;519520PhantomEgg->boundsL = (Zone->cameraBoundsL[0] + 64) << 16;521PhantomEgg->boundsR = (Zone->cameraBoundsR[0] - 64) << 16;522PhantomEgg->boundsM = self->position.x;523PhantomEgg->boundsT = (Zone->cameraBoundsT[0] + 48) << 16;524PhantomEgg->boundsB = (Zone->cameraBoundsB[0] - 96) << 16;525526self->position.y -= 0x1000000;527self->active = ACTIVE_NORMAL;528self->state = PhantomEgg_State_DimArena;529}530}531532void PhantomEgg_State_DimArena(void)533{534RSDK_THIS(PhantomEgg);535536if (self->timer) {537self->timer += 4;538RSDK.SetLimitedFade(0, 1, 2, self->timer, 128, 256);539540if (self->timer >= 384) {541self->timer = 0;542self->visible = true;543self->stateDraw = PhantomEgg_Draw_Normal;544self->state = PhantomEgg_State_EnterEggman;545546foreach_active(TMZCable, cable)547{548cable->parentPos = &self->position;549cable->state = TMZCable_State_Idle;550}551552int32 missileAngles[] = { -24, -10, 10, 24 };553int32 angle = 0;554int32 id = 0;555foreach_all(PhantomMissile, missile)556{557missile->angle = missileAngles[id];558missile->oscillateAngle = angle;559missile->active = ACTIVE_NORMAL;560missile->parent = self;561missile->id = id++;562angle += 64;563}564565Music_TransitionTrack(TRACK_EGGMAN1, 0.0125);566}567}568else {569if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x) {570SceneInfo->timeEnabled = false;571++self->timer;572}573}574}575576void PhantomEgg_State_EnterEggman(void)577{578RSDK_THIS(PhantomEgg);579580int32 startY = self->position.y;581self->velocity.y += self->position.y >= self->targetPos.y ? -0x3800 : 0x3800;582583if (self->velocity.y > 0x50000)584self->velocity.y = 0x50000;585586self->position.y += self->velocity.y;587if (startY >= self->targetPos.y) {588if (self->position.y < self->targetPos.y) {589++self->timer;590self->velocity.y = (4 * self->velocity.y) >> 3;591}592}593else {594if (self->position.y > self->targetPos.y) {595++self->timer;596self->velocity.y = (4 * self->velocity.y) >> 3;597}598}599600if (self->timer > 4) {601self->timer = 0;602self->state = PhantomEgg_State_AdjustStartingPos;603}604}605606void PhantomEgg_State_AdjustStartingPos(void)607{608RSDK_THIS(PhantomEgg);609610self->position.y += (self->targetPos.y - self->position.y) >> 3;611612if (++self->timer == 16) {613self->timer = 0;614foreach_active(TMZAlert, alert) { alert->state = TMZAlert_State_Activating; }615self->state = PhantomEgg_State_IntroHover;616}617}618619void PhantomEgg_State_IntroHover(void)620{621RSDK_THIS(PhantomEgg);622623PhantomEgg_HandleAnimations();624625RSDK.SetLimitedFade(0, 1, 2, (RSDK.Cos256(self->timer >> 2) >> 1) + 128, 128, 256);626self->timer += 11;627628self->angle = (self->angle + 3) & 0xFF;629self->position.y = (RSDK.Sin256(self->angle) << 11) + self->targetPos.y;630631if (self->timer == 3960) {632RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 14, &self->rubyAnimator, true, 0);633634SceneInfo->milliseconds = 0;635SceneInfo->seconds = 0;636SceneInfo->minutes = 0;637638PhantomRuby_PlaySfx(RUBYSFX_ATTACK1);639}640641if (self->timer == 4092) {642EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);643fxFade->speedIn = 32;644fxFade->speedOut = 32;645}646647if (self->timer >= 4608) {648self->timer = 0;649self->state = PhantomEgg_State_BeginFight;650651RSDK.CopyPalette(1, 128, 0, 128, 128);652}653}654655void PhantomEgg_State_BeginFight(void)656{657RSDK_THIS(PhantomEgg);658659PhantomEgg_HandleAnimations();660661self->position.y = BadnikHelpers_Oscillate(self->targetPos.y, 3, 11);662663if (++self->timer == 30) {664self->timer = 0;665SceneInfo->timeEnabled = true;666self->velocity.x = Player_GetNearestPlayerX()->position.x < self->position.x ? -0x60000 : 0x60000;667self->state = PhantomEgg_State_MoveAround;668CREATE_ENTITY(PhantomShield, self, self->position.x, self->position.y);669}670}671672void PhantomEgg_State_MoveAround(void)673{674RSDK_THIS(PhantomEgg);675676PhantomEgg_HandleAnimations();677678int32 startX = self->position.x;679if (self->position.x >= self->targetPos.x - 0x100000) {680if (self->position.x > self->targetPos.x + 0x100000)681self->velocity.x -= 0x1800;682}683else {684self->velocity.x += 0x1800;685}686687self->velocity.x = CLAMP(self->velocity.x, -0x50000, 0x50000);688self->position.x += self->velocity.x;689690if (startX >= self->targetPos.x - 0x100000) {691if (startX > self->targetPos.x + 0x100000 && self->position.x < self->targetPos.x + 0x100000)692self->velocity.x = (7 * self->velocity.x) >> 3;693}694else {695if (self->position.x > self->targetPos.x - 0x100000)696self->velocity.x = (7 * self->velocity.x) >> 3;697}698699if (self->timer <= 0) {700self->timer = RSDK.Rand(0x30, 0x60);701self->targetVelocity.x = self->targetPos.x + RSDK.Rand(-0x600000, 0x600000);702self->targetVelocity.y = self->targetPos.y + RSDK.Rand(-0x200000, 0x200000);703704int32 angle = RSDK.ATan2((self->targetVelocity.x - self->targetPos.x) >> 16, (self->targetVelocity.y - self->targetPos.y) >> 16);705self->targetVelocity.x = RSDK.Cos256(angle) << 9;706self->targetVelocity.y = RSDK.Sin256(angle) << 9;707PhantomEgg_HandleNextAttack();708}709else {710self->timer--;711}712713self->velocity.y += ((self->targetVelocity.y - self->velocity.y) >> 4);714self->position.y += self->velocity.y;715716if (self->position.y < PhantomEgg->boundsT && self->targetVelocity.y < 0)717self->targetVelocity.y = -self->targetVelocity.y;718719if (self->position.y > PhantomEgg->boundsB) {720if (self->targetVelocity.y > 0)721self->targetVelocity.y = -self->targetVelocity.y;722}723724PhantomEgg_CheckPlayerCollisions();725}726727void PhantomEgg_State_Attack_Jumped(void)728{729RSDK_THIS(PhantomEgg);730731PhantomEgg_HandleAnimations();732733self->velocity.y += 0x3800;734self->position.x += self->velocity.x;735self->position.y += self->velocity.y;736737Hitbox *hitbox = RSDK.GetHitbox(&self->legAnimator, 0);738if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, hitbox->bottom << 16, true)) {739RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 4, &self->armLAnimator, true, 0);740RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 4, &self->armRAnimator, true, 0);741742self->state = PhantomEgg_State_Attack_JumpLand;743RSDK.PlaySfx(PhantomEgg->sfxLand, false, 255);744}745746if (self->velocity.x < 0 && self->position.x < PhantomEgg->boundsL)747self->velocity.x = -self->velocity.x;748749if (self->velocity.x > 0 && self->position.x > PhantomEgg->boundsR)750self->velocity.x = -self->velocity.x;751752PhantomEgg_CheckPlayerCollisions();753}754755void PhantomEgg_State_Attack_JumpLand(void)756{757RSDK_THIS(PhantomEgg);758759RSDK.ProcessAnimation(&self->armLAnimator);760RSDK.ProcessAnimation(&self->armRAnimator);761RSDK.ProcessAnimation(&self->legAnimator);762763PhantomEgg_HandleAnimations();764765Hitbox *hitbox = RSDK.GetHitbox(&self->legAnimator, 0);766RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, hitbox->bottom << 16, 16);767768if (self->legAnimator.frameID == 5)769PhantomEgg_HandleNextAttack();770771if (self->legAnimator.frameID == 6) {772self->velocity.x = RSDK.Rand(0, 256) > 128 ? -0x20000 : 0x20000;773self->velocity.y = -0x80000;774self->state = PhantomEgg_State_Attack_JumpAttack;775776RSDK.PlaySfx(PhantomEgg->sfxJump, false, 255);777}778779PhantomEgg_CheckPlayerCollisions();780}781782void PhantomEgg_State_Attack_JumpAttack(void)783{784RSDK_THIS(PhantomEgg);785786RSDK.ProcessAnimation(&self->armLAnimator);787RSDK.ProcessAnimation(&self->armRAnimator);788RSDK.ProcessAnimation(&self->legAnimator);789790PhantomEgg_HandleAnimations();791792if (self->velocity.x < 0 && self->position.x < PhantomEgg->boundsL)793self->velocity.x = -self->velocity.x;794795if (self->velocity.x > 0 && self->position.x > PhantomEgg->boundsR)796self->velocity.x = -self->velocity.x;797798self->velocity.y += 0x3800;799self->position.x += self->velocity.x;800self->position.y += self->velocity.y;801802if (self->legAnimator.frameID == self->legAnimator.frameCount - 1) {803RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 0);804RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);805RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armRAnimator, true, 0);806807--self->remainingJumps;808if (self->remainingJumps > 0)809self->state = PhantomEgg_State_Attack_Jumped;810else811self->state = PhantomEgg_State_MoveAround;812}813814PhantomEgg_CheckPlayerCollisions();815}816817void PhantomEgg_State_Attack_CableShock(void)818{819RSDK_THIS(PhantomEgg);820821PhantomEgg_HandleAnimations();822823if (self->timer < 160 && !(self->timer & 0xF))824RSDK.PlaySfx(PhantomEgg->sfxShock, false, 255);825826if (++self->timer == 30) {827foreach_active(TMZCable, cable)828{829if (cable->state != TMZCable_State_Destroyed) {830RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 10, &cable->animator, true, 0);831cable->state = TMZCable_State_Charge;832}833}834835foreach_active(PhantomShield, shield)836{837RSDK.SetSpriteAnimation(PhantomShield->aniFrames, 2, &shield->animator, true, 0);838shield->state = PhantomShield_State_Disappear;839}840}841842if (self->timer == 4 * self->health + 160)843RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 14, &self->rubyAnimator, true, 0);844845if ((self->rubyAnimator.animationID == 14 && self->rubyAnimator.frameID == self->rubyAnimator.frameCount - 2) || self->timer > 288) {846self->timer = 0;847848CREATE_ENTITY(PhantomShield, self, self->position.x, self->position.y);849if (self->legAnimator.frameID > 0) {850self->legAnimator.frameID = 6;851self->legAnimator.timer = 0;852self->state = PhantomEgg_State_Attack_JumpLand;853}854else {855self->state = PhantomEgg_State_MoveAround;856}857}858859PhantomEgg_CheckPlayerCollisions();860}861862void PhantomEgg_State_Attack_PrepareWarp(void)863{864RSDK_THIS(PhantomEgg);865866if (!self->timer) {867foreach_active(PhantomShield, shield)868{869RSDK.SetSpriteAnimation(PhantomShield->aniFrames, 2, &shield->animator, true, 0);870shield->state = PhantomShield_State_Disappear;871}872}873874if (self->palBlendPercent < 112)875self->palBlendPercent += 4;876877RSDK.SetLimitedFade(0, 1, 2, self->palBlendPercent, 128, 256);878self->position.x += (PhantomEgg->boundsM - self->position.x) >> 4;879self->position.y += ((PhantomEgg->boundsB - self->position.y) - 0x400000) >> 4;880881if (++self->timer == 60) {882self->timer = 0;883self->position.x = PhantomEgg->boundsM;884self->position.y = PhantomEgg->boundsB - 0x400000;885886RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 5, &self->armLAnimator, true, 0);887RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 6, &self->armRAnimator, true, 0);888self->state = PhantomEgg_State_Attack_GrabPlayers;889}890}891892void PhantomEgg_State_Attack_GrabPlayers(void)893{894RSDK_THIS(PhantomEgg);895896RSDK.ProcessAnimation(&self->armLAnimator);897RSDK.ProcessAnimation(&self->armRAnimator);898RSDK.SetLimitedFade(0, 1, 2, self->palBlendPercent, 128, 256);899900if (!self->timer) {901CREATE_ENTITY(PhantomHand, self, self->position.x - 0x400000, self->position.y)->velocity.x = -0x8000;902903EntityPhantomHand *hand = CREATE_ENTITY(PhantomHand, self, self->position.x + 0x400000, self->position.y);904hand->direction = FLIP_X;905hand->velocity.x = 0x8000;906}907908++self->timer;909}910911void PhantomEgg_State_Attack_HandleWarp(void)912{913RSDK_THIS(PhantomEgg);914915if (PhantomEgg->endScanline == 96) {916EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);917fxFade->speedIn = 16;918fxFade->speedOut = 16;919}920921if (self->palBlendPercent > 0)922self->palBlendPercent -= 4;923924RSDK.ProcessAnimation(&self->armLAnimator);925RSDK.ProcessAnimation(&self->armRAnimator);926RSDK.SetLimitedFade(0, 1, 2, self->palBlendPercent, 128, 256);927928if (PhantomEgg->endScanline <= 0) {929RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);930RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armRAnimator, true, 0);931932if (self->visible) {933self->state = PhantomEgg_State_Attack_WarpAway;934PhantomEgg_HandlePhantomWarp(self->phantomID);935self->phantomID = (self->phantomID + 1) & 3;936}937else {938self->state = PhantomEgg_State_Attack_WarpReturn;939PhantomEgg_HandleReturnWarp();940self->visible = true;941}942}943else {944PhantomEgg->endScanline -= 4;945}946}947948void PhantomEgg_State_Attack_WarpAway(void)949{950RSDK_THIS(PhantomEgg);951952if (PhantomEgg->endScanline >= ScreenInfo->size.y) {953RSDK.GetTileLayer(Zone->fgLayer[0])->scanlineCallback = StateMachine_None;954RSDK.GetTileLayer(Zone->fgLayer[1])->scanlineCallback = StateMachine_None;955956self->timer = 0;957self->visible = false;958self->state = StateMachine_None;959}960else {961if (self->timer > 8)962PhantomEgg->endScanline += 2;963964if (self->timer == 60) {965foreach_active(PhantomHand, hand) { hand->state = PhantomHand_State_BreakApart; }966}967++self->timer;968}969}970971void PhantomEgg_State_Attack_WarpReturn(void)972{973RSDK_THIS(PhantomEgg);974975if (PhantomEgg->endScanline >= ScreenInfo->size.y) {976RSDK.GetTileLayer(Zone->fgLayer[0])->scanlineCallback = StateMachine_None;977RSDK.GetTileLayer(Zone->fgLayer[1])->scanlineCallback = StateMachine_None;978979CREATE_ENTITY(PhantomShield, self, self->position.x, self->position.y);980self->timer = 0;981self->state = PhantomEgg_State_MoveAround;982}983else {984if (self->timer > 8)985PhantomEgg->endScanline += 2;986987if (self->timer == 60) {988foreach_active(PhantomHand, hand) { hand->state = PhantomHand_State_BreakApart; }989}990++self->timer;991}992}993994void PhantomEgg_State_Destroyed(void)995{996RSDK_THIS(PhantomEgg);997998self->position.x = self->targetPos.x + RSDK.Rand(-0x20000, 0x20000);999self->position.y = self->targetPos.y + RSDK.Rand(-0x20000, 0x20000);10001001if (!RSDK.GetEntityCount(TMZCable->classID, true)) {1002int32 id = 0;1003foreach_active(PhantomMissile, missile)1004{1005switch (id++) {1006case 0:1007missile->velocity.x = -0x20000;1008missile->velocity.y = -0x20000;1009missile->groundVel = 16;1010break;10111012case 1:1013missile->velocity.x = -0x10000;1014missile->velocity.y = -0x40000;1015missile->groundVel = 8;1016break;10171018case 2:1019missile->velocity.x = 0x10000;1020missile->velocity.y = -0x40000;1021missile->groundVel = -8;1022break;10231024case 3:1025missile->velocity.x = 0x20000;1026missile->velocity.y = -0x20000;1027missile->groundVel = -16;1028break;10291030default: break;1031}10321033missile->state = PhantomMissile_State_Destroyed;1034}10351036foreach_active(PhantomShield, shield)1037{1038RSDK.SetSpriteAnimation(PhantomShield->aniFrames, 2, &shield->animator, true, 0);1039shield->state = PhantomShield_State_Disappear;1040}10411042CREATE_ENTITY(TMZ2Outro, NULL, self->position.x, self->position.y);1043RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 1);10441045for (int32 i = 0; i < 0x100; ++i) RSDK.SetPaletteEntry(7, i, RSDK.GetPaletteEntry(1, i) & 0xFF0000);10461047self->state = PhantomEgg_State_Exploding;1048}1049}10501051void PhantomEgg_State_Exploding(void)1052{1053RSDK_THIS(PhantomEgg);10541055self->targetPos.x += ((PhantomEgg->boundsM - self->targetPos.x) >> 5);1056self->targetPos.y += ((PhantomEgg->boundsB - self->targetPos.y - 0x400000) >> 5);10571058self->position.x = self->targetPos.x + RSDK.Rand(-0x20000, 0x20000);1059self->position.y = self->targetPos.y + RSDK.Rand(-0x20000, 0x20000);10601061PhantomEgg_Explode(&self->hitbox);10621063if (++self->timer == 152) {1064self->timer = 0;1065self->position.x = PhantomEgg->boundsM;1066self->position.y = PhantomEgg->boundsB - 0x400000;10671068bool32 goodEnd = (CHECK_CHARACTER_ID(ID_SONIC, 1) || (CHECK_CHARACTER_ID(ID_KNUCKLES, 1) && CHECK_CHARACTER_ID(ID_KNUCKLES, 2)))1069&& SaveGame_AllChaosEmeralds();10701071#if MANIA_USE_PLUS1072if (SceneInfo->filter & FILTER_ENCORE)1073goodEnd = false; // no ERZ for encore modes1074#endif1075if (goodEnd) {1076self->state = PhantomEgg_State_StartGoodEnd;1077Music_FadeOut(0.0125);1078}1079else {1080self->state = PhantomEgg_State_StartBadEnd;1081}1082}1083}10841085void PhantomEgg_State_StartBadEnd(void)1086{1087RSDK_THIS(PhantomEgg);10881089if (++self->timer == 30) {1090self->timer = 0;1091RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 1, &self->crackAnimator, false, 0);1092self->stateDraw = PhantomEgg_Draw_Cracked;10931094RSDK.PlaySfx(PhantomEgg->sfxRocketJet, false, 255);10951096EntityPhantomRuby *ruby = CREATE_ENTITY(PhantomRuby, NULL, self->position.x, self->position.y + 0x100000);1097ruby->state = PhantomRuby_State_MoveRotateGravity_CheckGround;1098ruby->velocity.x = -0x10000;1099ruby->velocity.y = -0x20000;1100self->state = PhantomEgg_State_CrackOpen;1101}1102}11031104void PhantomEgg_State_CrackOpen(void)1105{1106RSDK_THIS(PhantomEgg);11071108RSDK.ProcessAnimation(&self->crackAnimator);11091110if (++self->timer == 60) {1111EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);1112fxFade->speedIn = 512;1113fxFade->wait = 16;1114fxFade->speedOut = 16;1115RSDK.PlaySfx(PhantomEgg->sfxExplosion3, false, 255);1116}11171118if (self->timer == 64) {1119RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 2, &self->crackAnimator, false, 0);11201121self->drawGroup = Zone->objectDrawGroup[0] + 1;1122self->stateDraw = StateMachine_None;11231124EntityEggman *eggman = CREATE_ENTITY(Eggman, NULL, self->position.x, self->position.y + 0x100000);1125RSDK.SetSpriteAnimation(Eggman->aniFrames, 7, &eggman->animator, true, 0);1126eggman->onGround = false;1127eggman->state = Eggman_State_FallAndCollide;1128}11291130foreach_active(Eggman, eggman)1131{1132eggman->position.x = self->position.x;1133eggman->position.y = self->position.y;1134eggman->velocity.y = 0;1135}11361137if (self->timer == 96) {1138self->timer = 0;1139self->state = PhantomEgg_State_CrackedExploding;1140}1141}11421143void PhantomEgg_State_CrackedExploding(void)1144{1145RSDK_THIS(PhantomEgg);11461147if (!(Zone->timer & 0xF)) {1148int32 x = self->position.x + RSDK.Rand(-0x380000, -0x180000);1149int32 y = self->position.y + RSDK.Rand(-0x300000, -0x100000);1150EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSSPUFF), x, y);1151explosion->drawGroup = Zone->objectDrawGroup[1];1152}11531154if ((Zone->timer & 0xF) == 8) {1155int32 x = self->position.x + RSDK.Rand(0x180000, 0x380000);1156int32 y = self->position.y + RSDK.Rand(-0x300000, -0x100000);1157EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSSPUFF), x, y);1158explosion->drawGroup = Zone->objectDrawGroup[1];1159}11601161if (++self->timer == 120)1162self->state = StateMachine_None;1163}11641165void PhantomEgg_State_StartGoodEnd(void)1166{1167RSDK_THIS(PhantomEgg);11681169if (self->timer < 256 && !(Zone->timer % 3)) {1170RSDK.PlaySfx(PhantomEgg->sfxExplosion2, false, 255);11711172if (Zone->timer & 8) {1173int32 x = self->position.x + RSDK.Rand(-0x800000, 0x800000);1174int32 y = self->position.y + (RSDK.Rand(self->hitbox.top, self->hitbox.bottom + 64) << 16);1175CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];1176}1177}11781179if (++self->timer == 120) {1180CREATE_ENTITY(FXRuby, NULL, self->position.x, self->position.y + 0x100000)->radiusSpeed = 3;1181PhantomRuby_PlaySfx(RUBYSFX_REDCUBE);1182}11831184if (self->timer == 320) {1185EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);1186fxFade->speedIn = 16;1187fxFade->wait = 32;1188fxFade->fadeOutBlack = 1;1189fxFade->speedOut = 16;1190PhantomRuby_PlaySfx(RUBYSFX_ATTACK1);1191}11921193if (self->timer >= 512) {1194if (globals->saveSlotID != NO_SAVE_SLOT) {1195if (self->timer == 512) {1196if (Zone_IsZoneLastAct())1197GameProgress_MarkZoneCompleted(Zone_GetZoneID());11981199SaveGame_SaveFile(PhantomEgg_SaveGameCB);1200UIWaitSpinner_StartWait();1201}12021203if (PhantomEgg->savedGameProgress)1204UIWaitSpinner_FinishWait();1205}12061207if (globals->saveSlotID == NO_SAVE_SLOT || PhantomEgg->savedGameProgress) {1208++SceneInfo->listPos;1209RSDK.LoadScene();1210self->state = 0;1211}1212}1213}12141215#if MANIA_USE_PLUS1216void PhantomEgg_SaveGameCB(bool32 success) { PhantomEgg->savedGameProgress = true; }1217#else1218void PhantomEgg_SaveGameCB(void) { PhantomEgg->savedGameProgress = true; }1219#endif12201221#if GAME_INCLUDE_EDITOR1222void PhantomEgg_EditorDraw(void)1223{1224RSDK_THIS(PhantomEgg);12251226RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 0, &self->coreAnimator, false, 0);1227RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 17, &self->eggmanAnimator, false, 0);1228RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, false, 0);1229RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, false, 0);1230RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 13, &self->rubyAnimator, false, 0);12311232PhantomEgg_Draw_Normal();12331234if (showGizmos()) {1235RSDK_DRAWING_OVERLAY(true);12361237DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER, -SCREEN_YSIZE, WIDE_SCR_XCENTER, 0, 1 | 2 | 4 | 0, 0x00C0F0);12381239RSDK_DRAWING_OVERLAY(false);1240}1241}12421243void PhantomEgg_EditorLoad(void)1244{1245PhantomEgg->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomEgg.bin", SCOPE_STAGE);12461247RSDK_ACTIVE_VAR(PhantomEgg, type);1248RSDK_ENUM_VAR("Eggman", PHANTOMEGG_EGGMAN);1249}1250#endif12511252void PhantomEgg_Serialize(void) { RSDK_EDITABLE_VAR(PhantomEgg, VAR_ENUM, type); }125312541255