Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Puyo/PuyoBean.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: PuyoBean Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectPuyoBean *PuyoBean;
11
12
void PuyoBean_Update(void)
13
{
14
RSDK_THIS(PuyoBean);
15
16
StateMachine_Run(self->state);
17
}
18
19
void PuyoBean_LateUpdate(void) {}
20
21
void PuyoBean_StaticUpdate(void)
22
{
23
RSDK.ProcessAnimation(&PuyoBean->junkBeanAnimator);
24
25
if (--PuyoBean->shinkDelay <= 0) {
26
PuyoBean->shinkDelay = 15 * RSDK.Rand(1, 24);
27
28
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, PUYOBEAN_JUNK + PUYOBEAN_ANI_BOUNCE, &PuyoBean->junkBeanAnimator, true, 0);
29
}
30
}
31
32
void PuyoBean_Draw(void)
33
{
34
RSDK_THIS(PuyoBean);
35
36
if (SceneInfo->state != ENGINESTATE_FROZEN) {
37
if (self->isJunk)
38
RSDK.DrawSprite(&PuyoBean->junkBeanAnimator, NULL, false);
39
else
40
RSDK.DrawSprite(&self->beanAnimator, NULL, false);
41
}
42
}
43
44
void PuyoBean_Create(void *data)
45
{
46
RSDK_THIS(PuyoBean);
47
if (!SceneInfo->inEditor) {
48
self->visible = true;
49
self->drawGroup = PuyoGame ? Zone->objectDrawGroup[0] : Zone->objectDrawGroup[1];
50
self->active = ACTIVE_NORMAL;
51
self->updateRange.x = 0x800000;
52
self->updateRange.y = 0x800000;
53
54
self->angle = 0xC0;
55
self->position.x = (self->position.x & 0xFFF00000) + 0x80000;
56
self->position.y = (self->position.y & 0xFFF00000) + 0x80000;
57
self->origin.x = self->position.x - 0x280000;
58
self->origin.y = self->position.y - 0x80000;
59
self->controllerID = CONT_P1;
60
61
self->type = VOID_TO_INT(data);
62
if (self->type == PUYOBEAN_JUNK)
63
self->isJunk = true;
64
65
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type, &self->beanAnimator, true, 0);
66
}
67
}
68
69
void PuyoBean_StageLoad(void)
70
{
71
PuyoBean->aniFrames = RSDK.LoadSpriteAnimation("Puyo/PuyoBeans.bin", SCOPE_STAGE);
72
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, PUYOBEAN_JUNK + PUYOBEAN_ANI_BOUNCE, &PuyoBean->junkBeanAnimator, true, 10);
73
74
PuyoBean->hitboxBean.left = -8;
75
PuyoBean->hitboxBean.top = -8;
76
PuyoBean->hitboxBean.right = 8;
77
PuyoBean->hitboxBean.bottom = 8;
78
79
PuyoBean->sfxLand = RSDK.GetSfx("Puyo/Land.wav");
80
PuyoBean->sfxRotate = RSDK.GetSfx("Puyo/Rotate.wav");
81
PuyoBean->sfxJunk = RSDK.GetSfx("Puyo/Junk.wav");
82
PuyoBean->sfxFall = RSDK.GetSfx("Puyo/Fall.wav");
83
PuyoBean->chainFrames[0] = RSDK.GetSfx("Puyo/Chain0.wav");
84
PuyoBean->chainFrames[1] = RSDK.GetSfx("Puyo/Chain1.wav");
85
PuyoBean->chainFrames[2] = RSDK.GetSfx("Puyo/Chain2.wav");
86
PuyoBean->chainFrames[3] = RSDK.GetSfx("Puyo/Chain3.wav");
87
PuyoBean->chainFrames[4] = RSDK.GetSfx("Puyo/Chain4.wav");
88
PuyoBean->chainFrames[5] = RSDK.GetSfx("Puyo/Chain5.wav");
89
90
for (int32 i = 0; i < 0x100; ++i) PuyoBean->playfield[i] = NULL;
91
}
92
93
EntityPuyoBean *PuyoBean_GetPuyoBean(int32 playerID, int32 x, int32 y)
94
{
95
if (x >= 0 && y >= 0 && x < PUYO_PLAYFIELD_W && y < PUYO_PLAYFIELD_H)
96
return PuyoBean->playfield[128 * playerID + 8 * y + x];
97
98
return NULL;
99
}
100
101
void PuyoBean_Input_Player(void)
102
{
103
RSDK_THIS(PuyoBean);
104
105
if (self->controllerID < PLAYER_COUNT) {
106
RSDKControllerState *controller = &ControllerInfo[self->controllerID];
107
RSDKAnalogState *stick = &AnalogStickInfoL[self->controllerID];
108
109
self->down = controller->keyDown.down;
110
self->left = controller->keyLeft.down;
111
self->right = controller->keyRight.down;
112
113
#if MANIA_USE_PLUS
114
self->down |= stick->vDelta < -0.3;
115
self->left |= stick->hDelta < -0.3;
116
self->right |= stick->hDelta > 0.3;
117
#else
118
self->down |= stick->vDeltaL < -0.3;
119
self->left |= stick->hDeltaL < -0.3;
120
self->right |= stick->hDeltaL > 0.3;
121
#endif
122
123
if (self->left && self->right) {
124
self->left = false;
125
self->right = false;
126
}
127
self->rotateLeft = controller->keyB.press || controller->keyC.press || controller->keyY.press;
128
self->rotateRight = controller->keyA.press || controller->keyX.press;
129
self->forceRotateLeft = false;
130
self->forceRotateRight = false;
131
}
132
}
133
134
void PuyoBean_DestroyPuyoBeans(void)
135
{
136
foreach_all(PuyoBean, bean)
137
{
138
if (bean->stateInput && bean->state == PuyoBean_State_Controlled) {
139
if (bean->partner)
140
destroyEntity(bean->partner);
141
142
destroyEntity(bean);
143
}
144
}
145
}
146
147
void PuyoBean_HandleBeanLinks(void)
148
{
149
RSDK_THIS(PuyoBean);
150
151
if (self->stillPos.x >= 0 && self->stillPos.y < PUYO_PLAYFIELD_H && self->stillPos.x < PUYO_PLAYFIELD_W) {
152
self->linkSides = 0;
153
self->linkCount = 0;
154
155
int32 ny = self->stillPos.y - 1;
156
self->linkBeans[0] = NULL;
157
if (ny > -1) {
158
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, ny);
159
if (bean && bean->type == self->type) {
160
self->linkSides |= 1;
161
self->linkBeans[0] = bean;
162
}
163
}
164
165
ny = self->stillPos.y + 1;
166
self->linkBeans[1] = NULL;
167
if (ny < PUYO_PLAYFIELD_H) {
168
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, ny);
169
if (bean && bean->type == self->type) {
170
self->linkSides |= 2;
171
self->linkBeans[1] = bean;
172
}
173
}
174
175
int32 nx = self->stillPos.x - 1;
176
self->linkBeans[2] = NULL;
177
if (nx > -1) {
178
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, nx, self->stillPos.y);
179
if (bean && bean->type == self->type) {
180
self->linkSides |= 4;
181
self->linkBeans[2] = bean;
182
}
183
}
184
185
nx = self->stillPos.x + 1;
186
self->linkBeans[3] = NULL;
187
if (nx < PUYO_PLAYFIELD_W) {
188
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, nx, self->stillPos.y);
189
if (bean && bean->type == self->type) {
190
self->linkSides |= 8;
191
self->linkBeans[3] = bean;
192
}
193
}
194
}
195
}
196
197
void PuyoBean_CheckBeanLinks(EntityPuyoBean *bean, EntityPuyoBean *curLink)
198
{
199
RSDK_THIS(PuyoBean);
200
201
EntityPuyoBean **linkBeans = bean->linkBeans;
202
for (int32 i = 0; i < 4; ++i) {
203
EntityPuyoBean *link = bean->linkBeans[i];
204
205
if (link && link != curLink) {
206
if (++self->linkCount > 2) // Beans can only be connected on 2 axis at a time
207
return;
208
209
PuyoBean_CheckBeanLinks(link, bean);
210
}
211
212
++linkBeans;
213
}
214
}
215
216
void PuyoBean_HandleMoveBounds(void)
217
{
218
RSDK_THIS(PuyoBean);
219
220
EntityPuyoBean *partner = self->partner;
221
int32 entityX = self->stillPos.x;
222
int32 entityY = self->stillPos.y;
223
int32 partnerX = partner->stillPos.x;
224
int32 partnerY = partner->stillPos.y;
225
226
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, entityX, entityY);
227
EntityPuyoBean *bean2 = PuyoBean_GetPuyoBean(self->playerID, partnerX, partnerY);
228
229
self->position.x -= entityX << 20;
230
self->position.y -= entityY << 20;
231
232
bool32 canMoveRight = self->velocity.x < 0;
233
if (self->velocity.x >= 0) {
234
canMoveRight =
235
((self->angle > 0x40 && self->rotateSpeed > 0) || (self->angle < 0xC0 && self->rotateSpeed < 0)) && (self->targetAngle == 0x80);
236
}
237
238
if (entityX < 0 || partnerX < 0 || (canMoveRight && (bean || bean2))) {
239
++entityX;
240
++partnerX;
241
}
242
243
bool32 canMoveLeft = self->velocity.x > 0;
244
if (self->velocity.x <= 0) {
245
canMoveLeft = ((self->angle < 0x40 && self->rotateSpeed < 0) || (self->angle > 0xC0 && self->rotateSpeed > 0)) && (self->targetAngle == 0x00);
246
}
247
248
if (entityX >= PUYO_PLAYFIELD_W || partnerX >= PUYO_PLAYFIELD_W || (canMoveLeft && (bean || bean2))) {
249
--entityX;
250
--partnerX;
251
}
252
253
bool32 canMoveUp =
254
((self->angle > 0x00 && self->rotateSpeed > 0) || (self->angle < 0x80 && self->rotateSpeed < 0)) && (self->targetAngle == 0x40);
255
if (entityY >= PUYO_PLAYFIELD_H || partnerY >= PUYO_PLAYFIELD_H || (canMoveUp && (bean || bean2))) {
256
--entityY;
257
--partnerY;
258
}
259
260
self->stillPos.x = entityX;
261
self->stillPos.y = entityY;
262
partner->stillPos.x = partnerX;
263
partner->stillPos.y = partnerY;
264
265
self->position.x += self->stillPos.x << 20;
266
self->position.y += self->stillPos.y << 20;
267
}
268
269
bool32 PuyoBean_CheckAIRotationDisabled(EntityPuyoBean *bean)
270
{
271
if (bean->targetAngle != 0x40 && bean->targetAngle != 0xC0)
272
return 0;
273
274
uint8 disabledAxis = 0;
275
int32 y = bean->stillPos.y;
276
277
if (bean->stillPos.x > 0) {
278
int32 nx = bean->stillPos.x - 1;
279
if (PuyoBean_GetPuyoBean(bean->playerID, nx, y))
280
disabledAxis = 1;
281
}
282
else {
283
disabledAxis = 1;
284
}
285
286
if (bean->stillPos.x < (PUYO_PLAYFIELD_W - 1)) {
287
int32 nx = bean->stillPos.x + 1;
288
if (PuyoBean_GetPuyoBean(bean->playerID, nx, y))
289
disabledAxis |= 2;
290
}
291
else {
292
disabledAxis |= 2;
293
}
294
295
return disabledAxis == 3;
296
}
297
298
void PuyoBean_CheckCollisions(void)
299
{
300
RSDK_THIS(PuyoBean);
301
302
EntityPuyoBean *partner = self->partner;
303
uint8 wallCollisions = 0;
304
305
if (self->position.x - self->origin.x != self->stillPos.x << 20) {
306
int32 nx = self->stillPos.x - 1;
307
308
if (self->stillPos.x <= 0 || PuyoBean_GetPuyoBean(self->playerID, nx, self->stillPos.y)) {
309
self->left = false;
310
wallCollisions = 1;
311
}
312
313
nx = self->stillPos.x + 1;
314
if (self->stillPos.x >= (PUYO_PLAYFIELD_W - 1) || PuyoBean_GetPuyoBean(self->playerID, nx, self->stillPos.y)) {
315
self->right = false;
316
wallCollisions |= 2;
317
}
318
319
if (wallCollisions == 3) {
320
if (self->angle == 0x40 || self->angle == 0xC0) {
321
if (self->rotateRight)
322
self->forceRotateLeft = true;
323
else if (self->rotateLeft)
324
self->forceRotateRight = true;
325
}
326
327
self->rotateRight = false;
328
self->rotateLeft = false;
329
}
330
}
331
332
if (partner->position.x - partner->origin.x != partner->stillPos.x << 20) {
333
if (partner->stillPos.x >= 1) {
334
int32 nx = partner->stillPos.x - 1;
335
336
if (PuyoBean_GetPuyoBean(self->playerID, nx, partner->stillPos.y)) {
337
self->left = false;
338
}
339
}
340
else {
341
self->left = false;
342
}
343
344
int32 nx = partner->stillPos.x + 1;
345
if (partner->stillPos.x > 4 || PuyoBean_GetPuyoBean(self->playerID, nx, partner->stillPos.y)) {
346
self->right = false;
347
}
348
}
349
350
self->onGround = false;
351
partner->onGround = false;
352
353
if (self->position.y & 0xF0000) {
354
int32 ny = self->stillPos.y + 1;
355
356
if (self->stillPos.y == 13 || PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, ny)) {
357
self->onGround = true;
358
}
359
}
360
361
if (partner->position.y & 0xF0000) {
362
int32 ny = partner->stillPos.y + 1;
363
364
if (partner->stillPos.y == 13 || PuyoBean_GetPuyoBean(self->playerID, partner->stillPos.x, ny)) {
365
partner->onGround = true;
366
}
367
}
368
369
self->onGround |= partner->onGround;
370
}
371
372
int32 PuyoBean_GetBeanChainRemovalCount(int32 playerID, EntityPuyoBean *bean, int32 x, int32 y)
373
{
374
PuyoBean->beanLinkTable[PUYO_PLAYFIELD_W * y + x] = true;
375
376
int32 beanLinkCount = 0;
377
int32 junkBeanCount = 0;
378
379
for (int32 i = 0; i < 4; ++i) {
380
int32 nx = x;
381
int32 ny = y;
382
switch (i) {
383
case 0: nx = x - 1; break;
384
case 1: nx = x + 1; break;
385
case 2: ny = y - 1; break;
386
case 3: ny = y + 1; break;
387
default: break;
388
}
389
390
EntityPuyoBean *beanState = PuyoBean_GetPuyoBean(playerID, nx, ny);
391
if (beanState) {
392
if (bean->type == beanState->type) {
393
PuyoBean_SetupBeanLinkTable(playerID, nx, ny, false);
394
beanLinkCount += PuyoBean->beanLinkCount;
395
}
396
}
397
398
for (int32 b = 0; b < PuyoBean->beanLinkCount; ++b) {
399
Vector2 positions[4];
400
401
positions[0].x = PuyoBean->beanLinkPositions[b].x - 1;
402
positions[0].y = PuyoBean->beanLinkPositions[b].y;
403
404
positions[1].x = PuyoBean->beanLinkPositions[b].x + 1;
405
positions[1].y = PuyoBean->beanLinkPositions[b].y;
406
407
positions[2].x = PuyoBean->beanLinkPositions[b].x;
408
positions[2].y = PuyoBean->beanLinkPositions[b].y - 1;
409
410
positions[3].x = PuyoBean->beanLinkPositions[b].x;
411
positions[3].y = PuyoBean->beanLinkPositions[b].y + 1;
412
413
for (int32 p = 0; p < 4; ++p) {
414
int32 bx = positions[p].x;
415
int32 by = positions[p].y;
416
417
beanState = PuyoBean_GetPuyoBean(playerID, bx, by);
418
if (beanState && !PuyoBean->beanLinkTable[bx + PUYO_PLAYFIELD_W * by]) {
419
if (beanState->isJunk) {
420
PuyoBean->beanLinkTable[bx + PUYO_PLAYFIELD_W * by] = true;
421
++junkBeanCount;
422
}
423
}
424
}
425
}
426
}
427
428
return junkBeanCount + beanLinkCount + 1;
429
}
430
431
int32 PuyoBean_GetAvailableLinks(int32 playerID, EntityPuyoBean *bean, int32 x, int32 y)
432
{
433
int32 availableLinks = 0;
434
for (int32 i = 0; i < 4; ++i) {
435
int32 bx = x;
436
int32 by = y;
437
switch (i) {
438
default: break;
439
case 0: bx = x - 1; break;
440
case 1: bx = x + 1; break;
441
case 2: by = y - 1; break;
442
case 3: by = y + 1; break;
443
}
444
445
EntityPuyoBean *beanState = PuyoBean_GetPuyoBean(playerID, bx, by);
446
if (beanState && beanState->type != bean->type) {
447
PuyoBean_SetupBeanLinkTable(playerID, bx, by, true);
448
449
if (PuyoBean->beanLinkCount >= 3 && !PuyoBean_CheckLinkPosAvailable(playerID, x, y)) {
450
availableLinks++;
451
}
452
}
453
}
454
455
return availableLinks;
456
}
457
458
bool32 PuyoBean_CheckLinkPosAvailable(int32 playerID, int32 x, int32 y)
459
{
460
bool32 beanLinkTable[0x101];
461
memset(beanLinkTable, 0, sizeof(beanLinkTable));
462
463
for (int32 b = 0; b < PuyoBean->beanLinkCount; ++b) {
464
Vector2 possibleLinks[4];
465
466
possibleLinks[0].x = PuyoBean->beanLinkPositions[b].x - 1;
467
possibleLinks[0].y = PuyoBean->beanLinkPositions[b].y;
468
469
possibleLinks[1].x = PuyoBean->beanLinkPositions[b].x + 1;
470
possibleLinks[1].y = PuyoBean->beanLinkPositions[b].y;
471
472
possibleLinks[2].x = PuyoBean->beanLinkPositions[b].x;
473
possibleLinks[2].y = PuyoBean->beanLinkPositions[b].y - 1;
474
475
possibleLinks[3].x = PuyoBean->beanLinkPositions[b].x;
476
possibleLinks[3].y = PuyoBean->beanLinkPositions[b].y + 1;
477
478
for (int32 i = 0; i < 4; ++i) {
479
int32 bx = possibleLinks[i].x;
480
int32 by = possibleLinks[i].y;
481
482
if (!beanLinkTable[bx + PUYO_PLAYFIELD_W * by]) {
483
beanLinkTable[bx + PUYO_PLAYFIELD_W * by] = true;
484
485
if (bx != x || by != y) {
486
EntityPuyoBean *beanState = PuyoBean_GetPuyoBean(playerID, bx, by);
487
if (bx >= 0 && by >= 0 && bx <= (PUYO_PLAYFIELD_W - 1) && by <= (PUYO_PLAYFIELD_H - 1) && !beanState)
488
return true;
489
}
490
}
491
}
492
}
493
494
return false;
495
}
496
497
void PuyoBean_SetupBeanLinkTable(int32 playerID, int32 x, int32 y, bool32 useTempTable)
498
{
499
bool32 tempBeanLinkTable[0x101];
500
memset(tempBeanLinkTable, 0, sizeof(tempBeanLinkTable));
501
502
bool32 *beanLinkTable = useTempTable ? tempBeanLinkTable : PuyoBean->beanLinkTable;
503
504
if (!beanLinkTable[x + PUYO_PLAYFIELD_W * y]) {
505
for (int32 i = 0; i < (PUYO_PLAYFIELD_W * PUYO_PLAYFIELD_H); ++i) {
506
PuyoBean->beanLinkPositions[i].x = 0;
507
PuyoBean->beanLinkPositions[i].y = 0;
508
}
509
510
PuyoBean->beanLinkCount = 0;
511
if (x >= 0 && y >= 0 && x <= (PUYO_PLAYFIELD_W - 1) && y <= (PUYO_PLAYFIELD_H - 1)) {
512
EntityPuyoBean *beanState = PuyoBean_GetPuyoBean(playerID, x, y);
513
if (beanState) {
514
PuyoBean->beanLinkPositions[0].x = x;
515
PuyoBean->beanLinkPositions[0].y = y;
516
int32 linkCount = 1;
517
518
for (int32 p = 0; p < linkCount; ++p) {
519
if (linkCount >= 0xFF)
520
break;
521
522
int32 bx = PuyoBean->beanLinkPositions[linkCount].x;
523
int32 by = PuyoBean->beanLinkPositions[linkCount].y;
524
525
EntityPuyoBean *startBean = NULL, *curBean = NULL;
526
startBean = PuyoBean_GetPuyoBean(playerID, bx, by);
527
528
beanLinkTable[bx + PUYO_PLAYFIELD_W * by] = true;
529
530
if (startBean) {
531
int32 nx = bx - 1;
532
if (nx >= 0) {
533
if (!beanLinkTable[nx + PUYO_PLAYFIELD_W * by] && by >= 0 && nx <= (PUYO_PLAYFIELD_W - 1)
534
&& by <= (PUYO_PLAYFIELD_H - 1)) {
535
curBean = PuyoBean_GetPuyoBean(playerID, nx, by);
536
537
if (curBean && beanState->type == curBean->type) {
538
PuyoBean->beanLinkPositions[linkCount].x = nx;
539
PuyoBean->beanLinkPositions[linkCount++].y = by;
540
}
541
}
542
}
543
544
nx = bx + 1;
545
if (nx <= (PUYO_PLAYFIELD_W - 1)) {
546
if (!beanLinkTable[nx + PUYO_PLAYFIELD_W * by] && nx >= 0 && by >= 0 && by <= (PUYO_PLAYFIELD_H - 1)) {
547
curBean = PuyoBean_GetPuyoBean(playerID, nx, by);
548
549
if (curBean && beanState->type == curBean->type) {
550
PuyoBean->beanLinkPositions[linkCount].x = nx;
551
PuyoBean->beanLinkPositions[linkCount++].y = by;
552
}
553
}
554
}
555
556
int32 ny = by - 1;
557
if (ny >= 0) {
558
if (!beanLinkTable[bx + PUYO_PLAYFIELD_W * ny] && bx >= 0 && bx <= (PUYO_PLAYFIELD_W - 1)
559
&& ny <= (PUYO_PLAYFIELD_H - 1)) {
560
curBean = PuyoBean_GetPuyoBean(playerID, bx, ny);
561
562
if (curBean && beanState->type == curBean->type) {
563
PuyoBean->beanLinkPositions[linkCount].x = bx;
564
PuyoBean->beanLinkPositions[linkCount++].y = ny;
565
}
566
}
567
}
568
569
ny = by + 1;
570
if (ny <= (PUYO_PLAYFIELD_H - 1)) {
571
if (!beanLinkTable[bx + PUYO_PLAYFIELD_W * ny] && bx >= 0 && ny >= 0 && bx <= 5) {
572
curBean = PuyoBean_GetPuyoBean(playerID, bx, ny);
573
574
if (curBean && beanState->type == curBean->type) {
575
PuyoBean->beanLinkPositions[linkCount].x = bx;
576
PuyoBean->beanLinkPositions[linkCount++].y = ny;
577
}
578
}
579
}
580
}
581
}
582
583
PuyoBean->beanLinkCount = linkCount;
584
}
585
}
586
}
587
}
588
589
uint8 PuyoBean_GetColumnHeight(int32 playerID, int32 column, EntityPuyoBean *bean, EntityPuyoBean *partner)
590
{
591
if (column < 0 || column >= PUYO_PLAYFIELD_W)
592
return PUYO_PLAYFIELD_H - 1;
593
594
int32 height = 0;
595
for (int32 y = (PUYO_PLAYFIELD_H - 1); y >= 0; --y) {
596
if (bean && column == bean->stillPos.x && y == bean->stillPos.y)
597
break;
598
599
if (partner && column == partner->stillPos.x && y == partner->stillPos.y)
600
break;
601
602
if (!PuyoBean_GetPuyoBean(playerID, column, y))
603
break;
604
605
++height;
606
}
607
608
return height;
609
}
610
611
void PuyoBean_CalculateStillPos(EntityPuyoBean *bean)
612
{
613
if (bean->state == PuyoBean_State_PartnerControlled) {
614
EntityPuyoBean *partner = bean->partner;
615
616
switch (partner->targetAngle >> 6) {
617
case 0:
618
bean->stillPos.y = partner->stillPos.y;
619
bean->stillPos.x = partner->stillPos.x + 1;
620
break;
621
622
case 1:
623
bean->stillPos.x = partner->stillPos.x;
624
bean->stillPos.y = partner->stillPos.y + 1;
625
break;
626
627
case 2:
628
bean->stillPos.y = partner->stillPos.y;
629
bean->stillPos.x = partner->stillPos.x - 1;
630
break;
631
632
case 3:
633
bean->stillPos.x = partner->stillPos.x;
634
bean->stillPos.y = partner->stillPos.y - 1;
635
break;
636
637
default:
638
bean->stillPos.x = partner->stillPos.x;
639
bean->stillPos.y = partner->stillPos.y;
640
break;
641
}
642
}
643
else {
644
bean->stillPos.x = (bean->position.x - bean->origin.x) >> 20;
645
bean->stillPos.y = (bean->position.y - bean->origin.y) >> 20;
646
}
647
}
648
649
void PuyoBean_State_PartnerControlled(void)
650
{
651
RSDK_THIS(PuyoBean);
652
653
RSDK.ProcessAnimation(&self->beanAnimator);
654
}
655
656
void PuyoBean_State_Controlled(void)
657
{
658
RSDK_THIS(PuyoBean);
659
660
EntityPuyoBean *partner = self->partner;
661
662
RSDK.ProcessAnimation(&self->beanAnimator);
663
664
if (self->beanAnimator.animationID == self->type + PUYOBEAN_ANI_BOUNCE && self->beanAnimator.frameID == self->beanAnimator.frameCount - 1)
665
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type, &self->beanAnimator, true, 0);
666
667
StateMachine_Run(self->stateInput);
668
669
bool32 prevOnGround = self->onGround;
670
PuyoBean_CheckCollisions();
671
self->fallDelay = self->down ? 1 : PuyoBean->fallDelays[self->selectedLevel];
672
673
if (!self->rotateSpeed) {
674
self->targetAngle = self->angle;
675
self->forceRotationActive = false;
676
677
if (self->rotateRight) {
678
self->rotateSpeed = -8;
679
680
self->targetAngle = self->angle - 0x40;
681
if (self->targetAngle < 0)
682
self->targetAngle += 0x100;
683
684
RSDK.PlaySfx(PuyoBean->sfxRotate, false, 255);
685
}
686
else if (self->rotateLeft) {
687
self->targetAngle = (self->angle + 0x40) & 0xFF;
688
self->rotateSpeed = 8;
689
690
RSDK.PlaySfx(PuyoBean->sfxRotate, false, 255);
691
}
692
else {
693
if (self->forceRotateLeft) {
694
self->forceRotationActive = true;
695
696
self->rotateSpeed = -8;
697
self->targetAngle = (self->angle + 0x80) & 0xFF;
698
699
RSDK.PlaySfx(PuyoBean->sfxRotate, false, 255);
700
}
701
else if (self->forceRotateRight) {
702
self->forceRotationActive = true;
703
704
self->rotateSpeed = 8;
705
self->targetAngle = (self->angle + 0x80) & 0xFF;
706
707
RSDK.PlaySfx(PuyoBean->sfxRotate, false, 255);
708
}
709
}
710
711
if (self->forceRotateLeft || self->forceRotateRight) {
712
if (self->angle == 0xC0)
713
self->rotationDir = 1;
714
else if (self->angle == 0x40)
715
self->rotationDir = -1;
716
}
717
}
718
719
if (self->rotateSpeed < 0) {
720
self->rotateSpeed++;
721
self->angle -= 8 * (self->forceRotationActive != false) + 8;
722
self->angle &= 0xFF;
723
}
724
else if (self->rotateSpeed > 0) {
725
self->rotateSpeed--;
726
self->angle += 8 * (self->forceRotationActive != false) + 8;
727
self->angle &= 0xFF;
728
}
729
730
if (!self->onGround && ++self->fallTimer >= self->fallDelay) {
731
self->position.y += 0x80000;
732
self->fallTimer = 0;
733
}
734
735
if (!(self->moveTimer & 3)) {
736
if (self->left) {
737
if (self->velocity.x > 0)
738
self->moveTimer = 0;
739
740
self->velocity.x = -0x80000;
741
}
742
else if (self->right) {
743
if (self->velocity.x < 0)
744
self->moveTimer = 0;
745
746
self->velocity.x = 0x80000;
747
}
748
}
749
750
if (!self->left && !self->right && !(self->moveTimer & 3)) {
751
self->moveTimer = 0;
752
self->velocity.x = 0;
753
}
754
else {
755
if (++self->moveTimer >= 16) {
756
if ((self->moveTimer & 3) == 1 || (self->moveTimer & 3) == 2)
757
self->position.x += self->velocity.x;
758
}
759
else {
760
if (self->moveTimer < 3)
761
self->position.x += self->velocity.x;
762
}
763
}
764
765
PuyoBean_CalculateStillPos(self);
766
PuyoBean_CalculateStillPos(partner);
767
PuyoBean_HandleMoveBounds();
768
769
partner->position.x = self->position.x + (RSDK.Cos256(self->angle) << 12);
770
partner->position.y = self->position.y + (RSDK.Sin256(self->angle) << 12);
771
772
if (self->onGround) {
773
if (!prevOnGround) {
774
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, partner->type + PUYOBEAN_ANI_BOUNCE, &partner->beanAnimator, false, 0);
775
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_BOUNCE, &self->beanAnimator, false, 0);
776
777
RSDK.PlaySfx(PuyoBean->sfxLand, false, 255);
778
}
779
if (++self->idleTimer > 60 || self->down) {
780
self->position.x = ((self->origin.x + (self->stillPos.x << 20)) & 0xFFF00000) + 0x80000;
781
self->position.y = ((self->origin.y + (self->stillPos.y << 20)) & 0xFFF00000) + 0x80000;
782
partner->position.x = ((partner->origin.x + (partner->stillPos.x << 20)) & 0xFFF00000) + 0x80000;
783
partner->position.y = ((partner->origin.y + (partner->stillPos.y << 20)) & 0xFFF00000) + 0x80000;
784
785
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, partner->type + PUYOBEAN_ANI_BOUNCE, &partner->beanAnimator, false, 0);
786
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_BOUNCE, &self->beanAnimator, false, 0);
787
788
self->left = false;
789
self->right = false;
790
self->down = false;
791
self->rotateRight = false;
792
self->rotateLeft = false;
793
self->popTimer = 0;
794
partner->popTimer = 0;
795
796
partner->state = PuyoBean_State_Falling;
797
self->state = PuyoBean_State_Falling;
798
}
799
}
800
else {
801
if (self->idleTimer > 0)
802
self->idleTimer++;
803
}
804
}
805
806
void PuyoBean_State_BeanIdle(void)
807
{
808
RSDK_THIS(PuyoBean);
809
810
// Check if bean should be falling
811
if (self->stillPos.y < (PUYO_PLAYFIELD_H - 1)) {
812
int32 ny = self->stillPos.y + 1;
813
814
if (self->stillPos.x < 0 || ny < 0 || self->stillPos.x > (PUYO_PLAYFIELD_W - 1) || ny > (PUYO_PLAYFIELD_H - 1)
815
|| !PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, ny)) {
816
PuyoBean->playfield[128 * self->playerID + 8 * self->stillPos.y + self->stillPos.x] = NULL;
817
818
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_BOUNCE, &self->beanAnimator, true, 0);
819
820
self->timer = 0;
821
self->velocity.y = 0;
822
self->linkBeans[0] = NULL;
823
self->linkBeans[1] = NULL;
824
self->linkBeans[2] = NULL;
825
self->linkBeans[3] = NULL;
826
self->state = PuyoBean_State_Falling;
827
self->popTimer = 0;
828
}
829
}
830
831
if (self->state == PuyoBean_State_BeanIdle && !PuyoBean->disableBeanLink[self->playerID]) {
832
// Handle Bean links & Combos
833
PuyoBean_HandleBeanLinks();
834
if (self->linkSides > 0)
835
PuyoBean_CheckBeanLinks(self, NULL);
836
837
if (self->linkCount <= 2) {
838
self->connectTimer = 0;
839
}
840
else if (++self->connectTimer > 2) {
841
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_CONNECT, &self->beanAnimator, true, self->linkSides);
842
843
self->timer = 0;
844
self->connectTimer = 0;
845
self->state = PuyoBean_State_BeginBeanPop;
846
}
847
}
848
849
if (self->state == PuyoBean_State_BeanIdle) {
850
// Handle Bean animations
851
if (self->linkSides) {
852
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_CONNECT, &self->beanAnimator, true, self->linkSides);
853
}
854
else {
855
if (self->timer <= 0) {
856
if (self->beanAnimator.animationID - self->type == PUYOBEAN_ANI_CONNECT) {
857
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_IDLE, &self->beanAnimator, true, 0);
858
}
859
else {
860
RSDK.ProcessAnimation(&self->beanAnimator);
861
862
if (self->beanAnimator.frameID == self->beanAnimator.frameCount - 1)
863
self->timer = RSDK.Rand(120, 240);
864
}
865
}
866
else {
867
if (!--self->timer)
868
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_IDLE, &self->beanAnimator, true, 0);
869
}
870
}
871
}
872
}
873
874
void PuyoBean_State_Falling(void)
875
{
876
RSDK_THIS(PuyoBean);
877
878
if (++self->timer > 8) {
879
self->velocity.y += 0x3800;
880
self->position.y += self->velocity.y;
881
882
foreach_active(PuyoBean, bean)
883
{
884
if (bean != self && bean->state == PuyoBean_State_Falling && self->position.x == bean->position.x) {
885
if (self->position.y + 0x100000 > bean->position.y && self->position.y < bean->position.y) {
886
self->position.y = bean->position.y - 0x100000;
887
self->velocity.y = bean->velocity.y;
888
foreach_break;
889
}
890
}
891
}
892
893
PuyoBean_CalculateStillPos(self);
894
895
if (self->stillPos.y >= 0) {
896
if (self->stillPos.y > (PUYO_PLAYFIELD_H - 1))
897
self->stillPos.y = (PUYO_PLAYFIELD_H - 1);
898
899
int32 y = self->stillPos.y;
900
int32 ny = y + 1;
901
902
if (y >= (PUYO_PLAYFIELD_H - 1) || PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, ny)) {
903
if ((self->position.y & 0xF0000) >= 0x80000) {
904
self->position.y = (y << 20) + self->origin.y + 0x80000;
905
self->timer = 0;
906
int32 playfieldSlot = 128 * self->playerID + 8 * self->stillPos.y + self->stillPos.x;
907
int32 entitySlot = 0x600 + playfieldSlot;
908
909
if (self->isJunk) {
910
PuyoBean->playfield[playfieldSlot] = RSDK_GET_ENTITY(entitySlot, PuyoBean);
911
self->state = PuyoBean_State_JunkLand;
912
if (self->velocity.y > 0x8000)
913
RSDK.PlaySfx(PuyoBean->sfxJunk, false, 255);
914
}
915
else {
916
PuyoBean->playfield[playfieldSlot] = RSDK_GET_ENTITY(entitySlot, PuyoBean);
917
self->state = PuyoBean_State_BeanLand;
918
if (self->velocity.y > 0x8000)
919
RSDK.PlaySfx(PuyoBean->sfxLand, false, 255);
920
}
921
922
self->velocity.y = 0;
923
RSDK.AddDrawListRef(self->drawGroup, entitySlot);
924
RSDK.CopyEntity(RSDK_GET_ENTITY(entitySlot, PuyoBean), self, true);
925
}
926
}
927
}
928
}
929
}
930
931
void PuyoBean_State_BeanLand(void)
932
{
933
RSDK_THIS(PuyoBean);
934
935
RSDK.ProcessAnimation(&self->beanAnimator);
936
937
if (self->beanAnimator.frameID == self->beanAnimator.frameCount - 1) {
938
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, self->stillPos.y);
939
if (bean && bean != self)
940
destroyEntity(bean);
941
942
self->timer = RSDK.Rand(120, 240);
943
self->state = PuyoBean_State_BeanIdle;
944
}
945
}
946
947
void PuyoBean_State_JunkLand(void)
948
{
949
RSDK_THIS(PuyoBean);
950
951
if (++self->timer == 2) {
952
self->timer = 0;
953
self->state = PuyoBean_State_JunkIdle;
954
955
PuyoBean_CalculateStillPos(self);
956
}
957
}
958
959
void PuyoBean_State_JunkIdle(void)
960
{
961
RSDK_THIS(PuyoBean);
962
963
bool32 popJunk = false;
964
965
self->linkBeans[0] = NULL;
966
if (self->stillPos.x > -1) {
967
int32 y = self->stillPos.y - 1;
968
if (self->stillPos.x >= 0 && y >= 0 && self->stillPos.x <= (PUYO_PLAYFIELD_W - 1) && y <= (PUYO_PLAYFIELD_H - 1)) {
969
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, y);
970
971
if (bean && bean->state == PuyoBean_State_BeanPop && bean->popTimer < 2)
972
popJunk = true;
973
}
974
}
975
976
self->linkBeans[1] = NULL;
977
if (!popJunk && self->stillPos.y < PUYO_PLAYFIELD_H) {
978
int32 y = self->stillPos.y + 1;
979
if (self->stillPos.x >= 0 && y >= 0 && self->stillPos.x <= (PUYO_PLAYFIELD_W - 1) && y <= (PUYO_PLAYFIELD_H - 1)) {
980
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, y);
981
if (bean && bean->state == PuyoBean_State_BeanPop && bean->popTimer < 2)
982
popJunk = true;
983
}
984
}
985
986
self->linkBeans[2] = NULL;
987
if (!popJunk && self->stillPos.x > -1) {
988
int32 x = self->stillPos.x - 1;
989
if (x >= 0 && self->stillPos.y >= 0 && x <= (PUYO_PLAYFIELD_W - 1) && self->stillPos.y <= (PUYO_PLAYFIELD_H - 1)) {
990
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, x, self->stillPos.y);
991
if (bean && bean->state == PuyoBean_State_BeanPop && bean->popTimer < 2)
992
popJunk = true;
993
}
994
}
995
996
self->linkBeans[3] = NULL;
997
if (!popJunk && self->stillPos.x < PUYO_PLAYFIELD_W) {
998
int32 x = self->stillPos.x + 1;
999
if (x >= 0 && self->stillPos.y >= 0 && x <= (PUYO_PLAYFIELD_W - 1) && self->stillPos.y <= (PUYO_PLAYFIELD_H - 1)) {
1000
EntityPuyoBean *bean = PuyoBean_GetPuyoBean(self->playerID, x, self->stillPos.y);
1001
if (bean && bean->state == PuyoBean_State_BeanPop && bean->popTimer < 2)
1002
popJunk = true;
1003
}
1004
}
1005
1006
if (popJunk) {
1007
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, PUYOBEAN_JUNK + PUYOBEAN_ANI_IDLE, &self->beanAnimator, true, 0);
1008
1009
self->state = PuyoBean_State_JunkPopped;
1010
self->timer = 0;
1011
}
1012
else {
1013
if (self->stillPos.y < (PUYO_PLAYFIELD_H - 1)) {
1014
int32 y = self->stillPos.y + 1;
1015
if (self->stillPos.x < 0 || y < 0 || self->stillPos.x > (PUYO_PLAYFIELD_W - 1) || y > (PUYO_PLAYFIELD_H - 1)
1016
|| !PuyoBean_GetPuyoBean(self->playerID, self->stillPos.x, y)) {
1017
PuyoBean->playfield[128 * self->playerID + 8 * self->stillPos.y + self->stillPos.x] = NULL;
1018
1019
self->velocity.y = 0;
1020
self->popTimer = 0;
1021
self->state = PuyoBean_State_Falling;
1022
self->timer = 0;
1023
}
1024
}
1025
}
1026
}
1027
1028
void PuyoBean_State_JunkPopped(void)
1029
{
1030
RSDK_THIS(PuyoBean);
1031
1032
bool32 animationFinished = false;
1033
if (self->beanAnimator.frameID == self->beanAnimator.frameCount - 1) {
1034
animationFinished = true;
1035
self->visible = false;
1036
}
1037
else {
1038
RSDK.ProcessAnimation(&self->beanAnimator);
1039
}
1040
1041
if (self->timer < 26)
1042
self->timer++;
1043
1044
if (self->timer == 26)
1045
PuyoBean->playfield[128 * self->playerID + 8 * self->stillPos.y + self->stillPos.x] = NULL;
1046
1047
if (self->timer >= 26 && animationFinished)
1048
destroyEntity(self);
1049
}
1050
1051
void PuyoBean_State_BeginBeanPop(void)
1052
{
1053
RSDK_THIS(PuyoBean);
1054
1055
PuyoBean_HandleBeanLinks();
1056
1057
++self->timer;
1058
self->visible = !(self->timer & 1);
1059
1060
if (self->timer == 24) {
1061
self->timer = RSDK.Rand(8, 16);
1062
self->popTimer = 0;
1063
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_POP, &self->beanAnimator, true, 0);
1064
1065
self->state = PuyoBean_State_BeanPop;
1066
RSDK.PlaySfx(PuyoBean->chainFrames[MIN(PuyoBean->comboChainCount[self->playerID], 5)], false, 255);
1067
}
1068
}
1069
1070
void PuyoBean_State_BeanPop(void)
1071
{
1072
RSDK_THIS(PuyoBean);
1073
1074
if (self->popTimer >= 2)
1075
PuyoBean->playfield[128 * self->playerID + 8 * self->stillPos.y + self->stillPos.x] = NULL;
1076
else
1077
self->popTimer++;
1078
1079
if (self->timer <= 0) {
1080
for (int32 angle = 0; angle < 0x100; angle += 0x20) {
1081
int32 x = RSDK.Cos256(angle) << 10;
1082
int32 y = RSDK.Sin256(angle) << 10;
1083
EntityDebris *debris = CREATE_ENTITY(Debris, NULL, x + self->position.x, y + self->position.y);
1084
1085
debris->state = Debris_State_Fall;
1086
debris->gravityStrength = 0x4000;
1087
debris->timer = 14;
1088
debris->velocity.x = RSDK.Cos256(angle) << 9;
1089
debris->velocity.y = RSDK.Sin256(angle) << 9;
1090
debris->drawGroup = Zone->objectDrawGroup[1] + 1;
1091
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, self->type + PUYOBEAN_ANI_DEBRIS, &debris->animator, true, 0);
1092
}
1093
1094
destroyEntity(self);
1095
}
1096
else {
1097
self->timer--;
1098
}
1099
}
1100
1101
void PuyoBean_State_MatchLoseFall(void)
1102
{
1103
RSDK_THIS(PuyoBean);
1104
1105
if (self->timer <= 0) {
1106
self->velocity.y += 0x3800;
1107
self->position.y += self->velocity.y;
1108
1109
Vector2 range = { 0x800000, 0x800000 };
1110
if (!RSDK.CheckOnScreen(self, &range))
1111
destroyEntity(self);
1112
}
1113
else {
1114
self->velocity.y = 0;
1115
self->timer--;
1116
}
1117
}
1118
1119
#if GAME_INCLUDE_EDITOR
1120
void PuyoBean_EditorDraw(void) { RSDK.DrawSprite(&PuyoBean->junkBeanAnimator, NULL, false); }
1121
1122
void PuyoBean_EditorLoad(void)
1123
{
1124
PuyoBean->aniFrames = RSDK.LoadSpriteAnimation("Puyo/PuyoBeans.bin", SCOPE_STAGE);
1125
1126
RSDK.SetSpriteAnimation(PuyoBean->aniFrames, PUYOBEAN_BLUE + PUYOBEAN_ANI_FLASH, &PuyoBean->junkBeanAnimator, true, 0);
1127
}
1128
#endif
1129
1130
void PuyoBean_Serialize(void) {}
1131
1132