Path: blob/master/SonicMania/Objects/Common/Water.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: Water Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectWater *Water;1011void Water_Update(void)12{13RSDK_THIS(Water);1415StateMachine_Run(self->state);16}1718void Water_LateUpdate(void) {}1920void Water_StaticUpdate(void)21{22EntityPauseMenu *pauseMenu = RSDK_GET_ENTITY(SLOT_PAUSEMENU, PauseMenu);2324if (pauseMenu->classID != PauseMenu->classID) {25if (Water->newWaterLevel == Water->targetWaterLevel) {26Water->moveWaterLevel = 0;2728if (Water->waterLevelVolume > 0)29Water->waterLevelVolume--;30Water->waterLevelVolume = CLAMP(Water->waterLevelVolume, 0, 30);31}32else {33if (RSDK.CheckSceneFolder("HCZ") && Water->moveWaterLevel) {34if (!(Water->waterLevelVolume % 5))35Camera_ShakeScreen(0, 0, 1);36++Water->waterLevelVolume;37}3839if (Water->newWaterLevel >= Water->targetWaterLevel) {40Water->newWaterLevel -= Water->waterMoveSpeed;41if (Water->newWaterLevel < Water->targetWaterLevel)42Water->newWaterLevel = Water->targetWaterLevel;43}44else {45Water->newWaterLevel += Water->waterMoveSpeed;46if (Water->newWaterLevel > Water->targetWaterLevel)47Water->newWaterLevel = Water->targetWaterLevel;48}49}50}5152if (Water->playingWaterLevelSfx) {53if (Water->waterLevelVolume > 30)54Water->waterLevelVolume = 30;55float volume = Water->waterLevelVolume / 30.0;56RSDK.SetChannelAttributes(Water->waterLevelChannelL, volume, -1.0, 1.0);57RSDK.SetChannelAttributes(Water->waterLevelChannelR, volume, 1.0, 1.0);58}5960if (Water->waterLevelVolume > 0) {61if (!Water->playingWaterLevelSfx) {62Water->waterLevelChannelL = RSDK.PlaySfx(Water->sfxWaterLevelL, 1, 255);63RSDK.SetChannelAttributes(Water->waterLevelChannelL, 0.0, -1.0, 1.0);6465Water->waterLevelChannelR = RSDK.PlaySfx(Water->sfxWaterLevelR, 1, 255);66RSDK.SetChannelAttributes(Water->waterLevelChannelR, 0.0, 1.0, 1.0);6768Water->playingWaterLevelSfx = true;69}70}71else if (!Water->waterLevelVolume && Water->playingWaterLevelSfx) {72RSDK.StopSfx(Water->sfxWaterLevelL);73RSDK.StopSfx(Water->sfxWaterLevelR);74Water->playingWaterLevelSfx = false;75}7677bool32 wakeActive = false;78for (int32 i = 0; i < PLAYER_COUNT; ++i) wakeActive |= Water->wakePosX[i] > 0;7980if (wakeActive) {81if (pauseMenu->classID != PauseMenu->classID)82RSDK.ProcessAnimation(&Water->wakeAnimator);8384if (!Water->playingSkimSfx) {85RSDK.PlaySfx(Water->sfxSkim, 3570, 0xFF);86Water->playingSkimSfx = true;87}88}89else if (Water->playingSkimSfx) {90RSDK.StopSfx(Water->sfxSkim);91Water->playingSkimSfx = false;92}93}9495void Water_Draw(void)96{97RSDK_THIS(Water);9899StateMachine_Run(self->stateDraw);100}101102void Water_Create(void *data)103{104RSDK_THIS(Water);105106self->visible = true;107if (!SceneInfo->inEditor) {108if (data)109self->type = VOID_TO_INT(data);110111switch (self->type) {112case WATER_WATERLEVEL:113if (globals->gameMode == MODE_COMPETITION && RSDK.CheckSceneFolder("CPZ")) {114destroyEntity(self);115}116else {117self->active = ACTIVE_NORMAL;118self->inkEffect = INK_ADD;119self->drawGroup = Zone->hudDrawGroup - 1;120self->alpha = RSDK.CheckSceneFolder("AIZ") ? 0x60 : 0xE0;121RSDK.SetSpriteAnimation(Water->aniFrames, 0, &self->animator, true, 0);122self->state = Water_State_Water;123self->stateDraw = Water_Draw_Water;124self->size.x >>= 16;125Water->newWaterLevel = self->position.y;126Water->targetWaterLevel = self->position.y;127}128break;129130case WATER_POOL:131self->active = ACTIVE_BOUNDS;132self->drawFX = FX_FLIP;133switch (self->priority) {134case WATER_PRIORITY_LOWEST: self->drawGroup = Zone->objectDrawGroup[0] - 1; break;135case WATER_PRIORITY_LOW: self->drawGroup = Zone->playerDrawGroup[0]; break;136case WATER_PRIORITY_HIGH: self->drawGroup = Zone->playerDrawGroup[1]; break;137case WATER_PRIORITY_HIGHEST: self->drawGroup = Zone->hudDrawGroup - 1; break;138default: break;139}140141self->updateRange.x = self->size.x >> 1;142self->updateRange.y = self->size.y >> 1;143144self->hitbox.right = (self->size.x >> 17);145self->hitbox.left = -(self->size.x >> 17);146self->hitbox.bottom = (self->size.y >> 17);147self->hitbox.top = -(self->size.y >> 17);148149self->state = Water_State_Pool;150self->stateDraw = Water_Draw_Pool;151break;152153case WATER_BUBBLER:154case WATER_BIG_BUBBLER:155self->drawGroup = Zone->objectDrawGroup[0];156self->inkEffect = INK_ADD;157self->alpha = 0x100;158self->active = ACTIVE_BOUNDS;159self->updateRange.x = TO_FIXED(16);160self->updateRange.y = TO_FIXED(16);161162RSDK.SetSpriteAnimation(Water->aniFrames, 2, &self->animator, true, 0);163164self->state = Water_State_Bubbler;165self->stateDraw = Water_Draw_Bubbler;166break;167168case WATER_HEIGHT_TRIGGER:169self->active = ACTIVE_BOUNDS;170self->visible = false;171if (self->buttonTag > 0) {172self->active = ACTIVE_NORMAL;173Water_SetupTagLink();174}175176self->updateRange.x = self->size.x >> 1;177self->updateRange.y = self->size.y >> 1;178179self->state = Water_State_HeightTrigger;180break;181182case WATER_BTN_BIG_BUBBLE:183self->drawGroup = Zone->objectDrawGroup[0];184self->active = ACTIVE_BOUNDS;185self->updateRange.x = TO_FIXED(16);186self->updateRange.y = TO_FIXED(16);187188self->state = Water_State_BtnBigBubble;189self->stateDraw = StateMachine_None;190191Water_SetupTagLink();192break;193194case WATER_SPLASH:195self->active = ACTIVE_NORMAL;196self->drawGroup = Zone->hudDrawGroup - 2;197RSDK.SetSpriteAnimation(Water->aniFrames, 1, &self->animator, true, 0);198199self->state = Water_State_Splash;200self->stateDraw = Water_Draw_Splash;201break;202203case WATER_BUBBLE:204self->active = ACTIVE_NORMAL;205self->drawGroup = Zone->playerDrawGroup[1];206self->drawFX = FX_SCALE;207self->inkEffect = INK_ADD;208self->alpha = 0x100;209self->updateRange.x = TO_FIXED(128);210self->updateRange.y = TO_FIXED(128);211self->isPermanent = true;212213self->scale.x = 0x200;214self->scale.y = 0x200;215RSDK.SetSpriteAnimation(Water->aniFrames, 5, &self->animator, true, 0);216217self->state = Water_State_Bubble;218self->stateDraw = Water_Draw_Bubble;219break;220221case WATER_COUNTDOWN:222self->active = ACTIVE_NORMAL;223self->drawGroup = Zone->playerDrawGroup[1];224self->drawFX = FX_SCALE;225self->inkEffect = INK_ADD;226self->alpha = 0x100;227self->updateRange.x = TO_FIXED(128);228self->updateRange.y = TO_FIXED(128);229230self->scale.x = 0x200;231self->scale.y = 0x200;232RSDK.SetSpriteAnimation(Water->aniFrames, 7, &self->animator, true, 0);233234self->state = Water_State_Countdown;235self->stateDraw = Water_Draw_Countdown;236break;237238default: break;239}240}241}242243void Water_StageLoad(void)244{245Water->active = ACTIVE_ALWAYS;246247Water->aniFrames = RSDK.LoadSpriteAnimation("Global/Water.bin", SCOPE_STAGE);248249Water->waterLevel = 0x7FFFFFFF;250Water->newWaterLevel = Water->waterLevel;251Water->targetWaterLevel = Water->waterLevel;252253Water->disableWaterSplash = false;254255Water->hitboxPlayerBubble.left = -2;256Water->hitboxPlayerBubble.top = -2;257Water->hitboxPlayerBubble.right = 2;258Water->hitboxPlayerBubble.bottom = 2;259260Water->hitboxPoint.left = -1;261Water->hitboxPoint.top = -1;262Water->hitboxPoint.right = 1;263Water->hitboxPoint.bottom = 1;264265Water->sfxSplash = RSDK.GetSfx("Stage/Splash.wav");266Water->sfxBreathe = RSDK.GetSfx("Stage/Breathe.wav");267Water->sfxWarning = RSDK.GetSfx("Stage/Warning.wav");268Water->sfxDrown = RSDK.GetSfx("Stage/Drown.wav");269Water->sfxDrownAlert = RSDK.GetSfx("Stage/DrownAlert.wav");270Water->sfxSkim = RSDK.GetSfx("HCZ/Skim.wav");271272if (RSDK.CheckSceneFolder("HCZ")) {273Water->wakeFrames = RSDK.LoadSpriteAnimation("HCZ/Wake.bin", SCOPE_STAGE);274Water->bigBubbleFrames = RSDK.LoadSpriteAnimation("HCZ/BigBubble.bin", SCOPE_STAGE);275276Water->sfxWaterLevelL = RSDK.GetSfx("HCZ/WaterLevel_L.wav");277Water->sfxWaterLevelR = RSDK.GetSfx("HCZ/WaterLevel_R.wav");278Water->sfxDNAGrab = RSDK.GetSfx("CPZ/DNAGrab.wav");279Water->sfxDNABurst = RSDK.GetSfx("CPZ/DNABurst.wav");280281RSDK.SetSpriteAnimation(Water->wakeFrames, 0, &Water->wakeAnimator, true, 0);282}283}284285void Water_DrawHook_ApplyWaterPalette(void)286{287RSDKScreenInfo *screen = &ScreenInfo[SceneInfo->currentScreenID];288int32 waterDrawPos = CLAMP(FROM_FIXED(Water->waterLevel) - screen->position.y, 0, screen->size.y);289290RSDK.SetActivePalette(Water->waterPalette, waterDrawPos, screen->size.y);291ScreenInfo[SceneInfo->currentScreenID].waterDrawPos = waterDrawPos;292}293294void Water_DrawHook_RemoveWaterPalette(void) { RSDK.SetActivePalette(0, 0, ScreenInfo[SceneInfo->currentScreenID].size.y); }295296void Water_SetupTagLink(void)297{298RSDK_THIS(Water);299300self->taggedButton = NULL;301if (self->buttonTag > 0) {302if (Button) {303foreach_all(Button, button)304{305if (button->tag == self->buttonTag) {306self->taggedButton = button;307foreach_break;308}309}310}311312if (PullChain && !self->taggedButton) {313foreach_all(PullChain, chain)314{315if (chain->tag == self->buttonTag) {316self->taggedButton = (EntityButton *)chain;317foreach_break;318}319}320}321322if (self->taggedButton) {323if (self->updateRange.x < TO_FIXED(128) + abs(self->position.x - self->taggedButton->position.x))324self->updateRange.x = TO_FIXED(128) + abs(self->position.x - self->taggedButton->position.x);325326if (self->updateRange.y < TO_FIXED(128) + abs(self->position.y - self->taggedButton->position.y))327self->updateRange.y = TO_FIXED(128) + abs(self->position.y - self->taggedButton->position.y);328}329}330}331332void Water_SpawnBubble(EntityPlayer *player, int32 id)333{334if (Water->constBubbleTimer[id] <= 0) {335Water->constBubbleTimer[id] = 60;336if (RSDK.Rand(0, 3) == 1)337Water->randBubbleTimer[id] = RSDK.Rand(0, 16) + 8;338else339Water->randBubbleTimer[id] = 0x200;340}341else {342Water->constBubbleTimer[id]--;343if (Water->randBubbleTimer[id] <= 0)344Water->randBubbleTimer[id] = 0x200;345else346return;347}348349EntityWater *bubble = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), player->position.x, player->position.y);350if (player->direction) {351bubble->position.x -= TO_FIXED(6);352bubble->angle = 0x100;353}354else {355bubble->position.x += TO_FIXED(6);356}357358bubble->childPtr = player;359bubble->bubbleX = bubble->position.x;360bubble->velocity.y = -0x8800;361bubble->drawGroup = player->drawGroup + 1;362}363364void Water_SpawnCountDownBubble(EntityPlayer *player, int32 id, uint8 bubbleID)365{366EntityWater *bubble = CREATE_ENTITY(Water, INT_TO_VOID(WATER_COUNTDOWN), player->position.x, player->position.y);367if (player->direction) {368bubble->position.x -= TO_FIXED(6);369bubble->angle = 0x100;370}371else {372bubble->position.x += TO_FIXED(6);373}374bubble->bubbleX = bubble->position.x;375bubble->velocity.y = -0x8800;376bubble->childPtr = player;377bubble->countdownID = bubbleID;378bubble->drawGroup = Zone->playerDrawGroup[1] + 1;379}380381void Water_State_Water(void)382{383RSDK_THIS(Water);384385RSDK.ProcessAnimation(&self->animator);386387if (self->type == WATER_WATERLEVEL)388Water->waterLevel = (self->size.x * RSDK.Sin512(2 * Zone->timer)) + Water->newWaterLevel;389390for (int32 playerID = 0; playerID < Player->playerCount; ++playerID) {391EntityPlayer *player = RSDK_GET_ENTITY(playerID, Player);392393if (player->state == Player_State_FlyToPlayer && player->abilityPtrs[0]) {394player->position.x = ((Entity *)player->abilityPtrs[0])->position.x;395player->position.y = ((Entity *)player->abilityPtrs[0])->position.y;396}397398Water->wakePosX[playerID] = 0;399400bool32 canEnterWater = true;401if (!Player_CheckValidState(player) || player->state == Player_State_TransportTube) {402if (player->state != Player_State_FlyToPlayer)403canEnterWater = false;404}405406if (canEnterWater) {407EntityWater *currentPool = NULL;408uint16 underwater = 0;409foreach_active(Water, pool)410{411if (pool->type == WATER_POOL) {412if (RSDK.CheckObjectCollisionTouchBox(pool, &pool->hitbox, player, &Water->hitboxPoint)) {413currentPool = pool;414pool->childPtr = player;415underwater = RSDK.GetEntitySlot(pool);416}417else if (pool->childPtr == player) {418pool->childPtr = NULL;419if (!currentPool)420currentPool = pool;421}422}423}424425if (player->position.y > Water->waterLevel)426underwater = true;427428if (!Player_CheckValidState(player)) {429if (player->state != Player_State_FlyToPlayer)430underwater = false;431}432433int32 waterID = 0;434#if MANIA_USE_PLUS435if (!player->isGhost)436#endif437waterID = underwater;438439if (!waterID) {440if (player->underwater) {441EntityWater *waterSection = NULL;442if (player->underwater > 1)443waterSection = RSDK_GET_ENTITY(player->underwater, Water);444445player->underwater = false;446Player_UpdatePhysicsState(player);447if (player->velocity.y && (!Current || !((1 << RSDK.GetEntitySlot(player)) & Current->activePlayers))448&& (Player_CheckValidState(player) || player->state == Player_State_FlyToPlayer)) {449if (!Water->disableWaterSplash) {450if (waterSection) {451EntityWater *splash = CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), player->position.x,452waterSection->position.y - (waterSection->size.y >> 1));453splash->childPtr = waterSection;454splash->drawGroup = player->drawGroup;455}456else {457CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), player->position.x, Water->waterLevel);458}459460RSDK.PlaySfx(Water->sfxSplash, false, 0xFF);461}462463if (player->velocity.y >= -TO_FIXED(4)) {464player->velocity.y <<= 1;465if (player->velocity.y < -TO_FIXED(16))466player->velocity.y = -TO_FIXED(16);467}468}469}470else {471// if we're not underwater already but we would otherwise be472if (abs(player->groundVel) >= 0x78000) {473Hitbox *playerHitbox = Player_GetHitbox(player);474if (abs(player->position.y + (playerHitbox->bottom << 16) - Water->waterLevel) <= 0x40000 && player->groundedStore) {475Water->wakePosX[playerID] = player->position.x;476Water->wakeDir[playerID] = player->groundVel < 0;477if (!player->onGround) {478player->onGround = true;479player->position.y = Water->waterLevel - (playerHitbox->bottom << 16);480}481}482}483}484485if (!player->sidekick && player->drownTimer >= 1080) {486player->drownTimer = 0;487Music_JingleFadeOut(TRACK_DROWNING, false);488}489}490else {491bool32 notUnderwater = player->underwater == 0;492player->underwater = waterID;493494if (notUnderwater) {495Player_UpdatePhysicsState(player);496if (player->velocity.y && (!Current || !((1 << RSDK.GetEntitySlot(player)) & Current->activePlayers))) {497if (!Water->disableWaterSplash) {498if (currentPool) {499EntityWater *splash = CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), player->position.x,500currentPool->position.y - (currentPool->size.x >> 1));501splash->drawGroup = player->drawGroup;502splash->childPtr = currentPool;503}504else {505CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), player->position.x, Water->waterLevel);506}507508RSDK.PlaySfx(Water->sfxSplash, false, 255);509}510511player->velocity.y >>= 2;512}513514player->velocity.x >>= 1;515if (player->collisionMode == CMODE_FLOOR)516player->groundVel >>= 1;517518player->drownTimer = 0;519Water->constBubbleTimer[playerID] = 52;520Water->unused1[playerID] = 0;521}522else {523if (player->invincibleTimer <= 0) {524if (player->shield == SHIELD_FIRE) {525// No cool smoke fx in mania, just a loss of shield :(526player->shield = SHIELD_NONE;527destroyEntity(RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player), Shield));528}529530if (player->shield == SHIELD_LIGHTNING) {531// No cool electric flash fx in mania, just a loss of shield :(532player->shield = SHIELD_NONE;533destroyEntity(RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player), Shield));534}535}536537if (player->shield != SHIELD_BUBBLE) {538Water_SpawnBubble(player, playerID);539540bool32 playAlertSfx = false;541switch (++player->drownTimer) {542default: break;543544case 360:545case 660:546case 960:547if (!player->sidekick)548RSDK.PlaySfx(Water->sfxWarning, false, 0xFF);549break;550551case 1080:552#if MANIA_USE_PLUS553if (globals->gameMode != MODE_COMPETITION && globals->gameMode != MODE_ENCORE) {554#else555if (globals->gameMode != MODE_COMPETITION) {556#endif557if (!player->sidekick)558Music_PlayJingle(TRACK_DROWNING);559}560561Water_SpawnCountDownBubble(player, playerID, 5);562playAlertSfx = true;563break;564565case 1140: playAlertSfx = true; break;566567case 1200:568Water_SpawnCountDownBubble(player, playerID, 4);569playAlertSfx = true;570break;571572case 1260: playAlertSfx = true; break;573574case 1320:575Water_SpawnCountDownBubble(player, playerID, 3);576playAlertSfx = true;577break;578579case 1380: playAlertSfx = true; break;580581case 1440:582Water_SpawnCountDownBubble(player, playerID, 2);583playAlertSfx = true;584break;585586case 1500: playAlertSfx = true; break;587588case 1560:589Water_SpawnCountDownBubble(player, playerID, 1);590playAlertSfx = true;591break;592593case 1620: playAlertSfx = true; break;594595case 1680:596Water_SpawnCountDownBubble(player, playerID, 0);597playAlertSfx = true;598break;599600case 1740: playAlertSfx = true; break;601602case 1800:603player->deathType = PLAYER_DEATH_DROWN;604if (!pool)605player->drawGroup = Zone->playerDrawGroup[1];606playAlertSfx = true;607break;608}609610if (playAlertSfx) {611if (612#if MANIA_USE_PLUS613globals->gameMode == MODE_ENCORE ||614#endif615globals->gameMode == MODE_COMPETITION)616RSDK.PlaySfx(Water->sfxDrownAlert, false, 255);617}618}619}620}621}622623if (self->state == Player_State_FlyToPlayer && player->abilityPtrs[0]) {624self->position.x = player->position.x;625self->position.y = player->position.y;626}627}628}629630void Water_State_Pool(void)631{632// nothing, its handled by the water level type633}634635void Water_State_Splash(void)636{637RSDK_THIS(Water);638639EntityWater *water = self->childPtr;640if (water) {641if (water != (EntityWater *)1)642self->position.y = water->position.y - (water->size.y >> 1);643}644else {645self->position.y = Water->waterLevel;646}647648RSDK.ProcessAnimation(&self->animator);649650if (self->animator.frameID == self->animator.frameCount - 1)651destroyEntity(self);652}653654void Water_HandleBubbleMovement(void)655{656RSDK_THIS(Water);657658if (self->bubbleVelocity.x || self->bubbleVelocity.y) {659self->position.x += self->bubbleVelocity.x;660self->position.y += self->bubbleVelocity.y;661}662else {663self->position.x += self->velocity.x;664self->position.y += self->velocity.y;665}666667self->position.x += self->bubbleOffset.x;668self->position.y += self->bubbleOffset.y;669670int32 anim = self->animator.animationID;671if ((anim == 3 && self->animator.frameID < 12) || anim == 4 || anim == 5 || (anim == 7 && !self->activePlayers)) {672self->position.x = (RSDK.Sin512(self->angle) << 9) + self->bubbleX;673self->angle = (self->angle + 4) & 0x1FF;674}675676if (self->position.y < Water->waterLevel) {677bool32 inWater = false;678foreach_active(Water, pool)679{680if (pool->type == WATER_POOL && RSDK.CheckObjectCollisionTouchBox(pool, &pool->hitbox, self, &Water->hitboxPoint))681inWater = true;682}683684if (!inWater) {685if (self->animator.animationID == 3 && self->animator.frameID > 12) {686RSDK.SetSpriteAnimation(Water->aniFrames, 6, &self->animator, false, 0);687self->velocity.y = 0;688}689else if (self->animator.animationID == 7) {690Water_PopBigBubble(self, false);691}692else {693destroyEntity(self);694}695}696}697}698699void Water_PopBigBubble(EntityWater *self, bool32 jumpedOut)700{701if (self->timer <= 0) {702RSDK.SetSpriteAnimation(Water->aniFrames, 6, &self->animator, true, 0);703self->velocity.x = 0;704self->velocity.y = 0;705706RSDK.PlaySfx(Water->sfxDNABurst, false, 0xFF);707708foreach_active(Player, player)709{710int32 playerID = RSDK.GetEntitySlot(player);711if ((1 << playerID) & self->activePlayers) {712if (jumpedOut) {713RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, true, 0);714}715else {716if (player->state != Player_State_Static || player->animator.animationID != ANI_BUBBLE) {717EntityShield *shield = RSDK_GET_ENTITY(Player->playerCount + playerID, Shield);718if (shield)719shield->visible = true;720self->activePlayers &= ~(1 << playerID);721}722else {723RSDK.SetSpriteAnimation(player->aniFrames, ANI_AIR_WALK, &player->animator, true, 0);724EntityShield *shield = RSDK_GET_ENTITY(Player->playerCount + playerID, Shield);725if (shield)726shield->visible = true;727self->activePlayers &= ~(1 << playerID);728}729}730731player->state = Player_State_Air;732}733}734}735}736737void Water_State_Bubble(void)738{739RSDK_THIS(Water);740741EntityPlayer *playerPtr = (EntityPlayer *)self->childPtr;742743if (self->animator.animationID == 6 && self->animator.frameID == self->animator.frameCount - 1)744destroyEntity(self);745746if (playerPtr && playerPtr->state == Player_State_Bubble && self->animator.frameID < 3)747self->bubbleX += 0x40000;748749if (self->speed != -1) {750if (self->speed) {751self->bubbleX += self->velocity.x;752self->velocity.x += self->speed;753}754755Water_HandleBubbleMovement();756757if (self->tileCollisions != TILECOLLISION_NONE) {758if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, TO_FIXED(16), false)) {759while (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, TO_FIXED(16), 0)) self->position.y -= TO_FIXED(1);760}761else {762while (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, -TO_FIXED(16), false)763&& RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, -TO_FIXED(16), false)) {764self->position.y += TO_FIXED(1);765}766}767}768}769770RSDK.ProcessAnimation(&self->animator);771772if (self->animator.frameID >= 13 || self->animator.animationID == 7) {773if (self->isBigBubble) {774if (self->bigBubbleTimer == 16) {775RSDK.SetSpriteAnimation(Water->bigBubbleFrames, 7, &self->animator, true, 0);776self->scale.x = 0x180;777self->scale.y = 0x180;778}779780if (self->bigBubbleTimer <= 0) {781self->activePlayers = 0;782self->releasedPlayers = 0;783self->state = Water_State_BigBubble;784}785else {786self->bigBubbleTimer--;787}788}789else {790foreach_active(Player, player)791{792if (Player_CheckValidState(player) || player->state == Player_State_FlyToPlayer) {793if (player->shield != SHIELD_BUBBLE && player->underwater && !Water_GetPlayerBubble(player)794&& player->position.x >= self->position.x - TO_FIXED(16) && player->position.x <= self->position.x + TO_FIXED(16)) {795796bool32 inWater = false;797if (player->animator.animationID == ANI_FAN) {798if (player->position.y >= self->position.y - TO_FIXED(16))799inWater = (player->position.y <= self->position.y + TO_FIXED(16));800}801else {802if (player->position.y > self->position.y)803inWater = (player->position.y <= self->position.y + TO_FIXED(16));804}805806if (inWater) {807bool32 inBubble = false;808if (!(self->bubbleFlags & 1) && player->sidekick) {809self->bubbleFlags |= 1;810inBubble = true;811}812813if (!player->sidekick || inBubble) {814if (!inBubble) {815self->state = Water_State_BubbleBreathed;816self->countdownID = 0;817self->velocity.y = 0;818self->childPtr = player;819}820821if (player->state != Current_PlayerState_Right && player->state != Current_PlayerState_Left822&& player->state != Current_PlayerState_Up && player->state != Current_PlayerState_Down) {823player->velocity.x = 0;824player->velocity.y = 0;825player->groundVel = 0;826bool32 canBreathe = true;827828int32 anim = player->animator.animationID;829if (player->characterID == ID_TAILS) {830canBreathe = anim != ANI_FLY && anim != ANI_FLY_TIRED && anim != ANI_FLY_LIFT && anim != ANI_SWIM831&& anim != ANI_SWIM_LIFT;832}833else if (player->characterID == ID_KNUCKLES) {834canBreathe = anim != ANI_LEDGE_PULL_UP && anim != ANI_GLIDE && anim != ANI_GLIDE_SLIDE835&& anim != ANI_CLIMB_IDLE && anim != ANI_CLIMB_UP && anim != ANI_CLIMB_DOWN;836}837838if (canBreathe && (anim != ANI_FAN && anim != ANI_CLING)) {839RSDK.SetSpriteAnimation(player->aniFrames, ANI_BREATHE, &player->animator, false, 0);840841if (!player->sidekick)842self->playerInBubble = true;843}844845if (player->state == Player_State_FlyCarried) {846player->state = Player_State_Air;847RSDK_GET_ENTITY(SLOT_PLAYER2, Player)->flyCarryTimer = 30;848}849#if MANIA_USE_PLUS850else if (player->state == Player_State_MightyHammerDrop) {851player->state = Player_State_Air;852}853#endif854}855856player->drownTimer = 0;857if (!player->sidekick)858Music_JingleFadeOut(TRACK_DROWNING, false);859860RSDK.PlaySfx(Water->sfxBreathe, false, 255);861}862}863}864}865}866}867}868869if (self->speed != -1) {870if (!RSDK.CheckOnScreen(self, &self->updateRange))871destroyEntity(self);872}873}874875void Water_State_BubbleBreathed(void)876{877RSDK_THIS(Water);878879EntityPlayer *player = (EntityPlayer *)self->childPtr;880if (player->state == Player_State_Hurt || !Player_CheckValidState(player))881self->playerInBubble = false;882883if (self->speed) {884self->position.x += self->velocity.x;885self->velocity.x += self->speed;886}887888RSDK.ProcessAnimation(&self->animator);889890self->scale.x -= 0x18;891self->scale.y -= 0x18;892if (self->scale.x > 0) {893if (self->playerInBubble)894RSDK.SetSpriteAnimation(player->aniFrames, ANI_BREATHE, &player->animator, false, 0);895}896else {897self->scale.x = 0;898self->scale.y = 0;899900if (self->playerInBubble)901RSDK.SetSpriteAnimation(player->aniFrames, ANI_WALK, &player->animator, false, 0);902903destroyEntity(self);904}905}906907EntityWater *Water_GetPlayerBubble(EntityPlayer *player)908{909int32 playerID = RSDK.GetEntitySlot(player);910911foreach_active(Water, water)912{913if (water->state == Water_State_BigBubble && ((1 << playerID) & water->activePlayers)) {914foreach_return water;915}916}917return NULL;918}919920void Water_State_BigBubble(void)921{922RSDK_THIS(Water);923924if (self->animator.animationID == 6 && self->animator.frameID == self->animator.frameCount - 1)925destroyEntity(self);926927Water_HandleBubbleMovement();928self->countdownID = self->animator.frameID;929930RSDK.ProcessAnimation(&self->animator);931932if (self->animator.frameID == self->countdownID || self->animator.animationID == 7) {933if (self->scale.x < 0x200) {934self->scale.y += 8;935self->scale.x += 8;936}937}938else {939self->scale.x = 0x180;940self->scale.y = 0x180;941}942943if (self->animator.animationID != 6) {944foreach_active(Player, player)945{946int32 playerID = RSDK.GetEntitySlot(player);947if (!Player_CheckValidState(player) || !player->underwater) {948continue;949}950951if (!(self->activePlayers & (1 << playerID)) && !((1 << playerID) & self->releasedPlayers)) {952if (globals->gameMode == MODE_COMPETITION && self->activePlayers)953continue;954955if (Player_CheckCollisionTouch(player, self, &Water->hitboxPlayerBubble) && !Water_GetPlayerBubble(player)) {956RSDK.SetSpriteAnimation(player->aniFrames, ANI_BUBBLE, &player->animator, true, 0);957player->state = Player_State_Static;958EntityShield *shield = RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player), Shield);959if (shield)960shield->visible = false;961962player->drownTimer = 0;963if (!player->sidekick)964Music_JingleFadeOut(TRACK_DROWNING, false);965966RSDK.PlaySfx(Water->sfxDNAGrab, false, 255);967self->activePlayers |= 1 << playerID;968self->releasedPlayers |= 1 << playerID;969if (RSDK.GetEntitySlot(self) >= RESERVE_ENTITY_COUNT) {970int32 id = SLOT_BIGBUBBLE_P1;971for (; id < SLOT_BIGBUBBLE_P1 + PLAYER_COUNT; ++id) {972if (RSDK_GET_ENTITY_GEN(id)->classID == TYPE_BLANK)973break;974}975976if (id >= 0 && id < SLOT_BIGBUBBLE_P1 + PLAYER_COUNT) {977RSDK.AddDrawListRef(self->drawGroup, id);978RSDK.CopyEntity(RSDK_GET_ENTITY(id, Water), self, true);979foreach_return;980}981}982}983}984985if ((1 << playerID) & self->activePlayers) {986if (self->timer > 0) {987RSDK.SetSpriteAnimation(player->aniFrames, ANI_BUBBLE, &player->animator, false, 0);988player->state = Player_State_Static;989}990991player->drownTimer = 0;992player->position.x = self->position.x;993player->position.y = self->position.y;994player->velocity.x = self->velocity.x;995player->velocity.y = self->velocity.y;996if (player->sidekick) {997if (player->left)998player->direction = FLIP_X;999else if (player->right)1000player->direction = FLIP_NONE;10011002if (player->jumpPress) {1003RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, true, 0);1004player->state = Player_State_Air;1005self->activePlayers &= ~(1 << playerID);1006self->releasedPlayers |= 1 << playerID;1007}1008}1009else {1010self->drawGroup = player->drawGroup;1011self->collisionPlane = player->collisionPlane;1012if (player->state != Player_State_Static && player->animator.animationID != ANI_BUBBLE) {1013Water_PopBigBubble(self, false);1014foreach_return;1015}10161017int32 xVel = 0;1018if (player->left) {1019player->direction = FLIP_X;1020xVel = -0x11000;1021}1022else if (player->right) {1023player->direction = FLIP_NONE;1024xVel = 0x11000;1025}10261027int32 yVel = -0x8800;1028if (player->down)1029yVel = -0x4400;1030else if (player->up)1031yVel = -0x11000;10321033if (xVel - self->velocity.x)1034self->velocity.x += (2 * ((xVel - self->velocity.x) > 0) - 1) << 11;10351036if (yVel - self->velocity.y)1037self->velocity.y += (2 * ((yVel - self->velocity.y) > 0) - 1) << 11;10381039if (player->jumpPress) {1040Water_PopBigBubble(self, true);1041foreach_return;1042}1043}1044}10451046if ((1 << playerID) & self->releasedPlayers) {1047if (!Player_CheckCollisionTouch(player, self, &Water->hitboxPlayerBubble))1048self->releasedPlayers &= ~(1 << playerID);1049}1050}10511052if (self->timer > 0)1053self->timer--;10541055Hitbox hitboxSpike;1056hitboxSpike.left = -20;1057hitboxSpike.top = -20;1058hitboxSpike.right = 20;1059hitboxSpike.bottom = 20;10601061foreach_active(Spikes, spikes)1062{1063if (RSDK.CheckObjectCollisionTouchBox(self, &hitboxSpike, spikes, &spikes->hitbox)) {1064Water_PopBigBubble(self, false);1065foreach_return;1066}1067}10681069if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, -TO_FIXED(24), true)1070|| RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -TO_FIXED(24), 0, true)1071|| RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, TO_FIXED(24), 0, true)) {1072Water_PopBigBubble(self, false);1073}1074}1075}10761077void Water_State_BtnBigBubble(void)1078{1079RSDK_THIS(Water);10801081if (self->taggedButton) {1082if (self->timer <= 0) {1083Hitbox hitbox;1084hitbox.left = -32;1085hitbox.top = -32;1086hitbox.right = 32;1087hitbox.bottom = 32;10881089if (self->taggedButton->currentlyActive) {1090foreach_active(Player, player)1091{1092if (!player->sidekick && Player_CheckCollisionTouch(player, self, &hitbox)) {1093if (player->animator.animationID != ANI_BUBBLE) {1094EntityWater *bigBubble = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y);1095RSDK.SetSpriteAnimation(Water->bigBubbleFrames, 7, &bigBubble->animator, true, 0);10961097bigBubble->state = Water_State_BigBubble;1098bigBubble->isBigBubble = true;1099bigBubble->bigBubbleTimer = 0;1100bigBubble->velocity.y = -0x8800;1101bigBubble->childPtr = 0;1102bigBubble->bubbleX = bigBubble->position.x;1103bigBubble->timer = 0x1E;1104bigBubble->scale.x = 0x180;1105bigBubble->scale.y = 0x180;1106self->timer = 60;1107}1108}1109}1110}1111}1112else {1113self->timer--;1114}1115}1116}11171118void Water_State_Bubbler(void)1119{1120RSDK_THIS(Water);11211122self->visible = false;1123if (self->position.y > Water->waterLevel)1124self->visible = true;11251126foreach_active(Water, pool)1127{1128if (pool->type == WATER_POOL && RSDK.CheckObjectCollisionTouchBox(pool, &pool->hitbox, self, &Water->hitboxPoint))1129self->visible = true;1130}11311132if (self->visible && RSDK.CheckOnScreen(self, &self->updateRange)) {1133if (--self->countdownID < 0) {1134if (!self->bubbleFlags) {1135self->bubbleFlags = 1;1136int8 rand = RSDK.Rand(0, 0x10000);1137self->bubbleType1 = rand % 6;1138self->bubbleType2 = rand & 12;11391140if (!self->dudsRemaining--) {1141self->bubbleFlags |= 2;1142self->dudsRemaining = self->numDuds;1143}1144}11451146self->countdownID = RSDK.Rand(0, 32);11471148EntityWater *bubble = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y - 0x20000);1149int32 bubbleSize = Water->bubbleSizes[self->bubbleType1 + self->bubbleType2];1150bubble->animator.loopIndex = bubbleSize;1151bubble->animator.frameCount = bubbleSize + 1;1152if (self->type == WATER_BIG_BUBBLER) {1153bubble->isBigBubble = true;1154bubble->bigBubbleTimer = 16;1155}1156bubble->position.x += RSDK.Rand(-8, 9) << 16;1157bubble->velocity.y = -0x8800;1158bubble->angle = 2 * RSDK.Rand(0, 256);11591160bubble->childPtr = NULL;1161bubble->bubbleX = bubble->position.x;1162if (self->bubbleFlags & 2 && (!RSDK.Rand(0, 4) || !self->bubbleType1) && !(self->bubbleFlags & 4)) {1163RSDK.SetSpriteAnimation(Water->aniFrames, 3, &bubble->animator, false, 0);1164bubble->isPermanent = true;1165self->bubbleFlags |= 4;1166}11671168if (--self->bubbleType1 < 0) {1169self->bubbleFlags = 0;1170self->countdownID += RSDK.Rand(0, 128) + 128;1171}1172}1173}11741175RSDK.ProcessAnimation(&self->animator);1176}11771178void Water_State_Countdown(void)1179{1180RSDK_THIS(Water);11811182EntityPlayer *player = (EntityPlayer *)self->childPtr;11831184if (player->animator.animationID == ANI_FAN) {1185self->bubbleX += player->velocity.x;1186self->position.y += player->velocity.y;1187}11881189RSDK.ProcessAnimation(&self->animator);11901191bool32 isActive = false;1192if (self->position.y >= Water->waterLevel) {1193isActive = true;1194}1195else {1196foreach_active(Water, pool)1197{1198if (pool->type == WATER_POOL && RSDK.CheckObjectCollisionTouchBox(pool, &pool->hitbox, self, &Water->hitboxPoint)) {1199isActive = true;1200}1201}1202}12031204if ((self->animator.animationID != 7 || self->animator.frameID != self->animator.frameCount - 1) && isActive) {1205self->position.y += self->velocity.y;1206self->position.x = (RSDK.Sin512(self->angle) << 9) + self->bubbleX;1207self->angle = (self->angle + 4) & 0x1FF;1208}1209else {1210RSDK.SetSpriteAnimation(Water->aniFrames, self->countdownID + 8, &self->animator, true, 0);12111212if (player->camera) {1213self->size.x = (self->position.x & 0xFFFF0000) - (player->camera->position.x & 0xFFFF0000);1214self->size.y = (self->position.y & 0xFFFF0000) - (player->camera->position.y & 0xFFFF0000);1215}1216else {1217self->size.x = (self->position.x & 0xFFFF0000) - (player->position.x & 0xFFFF0000);1218self->size.y = (self->position.y & 0xFFFF0000) - (player->position.y & 0xFFFF0000);1219}12201221self->state = Water_State_CountdownFollow;1222}1223}12241225void Water_State_CountdownFollow(void)1226{1227RSDK_THIS(Water);12281229RSDK.ProcessAnimation(&self->animator);12301231if (self->angle >= 0x280) {1232self->scale.x -= 8;1233self->scale.y -= 8;1234if (self->scale.x <= 0)1235destroyEntity(self);1236}1237else {1238self->scale.x = (RSDK.Sin256(self->angle) >> 1) + 0x200;1239self->scale.y = (RSDK.Sin256(self->angle + 0x80) >> 1) + 0x200;12401241self->angle += 6;1242}1243}12441245void Water_State_HeightTrigger(void)1246{1247RSDK_THIS(Water);12481249bool32 activated = false;1250if (self->taggedButton) {1251EntityButton *button = self->taggedButton;1252if (button->currentlyActive) {1253Water->moveWaterLevel = true;1254activated = true;1255}1256}12571258int32 px = self->position.x + 1;1259if (!activated) {1260foreach_active(Player, player)1261{1262if (!RSDK.GetEntitySlot(player)) {1263if (abs(self->position.x - player->position.x) < self->updateRange.x) {1264if (abs(self->position.y - player->position.y) < self->updateRange.y) {1265activated = true;1266px = player->position.x;1267}1268}1269}1270}1271}12721273if (activated) {1274if (self->taggedButton)1275Water->targetWaterLevel = self->position.y;1276else if (px <= self->position.x)1277Water->targetWaterLevel = self->height.x;1278else1279Water->targetWaterLevel = self->height.y;12801281Water->waterMoveSpeed = self->speed << 15;1282if (self->destroyOnTrigger)1283destroyEntity(self);1284}1285}12861287void Water_Draw_Water(void)1288{1289RSDK_THIS(Water);1290RSDKScreenInfo *screen = &ScreenInfo[SceneInfo->currentScreenID];12911292Vector2 drawPos;1293drawPos.x = ((screen->position.x & 0xFFFFFFC0) + 32) << 16;1294drawPos.y = Water->waterLevel;1295for (int32 i = (screen->size.x >> 6) + 2; i > 0; --i) {1296RSDK.DrawSprite(&self->animator, &drawPos, false);1297drawPos.x += TO_FIXED(64);1298}12991300self->drawFX |= FX_FLIP;1301for (int32 i = 0; i < PLAYER_COUNT; ++i) {1302if (Water->wakePosX[i] > 0) {1303self->direction = Water->wakeDir[i];1304drawPos.x = Water->wakePosX[i];1305RSDK.DrawSprite(&Water->wakeAnimator, &drawPos, false);1306}1307}13081309self->drawFX &= ~FX_FLIP;1310}13111312void Water_Draw_Pool(void)1313{1314RSDK_THIS(Water);13151316RSDK.DrawRect(self->position.x - (self->size.x >> 1), self->position.y - (self->size.y >> 1), self->size.x, self->size.y,1317(self->r << 16) | (self->g << 8) | self->b, 0x100, INK_SUB, false);1318}13191320void Water_Draw_Splash(void)1321{1322RSDK_THIS(Water);13231324RSDK.DrawSprite(&self->animator, NULL, false);1325}13261327void Water_Draw_Countdown(void)1328{1329Vector2 drawPos;13301331RSDK_THIS(Water);1332if (self->state == Water_State_Countdown) {1333drawPos.x = self->position.x;1334drawPos.y = self->position.y;1335}1336else {1337EntityPlayer *player = (EntityPlayer *)self->childPtr;1338if (player->camera) {1339drawPos.x = player->camera->position.x + self->size.x;1340drawPos.y = player->camera->position.y + self->size.y;1341}1342else {1343drawPos.x = player->position.x + self->size.x;1344drawPos.y = player->position.y + self->size.y;1345}1346}13471348RSDK.DrawSprite(&self->animator, &drawPos, false);1349}13501351void Water_Draw_Bubbler(void)1352{1353RSDK_THIS(Water);13541355RSDK.DrawSprite(&self->animator, NULL, false);1356}13571358void Water_Draw_Bubble(void)1359{1360RSDK_THIS(Water);13611362RSDK.DrawSprite(&self->animator, NULL, false);1363}13641365#if GAME_INCLUDE_EDITOR1366void Water_EditorDraw(void)1367{1368RSDK_THIS(Water);13691370self->updateRange.x = TO_FIXED(128);1371self->updateRange.y = TO_FIXED(128);13721373self->inkEffect = INK_NONE;1374switch (self->type) {1375case WATER_WATERLEVEL:1376self->inkEffect = INK_ADD;1377self->alpha = RSDK.CheckSceneFolder("AIZ") ? 0x60 : 0xE0;1378RSDK.SetSpriteAnimation(Water->aniFrames, 0, &self->animator, true, 0);13791380Water->waterLevel = self->position.y;13811382Vector2 drawPos;1383drawPos.x = (((self->position.x >> 16) & 0xFFFFFFC0) + 32) << 16;1384drawPos.y = self->position.y;1385for (int32 i = (WIDE_SCR_XSIZE >> 6) + 2; i > 0; --i) {1386RSDK.DrawSprite(&self->animator, &drawPos, false);1387drawPos.x += TO_FIXED(64);1388}1389break;13901391case WATER_POOL:1392self->drawFX = FX_FLIP;1393self->updateRange.x = self->size.x >> 1;1394self->updateRange.y = self->size.y >> 1;13951396self->inkEffect = INK_BLEND;1397RSDK.DrawRect(self->position.x - (self->size.x >> 1), self->position.y - (self->size.y >> 1), self->size.x, self->size.y,1398(self->r << 16) | (self->g << 8) | self->b, 0x20, INK_ALPHA, false);1399if (showGizmos()) {1400self->inkEffect = INK_NONE;1401DrawHelpers_DrawRectOutline(self->position.x, self->position.y, self->size.x, self->size.y, 0xFFFF00);1402}1403break;14041405case WATER_BUBBLER:1406case WATER_BIG_BUBBLER:1407self->inkEffect = INK_ADD;1408self->alpha = 0x100;1409RSDK.SetSpriteAnimation(Water->aniFrames, 2, &self->animator, true, 0);14101411Water_Draw_Bubbler();1412break;14131414case WATER_HEIGHT_TRIGGER:1415self->active = ACTIVE_BOUNDS;1416self->visible = false;14171418RSDK.SetSpriteAnimation(Water->aniFrames, 0, &self->animator, true, 0);1419RSDK.DrawSprite(&self->animator, NULL, false);14201421if (showGizmos()) {1422Water_SetupTagLink();14231424RSDK_DRAWING_OVERLAY(true);1425if (self->taggedButton) {1426DrawHelpers_DrawArrow(self->taggedButton->position.x, self->taggedButton->position.y, self->position.x, self->position.y,14270xFFFF00, INK_NONE, 0xFF);1428}1429RSDK_DRAWING_OVERLAY(false);1430}1431break;14321433case WATER_BTN_BIG_BUBBLE:1434self->drawFX = FX_SCALE;1435self->inkEffect = INK_ADD;1436self->alpha = 0x100;1437self->scale.x = 0x200;1438self->scale.y = 0x200;1439RSDK.SetSpriteAnimation(Water->bigBubbleFrames, 7, &self->animator, true, 0);14401441Water_Draw_Bubble();14421443if (showGizmos()) {1444Water_SetupTagLink();14451446RSDK_DRAWING_OVERLAY(true);1447if (self->taggedButton) {1448DrawHelpers_DrawArrow(self->taggedButton->position.x, self->taggedButton->position.y, self->position.x, self->position.y,14490xFFFF00, INK_NONE, 0xFF);1450}1451RSDK_DRAWING_OVERLAY(false);1452}1453break;14541455default: break;1456}1457}14581459void Water_EditorLoad(void)1460{1461Water->aniFrames = RSDK.LoadSpriteAnimation("Global/Water.bin", SCOPE_STAGE);1462if (RSDK.CheckSceneFolder("HCZ")) {1463Water->wakeFrames = RSDK.LoadSpriteAnimation("HCZ/Wake.bin", SCOPE_STAGE);1464Water->bigBubbleFrames = RSDK.LoadSpriteAnimation("HCZ/BigBubble.bin", SCOPE_STAGE);1465RSDK.SetSpriteAnimation(Water->wakeFrames, 0, &Water->wakeAnimator, true, 0);1466}14671468RSDK_ACTIVE_VAR(Water, type);1469RSDK_ENUM_VAR("Water Level", WATER_WATERLEVEL);1470RSDK_ENUM_VAR("Pool", WATER_POOL);1471RSDK_ENUM_VAR("Bubbler", WATER_BUBBLER);1472RSDK_ENUM_VAR("Height Trigger", WATER_HEIGHT_TRIGGER);1473RSDK_ENUM_VAR("Big Bubbler", WATER_BIG_BUBBLER);1474RSDK_ENUM_VAR("Btn Big Bubble", WATER_BTN_BIG_BUBBLE);14751476RSDK_ACTIVE_VAR(Water, priority);1477RSDK_ENUM_VAR("Lowest", WATER_PRIORITY_LOWEST);1478RSDK_ENUM_VAR("Low", WATER_PRIORITY_LOW);1479RSDK_ENUM_VAR("High", WATER_PRIORITY_HIGH);1480RSDK_ENUM_VAR("Highest", WATER_PRIORITY_HIGHEST);1481}1482#endif14831484void Water_Serialize(void)1485{1486RSDK_EDITABLE_VAR(Water, VAR_ENUM, type);1487RSDK_EDITABLE_VAR(Water, VAR_UINT8, numDuds);1488RSDK_EDITABLE_VAR(Water, VAR_VECTOR2, size);1489RSDK_EDITABLE_VAR(Water, VAR_VECTOR2, height);1490RSDK_EDITABLE_VAR(Water, VAR_ENUM, speed);1491RSDK_EDITABLE_VAR(Water, VAR_ENUM, buttonTag);1492RSDK_EDITABLE_VAR(Water, VAR_UINT8, r);1493RSDK_EDITABLE_VAR(Water, VAR_UINT8, g);1494RSDK_EDITABLE_VAR(Water, VAR_UINT8, b);1495RSDK_EDITABLE_VAR(Water, VAR_UINT8, priority);1496RSDK_EDITABLE_VAR(Water, VAR_BOOL, destroyOnTrigger);1497}149814991500