Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Puyo/PuyoMatch.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: PuyoMatch Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectPuyoMatch *PuyoMatch;
11
12
void PuyoMatch_Update(void)
13
{
14
RSDK_THIS(PuyoMatch);
15
16
StateMachine_Run(self->state);
17
}
18
19
void PuyoMatch_LateUpdate(void) {}
20
21
void PuyoMatch_StaticUpdate(void)
22
{
23
foreach_active(PuyoMatch, match) { RSDK.AddDrawListRef(Zone->hudDrawGroup, RSDK.GetEntitySlot(match)); }
24
}
25
26
void PuyoMatch_Draw(void)
27
{
28
RSDK_THIS(PuyoMatch);
29
30
Vector2 drawPos;
31
if (SceneInfo->currentDrawGroup != Zone->hudDrawGroup) {
32
drawPos.x = self->position.x;
33
drawPos.y = self->position.y - 0x80000;
34
RSDK.DrawSprite(&self->beanLAnimator, &drawPos, false);
35
36
drawPos.y += 0x100000;
37
RSDK.DrawSprite(&self->beanRAnimator, &drawPos, false);
38
}
39
40
else if (self->junkBeanCount > 0)
41
PuyoMatch_DrawJunkBeanPreviews();
42
}
43
44
void PuyoMatch_Create(void *data)
45
{
46
RSDK_THIS(PuyoMatch);
47
48
if (!SceneInfo->inEditor) {
49
self->active = ACTIVE_BOUNDS;
50
self->visible = true;
51
self->drawGroup = Zone->objectDrawGroup[0];
52
self->updateRange.x = 0x800000;
53
self->updateRange.y = 0x800000;
54
55
self->stateInput = PuyoBean_Input_Player;
56
self->comboBonusTable = PuyoMatch->comboBonus;
57
self->beanDropPos = RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, PlatformNode)->position;
58
self->timer = 60;
59
60
RSDK.SetSpriteAnimation(PuyoMatch->aniFrames, 1, &self->lightAnimator, true, 0);
61
}
62
}
63
64
void PuyoMatch_StageLoad(void)
65
{
66
PuyoMatch->aniFrames = RSDK.LoadSpriteAnimation("Puyo/Combos.bin", SCOPE_STAGE);
67
68
PuyoMatch->comboPower = 120; // the lower this is, the more junk will drop from combos, likewise the higher it is, the less junk will drop
69
}
70
71
void PuyoMatch_AddPuyoCombo(int32 playerID, int32 score)
72
{
73
foreach_active(PuyoMatch, match)
74
{
75
if (match->playerID == playerID) {
76
match->junkDropCount += (score << 8) / PuyoMatch->comboPower;
77
match->junkBeanCount = match->junkDropCount >> 8;
78
}
79
}
80
}
81
82
void PuyoMatch_SetupNextBeans(EntityPuyoMatch *match)
83
{
84
int32 left = 6 * RSDK.RandSeeded(0, 5, &match->matchKey);
85
int32 right = 6 * RSDK.RandSeeded(0, 5, &match->matchKey);
86
87
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, left, &match->beanLAnimator, true, 0);
88
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, right, &match->beanRAnimator, true, 0);
89
}
90
91
void PuyoMatch_DropNextBeans(void)
92
{
93
RSDK_THIS(PuyoMatch);
94
95
if (!self->beanLAnimator.frameDuration)
96
PuyoMatch_SetupNextBeans(self);
97
98
EntityPuyoBean *partnerBean = CREATE_ENTITY(PuyoBean, INT_TO_VOID(self->beanLAnimator.animationID), self->beanDropPos.x, self->beanDropPos.y);
99
EntityPuyoBean *bean = CREATE_ENTITY(PuyoBean, INT_TO_VOID(self->beanRAnimator.animationID), self->beanDropPos.x, self->beanDropPos.y);
100
101
PuyoMatch_SetupNextBeans(self);
102
103
partnerBean->playerID = self->playerID;
104
partnerBean->partner = bean;
105
partnerBean->beanAnimator.speed = 0;
106
partnerBean->controllerID = self->playerID + 1;
107
partnerBean->state = PuyoBean_State_PartnerControlled;
108
109
bean->playerID = self->playerID;
110
bean->partner = partnerBean;
111
bean->controllerID = self->playerID + 1;
112
bean->state = PuyoBean_State_Controlled;
113
bean->selectedLevel = self->selectedLevel;
114
bean->stateInput = self->stateInput;
115
bean->position.y += 0x100000;
116
117
self->beanPtr = bean;
118
PuyoBean->comboChainCount[self->playerID] = 0;
119
}
120
121
void PuyoMatch_DropJunkBeans(void)
122
{
123
RSDK_THIS(PuyoMatch);
124
125
int32 beanColumnCount[PUYO_PLAYFIELD_W];
126
int32 count = 0;
127
128
for (int32 x = 0; x < PUYO_PLAYFIELD_W; ++x) {
129
beanColumnCount[x] = 0;
130
131
for (int32 y = 0; y < PUYO_PLAYFIELD_H; ++y) {
132
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, x, y);
133
if (!bean) {
134
++beanColumnCount[x];
135
++count;
136
}
137
}
138
}
139
140
if (count > 30)
141
count = 30;
142
143
if (count > self->junkBeanCount)
144
count = self->junkBeanCount;
145
146
self->junkBeanCount -= count;
147
self->junkDropCount -= count << 8;
148
149
int32 id = 6 * RSDK.Rand(0, 4);
150
int32 spawnY = self->beanDropPos.y + 0x100000;
151
152
while (count > 0) {
153
int32 column = PuyoMatch->beanDropColumnIDs[id];
154
if (beanColumnCount[column] > 0) {
155
EntityPuyoBean *junkBean = CREATE_ENTITY(PuyoBean, INT_TO_VOID(30), self->beanDropPos.x - 0x200000 + (column << 20), spawnY);
156
junkBean->playerID = self->playerID;
157
junkBean->origin.x = self->beanDropPos.x - 0x280000;
158
junkBean->origin.y = self->beanDropPos.y - 0x80000;
159
self->beanPtr = junkBean;
160
junkBean->state = PuyoBean_State_Falling;
161
--count;
162
--beanColumnCount[column];
163
}
164
165
id = (id + 1) % -24;
166
if (!(id % 6))
167
spawnY -= 0x100000;
168
}
169
}
170
171
void PuyoMatch_DrawJunkBeanPreviews(void)
172
{
173
RSDK_THIS(PuyoMatch);
174
175
Vector2 drawPos;
176
drawPos.x = self->beanDropPos.x - 0x280000;
177
drawPos.y = self->beanDropPos.y + 0x140000;
178
179
int32 count = self->junkBeanCount;
180
181
for (int32 i = 0; i < count / 30; ++i) {
182
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, 35, &self->junkPreviewAnimator, true, 0);
183
RSDK.DrawSprite(&self->junkPreviewAnimator, &drawPos, false);
184
drawPos.x += 0x120000;
185
}
186
count %= 30;
187
188
for (int32 i = 0; i < count / 6; ++i) {
189
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, 34, &self->junkPreviewAnimator, true, 0);
190
RSDK.DrawSprite(&self->junkPreviewAnimator, &drawPos, false);
191
drawPos.x += 0x100000;
192
}
193
count %= 6;
194
195
for (int32 i = 0; i < count; ++i) {
196
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, 33, &self->junkPreviewAnimator, true, 0);
197
RSDK.DrawSprite(&self->junkPreviewAnimator, &drawPos, false);
198
drawPos.x += 0xE0000;
199
}
200
}
201
202
void PuyoMatch_State_HandleMatch(void)
203
{
204
RSDK_THIS(PuyoMatch);
205
206
PuyoBean->disableBeanLink[self->playerID] = false;
207
208
foreach_active(PuyoBean, bean)
209
{
210
if (bean->playerID == self->playerID) {
211
if (bean->state != PuyoBean_State_BeanIdle && bean->state != PuyoBean_State_JunkIdle)
212
self->timer = 30;
213
214
if (bean->state == PuyoBean_State_BeanPop) {
215
PuyoBean->disableBeanLink[bean->playerID] = true;
216
self->state = PuyoMatch_State_HandleCombos;
217
foreach_break;
218
}
219
220
if (bean->state == PuyoBean_State_Falling || bean->state == PuyoBean_State_BeanLand)
221
PuyoBean->disableBeanLink[bean->playerID] = true;
222
}
223
}
224
225
if (self->timer) {
226
if (!--self->timer) {
227
// if the "dispenser" slot is filled, you lose!
228
if (PuyoBean_GetPuyoBean(self->playerID, 2, 2)) {
229
self->state = PuyoMatch_State_Lose;
230
231
if (self->playerID) {
232
foreach_active(CollapsingPlatform, platform)
233
{
234
if (platform->position.x > self->position.x) {
235
platform->stoodPos.x = self->position.x;
236
platform->delay = 1;
237
}
238
}
239
}
240
else {
241
foreach_active(CollapsingPlatform, platform)
242
{
243
if (platform->position.x < self->position.x) {
244
platform->stoodPos.x = self->position.x;
245
platform->delay = 1;
246
}
247
}
248
}
249
}
250
else {
251
self->comboCount = 0;
252
bool32 hasCombo = false;
253
254
foreach_active(PuyoMatch, match)
255
{
256
if (match->comboCount)
257
hasCombo = true;
258
}
259
260
if (!self->junkBeanCount || hasCombo)
261
PuyoMatch_DropNextBeans();
262
else
263
PuyoMatch_DropJunkBeans();
264
}
265
}
266
}
267
}
268
269
void PuyoMatch_State_HandleCombos(void)
270
{
271
RSDK_THIS(PuyoMatch);
272
273
self->comboBeanCount = 0;
274
if (++self->comboCount == 3 && self->stateInput == PuyoBean_Input_Player)
275
API_UnlockAchievement(&achievementList[ACH_CPZ]);
276
277
uint8 comboColors = 0;
278
EntityPuyoBean *targetBean = NULL;
279
foreach_active(PuyoBean, bean)
280
{
281
if (bean->playerID == self->playerID) {
282
if (bean->state == PuyoBean_State_BeginBeanPop || bean->state == PuyoBean_State_BeanPop) {
283
if (!self->comboBeanCount++)
284
targetBean = bean;
285
comboColors |= 1 << (bean->type / 6);
286
}
287
}
288
}
289
290
// Bonus for getting lots of beans in one go
291
int32 slot = self->comboBeanCount - 4;
292
if (slot >= 7)
293
slot = 7 - slot;
294
self->beanBonus = PuyoMatch->beanBonusTable[slot];
295
296
// Bonus for getting multiple combos in one go
297
self->concurrentBonus = 0;
298
for (int32 b = 0; b < 5; ++b) {
299
if (GET_BIT(comboColors, b))
300
++self->concurrentBonus;
301
}
302
303
self->concurrentBonus = PuyoMatch->concurrentBonusTable[self->concurrentBonus];
304
305
// Bonus for chaining multiple combos together
306
int32 chainBonus = self->comboBonusTable[MIN(PuyoBean->comboChainCount[self->playerID], 23)];
307
308
int32 comboBonus = CLAMP(self->beanBonus + self->concurrentBonus + chainBonus, 1, 999);
309
self->comboScore = 10 * comboBonus * self->comboBeanCount;
310
311
if (PuyoBean->comboChainCount[self->playerID] < 23)
312
PuyoBean->comboChainCount[self->playerID]++;
313
314
slot = -2;
315
if (!self->playerID)
316
slot = 2;
317
318
EntityPuyoMatch *match = RSDK_GET_ENTITY(SceneInfo->entitySlot + slot, PuyoMatch);
319
320
EntityPuyoAttack *attack = CREATE_ENTITY(PuyoAttack, INT_TO_VOID(self->playerID ^ 1), targetBean->position.x, targetBean->position.y);
321
attack->targetPos.x = match->beanDropPos.x - 0x100000;
322
attack->targetPos.y = match->beanDropPos.y + 0xC0000;
323
attack->score = self->comboScore;
324
self->score += self->comboScore;
325
self->state = PuyoMatch_State_HandleComboEnd;
326
}
327
328
void PuyoMatch_State_HandleComboEnd(void)
329
{
330
RSDK_THIS(PuyoMatch);
331
332
bool32 continueCombos = false;
333
foreach_active(PuyoBean, bean)
334
{
335
if (bean->playerID == self->playerID) {
336
if (bean->state == PuyoBean_State_BeginBeanPop || bean->state == PuyoBean_State_BeanPop) {
337
continueCombos = true;
338
}
339
}
340
}
341
342
if (!continueCombos)
343
self->state = PuyoMatch_State_HandleMatch;
344
}
345
346
void PuyoMatch_State_Lose(void)
347
{
348
RSDK_THIS(PuyoMatch);
349
350
if (++self->timer == 8) {
351
int32 delays[] = { 12, 8, 0, 4, 6, 16 };
352
353
for (int32 x = 0; x < PUYO_PLAYFIELD_W; ++x) {
354
for (int32 y = 0; y < PUYO_PLAYFIELD_H; ++y) {
355
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, x, y);
356
if (bean) {
357
bean->state = PuyoBean_State_MatchLoseFall;
358
bean->timer = delays[x];
359
}
360
}
361
}
362
363
RSDK.SetSpriteAnimation(-1, 0, &self->beanLAnimator, true, 0);
364
RSDK.SetSpriteAnimation(-1, 0, &self->beanRAnimator, true, 0);
365
366
StateMachine_Run(self->matchWinCB);
367
368
foreach_active(PuyoMatch, match)
369
{
370
if (match->playerID != self->playerID) {
371
RSDK.SetSpriteAnimation(-1, 0, &match->beanLAnimator, true, 0);
372
RSDK.SetSpriteAnimation(-1, 0, &match->beanRAnimator, true, 0);
373
374
StateMachine_Run(match->matchLoseCB);
375
376
if (RSDK.CheckSceneFolder("CPZ"))
377
match->state = StateMachine_None;
378
}
379
}
380
381
self->state = StateMachine_None;
382
}
383
}
384
385
#if GAME_INCLUDE_EDITOR
386
void PuyoMatch_EditorDraw(void)
387
{
388
RSDK_THIS(PuyoMatch);
389
390
RSDK.SetSpriteAnimation(PuyoMatch->aniFrames, self->playerID ? 18 : 6, &self->unusedAnimator, false, 0);
391
RSDK.DrawSprite(&self->unusedAnimator, NULL, false);
392
}
393
394
void PuyoMatch_EditorLoad(void)
395
{
396
PuyoMatch->aniFrames = RSDK.LoadSpriteAnimation("Puyo/PuyoBeans.bin", SCOPE_STAGE);
397
398
RSDK_ACTIVE_VAR(PuyoMatch, playerID);
399
RSDK_ENUM_VAR("Player 1", PUYOGAME_PLAYER1);
400
RSDK_ENUM_VAR("Player 2", PUYOGAME_PLAYER2);
401
}
402
#endif
403
404
void PuyoMatch_Serialize(void) { RSDK_EDITABLE_VAR(PuyoMatch, VAR_ENUM, playerID); }
405
406