Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Common/BreakableWall.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: BreakableWall Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectBreakableWall *BreakableWall;
11
12
void BreakableWall_Update(void)
13
{
14
RSDK_THIS(BreakableWall);
15
16
StateMachine_Run(self->state);
17
}
18
19
void BreakableWall_LateUpdate(void) {}
20
21
void BreakableWall_StaticUpdate(void) {}
22
23
void BreakableWall_Draw(void)
24
{
25
RSDK_THIS(BreakableWall);
26
27
StateMachine_Run(self->stateDraw);
28
}
29
30
void BreakableWall_Create(void *data)
31
{
32
RSDK_THIS(BreakableWall);
33
34
self->gravityStrength = 0x3800;
35
36
if (data) {
37
int32 type = VOID_TO_INT(data);
38
self->visible = true;
39
self->updateRange.x = TO_FIXED(16);
40
self->updateRange.y = TO_FIXED(16);
41
self->active = ACTIVE_NORMAL;
42
self->drawFX = FX_FLIP | (self->velocity.x != 0 ? FX_ROTATE : FX_NONE);
43
self->tileRotation = RSDK.Rand(-8, 8);
44
45
switch (type) {
46
case BREAKWALL_TILE_FIXED:
47
self->state = BreakableWall_State_Tile;
48
self->stateDraw = BreakableWall_Draw_Tile;
49
break;
50
51
default:
52
case BREAKWALL_TILE_DYNAMIC:
53
self->state = BreakableWall_State_FallingTile;
54
self->stateDraw = StateMachine_None;
55
break;
56
}
57
}
58
else {
59
self->drawFX |= FX_FLIP;
60
61
if (!SceneInfo->inEditor) {
62
self->visible = false;
63
self->drawGroup = Zone->objectDrawGroup[1];
64
self->active = ACTIVE_BOUNDS;
65
self->updateRange.x = TO_FIXED(128);
66
self->updateRange.y = TO_FIXED(128);
67
self->priority = self->priority == BREAKWALL_PRIO_HIGH ? Zone->fgLayer[1] : Zone->fgLayer[0];
68
69
self->size.x >>= 0x10;
70
self->size.y >>= 0x10;
71
72
switch (self->type) {
73
case BREAKWALL_TYPE_WALL:
74
if (!self->size.x) {
75
self->size.x = 2;
76
self->size.y = 4;
77
}
78
79
self->state = BreakableWall_State_Wall;
80
self->stateDraw = BreakableWall_Draw_Wall;
81
break;
82
83
case BREAKWALL_TYPE_FLOOR:
84
if (!self->size.x) {
85
self->size.x = 2;
86
self->size.y = 2;
87
}
88
89
self->state = BreakableWall_State_Floor;
90
self->stateDraw = BreakableWall_Draw_Floor;
91
break;
92
93
case BREAKWALL_TYPE_BURROWFLOOR:
94
case BREAKWALL_TYPE_BURROWFLOOR_B:
95
if (!self->size.x)
96
self->size.x = 2;
97
98
self->state = BreakableWall_State_BurrowFloor;
99
self->stateDraw = BreakableWall_Draw_Floor;
100
break;
101
102
case BREAKWALL_TYPE_BURROWFLOORUP:
103
if (!self->size.x)
104
self->size.x = 2;
105
106
self->state = BreakableWall_State_BurrowFloorUp;
107
self->stateDraw = BreakableWall_Draw_Floor;
108
break;
109
110
case BREAKWALL_TYPE_CEILING:
111
if (!self->size.x) {
112
self->size.x = 2;
113
self->size.y = 2;
114
}
115
116
self->state = BreakableWall_State_Ceiling;
117
self->stateDraw = BreakableWall_Draw_Floor;
118
break;
119
120
default: break;
121
}
122
123
self->hitbox.left = -(8 * self->size.x);
124
self->hitbox.top = -(8 * self->size.y);
125
self->hitbox.right = 8 * self->size.x;
126
self->hitbox.bottom = 8 * self->size.y;
127
}
128
}
129
}
130
131
void BreakableWall_StageLoad(void)
132
{
133
BreakableWall->aniFrames = RSDK.LoadSpriteAnimation("Global/TicMark.bin", SCOPE_STAGE);
134
RSDK.SetSpriteAnimation(BreakableWall->aniFrames, 0, &BreakableWall->animator, true, 0);
135
136
if (RSDK.CheckSceneFolder("AIZ"))
137
BreakableWall->sfxBreak = RSDK.GetSfx("Stage/LedgeBreak3.wav");
138
else
139
BreakableWall->sfxBreak = RSDK.GetSfx("Stage/LedgeBreak.wav");
140
141
BreakableWall->farPlaneLayer = RSDK.GetTileLayerID("Far Plane");
142
}
143
144
// States
145
void BreakableWall_State_BurrowFloorUp(void)
146
{
147
RSDK_THIS(BreakableWall);
148
149
self->visible = DebugMode->debugActive;
150
151
BreakableWall_CheckBreak_BurrowFloorUp();
152
}
153
void BreakableWall_State_Ceiling(void)
154
{
155
RSDK_THIS(BreakableWall);
156
157
self->visible = DebugMode->debugActive;
158
159
BreakableWall_CheckBreak_Ceiling();
160
}
161
void BreakableWall_State_FallingTile(void)
162
{
163
RSDK_THIS(BreakableWall);
164
165
if (--self->timer <= 0) {
166
RSDK.SetTile(self->targetLayer, self->tilePos.x, self->tilePos.y, -1);
167
168
if (self->drawGroup < Zone->objectDrawGroup[0] && BreakableWall->farPlaneLayer != (uint16)-1) {
169
RSDK.SetTile(BreakableWall->farPlaneLayer, self->tilePos.x, self->tilePos.y, -1);
170
}
171
172
self->state = BreakableWall_State_Tile;
173
self->stateDraw = BreakableWall_Draw_Tile;
174
}
175
}
176
void BreakableWall_State_Wall(void)
177
{
178
RSDK_THIS(BreakableWall);
179
180
self->visible = DebugMode->debugActive;
181
182
BreakableWall_CheckBreak_Wall();
183
}
184
void BreakableWall_State_Tile(void)
185
{
186
RSDK_THIS(BreakableWall);
187
188
self->position.x += self->velocity.x;
189
self->position.y += self->velocity.y;
190
self->velocity.y += self->gravityStrength;
191
192
if (self->velocity.x)
193
self->rotation += self->tileRotation;
194
195
if (self->drawGroup >= Zone->objectDrawGroup[0]) {
196
if (!RSDK.CheckOnScreen(self, &self->updateRange))
197
destroyEntity(self);
198
}
199
else {
200
if (++self->timer == 120)
201
destroyEntity(self);
202
}
203
}
204
void BreakableWall_State_Floor(void)
205
{
206
RSDK_THIS(BreakableWall);
207
208
self->visible = DebugMode->debugActive;
209
210
BreakableWall_CheckBreak_Floor();
211
}
212
void BreakableWall_State_BurrowFloor(void)
213
{
214
RSDK_THIS(BreakableWall);
215
216
self->visible = DebugMode->debugActive;
217
218
BreakableWall_CheckBreak_BurrowFloor();
219
}
220
221
// Draw States
222
void BreakableWall_Draw_Wall(void)
223
{
224
RSDK_THIS(BreakableWall);
225
226
Vector2 drawPos;
227
drawPos.x = self->position.x - (self->size.x << 19);
228
drawPos.y = self->position.y - (self->size.y << 19);
229
230
RSDK.DrawLine(drawPos.x - 0x10000, drawPos.y - 0x10000, drawPos.x + (self->size.x << 20), drawPos.y - 0x10000, 0xE0E0E0, 0, INK_NONE, false);
231
RSDK.DrawLine(drawPos.x - 0x10000, drawPos.y + (self->size.y << 20), drawPos.x + (self->size.x << 20), drawPos.y + (self->size.y << 20), 0xE0E0E0,
232
0, INK_NONE, false);
233
RSDK.DrawLine(drawPos.x - 0x10000, drawPos.y - 0x10000, drawPos.x - 0x10000, drawPos.y + (self->size.y << 20), 0xE0E0E0, 0, INK_NONE, false);
234
RSDK.DrawLine(drawPos.x + (self->size.x << 20), drawPos.y - 0x10000, drawPos.x + (self->size.x << 20), drawPos.y + (self->size.y << 20), 0xE0E0E0,
235
0, INK_NONE, false);
236
237
self->direction = FLIP_NONE;
238
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
239
240
drawPos.x += self->size.x << 20;
241
self->direction = FLIP_X;
242
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
243
244
drawPos.y += self->size.y << 20;
245
self->direction = FLIP_XY;
246
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
247
248
drawPos.x -= self->size.x << 20;
249
self->direction = FLIP_Y;
250
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
251
}
252
253
// The... same function as above?
254
void BreakableWall_Draw_Floor(void)
255
{
256
RSDK_THIS(BreakableWall);
257
258
Vector2 drawPos;
259
drawPos.x = self->position.x - (self->size.x << 19);
260
drawPos.y = self->position.y - (self->size.y << 19);
261
262
RSDK.DrawLine(drawPos.x - TO_FIXED(1), drawPos.y - TO_FIXED(1), drawPos.x + (self->size.x << 20), drawPos.y - TO_FIXED(1), 0xE0E0E0, 0x00,
263
INK_NONE, false);
264
RSDK.DrawLine(drawPos.x - TO_FIXED(1), drawPos.y + (self->size.y << 20), drawPos.x + (self->size.x << 20), drawPos.y + (self->size.y << 20),
265
0xE0E0E0, 0x00, INK_NONE, false);
266
RSDK.DrawLine(drawPos.x - TO_FIXED(1), drawPos.y - TO_FIXED(1), drawPos.x - TO_FIXED(1), drawPos.y + (self->size.y << 20), 0xE0E0E0, 0x00,
267
INK_NONE, false);
268
RSDK.DrawLine(drawPos.x + (self->size.x << 20), drawPos.y - TO_FIXED(1), drawPos.x + (self->size.x << 20), drawPos.y + (self->size.y << 20),
269
0xE0E0E0, 0x00, INK_NONE, false);
270
271
self->direction = FLIP_NONE;
272
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
273
274
drawPos.x += self->size.x << 20;
275
self->direction = FLIP_X;
276
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
277
278
drawPos.y += self->size.y << 20;
279
self->direction = FLIP_XY;
280
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
281
282
drawPos.x -= self->size.x << 20;
283
self->direction = FLIP_Y;
284
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
285
}
286
void BreakableWall_Draw_Tile(void)
287
{
288
RSDK_THIS(BreakableWall);
289
290
self->angle = self->rotation;
291
RSDK.DrawTile(&self->tileInfo, 1, 1, NULL, NULL, false);
292
}
293
294
// Breaking
295
void BreakableWall_CheckBreak_Wall(void)
296
{
297
RSDK_THIS(BreakableWall);
298
299
foreach_active(Player, player)
300
{
301
#if MANIA_USE_PLUS
302
if (self->onlyMighty) {
303
if (player->characterID != ID_MIGHTY && (!self->onlyKnux || player->characterID != ID_KNUCKLES)) {
304
Player_CheckCollisionBox(player, self, &self->hitbox);
305
continue;
306
}
307
}
308
else {
309
if (self->onlyKnux && player->characterID != ID_KNUCKLES && (!self->onlyMighty || player->characterID != ID_MIGHTY)) {
310
Player_CheckCollisionBox(player, self, &self->hitbox);
311
continue;
312
}
313
}
314
#else
315
if (self->onlyKnux && player->characterID != ID_KNUCKLES) {
316
Player_CheckCollisionBox(player, self, &self->hitbox);
317
continue;
318
}
319
#endif
320
321
bool32 canBreak = abs(player->groundVel) >= 0x48000 && player->onGround && player->animator.animationID == ANI_JUMP;
322
323
if (player->shield == SHIELD_FIRE) {
324
EntityShield *shield = RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player), Shield);
325
canBreak |= shield->shieldAnimator.animationID == SHIELDANI_FIREATTACK;
326
}
327
328
switch (player->characterID) {
329
default: break;
330
331
case ID_SONIC: canBreak |= player->superState == SUPERSTATE_SUPER; break;
332
333
case ID_KNUCKLES: canBreak = true; break;
334
}
335
336
if (player->state == Ice_PlayerState_Frozen)
337
canBreak |= abs(player->groundVel) >= 0x48000;
338
339
if (canBreak && !player->sidekick) {
340
if (Player_CheckCollisionTouch(player, self, &self->hitbox)) {
341
BreakableWall_Break(self, player->position.x > self->position.x);
342
343
if (player->characterID == ID_KNUCKLES) {
344
if (player->animator.animationID == ANI_GLIDE) {
345
player->abilitySpeed -= player->abilitySpeed >> 2;
346
player->velocity.x -= player->velocity.x >> 2;
347
if (abs(player->velocity.x) <= 0x30000) {
348
RSDK.SetSpriteAnimation(player->aniFrames, ANI_GLIDE_DROP, &player->animator, false, 0);
349
player->state = Player_State_KnuxGlideDrop;
350
}
351
}
352
else if (player->animator.animationID == ANI_GLIDE_SLIDE) {
353
player->abilitySpeed -= player->abilitySpeed >> 2;
354
player->velocity.x -= player->velocity.x >> 2;
355
}
356
}
357
358
RSDK.PlaySfx(BreakableWall->sfxBreak, false, 255);
359
destroyEntity(self);
360
}
361
continue; // skip to next loop, so we dont do the box collision
362
}
363
364
Player_CheckCollisionBox(player, self, &self->hitbox);
365
}
366
}
367
void BreakableWall_CheckBreak_Floor(void)
368
{
369
RSDK_THIS(BreakableWall);
370
371
foreach_active(Player, player)
372
{
373
#if MANIA_USE_PLUS
374
int32 velY = player->velocity.y;
375
#endif
376
377
if (Player_CheckCollisionBox(player, self, &self->hitbox) == C_TOP) {
378
#if MANIA_USE_PLUS
379
if (self->onlyMighty) {
380
if ((player->characterID != ID_MIGHTY || player->animator.animationID != ANI_HAMMERDROP)
381
&& (!self->onlyKnux || player->characterID != ID_KNUCKLES)) {
382
Player_CheckCollisionBox(player, self, &self->hitbox);
383
continue;
384
}
385
}
386
else {
387
if (self->onlyKnux && player->characterID != ID_KNUCKLES && (!self->onlyMighty || player->characterID != ID_MIGHTY)) {
388
Player_CheckCollisionBox(player, self, &self->hitbox);
389
continue;
390
}
391
}
392
#else
393
if (self->onlyKnux && player->characterID != ID_KNUCKLES) {
394
Player_CheckCollisionBox(player, self, &self->hitbox);
395
continue;
396
}
397
#endif
398
399
bool32 canBreak = player->animator.animationID == ANI_JUMP;
400
401
switch (player->characterID) {
402
default: break;
403
404
case ID_SONIC:
405
if (!canBreak)
406
canBreak = player->animator.animationID == ANI_DROPDASH;
407
break;
408
409
case ID_KNUCKLES: canBreak = true; break;
410
411
#if MANIA_USE_PLUS
412
case ID_MIGHTY:
413
if (!canBreak)
414
canBreak = player->state == Player_State_MightyHammerDrop;
415
break;
416
#endif
417
}
418
419
if (player->groundedStore && player->collisionMode != CMODE_LWALL && player->collisionMode != CMODE_RWALL)
420
canBreak = false;
421
422
if (canBreak && !player->sidekick) {
423
player->onGround = false;
424
425
BreakableWall_Break(self, FLIP_Y);
426
427
RSDK.PlaySfx(BreakableWall->sfxBreak, false, 255);
428
BreakableWall_GiveScoreBonus(player);
429
430
#if MANIA_USE_PLUS
431
if (player->characterID == ID_MIGHTY && player->state == Player_State_MightyHammerDrop)
432
player->velocity.y = velY - TO_FIXED(1);
433
else
434
#endif
435
player->velocity.y = -TO_FIXED(3);
436
437
destroyEntity(self);
438
439
continue; // skip to next loop, so we dont do the box collision
440
}
441
}
442
443
Player_CheckCollisionBox(player, self, &self->hitbox);
444
}
445
}
446
void BreakableWall_CheckBreak_BurrowFloor(void)
447
{
448
RSDK_THIS(BreakableWall);
449
450
foreach_active(Player, player)
451
{
452
#if MANIA_USE_PLUS
453
int32 velY = player->velocity.y;
454
#endif
455
bool32 onGround = player->onGround;
456
457
if (Player_CheckCollisionBox(player, self, &self->hitbox) == C_TOP && !player->sidekick
458
&& ((player->collisionPlane == 1 && self->type == BREAKWALL_TYPE_BURROWFLOOR_B) || self->type == BREAKWALL_TYPE_BURROWFLOOR)) {
459
#if MANIA_USE_PLUS
460
if (self->onlyMighty) {
461
if ((player->characterID != ID_MIGHTY || player->animator.animationID != ANI_HAMMERDROP)
462
&& (!self->onlyKnux || player->characterID != ID_KNUCKLES))
463
continue;
464
}
465
else {
466
if (self->onlyKnux && player->characterID != ID_KNUCKLES && (!self->onlyMighty || player->characterID != ID_MIGHTY))
467
continue;
468
}
469
#else
470
if (self->onlyKnux && player->characterID != ID_KNUCKLES)
471
continue;
472
#endif
473
474
bool32 canBreak = player->animator.animationID == ANI_JUMP;
475
476
switch (player->characterID) {
477
default: break;
478
479
case ID_SONIC:
480
if (!canBreak)
481
canBreak = player->animator.animationID == ANI_DROPDASH;
482
break;
483
484
#if MANIA_USE_PLUS
485
case ID_MIGHTY:
486
if (!canBreak)
487
canBreak = player->state == Player_State_MightyHammerDrop;
488
break;
489
#endif
490
}
491
492
if (onGround && player->collisionMode != CMODE_LWALL && player->collisionMode != CMODE_RWALL)
493
canBreak = false;
494
495
if (canBreak && !player->sidekick) {
496
player->onGround = false;
497
498
int32 sizeX = self->size.x;
499
int32 sizeY = self->size.y;
500
int32 posX = self->position.x;
501
int32 posY = self->position.y;
502
503
self->size.y = 1;
504
self->position.y += 0x80000 - (sizeY << 19);
505
506
BreakableWall_Break(self, FLIP_Y);
507
508
self->size.x = sizeX;
509
self->size.y = sizeY;
510
self->position.x = posX;
511
self->position.y = posY;
512
RSDK.PlaySfx(BreakableWall->sfxBreak, false, 255);
513
514
BreakableWall_GiveScoreBonus(player);
515
516
#if MANIA_USE_PLUS
517
if (player->characterID == ID_MIGHTY && player->state == Player_State_MightyHammerDrop)
518
player->velocity.y = velY - TO_FIXED(1);
519
else
520
#endif
521
player->velocity.y = 0;
522
523
self->hitbox.top += 8;
524
self->hitbox.bottom -= 8;
525
--self->size.y;
526
self->position.y += TO_FIXED(8);
527
528
if (self->size.y <= 0)
529
destroyEntity(self);
530
}
531
}
532
}
533
}
534
void BreakableWall_CheckBreak_BurrowFloorUp(void)
535
{
536
RSDK_THIS(BreakableWall);
537
538
foreach_active(Player, player)
539
{
540
int32 velY = player->velocity.y;
541
if (Player_CheckCollisionBox(player, self, &self->hitbox) == C_BOTTOM) {
542
#if MANIA_USE_PLUS
543
if (self->onlyMighty) {
544
if ((player->characterID != ID_MIGHTY) && (!self->onlyKnux || player->characterID != ID_KNUCKLES))
545
continue;
546
}
547
else {
548
if (self->onlyKnux && player->characterID != ID_KNUCKLES && (!self->onlyMighty || player->characterID != ID_MIGHTY))
549
continue;
550
}
551
#else
552
if (self->onlyKnux && player->characterID != ID_KNUCKLES)
553
continue;
554
#endif
555
556
if (!player->sidekick) {
557
int32 sizeX = self->size.x;
558
int32 sizeY = self->size.y;
559
int32 posX = self->position.x;
560
int32 posY = self->position.y;
561
562
if (sizeY > 2)
563
self->size.y = 2;
564
self->position.y += (sizeY - self->size.y) << 19;
565
566
BreakableWall_Break(self, FLIP_Y);
567
568
self->size.x = sizeX;
569
self->size.y = sizeY;
570
self->position.x = posX;
571
self->position.y = posY;
572
RSDK.PlaySfx(BreakableWall->sfxBreak, false, 255);
573
574
player->velocity.y = 0;
575
576
if (self->size.y < 2) {
577
self->hitbox.top += 8;
578
self->size.y -= 1;
579
self->hitbox.bottom -= 8;
580
self->position.y -= TO_FIXED(8);
581
}
582
else {
583
self->hitbox.top += 16;
584
self->size.y -= 2;
585
self->hitbox.bottom -= 16;
586
self->position.y -= TO_FIXED(16);
587
}
588
589
if (self->size.y <= 0)
590
destroyEntity(self);
591
592
player->velocity.y = velY;
593
}
594
}
595
}
596
}
597
void BreakableWall_CheckBreak_Ceiling(void)
598
{
599
RSDK_THIS(BreakableWall);
600
601
foreach_active(Player, player)
602
{
603
int32 velY = player->velocity.y;
604
if (Player_CheckCollisionBox(player, self, &self->hitbox) == C_BOTTOM) {
605
#if MANIA_USE_PLUS
606
if (self->onlyMighty) {
607
if ((player->characterID != ID_MIGHTY) && (!self->onlyKnux || player->characterID != ID_KNUCKLES))
608
continue;
609
}
610
else {
611
if (self->onlyKnux && player->characterID != ID_KNUCKLES && (!self->onlyMighty || player->characterID != ID_MIGHTY))
612
continue;
613
}
614
#else
615
if (self->onlyKnux && player->characterID != ID_KNUCKLES)
616
continue;
617
#endif
618
player->onGround = false;
619
620
BreakableWall_Break(self, FLIP_Y);
621
622
RSDK.PlaySfx(BreakableWall->sfxBreak, false, 0xFF);
623
player->velocity.y = velY;
624
destroyEntity(self);
625
}
626
}
627
}
628
void BreakableWall_Break(EntityBreakableWall *self, uint8 direction)
629
{
630
int32 startX = self->position.x;
631
int32 startY = self->position.y;
632
int32 endX = self->position.x - (self->size.x << 19) + TO_FIXED(8);
633
int32 endY = self->position.y - (self->size.y << 19) + TO_FIXED(8);
634
635
switch (direction) {
636
case FLIP_NONE: startX += self->size.x << 19; break;
637
case FLIP_X: startX -= self->size.x << 19; break;
638
case FLIP_Y: startY += self->size.y << 19; break;
639
case FLIP_XY: startY -= self->size.y << 19; break;
640
default: break;
641
}
642
643
int32 curY = endY - startY;
644
for (int32 y = 0; y < self->size.y; ++y) {
645
int32 curX = endX - startX;
646
int32 tileY = (curY + startY) >> 20;
647
int32 angleX = 2 * (endX - startX);
648
649
for (int32 x = 0; x < self->size.x; ++x) {
650
int32 tileX = (curX + startX) >> 20;
651
EntityBreakableWall *tile = CREATE_ENTITY(BreakableWall, INT_TO_VOID(BREAKWALL_TILE_FIXED), curX + startX, curY + startY);
652
tile->tileInfo = RSDK.GetTile(self->priority, tileX, tileY);
653
tile->drawGroup = self->drawGroup;
654
655
switch (direction) {
656
case FLIP_NONE:
657
case FLIP_X: {
658
int32 angle = RSDK.ATan2(angleX, curY);
659
int32 angle2 = 0;
660
if (abs(curX) > 0x80000) {
661
if (curX + startX >= startX)
662
angle2 = RSDK.ATan2(TO_FIXED(8), curY);
663
else
664
angle2 = RSDK.ATan2(-TO_FIXED(8), curY);
665
}
666
else {
667
angle2 = RSDK.ATan2(curX, curY);
668
}
669
670
tile->velocity.x = direction == FLIP_NONE ? -TO_FIXED(1) : TO_FIXED(1);
671
tile->velocity.y = TO_FIXED(1);
672
tile->velocity.x += 40 * (((self->size.y << 19) + 3 * abs(curX) - abs(curY)) >> 18) * RSDK.Cos256(angle);
673
tile->velocity.y += 32 * ((abs(curY) + abs(curX) + 2 * abs(curY)) >> 18) * RSDK.Sin256(angle2);
674
break;
675
}
676
677
case FLIP_Y: {
678
int32 angle = RSDK.ATan2(angleX, curY);
679
int32 velocity = (abs(curX) + 3 * abs(curY)) >> 18;
680
681
tile->velocity.x += 40 * velocity * RSDK.Cos256(angle);
682
tile->velocity.y += 40 * velocity * RSDK.Sin256(angle);
683
break;
684
}
685
}
686
687
RSDK.SetTile(self->priority, tileX, tileY, -1);
688
if (self->drawGroup < Zone->objectDrawGroup[0]) {
689
if (BreakableWall->farPlaneLayer != (uint16)-1)
690
RSDK.SetTile(BreakableWall->farPlaneLayer, tileX, tileY, -1);
691
}
692
693
curX += TO_FIXED(16);
694
angleX += TO_FIXED(32);
695
}
696
697
curY += TO_FIXED(16);
698
}
699
}
700
701
// Misc
702
void BreakableWall_GiveScoreBonus(EntityPlayer *player)
703
{
704
RSDK_THIS(BreakableWall);
705
706
EntityScoreBonus *scoreBonus = CREATE_ENTITY(ScoreBonus, NULL, self->position.x, self->position.y);
707
scoreBonus->drawGroup = Zone->objectDrawGroup[1];
708
scoreBonus->animator.frameID = player->scoreBonus;
709
710
switch (player->scoreBonus) {
711
case 0: Player_GiveScore(player, 100); break;
712
case 1: Player_GiveScore(player, 200); break;
713
case 2: Player_GiveScore(player, 500); break;
714
715
case 3:
716
case 4:
717
case 5:
718
case 6:
719
case 7:
720
case 8:
721
case 9:
722
case 10:
723
case 11:
724
case 12:
725
case 13:
726
case 14: Player_GiveScore(player, 1000); break;
727
728
case 15: Player_GiveScore(player, 10000); break;
729
730
default: break;
731
}
732
733
if (player->scoreBonus < 15)
734
player->scoreBonus++;
735
}
736
737
#if GAME_INCLUDE_EDITOR
738
void BreakableWall_EditorDraw(void)
739
{
740
RSDK_THIS(BreakableWall);
741
742
int32 sizeX = self->size.x;
743
int32 sizeY = self->size.y;
744
745
switch (self->type) {
746
case BREAKWALL_TYPE_WALL:
747
if (!sizeX) {
748
sizeX = TO_FIXED(2);
749
sizeY = TO_FIXED(4);
750
}
751
break;
752
753
case BREAKWALL_TYPE_FLOOR:
754
if (!sizeX) {
755
sizeX = TO_FIXED(2);
756
sizeY = TO_FIXED(2);
757
}
758
break;
759
760
case BREAKWALL_TYPE_BURROWFLOOR:
761
case BREAKWALL_TYPE_BURROWFLOOR_B:
762
if (!sizeX)
763
sizeX = TO_FIXED(2);
764
break;
765
766
case BREAKWALL_TYPE_BURROWFLOORUP:
767
if (!sizeX)
768
sizeX = TO_FIXED(2);
769
break;
770
771
case BREAKWALL_TYPE_CEILING:
772
if (!sizeX) {
773
sizeX = TO_FIXED(2);
774
sizeY = TO_FIXED(2);
775
}
776
break;
777
778
default: break;
779
}
780
781
sizeX <<= 4;
782
sizeY <<= 4;
783
784
Vector2 drawPos;
785
drawPos.x = self->position.x - sizeX;
786
drawPos.y = self->position.y - sizeY;
787
788
DrawHelpers_DrawRectOutline(self->position.x, self->position.y, sizeX, sizeY, 0xFFFF00);
789
790
drawPos.x += sizeX >> 1;
791
drawPos.y += sizeY >> 1;
792
793
self->direction = FLIP_NONE;
794
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
795
796
drawPos.x += sizeX;
797
self->direction = FLIP_X;
798
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
799
800
drawPos.y += sizeY;
801
self->direction = FLIP_XY;
802
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
803
804
drawPos.x -= sizeX;
805
self->direction = FLIP_Y;
806
RSDK.DrawSprite(&BreakableWall->animator, &drawPos, false);
807
}
808
809
void BreakableWall_EditorLoad(void)
810
{
811
BreakableWall->aniFrames = RSDK.LoadSpriteAnimation("Global/TicMark.bin", SCOPE_STAGE);
812
RSDK.SetSpriteAnimation(BreakableWall->aniFrames, 0, &BreakableWall->animator, true, 0);
813
814
RSDK_ACTIVE_VAR(BreakableWall, type);
815
RSDK_ENUM_VAR("Wall", BREAKWALL_TYPE_WALL);
816
RSDK_ENUM_VAR("Floor", BREAKWALL_TYPE_FLOOR);
817
RSDK_ENUM_VAR("Burrow Floor", BREAKWALL_TYPE_BURROWFLOOR);
818
RSDK_ENUM_VAR("Burrow Floor (Plane B Only)", BREAKWALL_TYPE_BURROWFLOOR_B);
819
RSDK_ENUM_VAR("Burrow Floor Up", BREAKWALL_TYPE_BURROWFLOORUP);
820
RSDK_ENUM_VAR("Ceiling", BREAKWALL_TYPE_CEILING);
821
822
RSDK_ACTIVE_VAR(BreakableWall, priority);
823
RSDK_ENUM_VAR("FG High", BREAKWALL_PRIO_HIGH);
824
RSDK_ENUM_VAR("FG Low", BREAKWALL_PRIO_LOW);
825
}
826
#endif
827
828
void BreakableWall_Serialize(void)
829
{
830
RSDK_EDITABLE_VAR(BreakableWall, VAR_UINT8, type);
831
RSDK_EDITABLE_VAR(BreakableWall, VAR_BOOL, onlyKnux);
832
RSDK_EDITABLE_VAR(BreakableWall, VAR_BOOL, onlyMighty);
833
RSDK_EDITABLE_VAR(BreakableWall, VAR_ENUM, priority);
834
RSDK_EDITABLE_VAR(BreakableWall, VAR_VECTOR2, size);
835
}
836
837