Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Pinball/PBL_Crane.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: PBL_Crane Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
#if MANIA_USE_PLUS
11
ObjectPBL_Crane *PBL_Crane;
12
13
void PBL_Crane_Update(void)
14
{
15
RSDK_THIS(PBL_Crane);
16
17
StateMachine_Run(self->state);
18
}
19
20
void PBL_Crane_LateUpdate(void) {}
21
22
void PBL_Crane_StaticUpdate(void) {}
23
24
void PBL_Crane_Draw(void)
25
{
26
RSDK_THIS(PBL_Crane);
27
28
if (self->stateDraw) {
29
StateMachine_Run(self->stateDraw);
30
}
31
else {
32
RSDK.DrawSprite(&self->displayAnimator, NULL, true);
33
}
34
}
35
36
void PBL_Crane_Create(void *data)
37
{
38
RSDK_THIS(PBL_Crane);
39
40
if (!SceneInfo->inEditor) {
41
self->drawGroup = 9;
42
self->active = ACTIVE_NORMAL;
43
self->type = VOID_TO_INT(data);
44
45
switch (self->type) {
46
case PBL_CRANE_MANAGER:
47
self->position.x = ScreenInfo->center.x << 16;
48
self->position.y = 0;
49
50
self->alpha = 0xFF;
51
self->scale.x = 0x600;
52
self->scale.y = 0x200;
53
54
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 2, &self->displayAnimator, true, 0);
55
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 0, &self->machineAnimator, true, 1);
56
57
self->stateDraw = PBL_Crane_Draw_CraneMachine;
58
break;
59
60
case PBL_CRANE_CRANE:
61
self->visible = true;
62
63
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 3, &self->bgAnimator, true, 0);
64
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 4, &self->craneBackAnimator, true, 0);
65
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 5, &self->craneFrontAnimator, true, 0);
66
67
self->scale.x = 0x200;
68
self->scale.y = 0x200;
69
70
self->state = PBL_Crane_StateCrane_Lower;
71
self->stateDraw = PBL_Crane_Draw_Crane;
72
break;
73
74
case PBL_CRANE_PRIZEDISPLAY:
75
self->visible = true;
76
77
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 6, &self->displayAnimator, true, 0);
78
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 2, &self->bgAnimator, true, 4);
79
80
self->state = PBL_Crane_StatePrizeDisplay_Move;
81
self->stateDraw = PBL_Crane_Draw_PrizeDisplay;
82
break;
83
84
case PBL_CRANE_PRIZE:
85
self->drawGroup = 12;
86
self->visible = true;
87
self->scale.x = 0x180;
88
self->scale.y = 0x180;
89
self->drawFX = FX_SCALE;
90
91
self->state = PBL_Crane_StatePrize_Bounce;
92
break;
93
94
default: break;
95
}
96
}
97
}
98
99
void PBL_Crane_StageLoad(void)
100
{
101
PBL_Crane->aniFrames = RSDK.LoadSpriteAnimation("Pinball/Backglass.bin", SCOPE_STAGE);
102
103
PBL_Crane->sfxBackGlass = RSDK.GetSfx("Pinball/Backglass.wav");
104
PBL_Crane->sfxCraneMove = RSDK.GetSfx("Pinball/CraneMove.wav");
105
PBL_Crane->sfxCraneDrop = RSDK.GetSfx("Pinball/CraneDrop.wav");
106
PBL_Crane->sfxCraneGrab = RSDK.GetSfx("Pinball/CraneGrab.wav");
107
PBL_Crane->sfxCraneRise = RSDK.GetSfx("Pinball/CraneRise.wav");
108
PBL_Crane->sfxPrizeGood = RSDK.GetSfx("Pinball/PrizeGood.wav");
109
PBL_Crane->sfxPrizeBad = RSDK.GetSfx("Pinball/PrizeBad.wav");
110
}
111
112
void PBL_Crane_HandlePrizes(void)
113
{
114
RSDK_THIS(PBL_Crane);
115
116
switch (self->displayAnimator.frameID) {
117
case PBL_CRANE_PRIZE_SONIC:
118
case PBL_CRANE_PRIZE_TAILS:
119
case PBL_CRANE_PRIZE_KNUCKLES:
120
case PBL_CRANE_PRIZE_MIGHTY:
121
case PBL_CRANE_PRIZE_RAY: {
122
int32 playerID = 1 << self->displayAnimator.frameID;
123
globals->characterFlags |= playerID;
124
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_BUDDY;
125
126
if (!GET_CHARACTER_ID(1))
127
globals->playerID |= playerID;
128
else if (!GET_CHARACTER_ID(2))
129
globals->playerID |= playerID << 8;
130
else if (!GET_STOCK_ID(1))
131
globals->stock |= playerID;
132
else if (!GET_STOCK_ID(2))
133
globals->stock |= playerID << 8;
134
else if (!GET_STOCK_ID(3))
135
globals->stock |= playerID << 16;
136
137
break;
138
}
139
140
case PBL_CRANE_PRIZE_EGGMAN:
141
PBL_Setup->rings = 0;
142
PBL_Setup->score = 0;
143
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_BAD;
144
break;
145
146
case PBL_CRANE_PRIZE_RINGS:
147
globals->restartRings += PBL_Setup->rings;
148
globals->restartRings = CLAMP(globals->restartRings, 0, 999);
149
150
if (globals->gameMode == MODE_MANIA && globals->restartRings >= globals->restart1UP) {
151
PBL_Setup_GiveLife();
152
globals->restart1UP += 100;
153
if (globals->restart1UP > 300)
154
globals->restart1UP = 1000;
155
}
156
157
PBL_Setup->rings = 0;
158
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_RINGS;
159
break;
160
161
case PBL_CRANE_PRIZE_SHIELD_BLUE:
162
case PBL_CRANE_PRIZE_SHIELD_BUBBLE:
163
case PBL_CRANE_PRIZE_SHIELD_FIRE:
164
case PBL_CRANE_PRIZE_SHIELD_ELECTRIC:
165
globals->restartPowerups &= ~0x3F;
166
globals->restartPowerups |= self->displayAnimator.frameID - PBL_CRANE_PRIZE_SHIELD_BLUE + SHIELD_BLUE; // Converts the frame ID to a shield ID
167
168
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_ITEM;
169
break;
170
171
case PBL_CRANE_PRIZE_TABLE_RESTORE: {
172
foreach_all(PBL_Bumper, bumper)
173
{
174
if (!bumper->state) {
175
bumper->velocity.y = 32;
176
bumper->state = PBL_TargetBumper_State_Rise;
177
bumper->active = ACTIVE_NORMAL;
178
}
179
}
180
181
foreach_all(PBL_Ring, ring)
182
{
183
if (ring->active == ACTIVE_NEVER) {
184
ring->active = ACTIVE_BOUNDS;
185
RSDK.SetSpriteAnimation(PBL_Ring->aniFrames, 1, &ring->animator, true, 0);
186
ring->state = PBL_Ring_State_Ring;
187
}
188
}
189
190
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_TBLRESTORE;
191
break;
192
}
193
194
case PBL_CRANE_PRIZE_1UP:
195
if (SaveGame_GetSaveRAM()->lives < 99)
196
SaveGame_GetSaveRAM()->lives++;
197
198
if (globals->restartLives[0] < 99)
199
globals->restartLives[0]++;
200
201
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_1UP;
202
break;
203
204
default: break;
205
}
206
}
207
208
void PBL_Crane_Draw_CraneMachine(void)
209
{
210
RSDK_THIS(PBL_Crane);
211
212
self->position.x = ScreenInfo->center.x << 16;
213
if (SceneInfo->currentDrawGroup == self->drawGroup) {
214
self->displayAnimator.frameID = 1;
215
self->inkEffect = INK_NONE;
216
RSDK.DrawSprite(&self->displayAnimator, NULL, true);
217
RSDK.DrawSprite(&self->machineAnimator, NULL, true);
218
219
RSDK.AddDrawListRef(11, SceneInfo->entitySlot);
220
221
RSDK.SetClipBounds(0, ScreenInfo->center.x - 96, 0, ScreenInfo->center.x + 96, (self->position.y >> 16) + 64);
222
}
223
else {
224
RSDK.SetClipBounds(0, 0, 0, ScreenInfo->size.x, ScreenInfo->size.y);
225
226
self->displayAnimator.frameID = 0;
227
RSDK.DrawSprite(&self->displayAnimator, NULL, true);
228
229
self->displayAnimator.frameID = 2;
230
self->drawFX = FX_SCALE;
231
self->inkEffect = INK_ADD;
232
RSDK.DrawSprite(&self->displayAnimator, NULL, true);
233
234
self->displayAnimator.frameID = 3;
235
RSDK.DrawSprite(&self->displayAnimator, NULL, true);
236
237
self->drawFX = FX_NONE;
238
}
239
}
240
241
void PBL_Crane_Draw_PrizeDisplay(void)
242
{
243
RSDK_THIS(PBL_Crane);
244
245
RSDK.DrawSprite(&self->bgAnimator, NULL, true);
246
RSDK.DrawSprite(&self->displayAnimator, NULL, true);
247
}
248
249
void PBL_Crane_Draw_Crane(void)
250
{
251
RSDK_THIS(PBL_Crane);
252
253
Vector2 drawPos;
254
255
drawPos.x = self->position.x;
256
drawPos.y = self->position.y + 0x1A0000;
257
RSDK.DrawSprite(&self->craneBackAnimator, NULL, true);
258
259
self->drawFX = FX_SCALE;
260
RSDK.DrawSprite(&self->displayAnimator, &drawPos, true);
261
262
self->drawFX = FX_NONE;
263
RSDK.DrawSprite(&self->bgAnimator, NULL, true);
264
RSDK.DrawSprite(&self->craneFrontAnimator, NULL, true);
265
}
266
267
void PBL_Crane_State_CreatePrizes(void)
268
{
269
RSDK_THIS(PBL_Crane);
270
271
EntityPBL_Camera *camera = RSDK_GET_ENTITY(SLOT_PBL_CAMERA, PBL_Camera);
272
273
self->position.y = 0;
274
camera->worldY = 0x1000000;
275
self->cameraTarget = camera->target;
276
camera->target = NULL;
277
self->visible = true;
278
279
int32 spawnX = self->position.x - 0x6C0000;
280
int32 spawnY = 0x600000 + self->position.y;
281
self->state = PBL_Crane_State_DisplayPrizes;
282
283
for (int32 i = 0; i < 6; ++i) {
284
EntityPBL_Crane *prize = CREATE_ENTITY(PBL_Crane, INT_TO_VOID(PBL_CRANE_PRIZEDISPLAY), spawnX, spawnY);
285
if (globals->gameMode == MODE_ENCORE) {
286
if (!((1 << i) & globals->characterFlags) || i == PBL_CRANE_PRIZE_EGGMAN) {
287
prize->displayAnimator.frameID = i;
288
}
289
else {
290
prize->displayAnimator.frameID = RSDK.Rand(PBL_CRANE_PRIZE_RINGS, PBL_CRANE_PRIZE_TABLE_RESTORE + 1);
291
}
292
}
293
else {
294
if (i == 5) {
295
prize->displayAnimator.frameID = PBL_CRANE_PRIZE_EGGMAN;
296
}
297
else {
298
prize->displayAnimator.frameID = RSDK.Rand(PBL_CRANE_PRIZE_RINGS, PBL_CRANE_PRIZE_1UP + 1);
299
}
300
}
301
302
spawnX += 0x280000;
303
prize->parent = self;
304
}
305
306
CREATE_ENTITY(PBL_Crane, INT_TO_VOID(PBL_CRANE_CRANE), self->position.x, -0x180000)->parent = self;
307
308
if (PBL_Setup->sectorID < PBL_Setup->sectorCount)
309
PBL_Setup->sectorID++;
310
311
PBL_Crane->prizeID = PBL_CRANE_PRIZEID_NOTHING;
312
}
313
314
void PBL_Crane_State_DisplayPrizes(void)
315
{
316
RSDK_THIS(PBL_Crane);
317
318
if (self->position.y >= 0x300000) {
319
self->timer = 0;
320
self->state = StateMachine_None;
321
}
322
else {
323
EntityPBL_Camera *camera = RSDK_GET_ENTITY(SLOT_PBL_CAMERA, PBL_Camera);
324
325
self->position.y += 0x10000;
326
camera->worldY += 0x28000;
327
camera->position.y -= 0x14000;
328
329
if (!self->timer)
330
--camera->rotationY;
331
332
self->timer = (self->timer + 1) & 3;
333
}
334
}
335
336
void PBL_Crane_StatePrizeDisplay_Move(void)
337
{
338
RSDK_THIS(PBL_Crane);
339
340
EntityPBL_Crane *parent = self->parent;
341
342
self->position.x -= 0x10000;
343
int32 dist = abs(self->position.x - parent->position.x);
344
self->position.y = (dist >> 13) * (dist >> 13) + (parent->position.y + 0x300000);
345
346
if (self->position.x < parent->position.x - 0x980000) {
347
self->position.x += 0x1100000;
348
}
349
}
350
351
void PBL_Crane_StateCrane_Lower(void)
352
{
353
RSDK_THIS(PBL_Crane);
354
355
EntityPBL_Crane *parent = self->parent;
356
357
if (!parent->state) {
358
if (self->position.y >= 0x100000)
359
self->state = PBL_Crane_StateCrane_Move;
360
else
361
self->position.y += 0x8000;
362
}
363
}
364
365
void PBL_Crane_StateCrane_Move(void)
366
{
367
RSDK_THIS(PBL_Crane);
368
369
self->velocity.x = (-((ControllerInfo[CONT_P1].keyLeft.down || AnalogStickInfoL[CONT_P1].keyLeft.down) != 0) & 0xFFFF0000) + 0x8000;
370
371
if (++self->timer == 30) {
372
self->timer = 0;
373
self->state = PBL_Crane_StateCrane_Controlled;
374
RSDK.PlaySfx(PBL_Crane->sfxCraneMove, false, 255);
375
}
376
}
377
378
void PBL_Crane_StateCrane_Controlled(void)
379
{
380
RSDK_THIS(PBL_Crane);
381
382
EntityPBL_Crane *parent = self->parent;
383
384
if (AnalogStickInfoL[CONT_P1].keyLeft.press || ControllerInfo[CONT_P1].keyLeft.press) {
385
++self->timer;
386
self->velocity.x = -0x18000;
387
RSDK.PlaySfx(PBL_Crane->sfxCraneMove, false, 0xFF);
388
}
389
else if (AnalogStickInfoL[CONT_P1].keyRight.press || ControllerInfo[CONT_P1].keyRight.press) {
390
++self->timer;
391
self->velocity.x = 0x18000;
392
RSDK.PlaySfx(PBL_Crane->sfxCraneMove, false, 0xFF);
393
}
394
else if (AnalogStickInfoL[CONT_P1].keyDown.press || ControllerInfo[CONT_P1].keyDown.press) {
395
self->timer = 4;
396
}
397
398
self->position.x += self->velocity.x;
399
400
if (self->velocity.x <= 0) {
401
if (self->position.x <= parent->position.x - 0x600000)
402
self->velocity.x = -self->velocity.x;
403
}
404
else {
405
if (self->position.x >= parent->position.x + 0x600000)
406
self->velocity.x = -self->velocity.x;
407
}
408
409
if (self->timer == 4) {
410
self->timer = 0;
411
self->state = PBL_Crane_StateCrane_Drop;
412
RSDK.PlaySfx(PBL_Crane->sfxCraneDrop, false, 255);
413
}
414
}
415
416
void PBL_Crane_StateCrane_Drop(void)
417
{
418
RSDK_THIS(PBL_Crane);
419
420
EntityPBL_Crane *parent = self->parent;
421
422
self->position.y += 0x10000;
423
if (self->position.y >= parent->position.y + 0x180000) {
424
foreach_active(PBL_Crane, crane)
425
{
426
if (crane->type == PBL_CRANE_PRIZEDISPLAY) {
427
if (abs(crane->position.x - self->position.x) < 0x100000) {
428
memcpy(&self->displayAnimator, &crane->displayAnimator, sizeof(Animator));
429
RSDK.SetSpriteAnimation(-1, 0, &crane->displayAnimator, false, 0);
430
}
431
}
432
}
433
434
self->state = PBL_Crane_StateCrane_Grab;
435
RSDK.PlaySfx(PBL_Crane->sfxCraneGrab, false, 255);
436
}
437
}
438
439
void PBL_Crane_StateCrane_Grab(void)
440
{
441
RSDK_THIS(PBL_Crane);
442
443
++self->timer;
444
445
if (self->scale.x > 0x180) {
446
self->scale.x -= 0x10;
447
self->scale.y -= 0x10;
448
}
449
450
if (self->timer > 8) {
451
self->craneBackAnimator.frameID = (self->timer - 8) >> 1;
452
self->craneFrontAnimator.frameID = (self->timer - 8) >> 1;
453
}
454
455
if (self->craneBackAnimator.frameID == self->craneBackAnimator.frameCount - 1) {
456
self->timer = 0;
457
self->state = PBL_Crane_StateCrane_Rise;
458
RSDK.PlaySfx(PBL_Crane->sfxCraneRise, false, 255);
459
}
460
}
461
462
void PBL_Crane_StateCrane_Rise(void)
463
{
464
RSDK_THIS(PBL_Crane);
465
466
EntityPBL_Crane *parent = self->parent;
467
468
self->position.y -= 0x10000;
469
470
if (self->scale.x > 0x180) {
471
self->scale.x -= 0x10;
472
self->scale.y -= 0x10;
473
}
474
475
if (self->position.y <= 0x100000) {
476
if (self->displayAnimator.animationID == 6) {
477
EntityPBL_Crane *crane = CREATE_ENTITY(PBL_Crane, INT_TO_VOID(PBL_CRANE_PRIZE), self->position.x, self->position.y + 0x1A0000);
478
crane->displayAnimator = self->displayAnimator;
479
crane->parent = self->parent;
480
RSDK.SetSpriteAnimation(-1, 0, &self->displayAnimator, false, 0);
481
482
crane->velocity.x = self->position.x > parent->position.x ? 0x20000 : -0x20000;
483
crane->velocity.y = -0x20000;
484
485
if (crane->displayAnimator.frameID != 5) {
486
RSDK.PlaySfx(PBL_Crane->sfxPrizeGood, false, 255);
487
self->state = StateMachine_None;
488
}
489
else {
490
RSDK.PlaySfx(PBL_Crane->sfxPrizeBad, false, 255);
491
self->state = StateMachine_None;
492
}
493
}
494
else {
495
parent->state = PBL_Crane_StatePrize_PrizeGet;
496
RSDK.PlaySfx(PBL_Crane->sfxPrizeBad, false, 255);
497
self->state = StateMachine_None;
498
}
499
}
500
}
501
502
void PBL_Crane_StatePrize_Bounce(void)
503
{
504
RSDK_THIS(PBL_Crane);
505
506
if (self->scale.x >= 0x200) {
507
self->drawFX = FX_NONE;
508
}
509
else {
510
self->scale.x += 0x10;
511
self->scale.y += 0x10;
512
}
513
514
self->velocity.y += 0x2800;
515
self->position.x += self->velocity.x;
516
self->position.y += self->velocity.y;
517
518
if (self->position.y > 0x500000) {
519
self->velocity.y = -(self->velocity.y >> 1);
520
521
if (++self->timer == 2) {
522
self->timer = 0;
523
self->state = PBL_Crane_StatePrize_Flash;
524
}
525
}
526
}
527
528
void PBL_Crane_StatePrize_Flash(void)
529
{
530
RSDK_THIS(PBL_Crane);
531
532
self->visible = (++self->timer >> 2) & 1;
533
534
if (self->timer == 60) {
535
PBL_Crane_HandlePrizes();
536
self->parent->state = PBL_Crane_StatePrize_PrizeGet;
537
destroyEntity(self);
538
}
539
}
540
541
void PBL_Crane_StatePrize_PrizeGet(void)
542
{
543
RSDK_THIS(PBL_Crane);
544
545
if (++self->timer == 24) {
546
foreach_active(PBL_HUD, hud)
547
{
548
hud->state = PBL_HUD_State_HideCrane;
549
hud->visible = true;
550
}
551
}
552
553
EntityPBL_Camera *camera = RSDK_GET_ENTITY(SLOT_PBL_CAMERA, PBL_Camera);
554
if (self->position.y <= 0) {
555
foreach_active(PBL_Crane, crane)
556
{
557
if (crane != self)
558
destroyEntity(crane);
559
}
560
561
self->timer = 0;
562
self->state = StateMachine_None;
563
564
foreach_all(PBL_Sector, sector)
565
{
566
if (sector->craneID == PBL_Setup->sectorID)
567
sector->active = ACTIVE_NORMAL;
568
}
569
570
LogHelpers_PrintInt32("Sector", PBL_Setup->sectorID);
571
camera->target = self->cameraTarget;
572
camera->rotationY = -96;
573
574
foreach_active(PBL_HUD, hud)
575
{
576
switch (PBL_Crane->prizeID) {
577
case PBL_CRANE_PRIZEID_NOTHING: PBL_HUD_DisplayMessage(hud, "TOO BAD!", PBL_HUD_MSG_FLASH); continue;
578
case PBL_CRANE_PRIZEID_BAD: PBL_HUD_DisplayMessage(hud, "OH NO!", PBL_HUD_MSG_FLASH); continue;
579
case PBL_CRANE_PRIZEID_BUDDY: PBL_HUD_DisplayMessage(hud, "NICE SAVE!", PBL_HUD_MSG_SCROLL_RIGHT); continue;
580
case PBL_CRANE_PRIZEID_RINGS: PBL_HUD_DisplayMessage(hud, "!RINGS DEPOSITED!", PBL_HUD_MSG_SCROLL_LEFT); break;
581
case PBL_CRANE_PRIZEID_ITEM: PBL_HUD_DisplayMessage(hud, "!ITEM COLLECTED!", PBL_HUD_MSG_SCROLL_LEFT); break;
582
case PBL_CRANE_PRIZEID_TBLRESTORE: PBL_HUD_DisplayMessage(hud, "!TABLE RESTORED!", PBL_HUD_MSG_SCROLL_LEFT); break;
583
case PBL_CRANE_PRIZEID_1UP: PBL_HUD_DisplayMessage(hud, "!EXTRA LIFE!", PBL_HUD_MSG_SCROLL_LEFT); break;
584
default: break;
585
}
586
}
587
}
588
else {
589
self->position.y = self->position.y - 0x10000;
590
camera->worldY -= 0x28000;
591
camera->position.y += 0x14000;
592
593
if ((self->timer & 3) == 1)
594
++camera->rotationY;
595
}
596
}
597
598
#if GAME_INCLUDE_EDITOR
599
void PBL_Crane_EditorDraw(void)
600
{
601
RSDK_THIS(PBL_Crane);
602
603
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 4, &self->craneBackAnimator, true, 0);
604
RSDK.SetSpriteAnimation(PBL_Crane->aniFrames, 5, &self->craneFrontAnimator, true, 0);
605
606
RSDK.DrawSprite(&self->craneBackAnimator, NULL, false);
607
RSDK.DrawSprite(&self->craneFrontAnimator, NULL, false);
608
}
609
610
void PBL_Crane_EditorLoad(void) { PBL_Crane->aniFrames = RSDK.LoadSpriteAnimation("Pinball/Backglass.bin", SCOPE_STAGE); }
611
#endif
612
613
void PBL_Crane_Serialize(void) {}
614
#endif
615
616