Path: blob/master/SonicMania/Objects/HCZ/ScrewMobile.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: ScrewMobile Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectScrewMobile *ScrewMobile;1011void ScrewMobile_Update(void)12{13RSDK_THIS(ScrewMobile);1415StateMachine_Run(self->state);16}1718void ScrewMobile_LateUpdate(void) {}1920void ScrewMobile_StaticUpdate(void)21{22foreach_active(ScrewMobile, screwMobile)23{24if (screwMobile->state != ScrewMobile_StateDepthCharge_Active) {25RSDK.AddDrawListRef(Zone->playerDrawGroup[0] - 1, RSDK.GetEntitySlot(screwMobile));26}27}2829if (ScrewMobile->fanSfxTimer) {30if (!ScrewMobile->playingFanSfx) {31RSDK.PlaySfx(ScrewMobile->sfxBigFan, 47208, 0xFF);32ScrewMobile->playingFanSfx = true;33}34}35else if (ScrewMobile->playingFanSfx) {36RSDK.StopSfx(ScrewMobile->sfxBigFan);37ScrewMobile->playingFanSfx = false;38}3940ScrewMobile->fanSfxTimer = 0;41}4243void ScrewMobile_Draw(void)44{45RSDK_THIS(ScrewMobile);4647StateMachine_Run(self->stateDraw);48}4950void ScrewMobile_Create(void *data)51{52RSDK_THIS(ScrewMobile);5354if (globals->gameMode == MODE_TIMEATTACK) {55destroyEntity(self);56}57else {58self->drawFX = FX_FLIP;59if (!SceneInfo->inEditor) {60self->visible = true;6162if (data) {63switch (VOID_TO_INT(data)) {64default: break;6566case 1:67self->active = ACTIVE_NORMAL;68self->drawGroup = Zone->hudDrawGroup - 2;69self->updateRange.x = 0x800000;70self->updateRange.y = 0x800000;71self->whirlpoolHeight = 0x3800;72RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 6, &self->mobileAnimator, true, 0);73self->state = ScrewMobile_StateDepthCharge_Active;74self->stateDraw = ScrewMobile_Draw_DepthCharge;75break;76}77}78else {79self->active = ACTIVE_BOUNDS;80self->drawGroup = Zone->hudDrawGroup - 1;81self->updateRange.x = 0x800000;82self->updateRange.y = 0x800000;83self->startPos = self->position;8485RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 0, &self->mobileAnimator, true, 0);86RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 1, &self->propellerAnimator, true, 0);87RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 7, &self->rackAnimator, true, 6);88RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 2, &self->whirlpoolAnimator, true, 0);89RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 3, &self->whirlpoolTopAnimator, true, 0);90RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 5, &self->whirlpoolBottomAnimator, true, 0);9192// ???93// The HCZ1 boss health is managed by DiveEggman... so what was this for??94self->health = 8;95self->propellerAnimator.speed = 0;96self->state = ScrewMobile_State_CheckPlayerEnter;97self->stateDraw = ScrewMobile_Draw_ScrewMobile;98}99}100}101}102103void ScrewMobile_StageLoad(void)104{105if (RSDK.CheckSceneFolder("HCZ"))106ScrewMobile->aniFrames = RSDK.LoadSpriteAnimation("HCZ/ScrewMobile.bin", SCOPE_STAGE);107108ScrewMobile->hitboxCockpit.left = -32;109ScrewMobile->hitboxCockpit.top = -20;110ScrewMobile->hitboxCockpit.right = 32;111ScrewMobile->hitboxCockpit.bottom = 20;112113ScrewMobile->hitboxDepthCharge.left = -8;114ScrewMobile->hitboxDepthCharge.top = -4;115ScrewMobile->hitboxDepthCharge.right = 8;116ScrewMobile->hitboxDepthCharge.bottom = 4;117118ScrewMobile->playingFanSfx = false;119ScrewMobile->fanSfxTimer = 0;120ScrewMobile->active = ACTIVE_ALWAYS;121122ScrewMobile->sfxButton = RSDK.GetSfx("Stage/Button2.wav");123ScrewMobile->sfxEggMobile = RSDK.GetSfx("HCZ/EggMobile.wav");124ScrewMobile->sfxBigFan = RSDK.GetSfx("HCZ/BigFan.wav");125ScrewMobile->sfxImpact = RSDK.GetSfx("Stage/Impact5.wav");126}127128void ScrewMobile_State_CheckPlayerEnter(void)129{130RSDK_THIS(ScrewMobile);131132Zone->cameraBoundsR[0] = (self->position.x >> 16) + 32;133Zone->playerBoundsR[0] = Zone->cameraBoundsR[0] << 16;134Zone->playerBoundActiveR[0] = true;135136if (Player->playerCount > 1) {137EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);138139if (Player_CheckCollisionTouch(player2, self, &ScrewMobile->hitboxCockpit)) {140player2->direction = FLIP_X;141player2->state = Player_State_Static;142player2->velocity.x = 0;143player2->velocity.y = 0;144player2->groundVel = 0;145player2->position.x = self->position.x;146player2->position.y = self->position.y - 0x100000;147player2->tileCollisions = TILECOLLISION_DOWN;148RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);149}150}151152EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);153if (Player_CheckCollisionTouch(player1, self, &ScrewMobile->hitboxCockpit)) {154Zone->cameraBoundsT[0] = (self->position.y >> 16) - 40;155Zone->playerBoundsR[0] = 0x7FFF;156Zone->cameraBoundsR[0] = 0x7FFF;157Zone->playerBoundActiveR[0] = false;158159player1->state = Player_State_Static;160player1->velocity.x = 0;161player1->velocity.y = 0;162player1->groundVel = 0;163player1->direction = FLIP_X;164player1->position.x = self->position.x;165player1->position.y = self->position.y - 0x100000;166player1->tileCollisions = TILECOLLISION_DOWN;167RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);168169Music_TransitionTrack(TRACK_MINIBOSS, 0.0125);170RSDK.PlaySfx(ScrewMobile->sfxEggMobile, false, 255);171DCEvent->canExplodeBombs = true;172self->state = ScrewMobile_State_PlayerRiding;173}174}175176void ScrewMobile_State_PlayerRiding(void)177{178RSDK_THIS(ScrewMobile);179180RSDK.ProcessAnimation(&self->propellerAnimator);181RSDK.ProcessAnimation(&self->rackAnimator);182RSDK.ProcessAnimation(&self->whirlpoolAnimator);183RSDK.ProcessAnimation(&self->whirlpoolTopAnimator);184RSDK.ProcessAnimation(&self->whirlpoolBottomAnimator);185186self->position.y = BadnikHelpers_Oscillate(self->startPos.y, 2, 10);187self->position.x += self->velocity.x;188189EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);190if (Player_CheckValidState(player1)) {191player1->position.x = self->position.x;192player1->position.y = self->position.y - 0x100000;193player1->velocity.x = 0;194player1->velocity.y = 0;195player1->outtaHereTimer = 0;196RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);197198if ((player1->left || player1->right) && !player1->jumpHold) {199if (player1->left) {200self->velocity.x -= 0xC00;201202if (self->velocity.x > 0)203self->velocity.x -= 0x1200;204else if (self->velocity.x < -0x20000)205self->velocity.x = -0x20000;206}207else if (player1->right) {208self->velocity.x += 0xC00;209210if (self->velocity.x < 0)211self->velocity.x += 0x1200;212else if (self->velocity.x > 0x20000)213self->velocity.x = 0x20000;214}215}216else {217if (self->velocity.x) {218if (self->velocity.x >= 0) {219self->velocity.x -= 0xC00;220221if (self->velocity.x < 0)222self->velocity.x = 0;223}224else {225self->velocity.x += 0xC00;226227if (self->velocity.x > 0)228self->velocity.x = 0;229}230}231}232233if (self->velocity.x) {234if (self->velocity.x <= 0) {235self->direction = FLIP_NONE;236237if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -0x200000, 0, true)) {238if (self->velocity.x <= -0x20000)239RSDK.PlaySfx(ScrewMobile->sfxImpact, false, 0xFF);240241self->velocity.x = 0;242}243}244else {245self->direction = FLIP_X;246if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, 0x200000, 0, true)) {247if (self->velocity.x >= 0x20000)248RSDK.PlaySfx(ScrewMobile->sfxImpact, false, 0xFF);249250self->velocity.x = 0;251}252}253}254255player1->direction = self->direction ^ FLIP_X;256}257258if (Player->playerCount >= 2) {259EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);260261if (Player_CheckValidState(player2)) {262if (player2->state == Player_State_Static) {263player2->position.x = self->position.x;264player2->position.y = self->position.y - 0x100000;265player2->velocity.x = 0;266player2->velocity.y = 0;267player2->outtaHereTimer = 0;268player2->direction = self->direction ^ FLIP_X;269RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);270}271else if (Player_CheckCollisionTouch(player2, self, &ScrewMobile->hitboxCockpit)) {272player2->state = Player_State_Static;273player2->velocity.x = 0;274player2->velocity.y = 0;275player2->groundVel = 0;276player2->direction = FLIP_X;277player2->position.x = self->position.x;278player2->position.y = self->position.y - 0x100000;279player2->drawGroup = Zone->playerDrawGroup[0];280player2->tileCollisions = TILECOLLISION_DOWN;281player2->direction = self->direction ^ FLIP_X;282RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);283}284}285}286287if (Player_CheckValidState(player1)) {288if (player1->jumpPress) {289if (!Zone->cameraBoundsT[0]) {290Player_Action_Jump(player1);291self->state = ScrewMobile_State_Idle;292return;293}294295if (!self->whirlPoolTimer && ScrewMobile->playingFanSfx == false) {296self->propellerAnimator.speed = 64;297RSDK.PlaySfx(ScrewMobile->sfxButton, false, 255);298}299}300if (!player1->jumpHold || self->whirlPoolTimer) {301EntityWhirlpool *whirlpool = self->whirlpool;302if (whirlpool) {303if (whirlpool->classID == Whirlpool->classID)304whirlpool->activePlayers = -3;305306self->whirlpool = NULL;307}308309if (self->propellerAnimator.speed)310self->propellerAnimator.speed -= 2;311312if (self->propellerAnimator.speed > 0xC0)313++ScrewMobile->fanSfxTimer;314}315else {316if (self->whirlpoolHeight > 0) {317EntityWhirlpool *whirlpool = self->whirlpool;318319if (whirlpool) {320if (whirlpool->classID == Whirlpool->classID)321whirlpool->position.x = self->position.x;322else323self->whirlpool = NULL;324}325else {326Vector2 pos;327pos.x = 0x1000000;328pos.y = 0x1000000;329whirlpool = CREATE_ENTITY(Whirlpool, &pos, self->position.x, self->startPos.y + 0xF00000);330whirlpool->activePlayers = -1;331whirlpool->angVel = 10;332whirlpool->alpha = 0;333whirlpool->isPermanent = true;334self->whirlpool = whirlpool;335}336}337338if (self->propellerAnimator.speed < 0x100)339self->propellerAnimator.speed += 2;340341if (self->propellerAnimator.speed > 0x40)342++ScrewMobile->fanSfxTimer;343}344}345346if (self->propellerAnimator.speed < 0xF0) {347if (self->whirlpoolHeight > 0)348self->whirlpoolHeight--;349}350else {351if (self->whirlpoolHeight < 86)352self->whirlpoolHeight++;353}354355if (player1->down) {356if (!self->bombPress && !self->timer && self->rackAnimator.frameID == 6) {357RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 7, &self->rackAnimator, true, 0);358self->bombPress = true;359}360}361else {362self->bombPress = false;363}364365if (self->rackAnimator.frameID == 2 && !self->timer) {366self->timer = 60;367368EntityScrewMobile *bomb = CREATE_ENTITY(ScrewMobile, INT_TO_VOID(SCREWMOBILE_BOMB), self->position.x, self->position.y + 0x130000);369if (self->direction)370bomb->position.x -= 0x350000;371else372bomb->position.x += 0x350000;373bomb->direction = self->direction;374bomb->velocity.y = -0x7000;375}376377if (self->whirlPoolTimer > (player1->jumpHold == true))378self->whirlPoolTimer--;379380if (self->timer)381self->timer--;382383if (self->invincibilityTimer > 0)384self->invincibilityTimer--;385}386387void ScrewMobile_State_BossFinished(void)388{389RSDK_THIS(ScrewMobile);390391EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);392player1->up = false;393player1->down = false;394player1->left = false;395player1->right = true;396player1->jumpPress = false;397player1->jumpHold = false;398399ScrewMobile_State_PlayerRiding();400401Zone->cameraBoundsL[0] = ScreenInfo->position.x;402Zone->playerBoundsL[0] = Zone->cameraBoundsL[0] << 16;403404if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, 0x200000, 0, true)) {405EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);406Zone->cameraBoundsL[0] += 2;407ScreenInfo->position.x = Zone->cameraBoundsL[0];408Zone->playerBoundsL[0] = Zone->cameraBoundsL[0] << 16;409camera->position.x = Zone->cameraBoundsL[0] << 16;410411if (Zone->cameraBoundsL[0] >= (self->position.x >> 16) - 32)412self->state = ScrewMobile_State_Idle;413}414}415416void ScrewMobile_State_Idle(void)417{418RSDK_THIS(ScrewMobile);419420self->position.y = BadnikHelpers_Oscillate(self->startPos.y, 2, 10);421422EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);423if (player1->state == Player_State_Static) {424if (player1->jumpPress) {425Player_Action_Jump(player1);426}427else {428player1->position.x = self->position.x;429player1->position.y = self->position.y;430player1->position.y -= 0x100000;431player1->velocity.x = 0;432player1->velocity.y = 0;433player1->outtaHereTimer = 0;434player1->direction = self->direction ^ FLIP_X;435RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);436}437}438else if (player1->velocity.y >= 0 && Player_CheckCollisionTouch(player1, self, &ScrewMobile->hitboxCockpit)) {439player1->state = Player_State_Static;440player1->velocity.x = 0;441player1->velocity.y = 0;442player1->groundVel = 0;443player1->direction = FLIP_X;444player1->position.x = self->position.x;445player1->position.y = self->position.y;446player1->position.y -= 0x100000;447player1->drawGroup = Zone->playerDrawGroup[0];448player1->tileCollisions = TILECOLLISION_DOWN;449player1->direction = self->direction ^ FLIP_X;450RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);451}452453if (Player->playerCount >= 2) {454EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);455456if (player2->state == Player_State_Static) {457if (player2->jumpPress) {458Player_Action_Jump(player2);459}460else {461player2->position.x = self->position.x;462player2->position.y = self->position.y;463player2->position.y -= 0x100000;464player2->velocity.x = 0;465player2->velocity.y = 0;466player2->outtaHereTimer = 0;467player2->direction = self->direction ^ FLIP_X;468RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);469}470}471else if (player2->velocity.y >= 0) {472if (Player_CheckCollisionTouch(player2, self, &ScrewMobile->hitboxCockpit)) {473player2->state = Player_State_Static;474player2->velocity.x = 0;475player2->velocity.y = 0;476player2->groundVel = 0;477player2->direction = FLIP_X;478player2->position.x = self->position.x;479player2->position.y = self->position.y;480player2->position.y -= 0x100000;481player2->drawGroup = Zone->playerDrawGroup[0];482player2->tileCollisions = TILECOLLISION_DOWN;483player2->direction = self->direction ^ FLIP_X;484RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);485}486}487}488}489490void ScrewMobile_Draw_ScrewMobile(void)491{492RSDK_THIS(ScrewMobile);493494if (SceneInfo->currentDrawGroup == Zone->playerDrawGroup[0] - 1) {495self->mobileAnimator.frameID = 1;496RSDK.DrawSprite(&self->mobileAnimator, NULL, false);497}498else {499if (self->invincibilityTimer & 1)500RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);501502self->mobileAnimator.frameID = 0;503RSDK.DrawSprite(&self->mobileAnimator, NULL, false);504RSDK.SetPaletteEntry(0, 128, 0x0000);505RSDK.DrawSprite(&self->rackAnimator, NULL, false);506507int32 storeDir = self->direction;508509self->direction = FLIP_NONE;510RSDK.DrawSprite(&self->propellerAnimator, NULL, false);511512if (self->propellerAnimator.speed >= 0x40) {513Vector2 drawPos = self->position;514515self->inkEffect = INK_ALPHA;516if (self->whirlpoolHeight <= 0) {517self->alpha = self->propellerAnimator.speed;518}519else {520self->alpha = 0xD0;521drawPos.y = Water->waterLevel - 0x100000;522523SpriteFrame *frame = RSDK.GetFrame(ScrewMobile->aniFrames, 2, self->whirlpoolAnimator.frameID);524525frame->height = 32;526frame->sprY = 33 * self->whirlpoolAnimator.frameID + 181;527frame->pivotY = -16;528529int32 count = self->whirlpoolHeight >> 5;530for (int32 i = 0; i < count; ++i) {531RSDK.DrawSprite(&self->whirlpoolAnimator, &drawPos, false);532drawPos.y -= 0x200000;533}534535frame->height = self->whirlpoolHeight % 32;536frame->sprY += 31 - frame->height;537frame->pivotY = 16 - frame->height;538RSDK.DrawSprite(&self->whirlpoolAnimator, &drawPos, false);539drawPos.y = Water->waterLevel;540RSDK.DrawSprite(&self->whirlpoolBottomAnimator, &drawPos, false);541self->alpha = 0x100;542}543544drawPos.y = Water->waterLevel - (self->whirlpoolHeight << 16);545RSDK.DrawSprite(&self->whirlpoolTopAnimator, &drawPos, false);546self->inkEffect = INK_NONE;547}548549self->direction = storeDir;550}551}552553void ScrewMobile_StateDepthCharge_Active(void)554{555RSDK_THIS(ScrewMobile);556557if (RSDK.CheckOnScreen(self, NULL)) {558self->position.y += self->velocity.y;559560if (self->whirlpoolHeight == 0x3800 && self->position.y >= Water->waterLevel) {561self->velocity.y = self->velocity.y >> 2;562self->whirlpoolHeight = 0;563CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);564RSDK.PlaySfx(Water->sfxSplash, false, 255);565}566567self->velocity.y += self->whirlpoolHeight; // this is "gravityStrength" here568569foreach_active(DiveEggman, eggman)570{571if (eggman->state == DiveEggman_StateBomb_Idle || eggman->state == DiveEggman_StateBomb_InWhirlpool572|| eggman->state == DiveEggman_StateBomb_WhirlpoolRise || eggman->state == DiveEggman_StateBomb_Falling) {573if (RSDK.CheckObjectCollisionTouchBox(self, &ScrewMobile->hitboxDepthCharge, eggman, &DiveEggman->hitboxBomb)) {574CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), eggman->position.x, eggman->position.y)->drawGroup =575Zone->objectDrawGroup[1];576RSDK.PlaySfx(DiveEggman->sfxExplosion, false, 255);577578EntityWater *water = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), eggman->position.x, eggman->position.y);579water->velocity.y = -0x8800;580water->angle = 2 * RSDK.Rand(0, 256);581water->bubbleX = water->position.x;582water->childPtr = NULL;583RSDK.SetSpriteAnimation(Water->aniFrames, 3, &water->animator, true, 0);584585destroyEntity(eggman);586destroyEntity(self);587foreach_break;588}589}590else if (RSDK.CheckObjectCollisionTouchBox(self, &ScrewMobile->hitboxDepthCharge, eggman, &DiveEggman->hitboxEggman)) {591RSDK.PlaySfx(DiveEggman->sfxRockemSockem, false, 255);592593self->velocity.x = RSDK.Rand(-4, 5) << 15;594self->velocity.y = -0x20000;595self->state = ScrewMobile_StateDepthCharge_Debris;596foreach_break;597}598}599}600else {601destroyEntity(self);602}603}604605void ScrewMobile_StateDepthCharge_Debris(void)606{607RSDK_THIS(ScrewMobile);608609self->position.x += self->velocity.x;610self->position.y += self->velocity.y;611self->velocity.y += 0x2800;612self->visible ^= true;613614if (!RSDK.CheckOnScreen(self, NULL))615destroyEntity(self);616}617618void ScrewMobile_Draw_DepthCharge(void)619{620RSDK_THIS(ScrewMobile);621622RSDK.DrawSprite(&self->mobileAnimator, NULL, false);623}624625#if GAME_INCLUDE_EDITOR626void ScrewMobile_EditorDraw(void)627{628RSDK_THIS(ScrewMobile);629630self->startPos = self->position;631RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 0, &self->mobileAnimator, true, 0);632RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 1, &self->propellerAnimator, true, 0);633RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 7, &self->rackAnimator, true, 6);634RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 2, &self->whirlpoolAnimator, true, 0);635RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 3, &self->whirlpoolTopAnimator, true, 0);636RSDK.SetSpriteAnimation(ScrewMobile->aniFrames, 5, &self->whirlpoolBottomAnimator, true, 0);637self->propellerAnimator.speed = 0;638639ScrewMobile_Draw_ScrewMobile();640}641642void ScrewMobile_EditorLoad(void) { ScrewMobile->aniFrames = RSDK.LoadSpriteAnimation("HCZ/ScrewMobile.bin", SCOPE_STAGE); }643#endif644645void ScrewMobile_Serialize(void) {}646647648