Path: blob/master/SonicMania/Objects/SBZ/Caterkiller.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: Caterkiller Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectCaterkiller *Caterkiller = NULL;1011void Caterkiller_Update(void)12{13RSDK_THIS(Caterkiller);1415StateMachine_Run(self->state);16}1718void Caterkiller_LateUpdate(void) {}1920void Caterkiller_StaticUpdate(void) {}2122void Caterkiller_Draw(void)23{24RSDK_THIS(Caterkiller);2526StateMachine_Run(self->stateDraw);27}2829void Caterkiller_Create(void *data)30{31RSDK_THIS(Caterkiller);3233self->visible = true;34if (self->planeFilter > 0 && ((uint8)(self->planeFilter - 1) & 2))35self->drawGroup = Zone->objectDrawGroup[1];36else37self->drawGroup = Zone->objectDrawGroup[0];3839self->drawFX |= FX_FLIP;40self->active = ACTIVE_BOUNDS;41self->updateRange.x = 0x800000;42self->updateRange.y = 0x800000;43if (data) {44self->state = (Type_StateMachine)data;4546if (self->state == Caterkiller_StateSplit_Head)47RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 0, &self->headAnimator, true, 0);48else49RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 1, &self->headAnimator, true, 0);5051self->stateDraw = Caterkiller_Draw_Segment;52}53else {54self->startPos = self->position;55self->startDir = self->direction;5657self->headOffset = 0;58int32 offset = self->startDir ? -0xC0000 : 0xC0000;5960int32 posX = self->position.x;61for (int32 i = 0; i < CATERKILLER_BODY_COUNT; ++i) {62posX += offset;63self->bodyPosition[i].x = posX;64self->bodyPosition[i].y = self->position.y;65self->bodyDirection[i] = self->direction;66}6768self->timer = 0;69RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 0, &self->headAnimator, true, 0);70RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 1, &self->bodyAnimator, true, 0);71self->state = Caterkiller_State_Init;72self->stateDraw = Caterkiller_Draw_Body;73}74}7576void Caterkiller_StageLoad(void)77{78if (RSDK.CheckSceneFolder("MMZ"))79Caterkiller->aniFrames = RSDK.LoadSpriteAnimation("MMZ/Caterkiller.bin", SCOPE_STAGE);8081Caterkiller->hitbox.left = -8;82Caterkiller->hitbox.top = -8;83Caterkiller->hitbox.right = 8;84Caterkiller->hitbox.bottom = 8;8586DEBUGMODE_ADD_OBJ(Caterkiller);87}8889void Caterkiller_DebugSpawn(void)90{91RSDK_THIS(DebugMode);9293CREATE_ENTITY(Caterkiller, NULL, self->position.x, self->position.y);94}9596void Caterkiller_DebugDraw(void)97{98RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 0, &DebugMode->animator, true, 0);99RSDK.DrawSprite(&DebugMode->animator, NULL, false);100}101102void Caterkiller_CheckOffScreen(void)103{104RSDK_THIS(Caterkiller);105106if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {107self->position = self->startPos;108self->direction = self->startDir;109Caterkiller_Create(NULL);110}111}112113void Caterkiller_CheckTileCollisions(void)114{115RSDK_THIS(Caterkiller);116117int32 storeX = 0;118int32 storeY = 0;119120if (self->state == Caterkiller_State_LiftHead) {121storeX = self->position.x;122storeY = self->position.y;123}124else {125if (!RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, 8))126self->direction ^= FLIP_X;127128storeX = self->position.x;129storeY = self->position.y;130131if (Caterkiller_CheckTileAngle(self->position.x, self->position.y, self->direction))132self->direction ^= FLIP_X;133}134135for (int32 i = 0; i < CATERKILLER_BODY_COUNT; ++i) {136if (self->state != Caterkiller_State_LowerHead || i != (CATERKILLER_BODY_COUNT - 1)) {137self->position.x = self->bodyPosition[i].x;138self->position.y = self->bodyPosition[i].y;139140if (!RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, 8))141self->bodyDirection[i] = self->direction;142143self->bodyPosition[i].y = self->position.y;144if (Caterkiller_CheckTileAngle(self->bodyPosition[i].x, self->bodyPosition[i].y, self->bodyDirection[i]))145self->bodyDirection[i] = self->direction;146}147}148149self->position.x = storeX;150self->position.y = storeY;151}152153void Caterkiller_Draw_Body(void)154{155RSDK_THIS(Caterkiller);156157int32 storeDir = self->direction;158for (int32 i = CATERKILLER_BODY_COUNT - 1; i >= 0; --i) {159Vector2 drawPos = self->bodyPosition[i];160drawPos.y -= self->bodyOffset[i] << 15;161self->direction = self->bodyDirection[i];162RSDK.DrawSprite(&self->bodyAnimator, &drawPos, false);163}164165Vector2 drawPos = self->position;166drawPos.y -= self->headOffset << 15;167self->direction = storeDir;168RSDK.DrawSprite(&self->headAnimator, &drawPos, false);169}170171void Caterkiller_Draw_Segment(void)172{173RSDK_THIS(Caterkiller);174175RSDK.DrawSprite(&self->headAnimator, NULL, false);176}177178void Caterkiller_HandlePlayerInteractions(void)179{180RSDK_THIS(Caterkiller);181182int32 storeX = self->position.x;183int32 storeY = self->position.y;184185foreach_active(Player, player)186{187if (self->planeFilter <= 0 || player->collisionPlane == ((self->planeFilter - 1) & 1)) {188if (Player_CheckBadnikTouch(player, self, &Caterkiller->hitbox)) {189Player_CheckBadnikBreak(player, self, true);190}191else {192for (int32 i = 0; i < CATERKILLER_BODY_COUNT; ++i) {193self->position.x = self->bodyPosition[i].x;194self->position.y = self->bodyPosition[i].y;195196if (Player_CheckCollisionTouch(player, self, &Caterkiller->hitbox)) {197Player_Hurt(player, self);198199for (int32 d = 0; d < CATERKILLER_BODY_COUNT + 1; ++d) {200int32 spawnX = storeX;201int32 spawnY = storeY;202int32 spawnDir = self->direction;203StateMachine(spawnState) = Caterkiller_StateSplit_Head;204205if (d) {206spawnX = self->bodyPosition[d - 1].x;207spawnY = self->bodyPosition[d - 1].y;208spawnDir = self->bodyDirection[d - 1];209spawnState = Caterkiller_StateSplit_Body;210}211212EntityCaterkiller *segment = CREATE_ENTITY(Caterkiller, spawnState, spawnX, spawnY);213segment->direction = spawnDir;214if (!segment->direction)215segment->velocity.x = (d & 1) ? -0x18000 : -0x20000;216else217segment->velocity.x = (d & 1) ? 0x18000 : 0x20000;218219if ((d & 3) >= 2)220segment->velocity.x = -segment->velocity.x;221segment->velocity.y = -0x40000;222223if (!d)224segment->headAnimator.frameID = self->headAnimator.frameID;225226segment->planeFilter = self->planeFilter;227segment->drawGroup = self->drawGroup;228}229230destroyEntity(self);231self->active = ACTIVE_DISABLED;232break;233}234}235}236237self->position.x = storeX;238self->position.y = storeY;239}240}241}242243bool32 Caterkiller_CheckTileAngle(int32 x, int32 y, int32 dir)244{245int32 tx = x >> 16;246int32 ty = (y >> 16) + 8;247248uint16 tile = RSDK.GetTile(Zone->fgLayer[1], tx, ty);249if (tile == (uint16)-1)250tile = RSDK.GetTile(Zone->fgLayer[0], tx, ty);251252uint8 angle = RSDK.GetTileAngle(tile, 0, CMODE_FLOOR);253254if (dir) {255if (angle > 0x80 && angle < 0xE8)256return true;257}258else {259if (angle > 0x18 && angle < 0x80)260return true;261}262263return false;264}265266void Caterkiller_State_Init(void)267{268RSDK_THIS(Caterkiller);269270self->active = ACTIVE_NORMAL;271272self->state = Caterkiller_State_Contract;273Caterkiller_State_Contract();274}275276void Caterkiller_State_Contract(void)277{278RSDK_THIS(Caterkiller);279280if (self->timer) {281self->timer--;282Caterkiller_HandlePlayerInteractions();283Caterkiller_CheckOffScreen();284}285else {286self->timer = 15;287self->headAnimator.frameID = 1;288289self->state = Caterkiller_State_LiftHead;290Caterkiller_State_LiftHead();291}292}293294void Caterkiller_State_LiftHead(void)295{296RSDK_THIS(Caterkiller);297298if (self->timer) {299self->timer--;300for (int32 b = 0; b < CATERKILLER_BODY_COUNT; ++b) {301if (self->bodyDirection[b])302self->bodyPosition[b].x += 0x4000 * (b + 1);303else304self->bodyPosition[b].x -= 0x4000 * (b + 1);305}306307++self->headOffset;308++self->bodyOffset[CATERKILLER_BODY_COUNT / 2];309}310else {311self->timer = 7;312self->state = Caterkiller_State_Uncontract;313}314315Caterkiller_CheckTileCollisions();316Caterkiller_HandlePlayerInteractions();317Caterkiller_CheckOffScreen();318}319320void Caterkiller_State_Uncontract(void)321{322RSDK_THIS(Caterkiller);323324if (self->timer) {325self->timer--;326327Caterkiller_HandlePlayerInteractions();328Caterkiller_CheckOffScreen();329}330else {331self->timer = 15;332self->headAnimator.frameID = 0;333334self->state = Caterkiller_State_LowerHead;335Caterkiller_State_LowerHead();336}337}338339void Caterkiller_State_LowerHead(void)340{341RSDK_THIS(Caterkiller);342343if (self->timer) {344self->timer--;345346int32 mult = 1;347for (int32 b = CATERKILLER_BODY_COUNT - 2; b >= 0; --b) {348self->bodyPosition[b].x += self->bodyDirection[b] ? (0x4000 * mult) : (-0x4000 * mult);349++mult;350}351352self->position.x += self->direction ? (0x4000 * mult) : (-0x4000 * mult);353354--self->headOffset;355--self->bodyOffset[CATERKILLER_BODY_COUNT / 2];356}357else {358self->timer = 7;359self->state = Caterkiller_State_Contract;360}361362Caterkiller_CheckTileCollisions();363Caterkiller_HandlePlayerInteractions();364Caterkiller_CheckOffScreen();365}366367void Caterkiller_StateSplit_Head(void)368{369RSDK_THIS(Caterkiller);370371self->position.x += self->velocity.x;372self->position.y += self->velocity.y;373self->velocity.y += 0x3800;374375if (RSDK.CheckOnScreen(self, &self->updateRange)) {376if (self->velocity.y > 0 && RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, 4)) {377self->velocity.y = -0x40000;378}379380foreach_active(Player, player)381{382if ((self->planeFilter <= 0 || player->collisionPlane == ((uint8)(self->planeFilter - 1) & 1))383&& Player_CheckBadnikTouch(player, self, &Caterkiller->hitbox)) {384Player_CheckBadnikBreak(player, self, true);385}386}387}388else {389destroyEntity(self);390}391}392393void Caterkiller_StateSplit_Body(void)394{395RSDK_THIS(Caterkiller);396397self->position.x += self->velocity.x;398self->position.y += self->velocity.y;399self->velocity.y += 0x3800;400401if (RSDK.CheckOnScreen(self, &self->updateRange)) {402if (self->velocity.y > 0 && RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, 4)) {403self->velocity.y = -0x40000;404}405406foreach_active(Player, player)407{408if (self->planeFilter <= 0 || player->collisionPlane == ((self->planeFilter - 1) & 1)) {409if (Player_CheckCollisionTouch(player, self, &Caterkiller->hitbox))410Player_Hurt(player, self);411}412}413}414else {415destroyEntity(self);416}417}418419#if GAME_INCLUDE_EDITOR420void Caterkiller_EditorDraw(void)421{422RSDK_THIS(Caterkiller);423424self->startPos = self->position;425426self->headOffset = 0;427int32 offset = self->startDir ? -0xC0000 : 0xC0000;428429int32 posX = self->position.x;430for (int32 i = 0; i < CATERKILLER_BODY_COUNT; ++i) {431posX += offset;432self->bodyPosition[i].x = posX;433self->bodyPosition[i].y = self->position.y;434self->bodyDirection[i] = self->direction;435}436437RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 0, &self->headAnimator, true, 0);438RSDK.SetSpriteAnimation(Caterkiller->aniFrames, 1, &self->bodyAnimator, true, 0);439440Caterkiller_Draw_Body();441}442443void Caterkiller_EditorLoad(void)444{445Caterkiller->aniFrames = RSDK.LoadSpriteAnimation("MMZ/Caterkiller.bin", SCOPE_STAGE);446447RSDK_ACTIVE_VAR(Caterkiller, planeFilter);448RSDK_ENUM_VAR("None", PLANEFILTER_NONE);449RSDK_ENUM_VAR("AL", PLANEFILTER_AL);450RSDK_ENUM_VAR("BL", PLANEFILTER_BL);451RSDK_ENUM_VAR("AH", PLANEFILTER_AH);452RSDK_ENUM_VAR("BH", PLANEFILTER_BH);453}454#endif455456void Caterkiller_Serialize(void) { RSDK_EDITABLE_VAR(Caterkiller, VAR_ENUM, planeFilter); }457458459