Path: blob/master/SonicMania/Objects/SPZ/Canista.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: Canista Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectCanista *Canista;1011// This object looks like it was based on Unused/WallCrawl12// Would explain WallCrawl's presence in the blueprint sheet, being a "proto" form of this1314void Canista_Update(void)15{16RSDK_THIS(Canista);1718StateMachine_Run(self->state);19}2021void Canista_LateUpdate(void) {}2223void Canista_StaticUpdate(void) {}2425void Canista_Draw(void)26{27RSDK_THIS(Canista);2829RSDK.DrawSprite(&self->mainAnimator, NULL, false);30RSDK.DrawSprite(&self->tapeAnimator, NULL, false);31RSDK.DrawSprite(&self->cannonAnimator, NULL, false);32}3334void Canista_Create(void *data)35{36RSDK_THIS(Canista);3738self->visible = true;39self->drawGroup = Zone->objectDrawGroup[0] + 1;4041if (data) {42self->drawFX |= FX_FLIP;43self->inkEffect = INK_ALPHA;44self->alpha = 0x100;45self->stopTimer = 90;46self->updateRange.x = 0x400000;47self->updateRange.y = 0x400000;48self->active = ACTIVE_NORMAL;49self->drawGroup = Zone->objectDrawGroup[0];50RSDK.SetSpriteAnimation(Canista->aniFrames, 3, &self->mainAnimator, true, 0);51self->state = Canista_StateProjectile_Shot;52}53else {54self->drawFX |= FX_ROTATE | FX_FLIP;55self->shootOffset <<= 16;56self->shootSize <<= 16;5758self->startPos = self->position;59self->startDir = self->direction;60self->active = ACTIVE_BOUNDS;61self->updateRange.x = 0x800000;62self->updateRange.y = 0x800000;6364self->detectedPlayer = NULL;65self->hitboxDetect.left = -self->detectSize.x >> 17;66self->hitboxDetect.top = -self->detectSize.y >> 17;67self->hitboxDetect.right = self->detectSize.x >> 17;68self->hitboxDetect.bottom = self->detectSize.y >> 17;6970RSDK.SetSpriteAnimation(Canista->aniFrames, 0, &self->mainAnimator, true, 0);71RSDK.SetSpriteAnimation(Canista->aniFrames, 1, &self->tapeAnimator, true, 0);72RSDK.SetSpriteAnimation(Canista->aniFrames, 2, &self->cannonAnimator, true, 0);73self->state = Canista_State_Init;74}75}7677void Canista_StageLoad(void)78{79if (RSDK.CheckSceneFolder("SPZ1"))80Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ1/Canista.bin", SCOPE_STAGE);81else if (RSDK.CheckSceneFolder("SPZ2"))82Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ2/Canista.bin", SCOPE_STAGE);8384Canista->hitboxBadnik.left = -14;85Canista->hitboxBadnik.top = -14;86Canista->hitboxBadnik.right = 14;87Canista->hitboxBadnik.bottom = 14;8889Canista->hitboxRange.left = -128;90Canista->hitboxRange.top = -128;91Canista->hitboxRange.right = 128;92Canista->hitboxRange.bottom = 128;9394Canista->hitboxDetect.left = -132;95Canista->hitboxDetect.top = 48;96Canista->hitboxDetect.right = 132;97Canista->hitboxDetect.bottom = 49;9899Canista->hitboxProjectile.left = -26;100Canista->hitboxProjectile.top = -5;101Canista->hitboxProjectile.right = -16;102Canista->hitboxProjectile.bottom = 5;103104DEBUGMODE_ADD_OBJ(Canista);105106Canista->sfxPon = RSDK.GetSfx("Stage/Pon.wav");107}108109void Canista_DebugSpawn(void)110{111RSDK_THIS(DebugMode);112113EntityCanista *canista = CREATE_ENTITY(Canista, NULL, self->position.x, self->position.y);114canista->direction = self->direction;115canista->startDir = self->direction;116}117118void Canista_DebugDraw(void)119{120RSDK.SetSpriteAnimation(Canista->aniFrames, 0, &DebugMode->animator, true, 0);121RSDK.DrawSprite(&DebugMode->animator, NULL, false);122}123124void Canista_CheckPlayerBadnikCollisions(void)125{126RSDK_THIS(Canista);127128int32 storeX = self->position.x;129int32 storeY = self->position.y;130131foreach_active(Player, player)132{133if (!self->detectDelay) {134if (self->triggerMode) {135self->position.x = self->startPos.x + self->detectOffset.x;136self->position.y = self->startPos.y + self->detectOffset.y;137if (Player_CheckCollisionTouch(player, self, &self->hitboxDetect)) {138if (self->detectedPlayer) {139if (abs(self->position.x - player->position.x) < abs(self->position.x - self->detectedPlayer->position.x))140self->detectedPlayer = player;141}142else {143self->detectedPlayer = player;144}145}146}147else {148if (Player_CheckCollisionTouch(player, self, &Canista->hitboxDetect)) {149if (self->detectedPlayer) {150if (abs(self->position.x - player->position.x) < abs(self->position.x - self->detectedPlayer->position.x))151self->detectedPlayer = player;152}153else {154self->detectedPlayer = player;155}156}157}158}159160self->position.x = storeX;161self->position.y = storeY;162163if (Player_CheckBadnikTouch(player, self, &Canista->hitboxBadnik))164Player_CheckBadnikBreak(player, self, true);165}166}167168void Canista_CheckOffScreen(void)169{170RSDK_THIS(Canista);171172if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {173self->position = self->startPos;174self->direction = self->startDir;175self->shootOffset >>= 16;176177Canista_Create(NULL);178}179}180181void Canista_State_Init(void)182{183RSDK_THIS(Canista);184185self->active = ACTIVE_NORMAL;186int32 offset = !(self->direction & FLIP_X) ? 0x100000 : -0x110000;187188RSDK.ObjectTileGrip(self, Zone->collisionLayers, (2 * ((self->direction & FLIP_X) != 0) + 1), 0, offset, -0x180000, 8);189self->moveDir = 0;190self->timer = 0;191self->stopTimer = 0;192self->updateRange.y = 0x2000000;193self->state = Canista_State_Moving;194Canista_State_Moving();195}196197void Canista_State_Moving(void)198{199RSDK_THIS(Canista);200201if (self->detectDelay)202self->detectDelay--;203204EntityPlayer *player = self->detectedPlayer;205if (player) {206int32 storeX = self->position.x;207int32 storeY = self->position.y;208209if (self->triggerMode) {210self->position.x = self->startPos.x + self->detectOffset.x;211self->position.y = self->startPos.y + self->detectOffset.y;212213if (!Player_CheckCollisionTouch(player, self, &self->hitboxDetect))214self->detectedPlayer = NULL;215}216else {217if (!Player_CheckCollisionTouch(player, self, &Canista->hitboxRange))218self->detectedPlayer = NULL;219}220221if (self->detectedPlayer) {222if (self->triggerMode) {223int32 playerY = player->position.y & 0xFFFF0000;224int32 shootY = (storeY + self->shootOffset - self->shootSize) & 0xFFFF0000;225226if (playerY < shootY || playerY > shootY) {227self->mainAnimator.speed = 128;228self->velocity.y = ((self->shootOffset + storeY) & 0xFFFF0000) < (player->position.y & 0xFFFF0000) ? 0x10000 : -0x10000;229}230else {231self->state = Canista_State_Idle;232self->stopTimer = 60;233self->detectedPlayer = NULL;234self->detectDelay = 180;235}236}237else {238if (Player_CheckCollisionTouch(player, self, &Canista->hitboxDetect)) {239self->state = Canista_State_Idle;240self->stopTimer = 60;241self->detectedPlayer = NULL;242self->detectDelay = 180;243}244else {245self->mainAnimator.speed = 128;246if (player->position.y - 0x400000 > storeY)247self->velocity.y = 0x10000;248else249self->velocity.y = -0x1000;250}251}252}253254self->position.x = storeX;255self->position.y = storeY;256}257else {258self->mainAnimator.speed = 85;259self->velocity.y = self->moveDir ? -0x8000 : 0x8000;260}261262self->position.y += self->velocity.y;263int32 tapeRotation = self->velocity.y >> 12;264if (self->direction)265self->rotation += tapeRotation;266else267self->rotation -= tapeRotation;268269int32 offsetX = !(self->direction & FLIP_X) ? 0x100000 : -0x110000;270int32 offsetY = ((self->velocity.y >> 31) & 0xFFD40000) + 0x140000;271if (!RSDK.ObjectTileGrip(self, Zone->collisionLayers, (2 * ((self->direction & FLIP_X) != 0) + CMODE_LWALL), 0, offsetX, offsetY, 0)) {272self->state = Canista_State_Idle;273self->stopTimer = 30;274if (self->detectedPlayer) {275self->detectedPlayer = NULL;276self->detectDelay = 180;277}278}279280RSDK.ProcessAnimation(&self->mainAnimator);281282if (self->timer) {283if (--self->timer == 1) {284self->cannonAnimator.frameID = 3;285}286}287else {288self->cannonAnimator.timer = (self->cannonAnimator.timer + (abs(self->velocity.y) >> 15)) % 7;289self->cannonAnimator.frameID = (self->cannonAnimator.timer / 3) & 1;290}291292Canista_CheckPlayerBadnikCollisions();293Canista_CheckOffScreen();294}295296void Canista_State_Idle(void)297{298RSDK_THIS(Canista);299300if (!--self->stopTimer) {301if (abs(self->velocity.y) == 0x8000)302self->moveDir ^= 1;303304self->state = Canista_State_Moving;305Canista_State_Moving();306}307else {308if (self->stopTimer == 30) {309int32 offsetX = !(self->direction & FLIP_X) ? 0x180000 : -0x180000;310311EntityCanista *shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);312shot->direction = self->direction;313shot->velocity.x = shot->direction == FLIP_X ? 0x40000 : -0x40000;314315shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);316shot->direction = self->direction;317shot->velocity.x = shot->direction == FLIP_X ? 0x4C000 : -0x4C000;318319shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);320shot->direction = self->direction;321shot->velocity.x = shot->direction == FLIP_X ? 0x58000 : -0x58000;322323shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);324shot->direction = self->direction;325shot->velocity.x = shot->direction == FLIP_X ? 0x64000 : -0x64000;326327self->cannonAnimator.frameID = 2;328self->timer = 4;329RSDK.PlaySfx(Canista->sfxPon, false, 0xFF);330}331332Canista_CheckPlayerBadnikCollisions();333Canista_CheckOffScreen();334}335336if (self->timer) {337if (--self->timer == 1) {338self->cannonAnimator.frameID = 3;339}340}341else {342self->cannonAnimator.frameID = (self->cannonAnimator.timer / 3) & 1;343}344345Canista_CheckOffScreen();346}347348void Canista_CheckPlayerProjectileCollisions(void)349{350RSDK_THIS(Canista);351352if (self->alpha >= 0x80) {353354foreach_active(Player, player)355{356if (Player_CheckCollisionTouch(player, self, &Canista->hitboxProjectile)) {357#if MANIA_USE_PLUS358if (Player_CheckMightyShellHit(player, self, -0x400, -0x400)) {359self->stopTimer = 0;360self->interaction = false;361}362else363#endif364Player_Hurt(player, self);365}366}367}368369RSDK.ProcessAnimation(&self->mainAnimator);370371if (!RSDK.CheckOnScreen(self, &self->updateRange))372destroyEntity(self);373}374375void Canista_StateProjectile_Shot(void)376{377RSDK_THIS(Canista);378379self->position.x += self->velocity.x;380381if (self->direction == FLIP_X) {382self->velocity.x -= 0x2000;383if (self->velocity.x < 0) {384self->startPos = self->position;385self->velocity.x = 0;386self->rotation = 6;387self->angle = 2;388389RSDK.SetSpriteAnimation(Canista->aniFrames, 4, &self->mainAnimator, true, 0);390self->state = Canista_StateProjectile_Fall;391}392}393else {394self->velocity.x += 0x2000;395if (self->velocity.x > 0) {396self->startPos = self->position;397self->velocity.x = 0;398self->rotation = 6;399self->angle = 2;400401RSDK.SetSpriteAnimation(Canista->aniFrames, 4, &self->mainAnimator, true, 0);402self->state = Canista_StateProjectile_Fall;403}404}405406Canista_CheckPlayerProjectileCollisions();407}408409void Canista_StateProjectile_Fall(void)410{411RSDK_THIS(Canista);412413self->angle += self->rotation;414if (!(Zone->timer & 0x1F) && RSDK.Rand(0, 2) == 1)415self->rotation = -self->rotation;416417self->startPos.x += self->velocity.x;418self->startPos.y += self->velocity.y;419self->velocity.y += 0x800;420421if (self->velocity.y > 0x10000) {422self->velocity.y -= 0x8000;423if (self->velocity.y < 0x10000)424self->velocity.y = 0x10000;425}426427if (self->velocity.x) {428if (self->velocity.x <= 0) {429self->velocity.x += 0x2000;430if (self->velocity.x > 0)431self->velocity.x = 0;432}433else {434self->velocity.x -= 0x2000;435if (self->velocity.x < 0)436self->velocity.x = 0;437}438}439440self->position.x = (RSDK.Cos256(self->angle) << 9) + self->startPos.x;441self->position.y = (RSDK.Sin256(self->angle) << 9) + self->startPos.y;442443if (self->stopTimer) {444self->stopTimer--;445}446else {447self->alpha -= 4;448if (self->alpha <= 0)449destroyEntity(self);450}451452Canista_CheckPlayerProjectileCollisions();453}454455#if GAME_INCLUDE_EDITOR456void Canista_EditorDraw(void)457{458RSDK_THIS(Canista);459460Canista_Draw();461462if (showGizmos()) {463RSDK_DRAWING_OVERLAY(true);464465if (self->triggerMode) {466self->hitboxDetect.left = -self->detectSize.x >> 17;467self->hitboxDetect.top = -self->detectSize.y >> 17;468self->hitboxDetect.right = self->detectSize.x >> 17;469self->hitboxDetect.bottom = self->detectSize.y >> 17;470471DrawHelpers_DrawHitboxOutline(self->position.x + self->detectOffset.x, self->position.y + self->detectOffset.y, &self->hitboxDetect,472self->direction, 0xFF0000);473}474else {475DrawHelpers_DrawHitboxOutline(self->position.x, self->position.y, &Canista->hitboxDetect, self->direction, 0xFF0000);476}477478RSDK_DRAWING_OVERLAY(false);479}480}481482void Canista_EditorLoad(void)483{484if (RSDK.CheckSceneFolder("SPZ1"))485Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ1/Canista.bin", SCOPE_STAGE);486else if (RSDK.CheckSceneFolder("SPZ2"))487Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ2/Canista.bin", SCOPE_STAGE);488489Canista->hitboxRange.left = -128;490Canista->hitboxRange.top = -128;491Canista->hitboxRange.right = 128;492Canista->hitboxRange.bottom = 128;493494Canista->hitboxDetect.left = -132;495Canista->hitboxDetect.top = 48;496Canista->hitboxDetect.right = 132;497Canista->hitboxDetect.bottom = 49;498499RSDK_ACTIVE_VAR(Canista, direction);500RSDK_ENUM_VAR("Left", FLIP_NONE);501RSDK_ENUM_VAR("Right", FLIP_X);502503RSDK_ACTIVE_VAR(Canista, triggerMode);504RSDK_ENUM_VAR("Use Static Size", CANISTA_TRIGGER_STATIC);505RSDK_ENUM_VAR("Use Detect Size", CANISTA_TRIGGER_DETECT);506}507#endif508509void Canista_Serialize(void)510{511RSDK_EDITABLE_VAR(Canista, VAR_UINT8, direction);512RSDK_EDITABLE_VAR(Canista, VAR_UINT8, triggerMode);513RSDK_EDITABLE_VAR(Canista, VAR_VECTOR2, detectOffset);514RSDK_EDITABLE_VAR(Canista, VAR_VECTOR2, detectSize);515RSDK_EDITABLE_VAR(Canista, VAR_ENUM, shootOffset);516RSDK_EDITABLE_VAR(Canista, VAR_ENUM, shootSize);517}518519520