Path: blob/master/SonicMania/Objects/Puyo/PuyoMatch.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: PuyoMatch Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectPuyoMatch *PuyoMatch;1011void PuyoMatch_Update(void)12{13RSDK_THIS(PuyoMatch);1415StateMachine_Run(self->state);16}1718void PuyoMatch_LateUpdate(void) {}1920void PuyoMatch_StaticUpdate(void)21{22foreach_active(PuyoMatch, match) { RSDK.AddDrawListRef(Zone->hudDrawGroup, RSDK.GetEntitySlot(match)); }23}2425void PuyoMatch_Draw(void)26{27RSDK_THIS(PuyoMatch);2829Vector2 drawPos;30if (SceneInfo->currentDrawGroup != Zone->hudDrawGroup) {31drawPos.x = self->position.x;32drawPos.y = self->position.y - 0x80000;33RSDK.DrawSprite(&self->beanLAnimator, &drawPos, false);3435drawPos.y += 0x100000;36RSDK.DrawSprite(&self->beanRAnimator, &drawPos, false);37}3839else if (self->junkBeanCount > 0)40PuyoMatch_DrawJunkBeanPreviews();41}4243void PuyoMatch_Create(void *data)44{45RSDK_THIS(PuyoMatch);4647if (!SceneInfo->inEditor) {48self->active = ACTIVE_BOUNDS;49self->visible = true;50self->drawGroup = Zone->objectDrawGroup[0];51self->updateRange.x = 0x800000;52self->updateRange.y = 0x800000;5354self->stateInput = PuyoBean_Input_Player;55self->comboBonusTable = PuyoMatch->comboBonus;56self->beanDropPos = RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, PlatformNode)->position;57self->timer = 60;5859RSDK.SetSpriteAnimation(PuyoMatch->aniFrames, 1, &self->lightAnimator, true, 0);60}61}6263void PuyoMatch_StageLoad(void)64{65PuyoMatch->aniFrames = RSDK.LoadSpriteAnimation("Puyo/Combos.bin", SCOPE_STAGE);6667PuyoMatch->comboPower = 120; // the lower this is, the more junk will drop from combos, likewise the higher it is, the less junk will drop68}6970void PuyoMatch_AddPuyoCombo(int32 playerID, int32 score)71{72foreach_active(PuyoMatch, match)73{74if (match->playerID == playerID) {75match->junkDropCount += (score << 8) / PuyoMatch->comboPower;76match->junkBeanCount = match->junkDropCount >> 8;77}78}79}8081void PuyoMatch_SetupNextBeans(EntityPuyoMatch *match)82{83int32 left = 6 * RSDK.RandSeeded(0, 5, &match->matchKey);84int32 right = 6 * RSDK.RandSeeded(0, 5, &match->matchKey);8586RSDK.SetSpriteAnimation(PuyoBean->aniFrames, left, &match->beanLAnimator, true, 0);87RSDK.SetSpriteAnimation(PuyoBean->aniFrames, right, &match->beanRAnimator, true, 0);88}8990void PuyoMatch_DropNextBeans(void)91{92RSDK_THIS(PuyoMatch);9394if (!self->beanLAnimator.frameDuration)95PuyoMatch_SetupNextBeans(self);9697EntityPuyoBean *partnerBean = CREATE_ENTITY(PuyoBean, INT_TO_VOID(self->beanLAnimator.animationID), self->beanDropPos.x, self->beanDropPos.y);98EntityPuyoBean *bean = CREATE_ENTITY(PuyoBean, INT_TO_VOID(self->beanRAnimator.animationID), self->beanDropPos.x, self->beanDropPos.y);99100PuyoMatch_SetupNextBeans(self);101102partnerBean->playerID = self->playerID;103partnerBean->partner = bean;104partnerBean->beanAnimator.speed = 0;105partnerBean->controllerID = self->playerID + 1;106partnerBean->state = PuyoBean_State_PartnerControlled;107108bean->playerID = self->playerID;109bean->partner = partnerBean;110bean->controllerID = self->playerID + 1;111bean->state = PuyoBean_State_Controlled;112bean->selectedLevel = self->selectedLevel;113bean->stateInput = self->stateInput;114bean->position.y += 0x100000;115116self->beanPtr = bean;117PuyoBean->comboChainCount[self->playerID] = 0;118}119120void PuyoMatch_DropJunkBeans(void)121{122RSDK_THIS(PuyoMatch);123124int32 beanColumnCount[PUYO_PLAYFIELD_W];125int32 count = 0;126127for (int32 x = 0; x < PUYO_PLAYFIELD_W; ++x) {128beanColumnCount[x] = 0;129130for (int32 y = 0; y < PUYO_PLAYFIELD_H; ++y) {131EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, x, y);132if (!bean) {133++beanColumnCount[x];134++count;135}136}137}138139if (count > 30)140count = 30;141142if (count > self->junkBeanCount)143count = self->junkBeanCount;144145self->junkBeanCount -= count;146self->junkDropCount -= count << 8;147148int32 id = 6 * RSDK.Rand(0, 4);149int32 spawnY = self->beanDropPos.y + 0x100000;150151while (count > 0) {152int32 column = PuyoMatch->beanDropColumnIDs[id];153if (beanColumnCount[column] > 0) {154EntityPuyoBean *junkBean = CREATE_ENTITY(PuyoBean, INT_TO_VOID(30), self->beanDropPos.x - 0x200000 + (column << 20), spawnY);155junkBean->playerID = self->playerID;156junkBean->origin.x = self->beanDropPos.x - 0x280000;157junkBean->origin.y = self->beanDropPos.y - 0x80000;158self->beanPtr = junkBean;159junkBean->state = PuyoBean_State_Falling;160--count;161--beanColumnCount[column];162}163164id = (id + 1) % -24;165if (!(id % 6))166spawnY -= 0x100000;167}168}169170void PuyoMatch_DrawJunkBeanPreviews(void)171{172RSDK_THIS(PuyoMatch);173174Vector2 drawPos;175drawPos.x = self->beanDropPos.x - 0x280000;176drawPos.y = self->beanDropPos.y + 0x140000;177178int32 count = self->junkBeanCount;179180for (int32 i = 0; i < count / 30; ++i) {181RSDK.SetSpriteAnimation(PuyoBean->aniFrames, 35, &self->junkPreviewAnimator, true, 0);182RSDK.DrawSprite(&self->junkPreviewAnimator, &drawPos, false);183drawPos.x += 0x120000;184}185count %= 30;186187for (int32 i = 0; i < count / 6; ++i) {188RSDK.SetSpriteAnimation(PuyoBean->aniFrames, 34, &self->junkPreviewAnimator, true, 0);189RSDK.DrawSprite(&self->junkPreviewAnimator, &drawPos, false);190drawPos.x += 0x100000;191}192count %= 6;193194for (int32 i = 0; i < count; ++i) {195RSDK.SetSpriteAnimation(PuyoBean->aniFrames, 33, &self->junkPreviewAnimator, true, 0);196RSDK.DrawSprite(&self->junkPreviewAnimator, &drawPos, false);197drawPos.x += 0xE0000;198}199}200201void PuyoMatch_State_HandleMatch(void)202{203RSDK_THIS(PuyoMatch);204205PuyoBean->disableBeanLink[self->playerID] = false;206207foreach_active(PuyoBean, bean)208{209if (bean->playerID == self->playerID) {210if (bean->state != PuyoBean_State_BeanIdle && bean->state != PuyoBean_State_JunkIdle)211self->timer = 30;212213if (bean->state == PuyoBean_State_BeanPop) {214PuyoBean->disableBeanLink[bean->playerID] = true;215self->state = PuyoMatch_State_HandleCombos;216foreach_break;217}218219if (bean->state == PuyoBean_State_Falling || bean->state == PuyoBean_State_BeanLand)220PuyoBean->disableBeanLink[bean->playerID] = true;221}222}223224if (self->timer) {225if (!--self->timer) {226// if the "dispenser" slot is filled, you lose!227if (PuyoBean_GetPuyoBean(self->playerID, 2, 2)) {228self->state = PuyoMatch_State_Lose;229230if (self->playerID) {231foreach_active(CollapsingPlatform, platform)232{233if (platform->position.x > self->position.x) {234platform->stoodPos.x = self->position.x;235platform->delay = 1;236}237}238}239else {240foreach_active(CollapsingPlatform, platform)241{242if (platform->position.x < self->position.x) {243platform->stoodPos.x = self->position.x;244platform->delay = 1;245}246}247}248}249else {250self->comboCount = 0;251bool32 hasCombo = false;252253foreach_active(PuyoMatch, match)254{255if (match->comboCount)256hasCombo = true;257}258259if (!self->junkBeanCount || hasCombo)260PuyoMatch_DropNextBeans();261else262PuyoMatch_DropJunkBeans();263}264}265}266}267268void PuyoMatch_State_HandleCombos(void)269{270RSDK_THIS(PuyoMatch);271272self->comboBeanCount = 0;273if (++self->comboCount == 3 && self->stateInput == PuyoBean_Input_Player)274API_UnlockAchievement(&achievementList[ACH_CPZ]);275276uint8 comboColors = 0;277EntityPuyoBean *targetBean = NULL;278foreach_active(PuyoBean, bean)279{280if (bean->playerID == self->playerID) {281if (bean->state == PuyoBean_State_BeginBeanPop || bean->state == PuyoBean_State_BeanPop) {282if (!self->comboBeanCount++)283targetBean = bean;284comboColors |= 1 << (bean->type / 6);285}286}287}288289// Bonus for getting lots of beans in one go290int32 slot = self->comboBeanCount - 4;291if (slot >= 7)292slot = 7 - slot;293self->beanBonus = PuyoMatch->beanBonusTable[slot];294295// Bonus for getting multiple combos in one go296self->concurrentBonus = 0;297for (int32 b = 0; b < 5; ++b) {298if (GET_BIT(comboColors, b))299++self->concurrentBonus;300}301302self->concurrentBonus = PuyoMatch->concurrentBonusTable[self->concurrentBonus];303304// Bonus for chaining multiple combos together305int32 chainBonus = self->comboBonusTable[MIN(PuyoBean->comboChainCount[self->playerID], 23)];306307int32 comboBonus = CLAMP(self->beanBonus + self->concurrentBonus + chainBonus, 1, 999);308self->comboScore = 10 * comboBonus * self->comboBeanCount;309310if (PuyoBean->comboChainCount[self->playerID] < 23)311PuyoBean->comboChainCount[self->playerID]++;312313slot = -2;314if (!self->playerID)315slot = 2;316317EntityPuyoMatch *match = RSDK_GET_ENTITY(SceneInfo->entitySlot + slot, PuyoMatch);318319EntityPuyoAttack *attack = CREATE_ENTITY(PuyoAttack, INT_TO_VOID(self->playerID ^ 1), targetBean->position.x, targetBean->position.y);320attack->targetPos.x = match->beanDropPos.x - 0x100000;321attack->targetPos.y = match->beanDropPos.y + 0xC0000;322attack->score = self->comboScore;323self->score += self->comboScore;324self->state = PuyoMatch_State_HandleComboEnd;325}326327void PuyoMatch_State_HandleComboEnd(void)328{329RSDK_THIS(PuyoMatch);330331bool32 continueCombos = false;332foreach_active(PuyoBean, bean)333{334if (bean->playerID == self->playerID) {335if (bean->state == PuyoBean_State_BeginBeanPop || bean->state == PuyoBean_State_BeanPop) {336continueCombos = true;337}338}339}340341if (!continueCombos)342self->state = PuyoMatch_State_HandleMatch;343}344345void PuyoMatch_State_Lose(void)346{347RSDK_THIS(PuyoMatch);348349if (++self->timer == 8) {350int32 delays[] = { 12, 8, 0, 4, 6, 16 };351352for (int32 x = 0; x < PUYO_PLAYFIELD_W; ++x) {353for (int32 y = 0; y < PUYO_PLAYFIELD_H; ++y) {354EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, x, y);355if (bean) {356bean->state = PuyoBean_State_MatchLoseFall;357bean->timer = delays[x];358}359}360}361362RSDK.SetSpriteAnimation(-1, 0, &self->beanLAnimator, true, 0);363RSDK.SetSpriteAnimation(-1, 0, &self->beanRAnimator, true, 0);364365StateMachine_Run(self->matchWinCB);366367foreach_active(PuyoMatch, match)368{369if (match->playerID != self->playerID) {370RSDK.SetSpriteAnimation(-1, 0, &match->beanLAnimator, true, 0);371RSDK.SetSpriteAnimation(-1, 0, &match->beanRAnimator, true, 0);372373StateMachine_Run(match->matchLoseCB);374375if (RSDK.CheckSceneFolder("CPZ"))376match->state = StateMachine_None;377}378}379380self->state = StateMachine_None;381}382}383384#if GAME_INCLUDE_EDITOR385void PuyoMatch_EditorDraw(void)386{387RSDK_THIS(PuyoMatch);388389RSDK.SetSpriteAnimation(PuyoMatch->aniFrames, self->playerID ? 18 : 6, &self->unusedAnimator, false, 0);390RSDK.DrawSprite(&self->unusedAnimator, NULL, false);391}392393void PuyoMatch_EditorLoad(void)394{395PuyoMatch->aniFrames = RSDK.LoadSpriteAnimation("Puyo/PuyoBeans.bin", SCOPE_STAGE);396397RSDK_ACTIVE_VAR(PuyoMatch, playerID);398RSDK_ENUM_VAR("Player 1", PUYOGAME_PLAYER1);399RSDK_ENUM_VAR("Player 2", PUYOGAME_PLAYER2);400}401#endif402403void PuyoMatch_Serialize(void) { RSDK_EDITABLE_VAR(PuyoMatch, VAR_ENUM, playerID); }404405406