Path: blob/master/SonicMania/Objects/LRZ/Flamethrower.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: Flamethrower Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectFlamethrower *Flamethrower;1011void Flamethrower_Update(void)12{13RSDK_THIS(Flamethrower);1415RSDK.ProcessAnimation(&self->animator);1617StateMachine_Run(self->state);18}1920void Flamethrower_LateUpdate(void) {}2122void Flamethrower_StaticUpdate(void)23{24int32 activeFlamethrowerCount = 0;2526if (RSDK_GET_ENTITY(SLOT_PAUSEMENU, PauseMenu)->classID != PauseMenu->classID) {27foreach_active(Flamethrower, flamethrower)28{29if (flamethrower->state == Flamethrower_State_EmittingFlames)30activeFlamethrowerCount++;31}3233if (activeFlamethrowerCount) {34if (!Flamethrower->playingFlameSfx) {35RSDK.PlaySfx(Flamethrower->sfxFlame, true, 255);36Flamethrower->playingFlameSfx = true;37}38}39}4041if (!activeFlamethrowerCount && Flamethrower->playingFlameSfx) {42RSDK.StopSfx(Flamethrower->sfxFlame);43Flamethrower->playingFlameSfx = false;44}45}4647void Flamethrower_Draw(void)48{49RSDK_THIS(Flamethrower);5051RSDK.DrawSprite(&self->animator, NULL, false);52}5354void Flamethrower_Create(void *data)55{56RSDK_THIS(Flamethrower);5758self->active = ACTIVE_BOUNDS;5960if (!RSDK.CheckSceneFolder("LRZ3")) {61if (Flamethrower->hitboxMouthH.left)62self->drawGroup = Zone->playerDrawGroup[0];63else64self->drawGroup = Zone->objectDrawGroup[0];65}66else {67self->drawGroup = Zone->playerDrawGroup[0];68}6970self->origin = self->position;71self->visible = true;72self->drawFX = FX_ROTATE | FX_FLIP;73self->updateRange.x = 0x800000;74self->updateRange.y = 0x800000;7576if (SceneInfo->inEditor) {77if (!self->maxDist)78self->maxDist = 96;7980if (!self->spread)81self->spread = 42;8283if (!self->duration)84self->duration = 120;8586if (!self->interval)87self->interval = 60;88}8990self->state = Flamethrower_State_Init;91}9293void Flamethrower_StageLoad(void)94{95Flamethrower->hitboxMouthH.left = -4;96Flamethrower->hitboxMouthH.top = -17;97Flamethrower->hitboxMouthH.right = 12;98Flamethrower->hitboxMouthH.bottom = 17;99100Flamethrower->hitboxMouthV.left = -16;101Flamethrower->hitboxMouthV.top = 0;102Flamethrower->hitboxMouthV.right = 16;103Flamethrower->hitboxMouthV.bottom = 12;104105Flamethrower->hitboxSmallFireball.left = -4;106Flamethrower->hitboxSmallFireball.top = -4;107Flamethrower->hitboxSmallFireball.right = 4;108Flamethrower->hitboxSmallFireball.bottom = 4;109110Flamethrower->hitboxMediumFireball.left = -6;111Flamethrower->hitboxMediumFireball.top = -6;112Flamethrower->hitboxMediumFireball.right = 6;113Flamethrower->hitboxMediumFireball.bottom = 6;114115Flamethrower->hitboxLargeFireball.left = -9;116Flamethrower->hitboxLargeFireball.top = -9;117Flamethrower->hitboxLargeFireball.right = 9;118Flamethrower->hitboxLargeFireball.bottom = 9;119120Flamethrower->sfxFlame = RSDK.GetSfx("Stage/Flame2.wav");121122if (RSDK.CheckSceneFolder("LRZ2")) {123Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ2/Flamethrower.bin", SCOPE_STAGE);124}125else if (RSDK.CheckSceneFolder("LRZ3")) {126Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ3/Flamethrower.bin", SCOPE_STAGE);127128Flamethrower->hitboxMouthH.left = 0;129}130131Flamethrower->active = ACTIVE_ALWAYS;132}133134void Flamethrower_SetupOrientation(uint8 orientation)135{136RSDK_THIS(Flamethrower);137138if (orientation == FLAMETHROWER_ORIENTATION_DOWN || orientation == FLAMETHROWER_ORIENTATION_UP)139RSDK.SetSpriteAnimation(Flamethrower->aniFrames, 1, &self->animator, true, 0);140else141RSDK.SetSpriteAnimation(Flamethrower->aniFrames, 0, &self->animator, true, 0);142}143144Hitbox Flamethrower_GetHitbox(void)145{146RSDK_THIS(Flamethrower);147148switch (self->animator.animationID) {149case 2:150case 5: return Flamethrower->hitboxSmallFireball;151152case 3:153case 6: return Flamethrower->hitboxMediumFireball;154155default:156case 4:157case 7: return Flamethrower->hitboxLargeFireball;158}159}160161void Flamethrower_CheckOffScreen(void)162{163RSDK_THIS(Flamethrower);164165if (!RSDK.CheckOnScreen(self, NULL)) {166self->active = ACTIVE_BOUNDS;167self->state = Flamethrower_State_AwaitInterval;168}169}170171void Flamethrower_HandleAnimations(void)172{173RSDK_THIS(Flamethrower);174175int32 dist = 0;176if (self->mode == FLAMETHROWER_MODE_SPRINKLER) {177dist = self->currentDist;178}179else {180if (self->orientation == FLAMETHROWER_ORIENTATION_DOWN || self->orientation == FLAMETHROWER_ORIENTATION_UP)181dist = abs(self->position.y - self->origin.y) >> 16;182else183dist = abs(self->position.x - self->origin.x) >> 16;184}185186int32 anim = dist > 0x20 ? (dist > 0x40 ? 2 : 1) : 0;187188if (self->orientation == FLAMETHROWER_ORIENTATION_DOWN || self->orientation == FLAMETHROWER_ORIENTATION_UP)189RSDK.SetSpriteAnimation(Flamethrower->aniFrames, anim + 5, &self->animator, false, 0);190else191RSDK.SetSpriteAnimation(Flamethrower->aniFrames, anim + 2, &self->animator, false, 0);192}193194void Flamethrower_HandleAngles(void)195{196RSDK_THIS(Flamethrower);197198if (self->mode != FLAMETHROWER_MODE_EMITTER) {199if (self->lastPos.x || self->lastPos.y) {200int32 angle = RSDK.ATan2(self->position.x - self->lastPos.x, self->position.y - self->lastPos.y);201int32 rot = 2 * angle;202203switch (self->orientation) {204default:205case FLAMETHROWER_ORIENTATION_RIGHT: self->rotation = rot; break;206case FLAMETHROWER_ORIENTATION_DOWN: self->rotation = rot + 0x180; break;207case FLAMETHROWER_ORIENTATION_LEFT: self->rotation = rot + 0x100; break;208case FLAMETHROWER_ORIENTATION_UP: self->rotation = rot + 0x180; break;209}210self->rotation &= 0x1FF;211}212else {213self->rotation = 0;214}215}216else {217self->rotation = self->orientation == FLAMETHROWER_ORIENTATION_UP ? 0x100 : 0x00;218}219}220221void Flamethrower_HandleTileCollisions(void)222{223RSDK_THIS(Flamethrower);224225if (self->timer >= 15) {226Hitbox hitbox = Flamethrower_GetHitbox();227228if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, hitbox.bottom << 13, 4)229|| RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_LWALL, 0, hitbox.right << 13, 0, 4)230|| RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, hitbox.top << 13, 4)231|| RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_RWALL, 0, hitbox.left << 13, 0, 4)) {232destroyEntity(self);233}234}235}236237void Flamethrower_CheckOutOfBounds(uint8 orientation)238{239RSDK_THIS(Flamethrower);240241int32 dist = 0;242if (self->mode == FLAMETHROWER_MODE_SPRINKLER) {243dist = self->currentDist;244}245else {246if (orientation == FLAMETHROWER_ORIENTATION_DOWN || orientation == FLAMETHROWER_ORIENTATION_UP)247dist = abs(self->position.y - self->origin.y) >> 16;248else249dist = abs(self->position.x - self->origin.x) >> 16;250}251252if (dist >= self->maxDist)253destroyEntity(self);254}255256void Flamethrower_CheckFlameCollisions(void)257{258RSDK_THIS(Flamethrower);259260Hitbox hitbox = Flamethrower_GetHitbox();261262foreach_active(Player, player)263{264if (Player_CheckCollisionTouch(player, self, &hitbox)) {265Player_ElementHurt(player, self, SHIELD_FIRE);266}267}268}269270void Flamethrower_CheckMouthCollisions(void)271{272RSDK_THIS(Flamethrower);273274if (Flamethrower->hitboxMouthH.left) {275Hitbox *hitbox;276if (self->orientation == FLAMETHROWER_ORIENTATION_DOWN || self->orientation == FLAMETHROWER_ORIENTATION_UP)277hitbox = &Flamethrower->hitboxMouthV;278else279hitbox = &Flamethrower->hitboxMouthH;280281foreach_active(Player, player) { Player_CheckCollisionBox(player, self, hitbox); }282}283}284285void Flamethrower_State_Init(void)286{287RSDK_THIS(Flamethrower);288289self->timer = 0;290self->active = ACTIVE_BOUNDS;291292switch (self->orientation) {293default:294case FLAMETHROWER_ORIENTATION_RIGHT: self->direction = FLIP_NONE; break;295case FLAMETHROWER_ORIENTATION_DOWN: self->direction = MANIA_USE_PLUS ? FLIP_NONE : FLIP_Y; break;296case FLAMETHROWER_ORIENTATION_LEFT: self->direction = FLIP_X; break;297case FLAMETHROWER_ORIENTATION_UP: self->direction = MANIA_USE_PLUS ? FLIP_Y : FLIP_NONE; break;298}299300Flamethrower_SetupOrientation(self->orientation);301if (!RSDK.CheckSceneFolder("LRZ3")) {302if (Flamethrower->hitboxMouthH.left)303self->drawGroup = Zone->playerDrawGroup[0];304else305self->drawGroup = Zone->objectDrawGroup[0];306}307else {308self->drawGroup = Zone->playerDrawGroup[0];309}310311self->state = Flamethrower_State_AwaitInterval;312Flamethrower_State_AwaitInterval();313}314315void Flamethrower_State_AwaitInterval(void)316{317RSDK_THIS(Flamethrower);318319self->active = ACTIVE_BOUNDS;320if (!self->interval || !((Zone->timer + self->intervalOffset) % (self->interval + self->duration))) {321self->active = ACTIVE_NORMAL;322self->timer = 0;323self->state = Flamethrower_State_EmittingFlames;324}325326Flamethrower_CheckMouthCollisions();327}328329void Flamethrower_State_EmittingFlames(void)330{331RSDK_THIS(Flamethrower);332333int32 angle = 0;334switch (self->orientation) {335default:336case FLAMETHROWER_ORIENTATION_RIGHT: angle = 0x00; break;337case FLAMETHROWER_ORIENTATION_DOWN: angle = 0x80; break;338case FLAMETHROWER_ORIENTATION_LEFT: angle = 0x100; break;339case FLAMETHROWER_ORIENTATION_UP: angle = 0x180; break;340}341342self->angle = (angle + (((self->spread >> 1) * RSDK.Sin512(8 * self->flameAngle)) >> 9)) & 0x1FF;343if (!(self->timer & 3)) {344EntityFlamethrower *flame = CREATE_ENTITY(Flamethrower, self, self->position.x, self->position.y);345flame->active = ACTIVE_NORMAL;346flame->visible = true;347if (RSDK.CheckSceneFolder("LRZ3"))348flame->drawGroup = Zone->objectDrawGroup[0] - 1;349else350flame->drawGroup = Zone->objectDrawGroup[0];351flame->maxDist = self->maxDist;352flame->state = Flamethrower_State_SetupFireball;353flame->angle = self->angle;354flame->orientation = self->orientation;355flame->mode = self->mode;356flame->direction = self->direction;357flame->parent = self;358flame->velocity.x = RSDK.Cos512(self->angle) << 17 >> 8;359flame->velocity.y = RSDK.Sin512(self->angle) << 17 >> 8;360}361362if (self->interval && self->timer >= self->duration) {363self->active = ACTIVE_BOUNDS;364self->timer = 0;365self->state = Flamethrower_State_AwaitInterval;366}367368Flamethrower_CheckOffScreen();369370++self->timer;371++self->flameAngle;372373Flamethrower_CheckMouthCollisions();374}375376void Flamethrower_State_SetupFireball(void)377{378RSDK_THIS(Flamethrower);379380self->timer = 0;381self->active = ACTIVE_NORMAL;382self->drawFX = FX_ROTATE | FX_FLIP;383384Flamethrower_HandleAnimations();385386switch (self->orientation) {387default:388case FLAMETHROWER_ORIENTATION_RIGHT: self->direction = FLIP_NONE; break;389case FLAMETHROWER_ORIENTATION_DOWN: self->direction = MANIA_USE_PLUS ? FLIP_NONE : FLIP_Y; break;390case FLAMETHROWER_ORIENTATION_LEFT: self->direction = FLIP_X; break;391case FLAMETHROWER_ORIENTATION_UP: self->direction = MANIA_USE_PLUS ? FLIP_Y : FLIP_NONE; break;392}393394if (RSDK.CheckSceneFolder("LRZ3"))395self->drawGroup = Zone->objectDrawGroup[0] - 1;396else397self->drawGroup = Zone->objectDrawGroup[0];398399self->state = Flamethrower_State_Fireball;400self->lastPos.x = 0;401self->lastPos.y = 0;402self->lastYVelocity = 0;403404Flamethrower_State_Fireball();405}406407void Flamethrower_State_Fireball(void)408{409RSDK_THIS(Flamethrower);410411if (self->mode == FLAMETHROWER_MODE_SPRINKLER)412self->velocity.y += 0x3800;413414if (self->velocity.y < self->lastYVelocity)415self->lastYVelocity = self->velocity.y;416417// Check for flipping downwards after going upwards418if (self->currentDist > 12 && self->lastYVelocity < 0 && self->velocity.y > 0 && self->drawGroup == Zone->objectDrawGroup[0])419self->drawGroup = Zone->objectDrawGroup[1];420421self->position.x += self->velocity.x;422self->position.y += self->velocity.y;423424if (HeavyRider && self->velocity.y > 0)425self->drawGroup = Zone->objectDrawGroup[1];426427if (self->lastPos.x && self->lastPos.y) {428int32 rx = (self->position.x - self->lastPos.x) >> 8;429int32 ry = (self->position.y - self->lastPos.y) >> 8;430self->currentDist += MathHelpers_SquareRoot((rx * rx + ry * ry) >> 16);431}432433#if MANIA_USE_PLUS434if (self->orientation == FLAMETHROWER_ORIENTATION_UP && self->velocity.y > 0) {435self->orientation = FLAMETHROWER_ORIENTATION_DOWN;436self->direction = FLIP_NONE;437}438#endif439440Flamethrower_HandleAnimations();441Flamethrower_HandleTileCollisions();442Flamethrower_HandleAngles();443444if (self->mode == FLAMETHROWER_MODE_EMITTER)445Flamethrower_CheckOutOfBounds(self->orientation);446447Flamethrower_CheckFlameCollisions();448449++self->timer;450self->lastPos = self->position;451}452453#if GAME_INCLUDE_EDITOR454void Flamethrower_EditorDraw(void)455{456RSDK_THIS(Flamethrower);457458Flamethrower_Create(NULL);459Flamethrower_SetupOrientation(self->orientation);460461switch (self->orientation) {462default:463case FLAMETHROWER_ORIENTATION_RIGHT: self->direction = FLIP_NONE; break;464case FLAMETHROWER_ORIENTATION_DOWN: self->direction = MANIA_USE_PLUS ? FLIP_NONE : FLIP_Y; break;465case FLAMETHROWER_ORIENTATION_LEFT: self->direction = FLIP_X; break;466case FLAMETHROWER_ORIENTATION_UP: self->direction = MANIA_USE_PLUS ? FLIP_Y : FLIP_NONE; break;467}468469Flamethrower_Draw();470}471472void Flamethrower_EditorLoad(void)473{474if (RSDK.CheckSceneFolder("LRZ2"))475Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ2/Flamethrower.bin", SCOPE_STAGE);476else if (RSDK.CheckSceneFolder("LRZ3"))477Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ3/Flamethrower.bin", SCOPE_STAGE);478479RSDK_ACTIVE_VAR(Flamethrower, orientation);480RSDK_ENUM_VAR("Right", FLAMETHROWER_ORIENTATION_RIGHT);481RSDK_ENUM_VAR("Down", FLAMETHROWER_ORIENTATION_DOWN);482RSDK_ENUM_VAR("Left", FLAMETHROWER_ORIENTATION_LEFT);483RSDK_ENUM_VAR("Up", FLAMETHROWER_ORIENTATION_UP);484485RSDK_ACTIVE_VAR(Flamethrower, mode);486RSDK_ENUM_VAR("Emitter", FLAMETHROWER_MODE_EMITTER);487RSDK_ENUM_VAR("Sprinkler", FLAMETHROWER_MODE_SPRINKLER);488}489#endif490491void Flamethrower_Serialize(void)492{493RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT8, orientation);494RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT8, mode);495RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT32, maxDist);496RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, spread);497RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, interval);498RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, intervalOffset);499RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, duration);500}501502503