Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/SSZ/Beanstalk.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Beanstalk Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectBeanstalk *Beanstalk;
11
12
void Beanstalk_Update(void)
13
{
14
RSDK_THIS(Beanstalk);
15
16
RSDK.ProcessAnimation(&self->nodeAnimator);
17
18
if (self->type > BEANSTALK_NODE)
19
RSDK.ProcessAnimation(&self->plantAnimator);
20
21
StateMachine_Run(self->state);
22
}
23
24
void Beanstalk_LateUpdate(void) {}
25
26
void Beanstalk_StaticUpdate(void) {}
27
28
void Beanstalk_Draw(void)
29
{
30
RSDK_THIS(Beanstalk);
31
32
if (self->shown) {
33
Beanstalk_DrawNodes();
34
Beanstalk_DrawCreationNode();
35
36
self->scale.x = self->size;
37
self->scale.y = self->size;
38
self->drawFX = FX_SCALE | FX_FLIP;
39
RSDK.DrawSprite(&self->nodeAnimator, NULL, false);
40
41
if (self->type > BEANSTALK_NODE)
42
RSDK.DrawSprite(&self->plantAnimator, NULL, false);
43
44
self->drawFX = FX_FLIP;
45
self->scale.x = 0x200;
46
self->scale.y = 0x200;
47
}
48
}
49
50
void Beanstalk_Create(void *data)
51
{
52
RSDK_THIS(Beanstalk);
53
54
self->active = ACTIVE_BOUNDS;
55
self->visible = true;
56
self->drawFX = FX_FLIP;
57
self->drawGroup = Zone->objectDrawGroup[0];
58
self->bezCtrlAngle = self->bezCtrlAngle & 0xFF;
59
60
self->updateRange.x = 0x1000000;
61
self->updateRange.y = 0x1000000;
62
self->curBezierPos = -1;
63
if (self->type == BEANSTALK_BEGIN) {
64
self->size = 512;
65
self->active = ACTIVE_NORMAL;
66
}
67
else {
68
self->size = 0;
69
}
70
71
self->startPos = self->position;
72
self->platformPos = self->position;
73
self->stoodAngle = 0;
74
self->timer = 0;
75
self->chomperOffset = 0;
76
self->state = Beanstalk_State_Init;
77
}
78
79
void Beanstalk_StageLoad(void)
80
{
81
Beanstalk->aniFrames = RSDK.LoadSpriteAnimation("SSZ1/Beanstalk.bin", SCOPE_STAGE);
82
83
Beanstalk->sfxImpact = RSDK.GetSfx("Stage/Impact6.wav");
84
Beanstalk->sfxBeanNode = RSDK.GetSfx("SSZ1/BeanNode.wav");
85
Beanstalk->sfxBeanChomp = RSDK.GetSfx("SSZ1/BeanChomp.wav");
86
87
Beanstalk->hitboxSeed.left = -1;
88
Beanstalk->hitboxSeed.top = -64;
89
Beanstalk->hitboxSeed.right = 1;
90
Beanstalk->hitboxSeed.bottom = 1;
91
92
Beanstalk->hitboxPlatform.left = 0;
93
Beanstalk->hitboxPlatform.top = -3;
94
Beanstalk->hitboxPlatform.right = 46;
95
Beanstalk->hitboxPlatform.bottom = 13;
96
97
Beanstalk->hitboxPlant.left = 8;
98
Beanstalk->hitboxPlant.top = -8;
99
Beanstalk->hitboxPlant.right = 44;
100
Beanstalk->hitboxPlant.bottom = 8;
101
}
102
103
int32 Beanstalk_GetNextNodeDistance(void)
104
{
105
RSDK_THIS(Beanstalk);
106
107
EntityBeanstalk *next = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) + 1, Beanstalk);
108
109
if (self->forceEnd || next->classID != Beanstalk->classID)
110
return 0;
111
112
int32 thisX = ((self->bezCtrlLength * RSDK.Cos256(self->bezCtrlAngle)) << 9) + self->position.x;
113
int32 thisY = ((self->bezCtrlLength * RSDK.Sin256(self->bezCtrlAngle)) << 9) + self->position.y;
114
115
uint8 angle = (next->bezCtrlAngle + 0x80);
116
int32 nextX = ((next->bezCtrlLength * RSDK.Cos256(angle)) << 9) + next->position.x;
117
int32 nextY = ((next->bezCtrlLength * RSDK.Sin256(angle)) << 9) + next->position.y;
118
return MathHelpers_GetBezierCurveLength(self->position.x, self->position.y, thisX, thisY, nextX, nextY, next->position.x, next->position.y)
119
/ 0xA0000;
120
}
121
122
int32 Beanstalk_GetRemainingDistance(void)
123
{
124
RSDK_THIS(Beanstalk);
125
126
int32 len = 0;
127
while (self->type) {
128
EntityBeanstalk *prev = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) - 1, Beanstalk);
129
if (prev->classID != Beanstalk->classID)
130
break;
131
132
int32 prevX = ((prev->bezCtrlLength * RSDK.Cos256(prev->bezCtrlAngle)) << 9) + prev->position.x;
133
int32 prevY = ((prev->bezCtrlLength * RSDK.Sin256(prev->bezCtrlAngle)) << 9) + prev->position.y;
134
135
uint8 angle = (self->bezCtrlAngle + 0x80);
136
int32 thisX = ((self->bezCtrlLength * RSDK.Cos256(angle)) << 9) + self->position.x;
137
int32 thisY = ((self->bezCtrlLength * RSDK.Sin256(angle)) << 9) + self->position.y;
138
len += MathHelpers_GetBezierCurveLength(prev->position.x, prev->position.y, prevX, prevY, thisX, thisY, self->position.x, self->position.y);
139
140
self = prev;
141
}
142
143
return len;
144
}
145
146
int32 Beanstalk_GetBezierInc(void)
147
{
148
RSDK_THIS(Beanstalk);
149
150
EntityBeanstalk *next = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) + 1, Beanstalk);
151
152
if (self->forceEnd || next->classID != Beanstalk->classID)
153
return 0;
154
155
int32 thisX = ((self->bezCtrlLength * RSDK.Cos256(self->bezCtrlAngle)) << 9) + self->position.x;
156
int32 thisY = ((self->bezCtrlLength * RSDK.Sin256(self->bezCtrlAngle)) << 9) + self->position.y;
157
158
uint8 angle = (next->bezCtrlAngle + 0x80);
159
int32 nextX = ((next->bezCtrlLength * RSDK.Cos256(angle)) << 9) + next->position.x;
160
int32 nextY = ((next->bezCtrlLength * RSDK.Sin256(angle)) << 9) + next->position.y;
161
return 0x10000
162
/ (MathHelpers_GetBezierCurveLength(self->position.x, self->position.y, thisX, thisY, nextX, nextY, next->position.x, next->position.y)
163
/ 0x32000);
164
}
165
166
void Beanstalk_DrawNodes(void)
167
{
168
RSDK_THIS(Beanstalk);
169
170
if (!self->forceEnd) {
171
EntityBeanstalk *next = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) + 1, Beanstalk);
172
173
if (next->classID == Beanstalk->classID) {
174
if (SceneInfo->inEditor) {
175
self->curBezierPos = Beanstalk_GetNextNodeDistance();
176
self->bezierPos = 0x10000;
177
}
178
179
if (self->curBezierPos == (uint16)-1)
180
self->curBezierPos = Beanstalk_GetNextNodeDistance();
181
182
if (self->curBezierPos) {
183
if (!self->setupDecorNodeID || SceneInfo->inEditor) {
184
int32 distance = Beanstalk_GetRemainingDistance();
185
self->setupDecorNodeID = true;
186
self->curDecorID = (distance / 0xA0000 / 3) % 9;
187
self->curNodeID = 3 * ((distance / 0xA0000 / 3) + 1) - distance / 0xA0000;
188
}
189
190
int32 thisX = self->position.x + ((self->bezCtrlLength * RSDK.Cos256(self->bezCtrlAngle + 0x00)) << 9);
191
int32 thisY = self->position.y + ((self->bezCtrlLength * RSDK.Sin256(self->bezCtrlAngle + 0x00)) << 9);
192
int32 nextX = next->position.x + ((next->bezCtrlLength * RSDK.Cos256(next->bezCtrlAngle + 0x80)) << 9);
193
int32 nextY = next->position.y + ((next->bezCtrlLength * RSDK.Sin256(next->bezCtrlAngle + 0x80)) << 9);
194
195
int32 bezInc = 0x10000 / self->curBezierPos;
196
197
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 1, &self->fillerAnimator, true, 0);
198
for (int32 bezPos = (0x10000 / self->curBezierPos) >> 1; bezPos <= 0x10000; bezPos += bezInc) {
199
if (bezPos >= self->bezierPos)
200
break;
201
202
Vector2 drawPos = MathHelpers_GetBezierPoint(bezPos, self->position.x, self->position.y, thisX, thisY, nextX, nextY,
203
next->position.x, next->position.y);
204
205
RSDK.DrawSprite(&self->fillerAnimator, &drawPos, false);
206
}
207
208
int32 nodeID = self->curNodeID;
209
int32 decorID = self->curDecorID;
210
for (int32 bezPos = (0x10000 / self->curBezierPos) >> 1; bezPos <= 0x10000; ++nodeID) {
211
if (bezPos >= self->bezierPos)
212
break;
213
214
Vector2 drawPos = MathHelpers_GetBezierPoint(bezPos, self->position.x, self->position.y, thisX, thisY, nextX, nextY,
215
next->position.x, next->position.y);
216
217
if (!(nodeID % 3)) {
218
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, decorID + 5, &self->fillerAnimator, true, 0);
219
RSDK.DrawSprite(&self->fillerAnimator, &drawPos, false);
220
221
decorID = (decorID + 1) % 9;
222
}
223
224
bezPos += bezInc;
225
}
226
}
227
}
228
}
229
}
230
231
void Beanstalk_DrawCreationNode(void)
232
{
233
RSDK_THIS(Beanstalk);
234
235
// Draw the node that "walks" along the path and "creates" the beanstalk behind it
236
if (!self->forceEnd) {
237
EntityBeanstalk *next = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) + 1, Beanstalk);
238
239
if (next->classID == Beanstalk->classID) {
240
if (self->curBezierPos) {
241
int32 thisX = self->position.x + ((self->bezCtrlLength * RSDK.Cos256(self->bezCtrlAngle + 0x00)) << 9);
242
int32 thisY = self->position.y + ((self->bezCtrlLength * RSDK.Sin256(self->bezCtrlAngle + 0x00)) << 9);
243
int32 nextX = next->position.x + ((next->bezCtrlLength * RSDK.Cos256(next->bezCtrlAngle + 0x80)) << 9);
244
int32 nextY = next->position.y + ((next->bezCtrlLength * RSDK.Sin256(next->bezCtrlAngle + 0x80)) << 9);
245
246
Vector2 drawPos = MathHelpers_GetBezierPoint(self->bezierPos, self->position.x, self->position.y, thisX, thisY, nextX, nextY,
247
next->position.x, next->position.y);
248
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 1, &self->fillerAnimator, true, 0);
249
RSDK.DrawSprite(&self->fillerAnimator, &drawPos, false);
250
}
251
}
252
}
253
}
254
255
void Beanstalk_HandleNodeMovement(void)
256
{
257
RSDK_THIS(Beanstalk);
258
259
if (!self->finished) {
260
EntityBeanstalk *node = NULL;
261
262
if (!self->forceEnd) {
263
node = RSDK_GET_ENTITY(RSDK.GetEntitySlot(self) + 1, Beanstalk);
264
if (node->classID != Beanstalk->classID)
265
node = NULL;
266
}
267
268
if (self->forceEnd || !node)
269
self->bezierPos = 0x10000;
270
271
if (self->bezierPos < 0x10000) {
272
if (!self->bezierInc)
273
self->bezierInc = Beanstalk_GetBezierInc();
274
275
self->bezierPos += self->bezierInc;
276
}
277
278
if (self->bezierPos > 0x10000)
279
self->bezierPos = 0x10000;
280
281
if (self->bezierPos == 0x10000) {
282
self->finished = true;
283
self->active = ACTIVE_BOUNDS;
284
285
if (node) {
286
node->shown = true;
287
node->active = ACTIVE_NORMAL;
288
}
289
}
290
}
291
}
292
293
void Beanstalk_HandleNodeAppear(void)
294
{
295
RSDK_THIS(Beanstalk);
296
297
self->size = (self->timer << 9) / 20;
298
if (self->timer < 20) {
299
if (!self->timer) {
300
if (RSDK.CheckOnScreen(self, NULL))
301
RSDK.PlaySfx(Beanstalk->sfxBeanNode, false, 255);
302
}
303
304
++self->timer;
305
}
306
}
307
308
void Beanstalk_CheckPlayerCollisions_Platform(void)
309
{
310
RSDK_THIS(Beanstalk);
311
312
int32 storeX = self->position.x;
313
int32 storeY = self->position.y;
314
315
self->position = self->platformPos;
316
self->position.x = ((RSDK.Sin512(2 * self->moveAngle) << 9) + self->position.x) & 0xFFFF0000;
317
self->position.y &= 0xFFFF0000;
318
319
if (!self->activePlayers) {
320
if (self->stoodAngle > 0)
321
self->stoodAngle -= 4;
322
}
323
else {
324
if (self->stoodAngle < 64)
325
self->stoodAngle += 4;
326
}
327
328
self->activePlayers = 0;
329
foreach_active(Player, player)
330
{
331
int32 playerID = RSDK.GetEntitySlot(player);
332
if (Player_CheckCollisionPlatform(player, self, &Beanstalk->hitboxPlatform)) {
333
player->position.x += self->position.x - storeX;
334
335
if (self->position.y - storeY > 0)
336
player->position.y += self->position.y - storeY;
337
338
player->position.y += 0x10000;
339
player->position.y &= 0xFFFF0000;
340
self->activePlayers |= 1 << playerID;
341
}
342
}
343
344
self->platformPos.x = self->startPos.x;
345
self->platformPos.y = self->startPos.y + (RSDK.Sin256(self->stoodAngle) << 10);
346
}
347
348
void Beanstalk_CheckPlayerCollisions_Chomper(void)
349
{
350
RSDK_THIS(Beanstalk);
351
352
int32 mult = 0;
353
if (self->chomperOffset >= 15) {
354
if (self->chomperOffset >= 60) {
355
self->chomperOffset = 0;
356
}
357
else {
358
mult = 0x15540 - 0x5B0 * self->chomperOffset;
359
self->chomperOffset++;
360
}
361
}
362
else {
363
mult = 0x1111 * self->chomperOffset;
364
self->chomperOffset++;
365
}
366
367
self->position = self->startPos;
368
if (self->direction == FLIP_X)
369
self->position.x += mult * -16;
370
else
371
self->position.x += mult * 16;
372
373
foreach_active(Player, player)
374
{
375
if (Player_CheckCollisionTouch(player, self, &Beanstalk->hitboxPlant)) {
376
#if MANIA_USE_PLUS
377
if (player->state == Player_State_MightyHammerDrop) {
378
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_ENEMY), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
379
RSDK.PlaySfx(Explosion->sfxDestroy, false, 255);
380
self->state = Beanstalk_State_Node;
381
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->plantAnimator, true, 0);
382
}
383
else if (!Player_CheckMightyUnspin(player, 0x400, 2, &player->uncurlTimer)) {
384
#endif
385
Player_Hurt(player, self);
386
#if MANIA_USE_PLUS
387
}
388
#endif
389
}
390
}
391
}
392
393
void Beanstalk_State_Init(void)
394
{
395
RSDK_THIS(Beanstalk);
396
397
switch (self->type) {
398
case BEANSTALK_BEGIN:
399
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 4, &self->nodeAnimator, true, 9);
400
self->state = Beanstalk_StateDirt_WaitForStart;
401
break;
402
403
case BEANSTALK_NODE:
404
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->nodeAnimator, true, 0);
405
self->state = Beanstalk_State_Node;
406
break;
407
408
case BEANSTALK_PLATFORM:
409
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->nodeAnimator, true, 0);
410
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 2, &self->plantAnimator, true, 0);
411
self->state = Beanstalk_State_Platform;
412
break;
413
414
case BEANSTALK_CHOMPER:
415
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->nodeAnimator, true, 0);
416
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 3, &self->plantAnimator, true, 0);
417
self->state = Beanstalk_State_Chomper;
418
break;
419
420
default: break;
421
}
422
}
423
424
void Beanstalk_StateDirt_WaitForStart(void)
425
{
426
RSDK_THIS(Beanstalk);
427
428
if (self->startGrowth) {
429
self->state = Beanstalk_StateDirt_GrowthDelay;
430
self->timer = 15;
431
}
432
}
433
434
void Beanstalk_StateDirt_GrowthDelay(void)
435
{
436
RSDK_THIS(Beanstalk);
437
438
if (self->timer <= 0) {
439
RSDK.PlaySfx(Beanstalk->sfxImpact, false, 255);
440
Camera_ShakeScreen(0, 0, 5);
441
442
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 4, &self->nodeAnimator, true, 0);
443
self->shown = true;
444
self->active = ACTIVE_NORMAL;
445
self->state = Beanstalk_StateDirt_Grow;
446
}
447
else {
448
self->timer--;
449
}
450
}
451
452
void Beanstalk_StateDirt_Grow(void) { Beanstalk_HandleNodeMovement(); }
453
454
void Beanstalk_State_Node(void)
455
{
456
RSDK_THIS(Beanstalk);
457
458
if (self->shown) {
459
Beanstalk_HandleNodeMovement();
460
Beanstalk_HandleNodeAppear();
461
462
self->position = self->startPos;
463
self->position.x = ((RSDK.Sin512(2 * self->moveAngle) << 9) + self->position.x) & 0xFFFF0000;
464
self->position.y &= 0xFFFF0000;
465
466
++self->moveAngle;
467
}
468
}
469
470
void Beanstalk_State_Platform(void)
471
{
472
RSDK_THIS(Beanstalk);
473
474
if (self->shown) {
475
Beanstalk_HandleNodeMovement();
476
Beanstalk_HandleNodeAppear();
477
478
Beanstalk_CheckPlayerCollisions_Platform();
479
480
++self->moveAngle;
481
}
482
}
483
484
void Beanstalk_State_Chomper(void)
485
{
486
RSDK_THIS(Beanstalk);
487
488
if (self->shown) {
489
Beanstalk_HandleNodeMovement();
490
Beanstalk_HandleNodeAppear();
491
492
Beanstalk_CheckPlayerCollisions_Chomper();
493
494
if (RSDK.CheckOnScreen(self, NULL)) {
495
if (self->plantAnimator.frameID == 5 && self->plantAnimator.timer == 1)
496
RSDK.PlaySfx(Beanstalk->sfxBeanChomp, false, 255);
497
}
498
}
499
}
500
501
#if GAME_INCLUDE_EDITOR
502
void Beanstalk_EditorDraw(void)
503
{
504
RSDK_THIS(Beanstalk);
505
506
self->bezCtrlAngle &= 0xFF;
507
self->curBezierPos = -1;
508
self->size = 0x200;
509
self->startPos = self->position;
510
self->platformPos = self->position;
511
self->stoodAngle = 0;
512
self->timer = 0;
513
self->chomperOffset = 0;
514
515
switch (self->type) {
516
case BEANSTALK_BEGIN: RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 4, &self->nodeAnimator, true, 9); break;
517
518
case BEANSTALK_NODE: RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->nodeAnimator, true, 0); break;
519
520
case BEANSTALK_PLATFORM:
521
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->nodeAnimator, true, 0);
522
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 2, &self->plantAnimator, true, 0);
523
break;
524
525
case BEANSTALK_CHOMPER:
526
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 0, &self->nodeAnimator, true, 0);
527
RSDK.SetSpriteAnimation(Beanstalk->aniFrames, 3, &self->plantAnimator, true, 0);
528
break;
529
530
default: break;
531
}
532
533
RSDK_DRAWING_OVERLAY(true);
534
Beanstalk_DrawNodes();
535
RSDK_DRAWING_OVERLAY(false);
536
537
self->scale.x = self->size;
538
self->scale.y = self->size;
539
self->drawFX = FX_SCALE | FX_FLIP;
540
RSDK.DrawSprite(&self->nodeAnimator, NULL, false);
541
542
if (self->type > BEANSTALK_NODE)
543
RSDK.DrawSprite(&self->plantAnimator, NULL, false);
544
545
self->drawFX = FX_FLIP;
546
self->scale.x = 0x200;
547
self->scale.y = 0x200;
548
}
549
550
void Beanstalk_EditorLoad(void)
551
{
552
Beanstalk->aniFrames = RSDK.LoadSpriteAnimation("SSZ1/Beanstalk.bin", SCOPE_STAGE);
553
554
RSDK_ACTIVE_VAR(Beanstalk, type);
555
RSDK_ENUM_VAR("Dirt Splash", BEANSTALK_BEGIN);
556
RSDK_ENUM_VAR("Node", BEANSTALK_NODE);
557
RSDK_ENUM_VAR("Platform", BEANSTALK_PLATFORM);
558
RSDK_ENUM_VAR("Chomper", BEANSTALK_CHOMPER);
559
560
RSDK_ACTIVE_VAR(Beanstalk, direction);
561
RSDK_ENUM_VAR("Right", FLIP_NONE);
562
RSDK_ENUM_VAR("Left", FLIP_X);
563
}
564
#endif
565
566
void Beanstalk_Serialize(void)
567
{
568
RSDK_EDITABLE_VAR(Beanstalk, VAR_UINT8, type);
569
RSDK_EDITABLE_VAR(Beanstalk, VAR_UINT8, direction);
570
RSDK_EDITABLE_VAR(Beanstalk, VAR_INT32, bezCtrlAngle);
571
RSDK_EDITABLE_VAR(Beanstalk, VAR_INT32, bezCtrlLength);
572
RSDK_EDITABLE_VAR(Beanstalk, VAR_BOOL, forceEnd);
573
}
574
575