Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/PGZ/Ice.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Ice Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectIce *Ice;
11
12
void Ice_Update(void)
13
{
14
RSDK_THIS(Ice);
15
16
StateMachine_Run(self->state);
17
}
18
19
void Ice_LateUpdate(void) {}
20
21
void Ice_StaticUpdate(void)
22
{
23
for (int32 i = 0; i < Player->playerCount; ++i) {
24
if (Ice->playerTimers[i] > 0)
25
Ice->playerTimers[i]--;
26
}
27
}
28
29
void Ice_Draw(void)
30
{
31
RSDK_THIS(Ice);
32
33
StateMachine_Run(self->stateDraw);
34
}
35
36
void Ice_Create(void *data)
37
{
38
RSDK_THIS(Ice);
39
40
if (!SceneInfo->inEditor) {
41
self->drawFX = FX_FLIP;
42
self->drawGroup = Zone->playerDrawGroup[0] + 1;
43
self->visible = true;
44
45
if (data) {
46
self->active = ACTIVE_NORMAL;
47
48
switch (VOID_TO_INT(data)) {
49
case ICE_CHILD_PLAYER:
50
self->hitboxBlock.left = -24;
51
self->hitboxBlock.top = -24;
52
self->hitboxBlock.right = 24;
53
self->hitboxBlock.bottom = 24;
54
55
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PLAYERBLOCK, &self->blockAnimator, true, 0);
56
57
self->isPermanent = true;
58
self->state = Ice_State_PlayerBlock;
59
self->stateDraw = Ice_Draw_PlayerBlock;
60
break;
61
62
case ICE_CHILD_PILLAR:
63
self->updateRange.x = 0x800000;
64
self->updateRange.y = 0x800000;
65
66
self->hitboxBlock.left = -19;
67
self->hitboxBlock.top = -110;
68
self->hitboxBlock.right = 19;
69
self->hitboxBlock.bottom = 0;
70
71
self->hitboxPlayerBlockCheck.left = -19;
72
self->hitboxPlayerBlockCheck.top = -110;
73
self->hitboxPlayerBlockCheck.right = 19;
74
self->hitboxPlayerBlockCheck.bottom = 0;
75
76
self->glintTimer = 240;
77
self->alpha = 0x80;
78
79
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PILLARBLOCK, &self->blockAnimator, true, 0);
80
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PILLARGLINT, &self->glintAnimator, true, 0);
81
82
self->state = Ice_State_IceBlock;
83
self->stateDraw = Ice_Draw_Pillar;
84
break;
85
case ICE_CHILD_SHARD:
86
self->updateRange.x = 0x400000;
87
self->updateRange.y = 0x400000;
88
89
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_SHARD, &self->blockAnimator, true, 0);
90
91
self->state = Ice_State_Shard;
92
self->stateDraw = Ice_Draw_Shard;
93
break;
94
}
95
}
96
else {
97
self->active = ACTIVE_BOUNDS;
98
self->updateRange.x = 0x800000;
99
self->updateRange.y = 0x800000;
100
101
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_ICEBLOCK, &self->blockAnimator, true, self->size);
102
switch (self->type) {
103
case ICE_BLOCK: RSDK.SetSpriteAnimation(-1, ICEANI_ICEBLOCK, &self->contentsAnimator, true, 0); break;
104
case ICE_1RING: RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_RINGS, &self->contentsAnimator, true, 0); break;
105
case ICE_3RINGS: RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_RINGS, &self->contentsAnimator, true, 1); break;
106
case ICE_5RINGS: RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_RINGS, &self->contentsAnimator, true, 2); break;
107
108
case ICE_SPIKES:
109
self->subType = self->subType & 3;
110
RSDK.SetSpriteAnimation(Spikes->aniFrames, self->subType >> 1, &self->contentsAnimator, true, 0);
111
if (!self->size) {
112
// direction of the spikes, matches the spikes "type" variable
113
switch (self->subType) {
114
case 0:
115
self->contentsOffset.y = 0x40000;
116
self->subFlip = FLIP_NONE;
117
break;
118
119
case 1:
120
self->contentsOffset.y = -0x40000;
121
self->subFlip = FLIP_Y;
122
break;
123
124
case 2:
125
self->contentsOffset.x = -0x80000;
126
self->subFlip = FLIP_NONE;
127
break;
128
129
case 3:
130
self->contentsOffset.x = 0x80000;
131
self->subFlip = FLIP_X;
132
break;
133
134
default: break;
135
}
136
}
137
break;
138
139
case ICE_SPRING:
140
// Ice Springs (0-2), Normal Springs (2-8)
141
if (self->subType >= 3)
142
RSDK.SetSpriteAnimation(Spring->aniFrames, (self->subType - 3), &self->contentsAnimator, true, 0);
143
else
144
RSDK.SetSpriteAnimation(IceSpring->aniFrames, self->subType, &self->contentsAnimator, true, 0);
145
146
if (self->size) {
147
switch (self->subType) {
148
case 0:
149
case 3:
150
case 4:
151
self->contentsOffset.x = 0;
152
if (!(self->subFlip & FLIP_Y))
153
self->contentsOffset.y = 0x80000;
154
else
155
self->contentsOffset.y = -0x80000;
156
break;
157
158
case 1:
159
case 5:
160
case 6:
161
self->contentsOffset.y = 0;
162
if (!(self->subFlip & FLIP_X))
163
self->contentsOffset.x = -0x80000;
164
else
165
self->contentsOffset.x = 0x80000;
166
break;
167
168
case 2:
169
case 7:
170
case 8:
171
self->contentsOffset.x = 0;
172
if (!(self->subFlip & FLIP_Y))
173
self->contentsOffset.y = -0x10000;
174
else
175
self->contentsOffset.y = 0x10000;
176
break;
177
178
default: break;
179
}
180
}
181
else {
182
switch (self->subType) {
183
case 0:
184
case 3:
185
case 4:
186
self->contentsOffset.x = 0;
187
if (!(self->subFlip & FLIP_Y))
188
self->contentsOffset.y = 0xC0000;
189
else
190
self->contentsOffset.y = -0xC0000;
191
break;
192
193
case 1:
194
case 5:
195
case 6:
196
self->contentsOffset.y = 0;
197
if (!(self->subFlip & FLIP_X))
198
self->contentsOffset.x = -0x100000;
199
else
200
self->contentsOffset.x = 0x100000;
201
break;
202
203
case 2:
204
case 7:
205
case 8:
206
if (!(self->subFlip & FLIP_X))
207
self->contentsOffset.x = -0x80000;
208
else
209
self->contentsOffset.x = 0x80000;
210
if (!(self->subFlip & FLIP_Y))
211
self->contentsOffset.y = 0x40000;
212
else
213
self->contentsOffset.y = -0x40000;
214
break;
215
216
default: break;
217
}
218
}
219
break;
220
221
case ICE_ITEMBOX_RINGS:
222
case ICE_ITEMBOX_BLUESHIELD:
223
case ICE_ITEMBOX_BUBBLESHIELD:
224
case ICE_ITEMBOX_FIRESHIELD:
225
case ICE_ITEMBOX_LIGHTNINGSHIELD:
226
case ICE_ITEMBOX_INVINCIBILITY:
227
case ICE_ITEMBOX_SNEAKERS:
228
case ICE_ITEMBOX_1UP:
229
case ICE_ITEMBOX_EGGMAN:
230
case ICE_ITEMBOX_HYPERRING:
231
case ICE_ITEMBOX_SWAP:
232
case ICE_ITEMBOX_RANDOM:
233
case ICE_ITEMBOX_SUPER:
234
default:
235
RSDK.SetSpriteAnimation(ItemBox->aniFrames, 0, &self->contentsAnimator, true, 0);
236
RSDK.SetSpriteAnimation(ItemBox->aniFrames, 2, &self->altContentsAnimator, true,
237
(self->type > ICE_ITEMBOX_1UP ? 2 : 0) + self->type - 5);
238
if (self->type != ICE_ITEMBOX_1UP) {
239
if (globals->gameMode == MODE_COMPETITION) {
240
if (globals->itemMode == ITEMS_RANDOM) {
241
self->type = ICE_ITEMBOX_EGGMAN;
242
self->altContentsAnimator.frameID = 13;
243
}
244
else if (globals->itemMode == ITEMS_TELEPORT) {
245
self->type = ICE_ITEMBOX_SWAP;
246
self->altContentsAnimator.frameID = 12;
247
}
248
}
249
}
250
else if (globals->gameMode != MODE_TIMEATTACK) {
251
switch (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->characterID) {
252
default:
253
case ID_SONIC: break;
254
255
case ID_TAILS: ++self->altContentsAnimator.frameID; break;
256
case ID_KNUCKLES: self->altContentsAnimator.frameID += 2; break;
257
#if MANIA_USE_PLUS
258
case ID_MIGHTY: self->altContentsAnimator.frameID += 8; break;
259
case ID_RAY: self->altContentsAnimator.frameID += 9; break;
260
#endif
261
}
262
263
if (globals->gameMode == MODE_COMPETITION) {
264
if (globals->itemMode == ITEMS_RANDOM) {
265
self->type = ICE_ITEMBOX_RANDOM;
266
self->altContentsAnimator.frameID = 13;
267
}
268
else if (globals->itemMode == 2) {
269
self->type = ICE_ITEMBOX_SWAP;
270
self->altContentsAnimator.frameID = 12;
271
}
272
}
273
}
274
else {
275
self->type = ICE_ITEMBOX_RINGS;
276
self->altContentsAnimator.frameID = 0;
277
}
278
break;
279
}
280
281
if (self->size == ICE_SIZE_SMALL) {
282
self->hitboxBlock.left = -16;
283
self->hitboxBlock.top = -16;
284
self->hitboxBlock.right = 16;
285
self->hitboxBlock.bottom = 16;
286
}
287
else if (!self->size) {
288
self->hitboxBlock.left = -24;
289
self->hitboxBlock.top = -20;
290
self->hitboxBlock.right = 24;
291
self->hitboxBlock.bottom = 20;
292
}
293
294
self->hitboxFallCheck.left = self->hitboxBlock.left - 8;
295
self->hitboxFallCheck.top = self->hitboxBlock.top;
296
self->hitboxFallCheck.right = self->hitboxBlock.right + 8;
297
self->hitboxFallCheck.bottom = self->hitboxBlock.bottom + 8;
298
299
self->hitboxPlayerBlockCheck.left = self->hitboxBlock.left;
300
self->hitboxPlayerBlockCheck.top = self->hitboxBlock.top - 8;
301
self->hitboxPlayerBlockCheck.right = self->hitboxBlock.right;
302
self->hitboxPlayerBlockCheck.bottom = self->hitboxBlock.bottom + 8;
303
304
self->alpha = 0x180;
305
self->state = Ice_State_IceBlock;
306
self->stateDraw = Ice_Draw_IceBlock;
307
}
308
}
309
}
310
311
void Ice_StageLoad(void)
312
{
313
if (RSDK.CheckSceneFolder("PSZ1")) // PGZ1 doesn't have ice anywhere, likely leftover from an earlier revision
314
Ice->aniFrames = RSDK.LoadSpriteAnimation("PSZ1/Ice.bin", SCOPE_STAGE);
315
else if (RSDK.CheckSceneFolder("PSZ2"))
316
Ice->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/Ice.bin", SCOPE_STAGE);
317
318
Ice->hitboxPlayerBlockInner.left = -15;
319
Ice->hitboxPlayerBlockInner.top = -24;
320
Ice->hitboxPlayerBlockInner.right = 15;
321
Ice->hitboxPlayerBlockInner.bottom = 24;
322
323
Ice->hitboxPlayerBlockOuter.left = -16;
324
Ice->hitboxPlayerBlockOuter.top = -24;
325
Ice->hitboxPlayerBlockOuter.right = 16;
326
Ice->hitboxPlayerBlockOuter.bottom = 24;
327
328
Ice->playerTimers[0] = 0;
329
Ice->playerTimers[1] = 0;
330
Ice->playerTimers[2] = 0;
331
Ice->playerTimers[3] = 0;
332
333
Ice->sfxFreeze = RSDK.GetSfx("PSZ/Freeze.wav");
334
Ice->sfxLedgeBreak = RSDK.GetSfx("Stage/LedgeBreak.wav");
335
Ice->sfxWindowShatter = RSDK.GetSfx("Stage/WindowShatter.wav");
336
Ice->sfxStruggle = RSDK.GetSfx("PSZ/Struggle.wav");
337
338
Zone->timeOverCallback = Ice_TimeOver_CheckFrozen;
339
Zone_AddVSSwapCallback(Ice_VSSwap_CheckFrozen);
340
}
341
342
void Ice_VSSwap_CheckFrozen(void)
343
{
344
#if MANIA_USE_PLUS
345
EntityPlayer *player = RSDK_GET_ENTITY(Zone->swapPlayerID, Player);
346
#else
347
EntityPlayer *player = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
348
#endif
349
350
if (player->state == Ice_PlayerState_Frozen) {
351
#if MANIA_USE_PLUS
352
Zone->playerSwapEnabled[Zone->swapPlayerID] = false;
353
#else
354
Ice_BreakPlayerBlock(player);
355
#endif
356
}
357
}
358
359
void Ice_FreezePlayer(EntityPlayer *player)
360
{
361
RSDK_THIS(Ice);
362
363
if (!Zone->gotTimeOver && player->state != Ice_PlayerState_Frozen && (player->shield != SHIELD_FIRE || player->invincibleTimer > 0)) {
364
EntityIce *ice = CREATE_ENTITY(Ice, INT_TO_VOID(ICE_CHILD_PLAYER), player->position.x, player->position.y);
365
ice->playerPtr = player;
366
367
#if MANIA_USE_PLUS
368
switch (player->characterID) {
369
case ID_SONIC: ice->animationID = 4 * (player->superState == SUPERSTATE_SUPER) + ICEANI_SONICIDLE; break;
370
case ID_TAILS: ice->animationID = ICEANI_TAILSIDLE; break;
371
case ID_KNUCKLES: ice->animationID = ICEANI_KNUXIDLE; break;
372
case ID_MIGHTY: ice->animationID = ICEANI_MIGHTYIDLE; break;
373
case ID_RAY: ice->animationID = ICEANI_RAYIDLE; break;
374
default: break;
375
}
376
#else
377
switch (player->characterID) {
378
case ID_SONIC: ice->animationID = ICEANI_SONIC; break;
379
case ID_TAILS: ice->animationID = ICEANI_TAILS; break;
380
case ID_KNUCKLES: ice->animationID = ICEANI_KNUX; break;
381
default: break;
382
}
383
#endif
384
385
RSDK.SetSpriteAnimation(Ice->aniFrames, ice->animationID, &ice->contentsAnimator, true, 0);
386
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PLAYERGLINT, &ice->altContentsAnimator, true, 0);
387
ice->alpha = 0x80;
388
ice->isPermanent = true;
389
390
player->velocity.x >>= 1;
391
player->groundVel >>= 1;
392
player->animator.speed = 0;
393
player->visible = false;
394
player->nextAirState = StateMachine_None;
395
player->nextGroundState = StateMachine_None;
396
player->state = Ice_PlayerState_Frozen;
397
player->velocity.y = 0;
398
399
RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, false, 0);
400
player->abilityPtrs[0] = self;
401
player->abilityPtrs[1] = ice;
402
if (!self->collisionMode)
403
player->position.y += Player_GetHitbox(player)->bottom - Ice->hitboxPlayerBlockOuter.bottom;
404
405
player->blinkTimer = 0;
406
player->outerbox = &Ice->hitboxPlayerBlockOuter;
407
player->innerbox = &Ice->hitboxPlayerBlockInner;
408
player->timer = 0;
409
player->abilityTimer = 0;
410
player->spindashCharge = 0;
411
412
RSDK.PlaySfx(Ice->sfxFreeze, false, 255);
413
}
414
}
415
416
bool32 Ice_CheckPlayerBlockSmashH(void)
417
{
418
RSDK_THIS(Player);
419
420
if (abs(self->skidding) >= 0x50000 && abs(self->pushing) <= abs(self->skidding)) {
421
self->velocity.x = self->skidding < 0 ? 0x18000 : -0x18000;
422
423
Ice_BreakPlayerBlock(self);
424
425
return true;
426
}
427
428
return false;
429
}
430
431
bool32 Ice_CheckPlayerBlockSmashV(void)
432
{
433
RSDK_THIS(Player);
434
435
if (abs(self->pushing) >= 0x80000 && abs(self->skidding) <= abs(self->pushing)) {
436
if (self->pushing > 0 && (self->angle & 0xFF) < 0x20) {
437
Ice_BreakPlayerBlock(self);
438
return true;
439
}
440
441
if (self->pushing && (self->angle + 0x80) < 0x20) {
442
Ice_BreakPlayerBlock(self);
443
return true;
444
}
445
}
446
447
return false;
448
}
449
450
void Ice_PlayerState_Frozen(void)
451
{
452
RSDK_THIS(Player);
453
454
bool32 onGround = self->onGround;
455
int32 xVel = self->groundVel;
456
int32 yVel = self->groundedStore;
457
458
if (!onGround) {
459
onGround |= self->groundedStore || self->velocity.x;
460
xVel = self->velocity.x;
461
yVel = self->velocity.y;
462
}
463
464
if (!onGround) {
465
onGround |= abs(self->skidding) < 0x50000 || !Ice_CheckPlayerBlockSmashH();
466
xVel = self->velocity.x;
467
yVel = self->velocity.y;
468
}
469
470
if (onGround) {
471
if (self->onGround && !self->groundVel) {
472
if (Ice_CheckPlayerBlockSmashH())
473
return;
474
}
475
476
self->skidding = xVel;
477
478
if (!yVel) {
479
if (Ice_CheckPlayerBlockSmashV())
480
return;
481
}
482
483
self->pushing = self->velocity.y;
484
if (self->shield == SHIELD_FIRE && self->invincibleTimer <= 0) {
485
self->state = Player_State_Air;
486
self->velocity.y = -self->jumpStrength >> 1;
487
self->onGround = false;
488
RSDK.SetSpriteAnimation(self->aniFrames, ANI_JUMP, &self->animator, true, 0);
489
490
Ice_ShatterGenerator(64, 24, 20, 0, 0, 2);
491
RSDK.PlaySfx(Ice->sfxWindowShatter, false, 255);
492
Ice->playerTimers[RSDK.GetEntitySlot(self)] = 30;
493
494
self->skidding = 0;
495
self->pushing = 0;
496
self->timer = 0;
497
self->abilityTimer = 0;
498
self->spindashCharge = 0;
499
}
500
else {
501
if (!self->invincibleTimer)
502
self->invincibleTimer = -1;
503
504
if (self->spindashCharge > 0) {
505
self->spindashCharge--;
506
}
507
else if (self->jumpPress) {
508
self->spindashCharge = 15;
509
510
if (++self->timer > 4) {
511
self->state = Player_State_Air;
512
self->velocity.y = -self->jumpStrength >> 1;
513
self->onGround = false;
514
RSDK.SetSpriteAnimation(self->aniFrames, ANI_JUMP, &self->animator, true, 0);
515
Ice_ShatterGenerator(64, 24, 20, 0, 0, 2);
516
RSDK.PlaySfx(Ice->sfxWindowShatter, false, 255);
517
Ice->playerTimers[RSDK.GetEntitySlot(self)] = 30;
518
self->skidding = 0;
519
self->pushing = 0;
520
self->timer = 0;
521
self->abilityTimer = 0;
522
self->spindashCharge = 0;
523
}
524
else {
525
Ice_ShatterGenerator(8, 24, 20, 0, 0, false);
526
RSDK.PlaySfx(Ice->sfxStruggle, false, 255);
527
}
528
}
529
}
530
531
if (self->state == Ice_PlayerState_Frozen) {
532
if (self->onGround) {
533
int32 rollFric = self->rollingFriction;
534
self->left = false;
535
self->right = false;
536
self->rollingFriction = 0;
537
Player_HandleRollDeceleration();
538
539
if (!self->groundVel)
540
self->groundVel += (5000 * RSDK.Sin256(self->angle)) >> 8;
541
542
if (self->camera)
543
self->camera->disableYOffset = false;
544
545
self->jumpAbilityState = 0;
546
self->rollingFriction = rollFric;
547
548
if (self->state == Player_State_Ground)
549
self->state = Ice_PlayerState_Frozen;
550
}
551
else {
552
self->velocity.y += 0x3800;
553
}
554
}
555
}
556
}
557
558
void Ice_ShatterGenerator(int32 count, int32 sizeX, int32 sizeY, int32 velX, int32 velY, int32 canBreak)
559
{
560
RSDK_THIS(Ice);
561
562
if (canBreak > 0)
563
count >>= 1;
564
565
for (int32 i = 0; i < MAX(0, count); ++i) {
566
int32 x = self->position.x + (RSDK.Rand(-sizeX, sizeX + 1) << 16);
567
int32 y = self->position.y + (RSDK.Rand(-sizeY, sizeY + 1) << 16);
568
EntityIce *ice = CREATE_ENTITY(Ice, INT_TO_VOID(ICE_CHILD_SHARD), x, y);
569
570
ice->velocity.x = velX + (RSDK.Rand(-6, 8) << 15);
571
ice->velocity.y = velY + (RSDK.Rand(-10, 2) << 15);
572
ice->direction = RSDK.Rand(0, 4);
573
ice->blockAnimator.speed = RSDK.Rand(1, 4);
574
575
if (canBreak) {
576
if (RSDK.Rand(0, 2)) {
577
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PIECE, &ice->blockAnimator, true, 0);
578
ice->velocity.x = (ice->velocity.x >> 1) + (ice->velocity.x >> 2);
579
ice->velocity.y = (ice->velocity.y >> 1) + (ice->velocity.y >> 2);
580
}
581
}
582
}
583
}
584
585
// Like Ice_Shatter, but "shatters" the contents too
586
void Ice_FullShatter(EntityPlayer *player, int32 velX, int32 velY)
587
{
588
RSDK_THIS(Ice);
589
590
if (self->type == ICE_SPIKES || (self->type == ICE_SPRING && self->subType < 3))
591
self->type = ICE_BLOCK;
592
593
EntityItemBox *itemBox = (EntityItemBox *)Ice_Shatter(self, velX, velY);
594
if (player && itemBox) {
595
if (player->sidekick)
596
player = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
597
598
itemBox->contentsPos.x = itemBox->position.x;
599
itemBox->contentsPos.y = itemBox->position.y - 0x30000;
600
ItemBox_Break(itemBox, player);
601
}
602
}
603
604
void Ice_BreakPlayerBlock(EntityPlayer *player)
605
{
606
RSDK.PlaySfx(Ice->sfxWindowShatter, false, 255);
607
Ice_ShatterGenerator(64, 24, 20, 0, 0, 2);
608
Ice->playerTimers[RSDK.GetEntitySlot(player)] = 30;
609
610
player->skidding = 0;
611
player->pushing = 0;
612
player->timer = 0;
613
player->abilityTimer = 0;
614
player->spindashCharge = 0;
615
player->visible = true;
616
if (player->state != Player_State_Death) {
617
player->outerbox = NULL;
618
player->innerbox = NULL;
619
player->state = Player_State_Hurt;
620
RSDK.SetSpriteAnimation(player->aniFrames, ANI_HURT, &player->animator, true, 0);
621
622
player->velocity.y = -0x38000;
623
player->onGround = false;
624
if (player->gravityStrength == 0x1000) {
625
player->velocity.x >>= 1;
626
player->velocity.y = -0x1C000;
627
}
628
}
629
}
630
631
EntityItemBox *Ice_Shatter(EntityIce *ice, int32 velX, int32 velY)
632
{
633
RSDK_THIS(Ice);
634
635
RSDK.PlaySfx(Ice->sfxWindowShatter, false, 255);
636
637
if (self->blockAnimator.animationID == ICEANI_PILLARBLOCK) {
638
self->position.y -= 0x370000;
639
Ice_ShatterGenerator(96, 19, 55, velX, velY, 2);
640
}
641
else if (self->size == ICE_SIZE_SMALL) {
642
Ice_ShatterGenerator(16, 16, 16, velX, velY, 2);
643
}
644
else {
645
Ice_ShatterGenerator(64, 24, 20, velX, velY, 2);
646
}
647
648
EntityItemBox *itemBox = NULL;
649
int32 count = 0;
650
switch (ice->type) {
651
case ICE_BLOCK: break;
652
653
case ICE_1RING: count = 1;
654
// [Fallthrough]
655
case ICE_3RINGS:
656
if (ice->type == ICE_3RINGS)
657
count = 3;
658
// [Fallthrough]
659
case ICE_5RINGS: {
660
if (ice->type == ICE_5RINGS)
661
count = 5;
662
663
int32 angle = 16 * (12 - (count >> 1));
664
for (int32 i = 0; i < count; ++i) {
665
EntityRing *ring = CREATE_ENTITY(Ring, INT_TO_VOID(1), ice->position.x, ice->position.y);
666
ring->velocity.x = velX + 640 * RSDK.Cos256(angle);
667
ring->velocity.y = velY + 640 * RSDK.Sin256(angle);
668
ring->position.x += ring->velocity.x * (3 * ice->type - 3);
669
ring->position.y += ring->velocity.y * (3 * ice->type - 3);
670
ring->timer = 32;
671
ring->collisionPlane = 0;
672
ring->inkEffect = INK_ALPHA;
673
ring->alpha = 256;
674
ring->state = Ring_State_Lost;
675
ring->position.y += (0x50000 * ice->type) - 0x50000;
676
ring->animator.speed = 512;
677
ring->stateDraw = Ring_Draw_Normal;
678
angle += 16;
679
}
680
break;
681
}
682
683
case ICE_SPIKES:
684
CREATE_ENTITY(Spikes, INT_TO_VOID(self->subType), ice->position.x + self->contentsOffset.x, ice->position.y + self->contentsOffset.y);
685
break;
686
687
case ICE_SPRING:
688
if (self->subType >= 3)
689
CREATE_ENTITY(Spring, INT_TO_VOID((self->subFlip << 8) - 3 + self->subType), ice->position.x + self->contentsOffset.x,
690
ice->position.y + self->contentsOffset.y);
691
else
692
CREATE_ENTITY(IceSpring, INT_TO_VOID(self->subType + (self->subFlip << 8)), ice->position.x + self->contentsOffset.x,
693
ice->position.y + self->contentsOffset.y);
694
break;
695
696
default: {
697
int32 type = ice->type - ICE_ITEMBOX_RINGS;
698
itemBox = CREATE_ENTITY(ItemBox, INT_TO_VOID(type + (ice->type > ICE_ITEMBOX_1UP ? 2 : 0)), ice->position.x, ice->position.y);
699
itemBox->velocity.y = -0x18000;
700
itemBox->state = ItemBox_State_Falling;
701
break;
702
}
703
}
704
705
Ice_UpdateBlockGravity();
706
destroyEntity(ice);
707
708
return itemBox;
709
}
710
711
void Ice_TimeOver_CheckFrozen(void)
712
{
713
EntityPlayer *player = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
714
715
if (player->state == Ice_PlayerState_Frozen)
716
Ice_BreakPlayerBlock(player);
717
}
718
719
void Ice_UpdateBlockGravity(void)
720
{
721
RSDK_THIS(Ice);
722
723
foreach_all(Ice, ice)
724
{
725
if (ice != self && ice->state == Ice_State_IceBlock
726
&& RSDK.CheckObjectCollisionTouchBox(self, &self->hitboxBlock, ice, &ice->hitboxFallCheck)) {
727
ice->state = Ice_State_StartBlockFall;
728
}
729
}
730
}
731
732
void Ice_State_IceBlock(void)
733
{
734
RSDK_THIS(Ice);
735
736
foreach_active(Player, player)
737
{
738
bool32 noCollision = true;
739
int32 playerX = player->position.x;
740
int32 playerY = player->position.y;
741
742
if (player->state == Ice_PlayerState_Frozen) {
743
switch (MathHelpers_CheckBoxCollision(self, &self->hitboxPlayerBlockCheck, player, &Ice->hitboxPlayerBlockOuter)) {
744
default:
745
case C_NONE:
746
player->position.x = playerX;
747
player->position.y = playerY;
748
749
self->position.x -= self->playerMoveOffset.x;
750
self->position.y -= self->playerMoveOffset.y;
751
if (Player_CheckCollisionBox(player, self, &self->hitboxBlock) == C_TOP) {
752
player->position.x += self->playerMoveOffset.x;
753
player->position.y += self->playerMoveOffset.y;
754
}
755
756
self->position.x += self->playerMoveOffset.x;
757
self->position.y += self->playerMoveOffset.y;
758
noCollision = false;
759
break;
760
761
case C_TOP:
762
if (player->velocity.y < 0x40000) {
763
player->position.x = playerX;
764
player->position.y = playerY;
765
766
self->position.x -= self->playerMoveOffset.x;
767
self->position.y -= self->playerMoveOffset.y;
768
if (Player_CheckCollisionBox(player, self, &self->hitboxBlock) == C_TOP) {
769
player->position.x += self->playerMoveOffset.x;
770
player->position.y += self->playerMoveOffset.y;
771
}
772
773
self->position.x += self->playerMoveOffset.x;
774
self->position.y += self->playerMoveOffset.y;
775
noCollision = false;
776
}
777
else {
778
Ice_FullShatter(player, 0, 0);
779
player->velocity.y = -0x20000;
780
781
foreach_return;
782
}
783
break;
784
785
case C_LEFT:
786
if (player->velocity.x < 0x20000) {
787
player->position.x = playerX;
788
player->position.y = playerY;
789
790
self->position.x -= self->playerMoveOffset.x;
791
self->position.y -= self->playerMoveOffset.y;
792
if (Player_CheckCollisionBox(player, self, &self->hitboxBlock) == C_TOP) {
793
player->position.x += self->playerMoveOffset.x;
794
player->position.y += self->playerMoveOffset.y;
795
}
796
797
self->position.x += self->playerMoveOffset.x;
798
self->position.y += self->playerMoveOffset.y;
799
noCollision = false;
800
}
801
else {
802
Ice_FullShatter(player, player->velocity.x >> 1, 0);
803
player->position.x = playerX;
804
player->position.y = playerY;
805
806
foreach_return;
807
}
808
break;
809
810
case C_RIGHT:
811
if (player->velocity.x > -0x20000) {
812
player->position.x = playerX;
813
player->position.y = playerY;
814
815
self->position.x -= self->playerMoveOffset.x;
816
self->position.y -= self->playerMoveOffset.y;
817
if (Player_CheckCollisionBox(player, self, &self->hitboxBlock) == C_TOP) {
818
player->position.x += self->playerMoveOffset.x;
819
player->position.y += self->playerMoveOffset.y;
820
}
821
822
self->position.x += self->playerMoveOffset.x;
823
self->position.y += self->playerMoveOffset.y;
824
noCollision = false;
825
}
826
else {
827
Ice_FullShatter(player, player->velocity.x >> 1, 0);
828
player->position.x = playerX;
829
player->position.y = playerY;
830
831
foreach_return;
832
}
833
break;
834
835
case C_BOTTOM:
836
if (player->velocity.y > -0x40000) {
837
player->position.x = playerX;
838
player->position.y = playerY;
839
840
self->position.x -= self->playerMoveOffset.x;
841
self->position.y -= self->playerMoveOffset.y;
842
if (Player_CheckCollisionBox(player, self, &self->hitboxBlock) == C_TOP) {
843
player->position.x += self->playerMoveOffset.x;
844
player->position.y += self->playerMoveOffset.y;
845
}
846
847
self->position.x += self->playerMoveOffset.x;
848
self->position.y += self->playerMoveOffset.y;
849
noCollision = false;
850
}
851
else {
852
Ice_FullShatter(player, 0, player->velocity.y >> 1);
853
player->position.x = playerX;
854
player->position.y = playerY;
855
856
foreach_return;
857
}
858
break;
859
}
860
}
861
862
if (noCollision) {
863
switch (MathHelpers_CheckBoxCollision(self, &self->hitboxBlock, player, &Ice->hitboxPlayerBlockOuter)) {
864
default:
865
case C_NONE:
866
case C_TOP: break;
867
868
case C_LEFT:
869
case C_RIGHT:
870
if (self->knuxSmash && player->characterID == ID_KNUCKLES) {
871
Ice_Shatter(self, 0, 0);
872
player->position.x = playerX;
873
player->position.y = playerY;
874
foreach_return;
875
}
876
else if (player->shield == SHIELD_FIRE && player->invincibleTimer <= 0) {
877
if (RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player), Shield)->shieldAnimator.animationID
878
== SHIELDANI_FIREATTACK) {
879
if (player->position.x >= self->position.x) {
880
if (player->velocity.x <= -0x78000) {
881
Ice_FullShatter(player, player->velocity.x, 0);
882
player->position.x = playerX;
883
player->position.y = playerY;
884
foreach_return;
885
}
886
}
887
else if (player->velocity.x >= 0x78000) {
888
Ice_FullShatter(player, player->velocity.x, 0);
889
player->position.x = playerX;
890
player->position.y = playerY;
891
foreach_return;
892
}
893
}
894
}
895
break;
896
897
case C_BOTTOM:
898
if (self->bottomSmash) {
899
Ice_Shatter(self, 0, 0);
900
player->position.x = playerX;
901
player->position.y = playerY;
902
foreach_return;
903
}
904
break;
905
}
906
907
player->position.x = playerX;
908
player->position.y = playerY;
909
self->position.x -= self->playerMoveOffset.x;
910
self->position.y -= self->playerMoveOffset.y;
911
912
#if MANIA_USE_PLUS
913
int32 prevVel = player->velocity.y;
914
#endif
915
int32 side = Player_CheckCollisionBox(player, self, &self->hitboxBlock);
916
if (side) {
917
if (player->shield == SHIELD_FIRE && player->invincibleTimer <= 0 && !self->glintTimer) {
918
if (self->blockAnimator.animationID == ICEANI_PILLARBLOCK) {
919
self->position.y -= 0x370000;
920
Ice_ShatterGenerator(36, 19, 55, 0, 0, false);
921
}
922
else if (self->size == ICE_SIZE_SMALL) {
923
Ice_ShatterGenerator(6, 19, 17, 0, 0, false);
924
}
925
else {
926
Ice_ShatterGenerator(12, 19, 20, 0, 0, false);
927
}
928
929
self->blockAnimator.frameID += 2;
930
self->glintTimer = 15;
931
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_LARGEGLINT + self->size, &self->glintAnimator, true, 0);
932
}
933
934
if (side == C_BOTTOM) {
935
if (player->onGround && !player->collisionMode && self->playerMoveOffset.y >= 0) {
936
if (player->shield == SHIELD_FIRE || player->invincibleTimer) {
937
player->position.x = playerX;
938
player->position.y = playerY;
939
Ice_Shatter(self, 0, 0);
940
foreach_return;
941
}
942
player->deathType = PLAYER_DEATH_DIE_USESFX;
943
}
944
}
945
else if (side == C_TOP && player->velocity.y >= 0) {
946
if (self->blockAnimator.animationID == ICEANI_PILLARBLOCK) {
947
Player_Hurt(player, self);
948
}
949
else {
950
if ((player->animator.animationID == ANI_JUMP || player->state == Player_State_DropDash) && !player->sidekick) {
951
Ice_Shatter(self, 0, 0);
952
player->velocity.y = -0x30000;
953
player->onGround = false;
954
955
foreach_return;
956
}
957
#if MANIA_USE_PLUS
958
else if (player->state == Player_State_MightyHammerDrop && !player->sidekick) {
959
Ice_FullShatter(player, 0, player->velocity.y);
960
player->velocity.y = prevVel - 0x10000;
961
player->onGround = false;
962
963
foreach_return;
964
}
965
#endif
966
}
967
968
player->position.x += self->playerMoveOffset.x;
969
player->position.y += self->playerMoveOffset.y;
970
}
971
}
972
973
self->position.x += self->playerMoveOffset.x;
974
self->position.y += self->playerMoveOffset.y;
975
}
976
}
977
978
RSDK.ProcessAnimation(&self->glintAnimator);
979
980
if (self->blockAnimator.animationID == ICEANI_PILLARBLOCK) {
981
if (!RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, self->hitboxBlock.bottom << 16, true)
982
&& self->state == Ice_State_IceBlock) {
983
self->state = Ice_State_StartBlockFall;
984
}
985
986
RSDK.ProcessAnimation(&self->blockAnimator);
987
988
if (--self->glintTimer == 54)
989
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PILLARGLINT, &self->glintAnimator, true, 1);
990
else if (!self->glintTimer)
991
Ice_Shatter(self, 0, 0);
992
}
993
else {
994
if (self->glintTimer > 0) {
995
self->glintTimer--;
996
997
if (!self->glintTimer) {
998
if (self->type == ICE_SPIKES || (self->type == ICE_SPRING && self->subType < 3))
999
self->type = ICE_BLOCK;
1000
1001
Ice_Shatter(self, 0, 0);
1002
}
1003
}
1004
}
1005
}
1006
1007
void Ice_State_StartBlockFall(void)
1008
{
1009
RSDK_THIS(Ice);
1010
1011
Ice_UpdateBlockGravity();
1012
1013
self->timer = 15;
1014
self->state = Ice_State_BlockFallDelay;
1015
self->active = ACTIVE_NORMAL;
1016
1017
Ice_State_IceBlock();
1018
}
1019
1020
void Ice_State_BlockFallDelay(void)
1021
{
1022
RSDK_THIS(Ice);
1023
1024
if (!--self->timer) {
1025
Ice_UpdateBlockGravity();
1026
self->state = Ice_State_IceBlockFall;
1027
}
1028
1029
Ice_State_IceBlockFall();
1030
self->velocity.y = 0;
1031
}
1032
1033
void Ice_State_IceBlockFall(void)
1034
{
1035
RSDK_THIS(Ice);
1036
1037
self->position.y = self->velocity.y + self->position.y;
1038
self->playerMoveOffset.y = -(int32)(self->position.y & 0xFFFF0000);
1039
self->playerMoveOffset.y += (self->velocity.y + self->position.y) & 0xFFFF0000;
1040
self->velocity.y += 0x3800;
1041
1042
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, self->hitboxBlock.bottom << 16, true)) {
1043
self->velocity.y = 0;
1044
1045
self->state = Ice_State_IceBlock;
1046
Ice_State_IceBlock();
1047
}
1048
else {
1049
int32 velY = self->velocity.y;
1050
foreach_all(Ice, ice)
1051
{
1052
if (ice != self) {
1053
if ((ice->stateDraw == Ice_Draw_IceBlock || ice->stateDraw == Ice_Draw_PlayerBlock)
1054
&& RSDK.CheckObjectCollisionPlatform(ice, &ice->hitboxBlock, self, &self->hitboxBlock, true)) {
1055
self->velocity.y = 0;
1056
1057
if (ice->state == Ice_State_IceBlock) {
1058
self->active = ACTIVE_BOUNDS;
1059
self->state = Ice_State_IceBlock;
1060
}
1061
}
1062
}
1063
}
1064
1065
foreach_all(Spikes, spikes)
1066
{
1067
if (RSDK.CheckObjectCollisionPlatform(spikes, &spikes->hitbox, self, &self->hitboxBlock, true)) {
1068
self->velocity.y = 0;
1069
self->active = ACTIVE_BOUNDS;
1070
self->state = Ice_State_IceBlock;
1071
}
1072
}
1073
1074
foreach_all(ItemBox, itemBox)
1075
{
1076
if ((itemBox->state == ItemBox_State_Idle || itemBox->state == ItemBox_State_Falling)
1077
&& RSDK.CheckObjectCollisionPlatform(itemBox, &ItemBox->hitboxItemBox, self, &self->hitboxBlock, true)) {
1078
self->velocity.y = 0;
1079
1080
if (itemBox->onGround) {
1081
self->active = ACTIVE_BOUNDS;
1082
self->state = Ice_State_IceBlock;
1083
}
1084
}
1085
}
1086
1087
if (!self->velocity.y && velY >= 0x60000) {
1088
if (ice) {
1089
if (ice->stateDraw == Ice_Draw_PlayerBlock) {
1090
Ice_BreakPlayerBlock(ice->playerPtr);
1091
}
1092
else {
1093
Entity *storeEntity = SceneInfo->entity;
1094
SceneInfo->entity = (Entity *)ice;
1095
1096
if (ice->type == ICE_SPIKES || (ice->type == ICE_SPRING && ice->subType < 3))
1097
ice->type = ICE_BLOCK;
1098
1099
Ice_Shatter(ice, 0, 0);
1100
1101
SceneInfo->entity = storeEntity;
1102
}
1103
}
1104
1105
if (self->type == ICE_SPIKES || (self->type == ICE_SPRING && self->subType < 3))
1106
self->type = ICE_BLOCK;
1107
1108
Ice_Shatter(self, 0, 0);
1109
}
1110
1111
Ice_State_IceBlock();
1112
}
1113
}
1114
1115
void Ice_State_PlayerBlock(void)
1116
{
1117
RSDK_THIS(Ice);
1118
1119
RSDK.ProcessAnimation(&self->contentsAnimator);
1120
EntityPlayer *playerPtr = self->playerPtr;
1121
1122
if (playerPtr->state == Ice_PlayerState_Frozen) {
1123
RSDK.ProcessAnimation(&self->altContentsAnimator);
1124
1125
#if MANIA_USE_PLUS
1126
if (self->contentsAnimator.animationID == self->animationID + 3) {
1127
if (self->contentsAnimator.frameID == self->contentsAnimator.frameCount - 1)
1128
RSDK.SetSpriteAnimation(Ice->aniFrames, self->animationID, &self->contentsAnimator, false, 0);
1129
1130
if (--self->glintTimer <= 0) {
1131
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PLAYERGLINT, &self->altContentsAnimator, true, 0);
1132
self->glintTimer = 30 * RSDK.Rand(1, 9);
1133
}
1134
1135
self->blockAnimator.frameID = playerPtr->timer;
1136
1137
if (playerPtr->spindashCharge) {
1138
self->contentsOffset.x = RSDK.Rand(-1, 2) << 16;
1139
self->contentsOffset.y = RSDK.Rand(-1, 2) << 16;
1140
}
1141
else {
1142
self->contentsOffset.x = 0;
1143
self->contentsOffset.y = 0;
1144
}
1145
1146
self->playerMoveOffset.x = (playerPtr->position.x & 0xFFFF0000) - (self->position.x & 0xFFFF0000);
1147
self->playerMoveOffset.y = (playerPtr->position.y & 0xFFFF0000) - (self->position.y & 0xFFFF0000);
1148
1149
self->position.x = playerPtr->position.x;
1150
self->position.y = playerPtr->position.y;
1151
self->drawGroup = playerPtr->drawGroup + 1;
1152
}
1153
else {
1154
if (playerPtr->jumpPress) {
1155
RSDK.SetSpriteAnimation(Ice->aniFrames, self->animationID + 3, &self->contentsAnimator, false, 0);
1156
}
1157
else {
1158
if (playerPtr->velocity.x >= -0x60000) {
1159
if (playerPtr->velocity.x <= 0x60000) {
1160
if (playerPtr->left)
1161
RSDK.SetSpriteAnimation(Ice->aniFrames, self->animationID + 1, &self->contentsAnimator, false, 0);
1162
else if (playerPtr->right)
1163
RSDK.SetSpriteAnimation(Ice->aniFrames, self->animationID + 2, &self->contentsAnimator, false, 0);
1164
}
1165
else {
1166
RSDK.SetSpriteAnimation(Ice->aniFrames, self->animationID + 2, &self->contentsAnimator, false, 0);
1167
}
1168
}
1169
else {
1170
RSDK.SetSpriteAnimation(Ice->aniFrames, self->animationID + 1, &self->contentsAnimator, false, 0);
1171
}
1172
}
1173
1174
if (--self->glintTimer <= 0) {
1175
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PLAYERGLINT, &self->altContentsAnimator, true, 0);
1176
self->glintTimer = 30 * RSDK.Rand(1, 9);
1177
}
1178
1179
self->blockAnimator.frameID = playerPtr->timer;
1180
1181
if (playerPtr->spindashCharge) {
1182
self->contentsOffset.x = RSDK.Rand(-1, 2) << 16;
1183
self->contentsOffset.y = RSDK.Rand(-1, 2) << 16;
1184
}
1185
else {
1186
self->contentsOffset.x = 0;
1187
self->contentsOffset.y = 0;
1188
}
1189
1190
self->playerMoveOffset.x = (playerPtr->position.x & 0xFFFF0000) - (self->position.x & 0xFFFF0000);
1191
self->playerMoveOffset.y = (playerPtr->position.y & 0xFFFF0000) - (self->position.y & 0xFFFF0000);
1192
1193
self->position.x = playerPtr->position.x;
1194
self->position.y = playerPtr->position.y;
1195
self->drawGroup = playerPtr->drawGroup + 1;
1196
}
1197
}
1198
else {
1199
if (playerPtr->state != Player_State_FlyToPlayer && playerPtr->state != Player_State_ReturnToPlayer) {
1200
if (!Player_CheckValidState(playerPtr)) {
1201
Ice_BreakPlayerBlock(self->playerPtr);
1202
}
1203
else {
1204
playerPtr->outerbox = NULL;
1205
playerPtr->innerbox = NULL;
1206
playerPtr->visible = true;
1207
}
1208
}
1209
1210
destroyEntity(self);
1211
}
1212
#else
1213
if (--self->glintTimer <= 0) {
1214
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_PLAYERGLINT, &self->altContentsAnimator, true, 0);
1215
self->glintTimer = 30 * RSDK.Rand(1, 9);
1216
}
1217
1218
self->blockAnimator.frameID = playerPtr->timer;
1219
1220
if (playerPtr->spindashCharge) {
1221
self->contentsOffset.x = RSDK.Rand(-1, 2) << 16;
1222
self->contentsOffset.y = RSDK.Rand(-1, 2) << 16;
1223
}
1224
else {
1225
self->contentsOffset.x = 0;
1226
self->contentsOffset.y = 0;
1227
}
1228
1229
self->playerMoveOffset.x = (playerPtr->position.x & 0xFFFF0000) - (self->position.x & 0xFFFF0000);
1230
self->playerMoveOffset.y = (playerPtr->position.y & 0xFFFF0000) - (self->position.y & 0xFFFF0000);
1231
1232
self->position.x = playerPtr->position.x;
1233
self->position.y = playerPtr->position.y;
1234
self->drawGroup = playerPtr->drawGroup + 1;
1235
}
1236
else {
1237
playerPtr->outerbox = NULL;
1238
playerPtr->innerbox = NULL;
1239
playerPtr->visible = true;
1240
1241
destroyEntity(self);
1242
}
1243
#endif
1244
1245
foreach_active(Player, player)
1246
{
1247
if (player != self->playerPtr && player->stateInput != Player_Input_P2_AI && player->state != Ice_PlayerState_Frozen && player->velocity.y > 0
1248
&& !player->onGround && player->position.y < self->position.y - 0x200000 && Player_CheckBadnikTouch(player, self, &self->hitboxBlock)) {
1249
if (player->animator.animationID == ANI_JUMP || player->animator.animationID == ANI_DROPDASH) {
1250
Ice_BreakPlayerBlock(self->playerPtr);
1251
player->velocity.y = -0x30000;
1252
player->onGround = false;
1253
1254
foreach_break;
1255
}
1256
}
1257
}
1258
1259
if (self->timer)
1260
self->timer--;
1261
}
1262
1263
void Ice_State_Shard(void)
1264
{
1265
RSDK_THIS(Ice);
1266
1267
RSDK.ProcessAnimation(&self->blockAnimator);
1268
1269
self->position.x += self->velocity.x;
1270
self->position.y += self->velocity.y;
1271
self->velocity.y += 0x3800;
1272
1273
self->visible ^= true;
1274
1275
if (!RSDK.CheckOnScreen(self, &self->updateRange))
1276
destroyEntity(self);
1277
}
1278
1279
void Ice_Draw_IceBlock(void)
1280
{
1281
RSDK_THIS(Ice);
1282
Vector2 drawPos;
1283
1284
int32 dirStore = self->direction;
1285
1286
drawPos.x = self->position.x + self->contentsOffset.x;
1287
drawPos.y = self->position.y + self->contentsOffset.y;
1288
self->direction = self->subFlip;
1289
RSDK.DrawSprite(&self->contentsAnimator, &drawPos, false);
1290
1291
self->direction = dirStore;
1292
drawPos.y -= 0x30000;
1293
RSDK.DrawSprite(&self->altContentsAnimator, &drawPos, false);
1294
1295
#if MANIA_USE_PLUS
1296
if (!SceneInfo->inEditor) {
1297
self->inkEffect = INK_ADD;
1298
self->blockAnimator.frameID += !self->glintTimer ? 4 : 2;
1299
RSDK.DrawSprite(&self->blockAnimator, NULL, false);
1300
1301
self->blockAnimator.frameID -= !self->glintTimer ? 4 : 2;
1302
self->inkEffect = INK_NONE;
1303
}
1304
#endif
1305
1306
RSDK.DrawSprite(&self->blockAnimator, NULL, false);
1307
1308
self->inkEffect = INK_ADD;
1309
RSDK.DrawSprite(&self->glintAnimator, NULL, false);
1310
1311
self->inkEffect = INK_NONE;
1312
}
1313
1314
void Ice_Draw_PlayerBlock(void)
1315
{
1316
RSDK_THIS(Ice);
1317
Vector2 drawPos;
1318
1319
drawPos.x = self->position.x + self->contentsOffset.x;
1320
drawPos.y = self->position.y + self->contentsOffset.y;
1321
1322
#if MANIA_USE_PLUS
1323
int32 frame = self->blockAnimator.frameID;
1324
self->inkEffect = INK_SUB;
1325
self->blockAnimator.frameID = 5;
1326
RSDK.DrawSprite(&self->blockAnimator, NULL, false);
1327
1328
self->blockAnimator.frameID = frame;
1329
self->inkEffect = INK_NONE;
1330
#endif
1331
1332
RSDK.DrawSprite(&self->contentsAnimator, &drawPos, false);
1333
RSDK.DrawSprite(&self->blockAnimator, NULL, false);
1334
1335
self->inkEffect = INK_ADD;
1336
RSDK.DrawSprite(&self->altContentsAnimator, NULL, false);
1337
1338
self->inkEffect = INK_NONE;
1339
}
1340
1341
void Ice_Draw_Pillar(void)
1342
{
1343
RSDK_THIS(Ice);
1344
1345
RSDK.DrawSprite(&self->blockAnimator, NULL, false);
1346
1347
self->inkEffect = INK_ADD;
1348
RSDK.DrawSprite(&self->glintAnimator, NULL, false);
1349
1350
self->inkEffect = INK_NONE;
1351
}
1352
1353
void Ice_Draw_Shard(void)
1354
{
1355
RSDK_THIS(Ice);
1356
1357
RSDK.DrawSprite(&self->blockAnimator, NULL, false);
1358
}
1359
1360
#if GAME_INCLUDE_EDITOR
1361
void Ice_EditorDraw(void)
1362
{
1363
RSDK_THIS(Ice);
1364
1365
self->drawFX = FX_FLIP;
1366
self->drawGroup = Zone->playerDrawGroup[0] + 1;
1367
self->visible = true;
1368
self->active = ACTIVE_BOUNDS;
1369
self->updateRange.x = 0x800000;
1370
self->updateRange.y = 0x800000;
1371
self->alpha = 0x180;
1372
1373
int32 type = self->type;
1374
int32 subType = self->subType;
1375
int32 subFlip = self->subFlip;
1376
1377
RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_ICEBLOCK, &self->blockAnimator, true, self->size);
1378
RSDK.SetSpriteAnimation(-1, 0, &self->altContentsAnimator, true, 0);
1379
1380
switch (self->type) {
1381
case ICE_BLOCK: RSDK.SetSpriteAnimation(-1, ICEANI_ICEBLOCK, &self->contentsAnimator, true, 0); break;
1382
case ICE_1RING: RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_RINGS, &self->contentsAnimator, true, 0); break;
1383
case ICE_3RINGS: RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_RINGS, &self->contentsAnimator, true, 1); break;
1384
case ICE_5RINGS: RSDK.SetSpriteAnimation(Ice->aniFrames, ICEANI_RINGS, &self->contentsAnimator, true, 2); break;
1385
1386
case ICE_SPIKES:
1387
self->subType = self->subType & 3;
1388
RSDK.SetSpriteAnimation(Spikes->aniFrames, self->subType >> 1, &self->contentsAnimator, true, 0);
1389
1390
if (!self->size) {
1391
// direction of da spikes, matches the spikes "type" variable
1392
switch (self->subType) {
1393
case 0:
1394
self->contentsOffset.y = 0x40000;
1395
self->subFlip = FLIP_NONE;
1396
break;
1397
1398
case 1:
1399
self->contentsOffset.y = -0x40000;
1400
self->subFlip = FLIP_Y;
1401
break;
1402
1403
case 2:
1404
self->contentsOffset.x = -0x80000;
1405
self->subFlip = FLIP_NONE;
1406
break;
1407
1408
case 3:
1409
self->contentsOffset.x = 0x80000;
1410
self->subFlip = FLIP_X;
1411
break;
1412
1413
default: break;
1414
}
1415
}
1416
break;
1417
1418
case ICE_SPRING:
1419
// Ice Springs (0-2), Normal Springs (2-8)
1420
if (self->subType >= 3)
1421
RSDK.SetSpriteAnimation(Spring->aniFrames, (self->subType - 3), &self->contentsAnimator, true, 0);
1422
else
1423
RSDK.SetSpriteAnimation(IceSpring->aniFrames, self->subType, &self->contentsAnimator, true, 0);
1424
1425
if (self->size) {
1426
switch (self->subType) {
1427
case 0:
1428
case 3:
1429
case 4:
1430
self->contentsOffset.x = 0;
1431
if (!(self->subFlip & FLIP_Y))
1432
self->contentsOffset.y = 0x80000;
1433
else
1434
self->contentsOffset.y = -0x80000;
1435
break;
1436
1437
case 1:
1438
case 5:
1439
case 6:
1440
self->contentsOffset.y = 0;
1441
if (!(self->subFlip & FLIP_X))
1442
self->contentsOffset.x = -0x80000;
1443
else
1444
self->contentsOffset.x = 0x80000;
1445
break;
1446
1447
case 2:
1448
case 7:
1449
case 8:
1450
self->contentsOffset.x = 0;
1451
if (!(self->subFlip & FLIP_Y))
1452
self->contentsOffset.y = -0x10000;
1453
else
1454
self->contentsOffset.y = 0x10000;
1455
break;
1456
1457
default: break;
1458
}
1459
}
1460
else {
1461
switch (self->subType) {
1462
case 0:
1463
case 3:
1464
case 4:
1465
self->contentsOffset.x = 0;
1466
if (!(self->subFlip & FLIP_Y))
1467
self->contentsOffset.y = 0xC0000;
1468
else
1469
self->contentsOffset.y = -0xC0000;
1470
break;
1471
1472
case 1:
1473
case 5:
1474
case 6:
1475
self->contentsOffset.y = 0;
1476
if (!(self->subFlip & FLIP_X))
1477
self->contentsOffset.x = -0x100000;
1478
else
1479
self->contentsOffset.x = 0x100000;
1480
break;
1481
1482
case 2:
1483
case 7:
1484
case 8:
1485
if (!(self->subFlip & FLIP_X))
1486
self->contentsOffset.x = -0x80000;
1487
else
1488
self->contentsOffset.x = 0x80000;
1489
if (!(self->subFlip & FLIP_Y))
1490
self->contentsOffset.y = 0x40000;
1491
else
1492
self->contentsOffset.y = -0x40000;
1493
break;
1494
1495
default: break;
1496
}
1497
}
1498
break;
1499
1500
case ICE_ITEMBOX_RINGS:
1501
case ICE_ITEMBOX_BLUESHIELD:
1502
case ICE_ITEMBOX_BUBBLESHIELD:
1503
case ICE_ITEMBOX_FIRESHIELD:
1504
case ICE_ITEMBOX_LIGHTNINGSHIELD:
1505
case ICE_ITEMBOX_INVINCIBILITY:
1506
case ICE_ITEMBOX_SNEAKERS:
1507
case ICE_ITEMBOX_1UP:
1508
case ICE_ITEMBOX_EGGMAN:
1509
case ICE_ITEMBOX_HYPERRING:
1510
case ICE_ITEMBOX_SWAP:
1511
case ICE_ITEMBOX_RANDOM:
1512
case ICE_ITEMBOX_SUPER:
1513
default:
1514
RSDK.SetSpriteAnimation(ItemBox->aniFrames, 0, &self->contentsAnimator, true, 0);
1515
RSDK.SetSpriteAnimation(ItemBox->aniFrames, 2, &self->altContentsAnimator, true, (self->type > ICE_ITEMBOX_1UP ? 2 : 0) + self->type - 5);
1516
1517
if (self->type != ICE_ITEMBOX_1UP) {
1518
if (globals->gameMode == MODE_COMPETITION) {
1519
if (globals->itemMode == ITEMS_RANDOM) {
1520
self->type = ICE_ITEMBOX_EGGMAN;
1521
self->altContentsAnimator.frameID = 13;
1522
}
1523
else if (globals->itemMode == ITEMS_TELEPORT) {
1524
self->type = ICE_ITEMBOX_SWAP;
1525
self->altContentsAnimator.frameID = 12;
1526
}
1527
}
1528
}
1529
else if (globals->gameMode != MODE_TIMEATTACK) {
1530
switch (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->characterID) {
1531
default:
1532
case ID_SONIC: break;
1533
case ID_TAILS: ++self->altContentsAnimator.frameID; break;
1534
case ID_KNUCKLES: self->altContentsAnimator.frameID += 2; break;
1535
#if MANIA_USE_PLUS
1536
case ID_MIGHTY: self->altContentsAnimator.frameID += 8; break;
1537
case ID_RAY: self->altContentsAnimator.frameID += 9; break;
1538
#endif
1539
}
1540
1541
if (globals->gameMode == MODE_COMPETITION) {
1542
if (globals->itemMode == ITEMS_RANDOM) {
1543
self->type = ICE_ITEMBOX_RANDOM;
1544
self->altContentsAnimator.frameID = 13;
1545
}
1546
else if (globals->itemMode == 2) {
1547
self->type = ICE_ITEMBOX_SWAP;
1548
self->altContentsAnimator.frameID = 12;
1549
}
1550
}
1551
}
1552
else {
1553
self->type = ICE_ITEMBOX_RINGS;
1554
self->altContentsAnimator.frameID = 0;
1555
}
1556
break;
1557
}
1558
1559
Ice_Draw_IceBlock();
1560
1561
self->type = type;
1562
self->subType = subType;
1563
self->subFlip = subFlip;
1564
}
1565
1566
void Ice_EditorLoad(void)
1567
{
1568
if (RSDK.CheckSceneFolder("PSZ1"))
1569
Ice->aniFrames = RSDK.LoadSpriteAnimation("PSZ1/Ice.bin", SCOPE_STAGE);
1570
else if (RSDK.CheckSceneFolder("PSZ2"))
1571
Ice->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/Ice.bin", SCOPE_STAGE);
1572
1573
RSDK_ACTIVE_VAR(Ice, size);
1574
RSDK_ENUM_VAR("Large", ICE_SIZE_LARGE);
1575
RSDK_ENUM_VAR("Small", ICE_SIZE_SMALL);
1576
1577
RSDK_ACTIVE_VAR(Ice, type);
1578
RSDK_ENUM_VAR("Empty Block", ICE_BLOCK);
1579
RSDK_ENUM_VAR("1 Ring", ICE_1RING);
1580
RSDK_ENUM_VAR("3 Rings", ICE_3RINGS);
1581
RSDK_ENUM_VAR("5 Rings", ICE_5RINGS);
1582
RSDK_ENUM_VAR("Spikes", ICE_SPIKES);
1583
RSDK_ENUM_VAR("ItemBox (Rings)", ICE_ITEMBOX_RINGS);
1584
RSDK_ENUM_VAR("ItemBox (Blue Shield)", ICE_ITEMBOX_BLUESHIELD);
1585
RSDK_ENUM_VAR("ItemBox (Bubble Shield)", ICE_ITEMBOX_BUBBLESHIELD);
1586
RSDK_ENUM_VAR("ItemBox (Fire Shield)", ICE_ITEMBOX_FIRESHIELD);
1587
RSDK_ENUM_VAR("ItemBox (Lightning Shield)", ICE_ITEMBOX_LIGHTNINGSHIELD);
1588
RSDK_ENUM_VAR("ItemBox (Invincibility)", ICE_ITEMBOX_INVINCIBILITY);
1589
RSDK_ENUM_VAR("ItemBox (Speed Shoes)", ICE_ITEMBOX_SNEAKERS);
1590
RSDK_ENUM_VAR("ItemBox (1UP)", ICE_ITEMBOX_1UP);
1591
RSDK_ENUM_VAR("ItemBox (Eggman)", ICE_ITEMBOX_EGGMAN);
1592
RSDK_ENUM_VAR("ItemBox (Hyper Ring)", ICE_ITEMBOX_HYPERRING);
1593
RSDK_ENUM_VAR("ItemBox (Swap)", ICE_ITEMBOX_SWAP);
1594
RSDK_ENUM_VAR("ItemBox (Random)", ICE_ITEMBOX_SUPER);
1595
RSDK_ENUM_VAR("ItemBox (Super)", ICE_ITEMBOX_RANDOM);
1596
RSDK_ENUM_VAR("Spring", ICE_SPRING);
1597
1598
RSDK_ACTIVE_VAR(Ice, subFlip);
1599
RSDK_ENUM_VAR("No Flip", FLIP_NONE);
1600
RSDK_ENUM_VAR("Flip X", FLIP_X);
1601
RSDK_ENUM_VAR("Flip Y", FLIP_Y);
1602
RSDK_ENUM_VAR("Flip XY", FLIP_XY);
1603
}
1604
#endif
1605
1606
void Ice_Serialize(void)
1607
{
1608
RSDK_EDITABLE_VAR(Ice, VAR_UINT8, size);
1609
RSDK_EDITABLE_VAR(Ice, VAR_UINT8, type);
1610
RSDK_EDITABLE_VAR(Ice, VAR_UINT8, subType);
1611
RSDK_EDITABLE_VAR(Ice, VAR_UINT8, subFlip);
1612
RSDK_EDITABLE_VAR(Ice, VAR_BOOL, bottomSmash);
1613
RSDK_EDITABLE_VAR(Ice, VAR_BOOL, knuxSmash);
1614
}
1615
1616