Path: blob/master/SonicMania/Objects/OOZ/BallCannon.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: BallCannon Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectBallCannon *BallCannon;1011void BallCannon_Update(void)12{13RSDK_THIS(BallCannon);1415StateMachine_Run(self->state);16}1718void BallCannon_LateUpdate(void) {}1920void BallCannon_StaticUpdate(void) {}2122void BallCannon_Draw(void)23{24RSDK_THIS(BallCannon);2526RSDK.DrawSprite(&self->animator, NULL, false);27}2829void BallCannon_Create(void *data)30{31RSDK_THIS(BallCannon);3233self->drawGroup = Zone->playerDrawGroup[0];34self->visible = true;3536if (!SceneInfo->inEditor) {37if (data) {38self->visible = true;39self->drawFX = FX_ROTATE;40self->active = ACTIVE_NORMAL;41self->rotationSpeed = RSDK.Rand(-8, 8);4243RSDK.SetSpriteAnimation(BallCannon->aniFrames, 3, &self->animator, true, VOID_TO_INT(data));44self->state = BallCannon_State_CorkDebris;45}46else {47self->drawFX = FX_ROTATE | FX_FLIP;48self->active = ACTIVE_BOUNDS;49self->updateRange.x = 0x400000;50self->updateRange.y = 0x400000;5152switch (self->type) {53case BALLCANNON_CANNON:54if (self->angle >= 4)55self->direction = FLIP_X;5657self->rotation = (self->angle + self->direction + 1) << 7;5859switch (self->angle) {60case BALLCANNON_DIR_RIGHT_CW: // Right -> Down61case BALLCANNON_DIR_LEFT_CCW: // Left -> Down62self->velocity.y = 0x100000;63break;6465case BALLCANNON_DIR_DOWN_CW: // Down -> Left66case BALLCANNON_DIR_UP_CCW: // Up -> Left67self->velocity.x = -0x100000;68break;6970case BALLCANNON_DIR_LEFT_CW: // Left -> Up71case BALLCANNON_DIR_RIGHT_CCW: // Right -> Up72self->velocity.y = -0x100000;73break;7475case BALLCANNON_DIR_UP_CW: // Up -> Right76case BALLCANNON_DIR_DOWN_CCW: // Down -> Right77self->velocity.x = 0x100000;78break;7980default: break;81}82RSDK.SetSpriteAnimation(BallCannon->aniFrames, 0, &self->animator, true, 0);83self->state = BallCannon_State_Idle;84break;8586case BALLCANNON_CORKV:87RSDK.SetSpriteAnimation(BallCannon->aniFrames, 3, &self->animator, true, 0);88self->velocity.y = -0x80000;89self->state = BallCannon_State_CorkBlocked;90break;9192case BALLCANNON_CORKH:93RSDK.SetSpriteAnimation(BallCannon->aniFrames, 4, &self->animator, true, 0);94self->velocity.x = 0x80000;95self->state = BallCannon_State_CorkBlocked;96break;97}98}99}100}101102void BallCannon_StageLoad(void)103{104BallCannon->aniFrames = RSDK.LoadSpriteAnimation("OOZ/BallCannon.bin", SCOPE_STAGE);105106BallCannon->hitboxCannon.top = -4;107BallCannon->hitboxCannon.left = -4;108BallCannon->hitboxCannon.right = 4;109BallCannon->hitboxCannon.bottom = 4;110111BallCannon->hitboxCorkBlock.top = -16;112BallCannon->hitboxCorkBlock.left = -16;113BallCannon->hitboxCorkBlock.right = 16;114BallCannon->hitboxCorkBlock.bottom = 16;115116BallCannon->hitboxCorkEntry.top = -4;117BallCannon->hitboxCorkEntry.left = -8;118BallCannon->hitboxCorkEntry.right = 8;119BallCannon->hitboxCorkEntry.bottom = 4;120121BallCannon->sfxLedgeBreak = RSDK.GetSfx("Stage/LedgeBreak.wav");122BallCannon->sfxFire = RSDK.GetSfx("Stage/CannonFire.wav");123}124125void BallCannon_CheckPlayerEntry(void)126{127RSDK_THIS(BallCannon);128129if (RSDK.CheckOnScreen(self, NULL)) {130foreach_all(Player, player)131{132if (Player_CheckValidState(player)) {133int32 playerID = RSDK.GetEntitySlot(player);134135if (self->playerTimers[playerID]) {136self->playerTimers[playerID]--;137}138else {139if ((1 << playerID) & self->activePlayers) {140if (player->state != Player_State_Static)141self->activePlayers &= ~(1 << playerID);142}143else {144if (Player_CheckCollisionTouch(player, self, &BallCannon->hitboxCannon)) {145RSDK.PlaySfx(Player->sfxRoll, false, 0xFF);146RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, false, 0);147148player->position.x = self->position.x;149player->position.y = self->position.y;150player->velocity.x = 0;151player->velocity.y = 0;152player->tileCollisions = TILECOLLISION_NONE;153player->interaction = false;154player->blinkTimer = 0;155player->visible = false;156player->state = Player_State_Static;157self->activePlayers |= 1 << playerID;158self->active = ACTIVE_NORMAL;159self->rotation = (self->angle + self->direction + 1) << 7;160self->drawFX = FX_ROTATE;161RSDK.SetSpriteAnimation(BallCannon->aniFrames, 0, &self->animator, true, 0);162self->state = BallCannon_State_Inserted;163}164}165}166}167}168}169else {170self->active = ACTIVE_BOUNDS;171self->rotation = (self->angle + self->direction + 1) << 7;172}173}174175void BallCannon_State_Idle(void) { BallCannon_CheckPlayerEntry(); }176177void BallCannon_State_Inserted(void)178{179RSDK_THIS(BallCannon);180181BallCannon_CheckPlayerEntry();182183RSDK.ProcessAnimation(&self->animator);184185if (self->animator.frameID == self->animator.frameCount - 1) {186RSDK.SetSpriteAnimation(BallCannon->aniFrames, 1, &self->animator, true, 0);187self->drawFX = FX_FLIP;188self->state = BallCannon_State_Turning;189}190}191192void BallCannon_State_Turning(void)193{194RSDK_THIS(BallCannon);195196BallCannon_CheckPlayerEntry();197198RSDK.ProcessAnimation(&self->animator);199200if (self->animator.frameID == self->animator.frameCount - 1) {201RSDK.SetSpriteAnimation(BallCannon->aniFrames, 2, &self->animator, true, 0);202self->drawFX = FX_ROTATE;203self->state = BallCannon_State_EjectPlayer;204self->rotation = (self->angle - self->direction + 2) << 7;205}206}207208void BallCannon_State_EjectPlayer(void)209{210RSDK_THIS(BallCannon);211212BallCannon_CheckPlayerEntry();213214RSDK.ProcessAnimation(&self->animator);215216if (self->animator.frameID == self->animator.frameCount - 1) {217foreach_all(Player, player)218{219if (Player_CheckValidState(player)) {220int32 playerID = RSDK.GetEntitySlot(player);221222if (((1 << playerID) & self->activePlayers)) {223RSDK.PlaySfx(BallCannon->sfxFire, false, 0xFF);224225player->velocity = self->velocity;226player->visible = true;227228if (self->exit) {229player->onGround = false;230player->applyJumpCap = false;231player->state = Player_State_Air;232player->tileCollisions = TILECOLLISION_DOWN;233player->interaction = true;234}235236self->activePlayers &= ~(1 << playerID);237self->playerTimers[playerID] = 15;238}239}240}241242self->state = BallCannon_State_Idle;243}244}245246void BallCannon_State_CorkBlocked(void)247{248RSDK_THIS(BallCannon);249250foreach_active(Player, player)251{252Animator animator;253254memcpy(&animator, &player->animator, sizeof(Animator));255int32 storeX = player->position.x;256int32 storeY = player->position.y;257int32 storeVelX = player->velocity.x;258int32 storeVelY = player->velocity.y;259260if (Player_CheckCollisionBox(player, self, &BallCannon->hitboxCorkBlock) == C_TOP) {261if (player->animator.animationID == ANI_JUMP || player->state == Player_State_DropDash262#if MANIA_USE_PLUS263|| player->state == Player_State_MightyHammerDrop264#endif265) {266if (storeVelY >= 0 && !player->groundedStore) {267for (int32 i = 0; i < 16; ++i) {268// Bug Details:269// The original starts iterating 1 element before each corkDebris____ array.270// The code below was fixed. To reproduce the bug, change the offsets: "+ 0" ---> "- 1" and "+ 1" ---> "+ 0"271EntityBallCannon *debris =272CREATE_ENTITY(BallCannon, INT_TO_VOID((i & 3) + 1), self->position.x + BallCannon->corkDebrisOffset[(i * 2) + 0],273self->position.y + BallCannon->corkDebrisOffset[(i * 2) + 1]);274debris->velocity.x = BallCannon->corkDebrisVelocity[(i * 2) + 0];275debris->velocity.y = BallCannon->corkDebrisVelocity[(i * 2) + 1];276}277278RSDK.PlaySfx(BallCannon->sfxLedgeBreak, false, 0xFF);279280memcpy(&player->animator, &animator, sizeof(Animator));281player->velocity.x = storeVelX;282player->velocity.y = storeVelY;283player->position.x = storeX;284player->position.y = storeY;285player->onGround = false;286self->active = ACTIVE_NORMAL;287self->visible = false;288self->state = BallCannon_State_CorkOpened;289290foreach_break;291}292}293}294}295}296297void BallCannon_State_CorkOpened(void)298{299RSDK_THIS(BallCannon);300301if (RSDK.CheckOnScreen(self, NULL)) {302foreach_active(Player, player)303{304int32 playerID = RSDK.GetEntitySlot(player);305306if (self->playerTimers[playerID]) {307self->playerTimers[playerID]--;308}309else {310if (Player_CheckCollisionTouch(player, self, &BallCannon->hitboxCorkEntry)) {311RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, false, 0);312RSDK.PlaySfx(BallCannon->sfxFire, false, 0xFF);313314player->state = Player_State_Static;315player->nextGroundState = StateMachine_None;316player->nextAirState = StateMachine_None;317player->position = self->position;318player->velocity = self->velocity;319player->tileCollisions = TILECOLLISION_NONE;320player->interaction = false;321player->onGround = false;322self->playerTimers[playerID] = 15;323}324}325}326}327else {328self->visible = true;329for (int32 i = 0; i < Player->playerCount; ++i) self->playerTimers[i] = 0;330self->state = BallCannon_State_CorkBlocked;331}332}333334void BallCannon_State_CorkDebris(void)335{336RSDK_THIS(BallCannon);337338self->position.x += self->velocity.x;339self->position.y += self->velocity.y;340self->velocity.y += 0x3800;341342self->rotation += self->rotationSpeed;343344if (!RSDK.CheckOnScreen(self, &self->updateRange))345destroyEntity(self);346}347348#if GAME_INCLUDE_EDITOR349void BallCannon_EditorDraw(void)350{351RSDK_THIS(BallCannon);352353self->drawFX = FX_ROTATE | FX_FLIP;354self->rotation = 0;355self->active = ACTIVE_BOUNDS;356self->updateRange.x = 0x400000;357self->updateRange.y = 0x400000;358self->velocity.x = 0;359self->velocity.y = 0;360361switch (self->type) {362default: break;363364case BALLCANNON_CANNON:365if (self->angle >= 4)366self->direction = FLIP_X;367368self->rotation = (self->angle + self->direction + 1) << 7;369370switch (self->angle) {371case BALLCANNON_DIR_RIGHT_CW: // Right -> Down372case BALLCANNON_DIR_LEFT_CCW: // Left -> Down373self->velocity.y = 0x200000;374break;375376case BALLCANNON_DIR_DOWN_CW: // Down -> Left377case BALLCANNON_DIR_UP_CCW: // Up -> Left378self->velocity.x = -0x200000;379break;380381case BALLCANNON_DIR_LEFT_CW: // Left -> Up382case BALLCANNON_DIR_RIGHT_CCW: // Right -> Up383self->velocity.y = -0x200000;384break;385386case BALLCANNON_DIR_UP_CW: // Up -> Right387case BALLCANNON_DIR_DOWN_CCW: // Down -> Right388self->velocity.x = 0x200000;389break;390391default: break;392}393394RSDK.SetSpriteAnimation(BallCannon->aniFrames, 0, &self->animator, true, 0);395break;396397case BALLCANNON_CORKV: RSDK.SetSpriteAnimation(BallCannon->aniFrames, 3, &self->animator, true, 0); break;398399case BALLCANNON_CORKH: RSDK.SetSpriteAnimation(BallCannon->aniFrames, 4, &self->animator, true, 0); break;400}401402BallCannon_Draw();403404if (showGizmos() && self->type == BALLCANNON_CANNON) {405self->rotation = (self->angle - self->direction + 2) << 7;406407self->inkEffect = INK_BLEND;408409BallCannon_Draw();410411self->inkEffect = INK_NONE;412413// Draw the direction the player will be shot from (the names are a little confusing on their own)414DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x + self->velocity.x, self->position.y + self->velocity.y, 0x00FF00,415INK_NONE, 0xFF);416}417}418419void BallCannon_EditorLoad(void)420{421BallCannon->aniFrames = RSDK.LoadSpriteAnimation("OOZ/BallCannon.bin", SCOPE_STAGE);422423RSDK_ACTIVE_VAR(BallCannon, type);424RSDK_ENUM_VAR("Cannon", BALLCANNON_CANNON);425RSDK_ENUM_VAR("Cork V", BALLCANNON_CORKV);426RSDK_ENUM_VAR("Cork H", BALLCANNON_CORKH);427428RSDK_ACTIVE_VAR(BallCannon, angle);429RSDK_ENUM_VAR("Right (Rotates Clockwise)", BALLCANNON_DIR_RIGHT_CW);430RSDK_ENUM_VAR("Down (Rotates Clockwise)", BALLCANNON_DIR_DOWN_CW);431RSDK_ENUM_VAR("Left (Rotates Clockwise)", BALLCANNON_DIR_LEFT_CW);432RSDK_ENUM_VAR("Up (Rotates Clockwise)", BALLCANNON_DIR_UP_CW);433RSDK_ENUM_VAR("Down (Rotates Anti-Clockwise)", BALLCANNON_DIR_DOWN_CCW);434RSDK_ENUM_VAR("Left (Rotates Anti-Clockwise)", BALLCANNON_DIR_LEFT_CCW);435RSDK_ENUM_VAR("Up (Rotates Anti-Clockwise)", BALLCANNON_DIR_UP_CCW);436RSDK_ENUM_VAR("Right (Rotates Anti-Clockwise)", BALLCANNON_DIR_RIGHT_CCW);437}438#endif439440void BallCannon_Serialize(void)441{442RSDK_EDITABLE_VAR(BallCannon, VAR_UINT8, type);443RSDK_EDITABLE_VAR(BallCannon, VAR_ENUM, angle);444RSDK_EDITABLE_VAR(BallCannon, VAR_BOOL, exit);445}446447448