Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/SPZ/Canista.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Canista Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectCanista *Canista;
11
12
// This object looks like it was based on Unused/WallCrawl
13
// Would explain WallCrawl's presence in the blueprint sheet, being a "proto" form of this
14
15
void Canista_Update(void)
16
{
17
RSDK_THIS(Canista);
18
19
StateMachine_Run(self->state);
20
}
21
22
void Canista_LateUpdate(void) {}
23
24
void Canista_StaticUpdate(void) {}
25
26
void Canista_Draw(void)
27
{
28
RSDK_THIS(Canista);
29
30
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
31
RSDK.DrawSprite(&self->tapeAnimator, NULL, false);
32
RSDK.DrawSprite(&self->cannonAnimator, NULL, false);
33
}
34
35
void Canista_Create(void *data)
36
{
37
RSDK_THIS(Canista);
38
39
self->visible = true;
40
self->drawGroup = Zone->objectDrawGroup[0] + 1;
41
42
if (data) {
43
self->drawFX |= FX_FLIP;
44
self->inkEffect = INK_ALPHA;
45
self->alpha = 0x100;
46
self->stopTimer = 90;
47
self->updateRange.x = 0x400000;
48
self->updateRange.y = 0x400000;
49
self->active = ACTIVE_NORMAL;
50
self->drawGroup = Zone->objectDrawGroup[0];
51
RSDK.SetSpriteAnimation(Canista->aniFrames, 3, &self->mainAnimator, true, 0);
52
self->state = Canista_StateProjectile_Shot;
53
}
54
else {
55
self->drawFX |= FX_ROTATE | FX_FLIP;
56
self->shootOffset <<= 16;
57
self->shootSize <<= 16;
58
59
self->startPos = self->position;
60
self->startDir = self->direction;
61
self->active = ACTIVE_BOUNDS;
62
self->updateRange.x = 0x800000;
63
self->updateRange.y = 0x800000;
64
65
self->detectedPlayer = NULL;
66
self->hitboxDetect.left = -self->detectSize.x >> 17;
67
self->hitboxDetect.top = -self->detectSize.y >> 17;
68
self->hitboxDetect.right = self->detectSize.x >> 17;
69
self->hitboxDetect.bottom = self->detectSize.y >> 17;
70
71
RSDK.SetSpriteAnimation(Canista->aniFrames, 0, &self->mainAnimator, true, 0);
72
RSDK.SetSpriteAnimation(Canista->aniFrames, 1, &self->tapeAnimator, true, 0);
73
RSDK.SetSpriteAnimation(Canista->aniFrames, 2, &self->cannonAnimator, true, 0);
74
self->state = Canista_State_Init;
75
}
76
}
77
78
void Canista_StageLoad(void)
79
{
80
if (RSDK.CheckSceneFolder("SPZ1"))
81
Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ1/Canista.bin", SCOPE_STAGE);
82
else if (RSDK.CheckSceneFolder("SPZ2"))
83
Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ2/Canista.bin", SCOPE_STAGE);
84
85
Canista->hitboxBadnik.left = -14;
86
Canista->hitboxBadnik.top = -14;
87
Canista->hitboxBadnik.right = 14;
88
Canista->hitboxBadnik.bottom = 14;
89
90
Canista->hitboxRange.left = -128;
91
Canista->hitboxRange.top = -128;
92
Canista->hitboxRange.right = 128;
93
Canista->hitboxRange.bottom = 128;
94
95
Canista->hitboxDetect.left = -132;
96
Canista->hitboxDetect.top = 48;
97
Canista->hitboxDetect.right = 132;
98
Canista->hitboxDetect.bottom = 49;
99
100
Canista->hitboxProjectile.left = -26;
101
Canista->hitboxProjectile.top = -5;
102
Canista->hitboxProjectile.right = -16;
103
Canista->hitboxProjectile.bottom = 5;
104
105
DEBUGMODE_ADD_OBJ(Canista);
106
107
Canista->sfxPon = RSDK.GetSfx("Stage/Pon.wav");
108
}
109
110
void Canista_DebugSpawn(void)
111
{
112
RSDK_THIS(DebugMode);
113
114
EntityCanista *canista = CREATE_ENTITY(Canista, NULL, self->position.x, self->position.y);
115
canista->direction = self->direction;
116
canista->startDir = self->direction;
117
}
118
119
void Canista_DebugDraw(void)
120
{
121
RSDK.SetSpriteAnimation(Canista->aniFrames, 0, &DebugMode->animator, true, 0);
122
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
123
}
124
125
void Canista_CheckPlayerBadnikCollisions(void)
126
{
127
RSDK_THIS(Canista);
128
129
int32 storeX = self->position.x;
130
int32 storeY = self->position.y;
131
132
foreach_active(Player, player)
133
{
134
if (!self->detectDelay) {
135
if (self->triggerMode) {
136
self->position.x = self->startPos.x + self->detectOffset.x;
137
self->position.y = self->startPos.y + self->detectOffset.y;
138
if (Player_CheckCollisionTouch(player, self, &self->hitboxDetect)) {
139
if (self->detectedPlayer) {
140
if (abs(self->position.x - player->position.x) < abs(self->position.x - self->detectedPlayer->position.x))
141
self->detectedPlayer = player;
142
}
143
else {
144
self->detectedPlayer = player;
145
}
146
}
147
}
148
else {
149
if (Player_CheckCollisionTouch(player, self, &Canista->hitboxDetect)) {
150
if (self->detectedPlayer) {
151
if (abs(self->position.x - player->position.x) < abs(self->position.x - self->detectedPlayer->position.x))
152
self->detectedPlayer = player;
153
}
154
else {
155
self->detectedPlayer = player;
156
}
157
}
158
}
159
}
160
161
self->position.x = storeX;
162
self->position.y = storeY;
163
164
if (Player_CheckBadnikTouch(player, self, &Canista->hitboxBadnik))
165
Player_CheckBadnikBreak(player, self, true);
166
}
167
}
168
169
void Canista_CheckOffScreen(void)
170
{
171
RSDK_THIS(Canista);
172
173
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
174
self->position = self->startPos;
175
self->direction = self->startDir;
176
self->shootOffset >>= 16;
177
178
Canista_Create(NULL);
179
}
180
}
181
182
void Canista_State_Init(void)
183
{
184
RSDK_THIS(Canista);
185
186
self->active = ACTIVE_NORMAL;
187
int32 offset = !(self->direction & FLIP_X) ? 0x100000 : -0x110000;
188
189
RSDK.ObjectTileGrip(self, Zone->collisionLayers, (2 * ((self->direction & FLIP_X) != 0) + 1), 0, offset, -0x180000, 8);
190
self->moveDir = 0;
191
self->timer = 0;
192
self->stopTimer = 0;
193
self->updateRange.y = 0x2000000;
194
self->state = Canista_State_Moving;
195
Canista_State_Moving();
196
}
197
198
void Canista_State_Moving(void)
199
{
200
RSDK_THIS(Canista);
201
202
if (self->detectDelay)
203
self->detectDelay--;
204
205
EntityPlayer *player = self->detectedPlayer;
206
if (player) {
207
int32 storeX = self->position.x;
208
int32 storeY = self->position.y;
209
210
if (self->triggerMode) {
211
self->position.x = self->startPos.x + self->detectOffset.x;
212
self->position.y = self->startPos.y + self->detectOffset.y;
213
214
if (!Player_CheckCollisionTouch(player, self, &self->hitboxDetect))
215
self->detectedPlayer = NULL;
216
}
217
else {
218
if (!Player_CheckCollisionTouch(player, self, &Canista->hitboxRange))
219
self->detectedPlayer = NULL;
220
}
221
222
if (self->detectedPlayer) {
223
if (self->triggerMode) {
224
int32 playerY = player->position.y & 0xFFFF0000;
225
int32 shootY = (storeY + self->shootOffset - self->shootSize) & 0xFFFF0000;
226
227
if (playerY < shootY || playerY > shootY) {
228
self->mainAnimator.speed = 128;
229
self->velocity.y = ((self->shootOffset + storeY) & 0xFFFF0000) < (player->position.y & 0xFFFF0000) ? 0x10000 : -0x10000;
230
}
231
else {
232
self->state = Canista_State_Idle;
233
self->stopTimer = 60;
234
self->detectedPlayer = NULL;
235
self->detectDelay = 180;
236
}
237
}
238
else {
239
if (Player_CheckCollisionTouch(player, self, &Canista->hitboxDetect)) {
240
self->state = Canista_State_Idle;
241
self->stopTimer = 60;
242
self->detectedPlayer = NULL;
243
self->detectDelay = 180;
244
}
245
else {
246
self->mainAnimator.speed = 128;
247
if (player->position.y - 0x400000 > storeY)
248
self->velocity.y = 0x10000;
249
else
250
self->velocity.y = -0x1000;
251
}
252
}
253
}
254
255
self->position.x = storeX;
256
self->position.y = storeY;
257
}
258
else {
259
self->mainAnimator.speed = 85;
260
self->velocity.y = self->moveDir ? -0x8000 : 0x8000;
261
}
262
263
self->position.y += self->velocity.y;
264
int32 tapeRotation = self->velocity.y >> 12;
265
if (self->direction)
266
self->rotation += tapeRotation;
267
else
268
self->rotation -= tapeRotation;
269
270
int32 offsetX = !(self->direction & FLIP_X) ? 0x100000 : -0x110000;
271
int32 offsetY = ((self->velocity.y >> 31) & 0xFFD40000) + 0x140000;
272
if (!RSDK.ObjectTileGrip(self, Zone->collisionLayers, (2 * ((self->direction & FLIP_X) != 0) + CMODE_LWALL), 0, offsetX, offsetY, 0)) {
273
self->state = Canista_State_Idle;
274
self->stopTimer = 30;
275
if (self->detectedPlayer) {
276
self->detectedPlayer = NULL;
277
self->detectDelay = 180;
278
}
279
}
280
281
RSDK.ProcessAnimation(&self->mainAnimator);
282
283
if (self->timer) {
284
if (--self->timer == 1) {
285
self->cannonAnimator.frameID = 3;
286
}
287
}
288
else {
289
self->cannonAnimator.timer = (self->cannonAnimator.timer + (abs(self->velocity.y) >> 15)) % 7;
290
self->cannonAnimator.frameID = (self->cannonAnimator.timer / 3) & 1;
291
}
292
293
Canista_CheckPlayerBadnikCollisions();
294
Canista_CheckOffScreen();
295
}
296
297
void Canista_State_Idle(void)
298
{
299
RSDK_THIS(Canista);
300
301
if (!--self->stopTimer) {
302
if (abs(self->velocity.y) == 0x8000)
303
self->moveDir ^= 1;
304
305
self->state = Canista_State_Moving;
306
Canista_State_Moving();
307
}
308
else {
309
if (self->stopTimer == 30) {
310
int32 offsetX = !(self->direction & FLIP_X) ? 0x180000 : -0x180000;
311
312
EntityCanista *shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);
313
shot->direction = self->direction;
314
shot->velocity.x = shot->direction == FLIP_X ? 0x40000 : -0x40000;
315
316
shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);
317
shot->direction = self->direction;
318
shot->velocity.x = shot->direction == FLIP_X ? 0x4C000 : -0x4C000;
319
320
shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);
321
shot->direction = self->direction;
322
shot->velocity.x = shot->direction == FLIP_X ? 0x58000 : -0x58000;
323
324
shot = CREATE_ENTITY(Canista, INT_TO_VOID(true), offsetX + self->position.x, self->position.y);
325
shot->direction = self->direction;
326
shot->velocity.x = shot->direction == FLIP_X ? 0x64000 : -0x64000;
327
328
self->cannonAnimator.frameID = 2;
329
self->timer = 4;
330
RSDK.PlaySfx(Canista->sfxPon, false, 0xFF);
331
}
332
333
Canista_CheckPlayerBadnikCollisions();
334
Canista_CheckOffScreen();
335
}
336
337
if (self->timer) {
338
if (--self->timer == 1) {
339
self->cannonAnimator.frameID = 3;
340
}
341
}
342
else {
343
self->cannonAnimator.frameID = (self->cannonAnimator.timer / 3) & 1;
344
}
345
346
Canista_CheckOffScreen();
347
}
348
349
void Canista_CheckPlayerProjectileCollisions(void)
350
{
351
RSDK_THIS(Canista);
352
353
if (self->alpha >= 0x80) {
354
355
foreach_active(Player, player)
356
{
357
if (Player_CheckCollisionTouch(player, self, &Canista->hitboxProjectile)) {
358
#if MANIA_USE_PLUS
359
if (Player_CheckMightyShellHit(player, self, -0x400, -0x400)) {
360
self->stopTimer = 0;
361
self->interaction = false;
362
}
363
else
364
#endif
365
Player_Hurt(player, self);
366
}
367
}
368
}
369
370
RSDK.ProcessAnimation(&self->mainAnimator);
371
372
if (!RSDK.CheckOnScreen(self, &self->updateRange))
373
destroyEntity(self);
374
}
375
376
void Canista_StateProjectile_Shot(void)
377
{
378
RSDK_THIS(Canista);
379
380
self->position.x += self->velocity.x;
381
382
if (self->direction == FLIP_X) {
383
self->velocity.x -= 0x2000;
384
if (self->velocity.x < 0) {
385
self->startPos = self->position;
386
self->velocity.x = 0;
387
self->rotation = 6;
388
self->angle = 2;
389
390
RSDK.SetSpriteAnimation(Canista->aniFrames, 4, &self->mainAnimator, true, 0);
391
self->state = Canista_StateProjectile_Fall;
392
}
393
}
394
else {
395
self->velocity.x += 0x2000;
396
if (self->velocity.x > 0) {
397
self->startPos = self->position;
398
self->velocity.x = 0;
399
self->rotation = 6;
400
self->angle = 2;
401
402
RSDK.SetSpriteAnimation(Canista->aniFrames, 4, &self->mainAnimator, true, 0);
403
self->state = Canista_StateProjectile_Fall;
404
}
405
}
406
407
Canista_CheckPlayerProjectileCollisions();
408
}
409
410
void Canista_StateProjectile_Fall(void)
411
{
412
RSDK_THIS(Canista);
413
414
self->angle += self->rotation;
415
if (!(Zone->timer & 0x1F) && RSDK.Rand(0, 2) == 1)
416
self->rotation = -self->rotation;
417
418
self->startPos.x += self->velocity.x;
419
self->startPos.y += self->velocity.y;
420
self->velocity.y += 0x800;
421
422
if (self->velocity.y > 0x10000) {
423
self->velocity.y -= 0x8000;
424
if (self->velocity.y < 0x10000)
425
self->velocity.y = 0x10000;
426
}
427
428
if (self->velocity.x) {
429
if (self->velocity.x <= 0) {
430
self->velocity.x += 0x2000;
431
if (self->velocity.x > 0)
432
self->velocity.x = 0;
433
}
434
else {
435
self->velocity.x -= 0x2000;
436
if (self->velocity.x < 0)
437
self->velocity.x = 0;
438
}
439
}
440
441
self->position.x = (RSDK.Cos256(self->angle) << 9) + self->startPos.x;
442
self->position.y = (RSDK.Sin256(self->angle) << 9) + self->startPos.y;
443
444
if (self->stopTimer) {
445
self->stopTimer--;
446
}
447
else {
448
self->alpha -= 4;
449
if (self->alpha <= 0)
450
destroyEntity(self);
451
}
452
453
Canista_CheckPlayerProjectileCollisions();
454
}
455
456
#if GAME_INCLUDE_EDITOR
457
void Canista_EditorDraw(void)
458
{
459
RSDK_THIS(Canista);
460
461
Canista_Draw();
462
463
if (showGizmos()) {
464
RSDK_DRAWING_OVERLAY(true);
465
466
if (self->triggerMode) {
467
self->hitboxDetect.left = -self->detectSize.x >> 17;
468
self->hitboxDetect.top = -self->detectSize.y >> 17;
469
self->hitboxDetect.right = self->detectSize.x >> 17;
470
self->hitboxDetect.bottom = self->detectSize.y >> 17;
471
472
DrawHelpers_DrawHitboxOutline(self->position.x + self->detectOffset.x, self->position.y + self->detectOffset.y, &self->hitboxDetect,
473
self->direction, 0xFF0000);
474
}
475
else {
476
DrawHelpers_DrawHitboxOutline(self->position.x, self->position.y, &Canista->hitboxDetect, self->direction, 0xFF0000);
477
}
478
479
RSDK_DRAWING_OVERLAY(false);
480
}
481
}
482
483
void Canista_EditorLoad(void)
484
{
485
if (RSDK.CheckSceneFolder("SPZ1"))
486
Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ1/Canista.bin", SCOPE_STAGE);
487
else if (RSDK.CheckSceneFolder("SPZ2"))
488
Canista->aniFrames = RSDK.LoadSpriteAnimation("SPZ2/Canista.bin", SCOPE_STAGE);
489
490
Canista->hitboxRange.left = -128;
491
Canista->hitboxRange.top = -128;
492
Canista->hitboxRange.right = 128;
493
Canista->hitboxRange.bottom = 128;
494
495
Canista->hitboxDetect.left = -132;
496
Canista->hitboxDetect.top = 48;
497
Canista->hitboxDetect.right = 132;
498
Canista->hitboxDetect.bottom = 49;
499
500
RSDK_ACTIVE_VAR(Canista, direction);
501
RSDK_ENUM_VAR("Left", FLIP_NONE);
502
RSDK_ENUM_VAR("Right", FLIP_X);
503
504
RSDK_ACTIVE_VAR(Canista, triggerMode);
505
RSDK_ENUM_VAR("Use Static Size", CANISTA_TRIGGER_STATIC);
506
RSDK_ENUM_VAR("Use Detect Size", CANISTA_TRIGGER_DETECT);
507
}
508
#endif
509
510
void Canista_Serialize(void)
511
{
512
RSDK_EDITABLE_VAR(Canista, VAR_UINT8, direction);
513
RSDK_EDITABLE_VAR(Canista, VAR_UINT8, triggerMode);
514
RSDK_EDITABLE_VAR(Canista, VAR_VECTOR2, detectOffset);
515
RSDK_EDITABLE_VAR(Canista, VAR_VECTOR2, detectSize);
516
RSDK_EDITABLE_VAR(Canista, VAR_ENUM, shootOffset);
517
RSDK_EDITABLE_VAR(Canista, VAR_ENUM, shootSize);
518
}
519
520