Path: blob/master/SonicMania/Objects/HCZ/Fan.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: Fan Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectFan *Fan;1011void Fan_Update(void)12{13RSDK_THIS(Fan);1415StateMachine_Run(self->stateActivate);16StateMachine_Run(self->stateDeactivate);1718RSDK.ProcessAnimation(&self->animator);1920StateMachine_Run(self->state);21}2223void Fan_LateUpdate(void) {}2425void Fan_StaticUpdate(void)26{27Fan->activePlayers = 0;2829if (Water) {30int32 offsetH = 0;31int32 offsetV = 0;3233foreach_active(Water, water)34{35if (water->state == Water_State_BigBubble && water->activePlayers) {36foreach_active(Fan, fan)37{38Fan->hitboxTop.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;39if (fan->state == Fan_HandlePlayerInteractions_Top) {40if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,41&Fan->hitboxTop)) {42offsetV -= 0x20000;43}44}4546Fan->hitboxBottom.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;47if (fan->state == Fan_HandlePlayerInteractions_Bottom) {48if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,49&Fan->hitboxBottom)) {50offsetV += 0x20000;51}52}5354Fan->hitboxSides.left = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;55if (fan->state == Fan_HandlePlayerInteractions_Left) {56if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,57&Fan->hitboxSides)) {58offsetH -= 0x20000;59}60}61if (fan->state == Fan_HandlePlayerInteractions_Right) {62if (MathHelpers_PointInHitbox(fan->position.x, fan->position.y, water->position.x, water->position.y, fan->direction,63&Fan->hitboxSides)) {64offsetH += 0x20000;65}66}67}6869offsetH = CLAMP(offsetH, -0x20000, 0x20000);70offsetV = CLAMP(offsetV, -0x20000, 0x20000);7172if (water->bubbleOffset.x < offsetH) {73water->bubbleOffset.x += 0x800;7475if (water->bubbleOffset.x > offsetH)76water->bubbleOffset.x = offsetH;77}7879if (water->bubbleOffset.x > offsetH) {80water->bubbleOffset.x -= 0x800;8182if (water->bubbleOffset.x < offsetH)83water->bubbleOffset.x = offsetH;84}8586if (water->bubbleOffset.y < offsetV) {87water->bubbleOffset.y += 0x800;8889if (water->bubbleOffset.y > offsetV)90water->bubbleOffset.y = offsetV;91}9293if (water->bubbleOffset.y > offsetV) {94water->bubbleOffset.y -= 0x800;9596if (water->bubbleOffset.y < offsetV)97water->bubbleOffset.y = offsetV;98}99}100}101}102103if (HangConveyor) {104foreach_active(HangConveyor, conveyor)105{106if (conveyor->movementActivePlayers) {107foreach_active(Player, player)108{109int32 playerID = RSDK.GetEntitySlot(player);110111if (((1 << playerID) & conveyor->movementActivePlayers)) {112bool32 fanning = false;113114foreach_active(Fan, fan)115{116Fan->hitboxTop.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - fan->size;117118if (fan->state == Fan_HandlePlayerInteractions_Top) {119if (Player_CheckCollisionTouch(player, fan, &Fan->hitboxTop) && conveyor->fanTimer[playerID] < 12) {120conveyor->fanTimer[playerID]++;121fanning = true;122foreach_break;123}124}125}126127if (conveyor->fanTimer[playerID] > 0 && !fanning)128conveyor->fanTimer[playerID]--;129}130}131}132}133}134135if (RSDK.CheckSceneFolder("HCZ")) {136int32 count = 0;137138if (RSDK_GET_ENTITY(SLOT_PAUSEMENU, PauseMenu)->classID != PauseMenu->classID) {139Vector2 range = { 0x400000, 0x400000 };140foreach_active(Fan, fan)141{142uint16 tile = RSDK.GetTile(Zone->fgLayer[1], fan->position.x >> 20, fan->position.y >> 20);143if (fan->state && fan->state != Fan_State_Stopped && tile == 0xFFFF && RSDK.CheckOnScreen(fan, &range))144++count;145}146147if (count) {148if (!Fan->playingFanSfx) {149RSDK.PlaySfx(Fan->sfxFan, 37404, 0xFF);150Fan->playingFanSfx = true;151}152}153}154155if (!count && Fan->playingFanSfx) {156RSDK.StopSfx(Fan->sfxFan);157Fan->playingFanSfx = false;158}159}160}161162void Fan_Draw(void)163{164RSDK_THIS(Fan);165166RSDK.DrawSprite(&self->animator, NULL, false);167}168169void Fan_Create(void *data)170{171RSDK_THIS(Fan);172173self->drawFX = FX_FLIP;174175if (!SceneInfo->inEditor) {176self->active = ACTIVE_BOUNDS;177self->visible = true;178self->drawGroup = Zone->objectDrawGroup[0] + 1;179self->updateRange.x = 0x800000;180self->updateRange.y = 0x800000;181182if (self->type == FAN_V)183self->direction *= FLIP_Y;184185if (!self->size)186self->size = 5;187188self->size *= 16;189RSDK.SetSpriteAnimation(Fan->aniFrames, self->type, &self->animator, true, 0);190self->state = Fan_State_Stopped;191self->animator.speed = 0;192193switch (self->activation) {194case FAN_ACTIVATE_NONE:195Fan_Activate();196self->animator.speed = 128;197break;198199case FAN_ACTIVATE_INTERVAL: self->stateActivate = Fan_Activate_Interval; break;200201case FAN_ACTIVATE_PLATFORM: self->stateActivate = Fan_Activate_Platform; break;202203case FAN_ACTIVATE_BUTTON:204Fan_SetupTagLink();205self->stateActivate = Fan_Activate_Button;206break;207208default: break;209}210211if (self->deactivation == FAN_DEACTIVATE_BUTTON) {212Fan_SetupTagLink();213self->stateDeactivate = Fan_Deactivate_Button;214}215}216}217218void Fan_StageLoad(void)219{220if (RSDK.CheckSceneFolder("OOZ1") || RSDK.CheckSceneFolder("OOZ2")) {221Fan->aniFrames = RSDK.LoadSpriteAnimation("OOZ/Fan.bin", SCOPE_STAGE);222223Fan->hitboxTop.left = -64;224Fan->hitboxTop.right = 64;225226Fan->unused = 5;227Fan->minVelocity = -0x50000;228229Fan->hitboxBottom.left = -16;230Fan->hitboxBottom.right = 16;231Fan->hitboxBottom.top = -160;232Fan->hitboxBottom.bottom = 32;233234Fan->hitboxSides.top = -160;235Fan->hitboxSides.bottom = 112;236Fan->hitboxSides.left = -112;237Fan->hitboxSides.right = 32;238}239else if (RSDK.CheckSceneFolder("HCZ")) {240Fan->aniFrames = RSDK.LoadSpriteAnimation("HCZ/Fan.bin", SCOPE_STAGE);241242Fan->hitboxTop.left = -16;243Fan->hitboxTop.right = 16;244245Fan->unused = 20;246Fan->minVelocity = -0x20000;247248Fan->hitboxBottom.left = -16;249Fan->hitboxBottom.right = 16;250Fan->hitboxBottom.top = -160;251Fan->hitboxBottom.bottom = 32;252253Fan->hitboxSides.top = -24;254Fan->hitboxSides.bottom = 24;255Fan->hitboxSides.left = -112;256Fan->hitboxSides.right = 32;257258Fan->sfxFan = RSDK.GetSfx("HCZ/SmallFan.wav");259Fan->active = ACTIVE_ALWAYS;260}261262Fan->playerHitbox.left = -1;263Fan->playerHitbox.top = -1;264Fan->playerHitbox.right = 1;265Fan->playerHitbox.bottom = 1;266}267268void Fan_SetupTagLink(void)269{270RSDK_THIS(Fan);271272self->taggedButton = NULL;273EntityButton *taggedButton = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) - 1, Button);274275if (self->buttonTag > 0) {276bool32 matchedTag = false;277278if (Button) {279foreach_all(Button, button)280{281if (button->tag == self->buttonTag) {282taggedButton = button;283matchedTag = true;284}285}286}287288if (PullChain && !matchedTag) {289foreach_all(PullChain, chain)290{291if (chain->tag == self->buttonTag) {292taggedButton = (EntityButton *)chain;293matchedTag = true;294}295}296}297}298299if (taggedButton) {300if ((Button && taggedButton->classID == Button->classID) || (PullChain && taggedButton->classID == PullChain->classID)) {301int32 distX = abs(self->position.x - taggedButton->position.x) + 0x800000;302int32 distY = abs(self->position.y - taggedButton->position.y) + 0x800000;303304if (self->updateRange.x < distX)305self->updateRange.x = distX;306307if (self->updateRange.y < distY)308self->updateRange.y = distY;309310self->taggedButton = taggedButton;311}312}313}314315void Fan_HandlePlayerInteractions_Top(void)316{317RSDK_THIS(Fan);318319Fan->hitboxTop.top = (RSDK.Sin256(2 * Zone->timer) >> 5) - self->size;320Fan->hitboxTop.bottom = 48;321322int32 playerID = 1;323foreach_active(Player, player)324{325if (player->state != Player_State_Static) {326int32 anim = player->animator.animationID;327if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN328&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxTop, player, &Fan->playerHitbox)) {329if (Water && player->position.y > Water->waterLevel)330RSDK.SetSpriteAnimation(player->aniFrames, ANI_FAN, &player->animator, false, 1);331else332RSDK.SetSpriteAnimation(player->aniFrames, ANI_SPRING_CS, &player->animator, false, 1);333334player->state = Player_State_Air;335player->tileCollisions = TILECOLLISION_DOWN;336player->onGround = false;337338int32 velocity = MAX((self->position.y + (Fan->hitboxTop.top << 16) - player->position.y) >> 4, Fan->minVelocity);339if (player->velocity.y <= velocity) {340player->velocity.y = velocity;341}342else {343player->velocity.y = player->velocity.y + (velocity >> 2) + (velocity >> 1);344if (player->velocity.y < velocity)345player->velocity.y = velocity;346}347348if (!(playerID & Fan->activePlayers)) {349if (player->velocity.y > -0x40000 && player->velocity.y < 0)350player->velocity.x += (32 * player->velocity.x / 31) >> 5;351352Fan->activePlayers |= playerID;353}354}355}356357playerID <<= 1;358}359360if (Water) {361if (self->position.y > Water->waterLevel && !(Zone->timer & 3)) {362EntityWater *water = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y - 0x100000);363364water->isPermanent = false;365water->position.x += ZONE_RAND(-6, 7) << 16;366water->bubbleX = water->position.x;367water->velocity.y = -0x40000;368water->childPtr = NULL;369}370}371372Fan_State_Started();373}374375void Fan_HandlePlayerInteractions_Bottom(void)376{377RSDK_THIS(Fan);378379Fan->hitboxBottom.bottom = self->size - (RSDK.Sin256(2 * Zone->timer) >> 5);380381foreach_active(Player, player)382{383if (player->state != Player_State_Static) {384int32 anim = player->animator.animationID;385386if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN387&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxBottom, player, &Fan->playerHitbox)) {388int32 max = (self->position.y - player->position.y + 0xA00000) >> 4;389if (player->velocity.y < max)390player->velocity.y += ((self->position.y - player->position.y + 0xA00000) >> 9);391}392}393}394395Fan_State_Started();396}397398void Fan_HandlePlayerInteractions_Left(void)399{400RSDK_THIS(Fan);401402Fan->hitboxSides.left = (RSDK.Sin256(2 * Zone->timer) >> 5) - self->size;403404foreach_active(Player, player)405{406if (player->state != Player_State_Static) {407int32 anim = player->animator.animationID;408409if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN && player->collisionMode != CMODE_LWALL410&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxSides, player, &Fan->playerHitbox)) {411player->position.x += (self->position.x - player->position.x - 0xA00000) >> 4;412}413}414}415416Fan_State_Started();417}418419void Fan_HandlePlayerInteractions_Right(void)420{421RSDK_THIS(Fan);422423Fan->hitboxSides.left = (RSDK.Sin256(2 * Zone->timer) >> 5) - self->size;424425foreach_active(Player, player)426{427if (player->state != Player_State_Static) {428int32 anim = player->animator.animationID;429430if (anim != ANI_HURT && anim != ANI_DIE && anim != ANI_DROWN) {431if (player->collisionMode != CMODE_LWALL && player->collisionMode != CMODE_RWALL432&& RSDK.CheckObjectCollisionTouchBox(self, &Fan->hitboxSides, player, &Fan->playerHitbox)) {433player->position.x += (self->position.x - player->position.x + 0xA00000) >> 4;434}435}436}437}438439Fan_State_Started();440}441442void Fan_State_Started(void)443{444RSDK_THIS(Fan);445446if (self->animator.speed < 0x80)447self->animator.speed += 4;448}449450void Fan_State_Stopped(void)451{452RSDK_THIS(Fan);453454if (self->animator.speed)455self->animator.speed -= 2;456}457458void Fan_Activate_Interval(void)459{460RSDK_THIS(Fan);461462if (self->delay) {463if (!--self->delay)464self->state = Fan_State_Stopped;465}466else {467if (!((Zone->timer + self->intervalOffset) % self->interval) && self->state == Fan_State_Stopped) {468self->active = ACTIVE_NORMAL;469Fan_Activate();470}471}472}473474void Fan_Activate_Button(void)475{476RSDK_THIS(Fan);477478EntityButton *button = self->taggedButton;479if ((!button || button->activated) && self->state == Fan_State_Stopped) {480self->active = ACTIVE_NORMAL;481Fan_Activate();482}483}484485void Fan_Deactivate_Button(void)486{487RSDK_THIS(Fan);488489EntityButton *button = self->taggedButton;490if ((!button || button->activated) && self->state != Fan_State_Stopped) {491self->active = ACTIVE_BOUNDS;492self->state = Fan_State_Stopped;493}494}495496void Fan_Activate_Platform(void)497{498RSDK_THIS(Fan);499500int32 slot = SceneInfo->entitySlot - 1;501EntityPlatform *platform = RSDK_GET_ENTITY(slot, Platform);502while (platform->classID == Fan->classID) {503--slot;504platform = RSDK_GET_ENTITY(slot, Platform);505}506507if (platform->classID == Platform->classID) {508if (platform->amplitude.y == platform->amplitude.x)509self->state = Fan_State_Stopped;510else if (self->state == Fan_State_Stopped)511Fan_Activate();512}513}514515void Fan_Activate(void)516{517RSDK_THIS(Fan);518519if (self->type != FAN_V) {520if (self->direction == FLIP_NONE)521self->state = Fan_HandlePlayerInteractions_Left;522else523self->state = Fan_HandlePlayerInteractions_Right;524525self->delay = self->duration;526}527else {528if (self->direction == FLIP_NONE)529self->state = Fan_HandlePlayerInteractions_Top;530else531self->state = Fan_HandlePlayerInteractions_Bottom;532533self->delay = self->duration;534}535}536537#if GAME_INCLUDE_EDITOR538void Fan_EditorDraw(void)539{540RSDK_THIS(Fan);541542RSDK.SetSpriteAnimation(Fan->aniFrames, self->type, &self->animator, true, 0);543544self->updateRange.x = 0x800000;545self->updateRange.y = 0x800000;546547int32 dir = self->direction;548if (self->type == FAN_V)549self->direction *= FLIP_Y;550551Fan_Draw();552553self->direction = dir;554555if (showGizmos()) {556if (self->activation == FAN_ACTIVATE_BUTTON || self->deactivation == FAN_DEACTIVATE_BUTTON) {557Fan_SetupTagLink();558559RSDK_DRAWING_OVERLAY(true);560if (self->taggedButton) {561DrawHelpers_DrawArrow(self->taggedButton->position.x, self->taggedButton->position.y, self->position.x, self->position.y, 0xFFFF00,562INK_NONE, 0xFF);563}564RSDK_DRAWING_OVERLAY(false);565}566}567}568569void Fan_EditorLoad(void)570{571if (RSDK.CheckSceneFolder("OOZ1") || RSDK.CheckSceneFolder("OOZ2"))572Fan->aniFrames = RSDK.LoadSpriteAnimation("OOZ/Fan.bin", SCOPE_STAGE);573else if (RSDK.CheckSceneFolder("HCZ"))574Fan->aniFrames = RSDK.LoadSpriteAnimation("HCZ/Fan.bin", SCOPE_STAGE);575576RSDK_ACTIVE_VAR(Fan, type);577RSDK_ENUM_VAR("Vertical", FAN_V);578RSDK_ENUM_VAR("Horiozontal", FAN_H);579580RSDK_ACTIVE_VAR(Fan, direction);581RSDK_ENUM_VAR("No Flip", FLIP_NONE);582RSDK_ENUM_VAR("Flipped", FLIP_X);583584RSDK_ACTIVE_VAR(Fan, activation);585RSDK_ENUM_VAR("None", FAN_ACTIVATE_NONE);586RSDK_ENUM_VAR("On Interval", FAN_ACTIVATE_INTERVAL);587RSDK_ENUM_VAR("On Platform Moved", FAN_ACTIVATE_PLATFORM);588RSDK_ENUM_VAR("On Button Press", FAN_ACTIVATE_BUTTON);589590RSDK_ACTIVE_VAR(Fan, deactivation);591RSDK_ENUM_VAR("None", FAN_DEACTIVATE_NONE);592RSDK_ENUM_VAR("On Button Press", FAN_DEACTIVATE_BUTTON);593}594#endif595596void Fan_Serialize(void)597{598RSDK_EDITABLE_VAR(Fan, VAR_UINT8, type);599RSDK_EDITABLE_VAR(Fan, VAR_UINT8, direction);600RSDK_EDITABLE_VAR(Fan, VAR_UINT8, activation);601RSDK_EDITABLE_VAR(Fan, VAR_UINT8, deactivation);602RSDK_EDITABLE_VAR(Fan, VAR_ENUM, size);603RSDK_EDITABLE_VAR(Fan, VAR_UINT16, interval);604RSDK_EDITABLE_VAR(Fan, VAR_UINT16, intervalOffset);605RSDK_EDITABLE_VAR(Fan, VAR_UINT16, duration);606RSDK_EDITABLE_VAR(Fan, VAR_ENUM, buttonTag);607}608609610