Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/ERZ/PhantomShinobi.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: PhantomShinobi Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectPhantomShinobi *PhantomShinobi;
11
12
void PhantomShinobi_Update(void)
13
{
14
RSDK_THIS(PhantomShinobi);
15
16
if (self->invincibilityTimer > 0)
17
self->invincibilityTimer--;
18
19
StateMachine_Run(self->state);
20
21
self->rotStore += self->rotSpeed;
22
self->rotStore &= 0xFFFF;
23
}
24
25
void PhantomShinobi_LateUpdate(void) {}
26
27
void PhantomShinobi_StaticUpdate(void) {}
28
29
void PhantomShinobi_Draw(void)
30
{
31
RSDK_THIS(PhantomShinobi);
32
33
RSDK.SetActivePalette(4, 0, ScreenInfo[SceneInfo->currentScreenID].size.y);
34
35
if (self->invincibilityTimer & 1)
36
RSDK.CopyPalette(6, 128, 4, 128, 128);
37
38
self->rotation = self->rotStore >> 7;
39
40
for (int32 i = 0; i < 8; ++i) {
41
Vector2 drawPos;
42
43
drawPos.x = self->finRadius * RSDK.Sin512(self->rotation) + self->position.x;
44
drawPos.y = self->position.y - self->finRadius * RSDK.Cos512(self->rotation);
45
self->finAnimator.frameID = ((self->rotation + 16) >> 5) & 0xF;
46
RSDK.DrawSprite(&self->finAnimator, &drawPos, false);
47
48
drawPos.x = ((5 * self->finRadius * RSDK.Sin512(self->rotation)) >> 3) + self->position.x;
49
drawPos.y = self->position.y - ((5 * self->finRadius * RSDK.Cos512(self->rotation)) >> 3);
50
RSDK.DrawSprite(&self->armAnimator, &drawPos, false);
51
52
self->rotation += 64;
53
}
54
55
RSDK.DrawSprite(&self->bodyAnimator, NULL, false);
56
57
if (self->invincibilityTimer & 1)
58
RSDK.CopyPalette(5, 128, 4, 128, 128);
59
60
RSDK.SetActivePalette(0, 0, ScreenInfo[SceneInfo->currentScreenID].size.y);
61
}
62
63
void PhantomShinobi_Create(void *data)
64
{
65
RSDK_THIS(PhantomShinobi);
66
67
if (!SceneInfo->inEditor) {
68
self->visible = true;
69
self->drawFX = FX_ROTATE | FX_FLIP;
70
self->drawGroup = Zone->objectDrawGroup[0];
71
self->active = ACTIVE_NEVER;
72
self->updateRange.x = 0x800000;
73
self->updateRange.y = 0x800000;
74
self->collisionLayers = Zone->collisionLayers;
75
self->startPos = self->position;
76
self->tileCollisions = TILECOLLISION_DOWN;
77
self->finRadius = 0x1600;
78
79
PhantomShinobi_ResetStates();
80
81
RSDK.SetSpriteAnimation(PhantomShinobi->aniFrames, 0, &self->bodyAnimator, true, 0);
82
RSDK.SetSpriteAnimation(PhantomShinobi->aniFrames, 0, &self->armAnimator, true, 1);
83
RSDK.SetSpriteAnimation(PhantomShinobi->aniFrames, 1, &self->finAnimator, true, 0);
84
}
85
}
86
87
void PhantomShinobi_StageLoad(void)
88
{
89
PhantomShinobi->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomShinobi.bin", SCOPE_STAGE);
90
91
PhantomShinobi->hitbox.left = -24;
92
PhantomShinobi->hitbox.top = -24;
93
PhantomShinobi->hitbox.right = 24;
94
PhantomShinobi->hitbox.bottom = 24;
95
96
PhantomShinobi->sfxBlade = RSDK.GetSfx("TMZ3/ShinobiBlade.wav");
97
PhantomShinobi->sfxHit = RSDK.GetSfx("TMZ3/ShinobiHit.wav");
98
99
Soundboard_LoadSfx("TMZ3/ShinobiBlade.wav", true, PhantomShinobi_SfxCheck_ShinobiBlade, StateMachine_None);
100
}
101
102
void PhantomShinobi_CheckPlayerCollisions(void)
103
{
104
RSDK_THIS(PhantomShinobi);
105
106
foreach_active(Player, playerLoop)
107
{
108
if (!self->invincibilityTimer && Player_CheckBadnikTouch(playerLoop, self, &PhantomShinobi->hitbox)
109
&& Player_CheckBossHit(playerLoop, self)) {
110
PhantomShinobi_Hit();
111
foreach_return;
112
}
113
}
114
115
int32 storeX = self->position.x;
116
int32 storeY = self->position.y;
117
int32 angle = self->rotStore;
118
119
foreach_active(Player, player)
120
{
121
for (int32 f = 0; f < 8; ++f) {
122
self->position.x = storeX + self->finRadius * RSDK.Sin512(angle);
123
self->position.y = storeY - self->finRadius * RSDK.Cos512(angle);
124
if (RSDK.CheckObjectCollisionTouchCircle(player, 0xC0000, self, 0x80000)) {
125
Player_Hurt(player, self);
126
}
127
128
angle += 0x40;
129
}
130
}
131
132
self->position.x = storeX;
133
self->position.y = storeY;
134
}
135
136
void PhantomShinobi_Hit(void)
137
{
138
RSDK_THIS(PhantomShinobi);
139
140
self->invincibilityTimer = 48;
141
RSDK.PlaySfx(PhantomEgg->sfxHit, false, 255);
142
}
143
144
void PhantomShinobi_HandleTileCollisions(void)
145
{
146
RSDK_THIS(PhantomShinobi);
147
148
if (self->onGround) {
149
self->finRadius += (0x1000 - self->finRadius) >> 3;
150
if (!self->prevOnGround) {
151
if (self->numBounces > 0) {
152
self->velocity.y = -0x30000;
153
self->numBounces--;
154
self->onGround = false;
155
}
156
}
157
}
158
else {
159
self->velocity.y += 0x3800;
160
if (self->velocity.y > 0xC0000)
161
self->velocity.y = 0xC0000;
162
163
self->finRadius += (0x1600 - self->finRadius) >> 3;
164
}
165
166
int32 size = self->finRadius / 88;
167
168
self->outerBox.left = -size;
169
self->outerBox.top = -size;
170
self->outerBox.right = size;
171
self->outerBox.bottom = size;
172
173
self->innerBox.left = 2 - size;
174
self->innerBox.top = -size;
175
self->innerBox.right = size - 2;
176
self->innerBox.bottom = size;
177
178
self->prevOnGround = self->onGround;
179
RSDK.ProcessObjectMovement(self, &self->outerBox, &self->innerBox);
180
}
181
182
void PhantomShinobi_ResetStates(void)
183
{
184
RSDK_THIS(PhantomShinobi);
185
186
self->position = self->startPos;
187
self->position.y -= 0x400000;
188
self->finRadius = 0;
189
self->attackCount = 0;
190
191
self->state = PhantomShinobi_State_EnterShinobi;
192
self->direction = RSDK.Rand(FLIP_NONE, FLIP_X + 1);
193
}
194
195
void PhantomShinobi_State_EnterShinobi(void)
196
{
197
RSDK_THIS(PhantomShinobi);
198
199
if (self->position.y < self->startPos.y) {
200
self->position.y += 0x8000;
201
}
202
203
if (++self->timer >= 128) {
204
self->finRadius += 0x100;
205
if (self->finRadius >= 0x1600) {
206
self->timer = 0;
207
self->finRadius = 0x1600;
208
self->state = PhantomShinobi_State_AttackDelay;
209
}
210
}
211
}
212
213
void PhantomShinobi_State_AttackDelay(void)
214
{
215
RSDK_THIS(PhantomShinobi);
216
217
if (++self->timer == 16) {
218
self->timer = 0;
219
self->state = PhantomShinobi_State_SetupAttack;
220
}
221
}
222
223
void PhantomShinobi_State_SetupAttack(void)
224
{
225
RSDK_THIS(PhantomShinobi);
226
227
if (self->direction) {
228
if (self->rotSpeed <= -0x600) {
229
self->velocity.x -= 0x20000;
230
self->rotSpeed = -0x600;
231
self->state = PhantomShinobi_State_Moving;
232
self->onGround = false;
233
self->numBounces = 1;
234
self->velocity.y = -0x30000;
235
}
236
else {
237
self->rotSpeed -= 12;
238
}
239
}
240
else {
241
if (self->rotSpeed >= 0x600) {
242
self->rotSpeed = 0x600;
243
self->velocity.x = 0x20000;
244
self->state = PhantomShinobi_State_Moving;
245
self->onGround = false;
246
self->numBounces = 1;
247
self->velocity.y = -0x30000;
248
}
249
else {
250
self->rotSpeed += 12;
251
}
252
}
253
254
PhantomShinobi_CheckPlayerCollisions();
255
}
256
257
void PhantomShinobi_State_Moving(void)
258
{
259
RSDK_THIS(PhantomShinobi);
260
261
PhantomShinobi_HandleTileCollisions();
262
263
if (self->onGround && !self->prevOnGround) {
264
self->groundVel = self->direction == FLIP_NONE ? 0x80000 : -0x80000;
265
}
266
267
if (!self->groundVel) {
268
self->onGround = false;
269
self->velocity.x = self->direction == FLIP_NONE ? -0x38000 : 0x38000;
270
self->velocity.y = -0x40000;
271
272
self->numBounces = 1;
273
self->direction ^= FLIP_X;
274
self->state = PhantomShinobi_State_PrepareFinAttack;
275
}
276
277
PhantomShinobi_CheckPlayerCollisions();
278
}
279
280
void PhantomShinobi_State_PrepareFinAttack(void)
281
{
282
RSDK_THIS(PhantomShinobi);
283
284
if (self->velocity.y < 0x20000) {
285
self->velocity.y += 0x2800;
286
self->position.x += self->velocity.x;
287
self->position.y += self->velocity.y;
288
}
289
290
if (abs(self->rotSpeed) <= 256) {
291
if (!(self->rotStore & 0x1C00)) {
292
self->rotSpeed = 0;
293
self->rotStore = 0;
294
self->timer = 0;
295
self->state = PhantomShinobi_State_ExtendFins;
296
}
297
}
298
else {
299
self->rotSpeed -= (self->rotSpeed >> 5);
300
}
301
302
PhantomShinobi_CheckPlayerCollisions();
303
}
304
305
void PhantomShinobi_State_ExtendFins(void)
306
{
307
RSDK_THIS(PhantomShinobi);
308
309
self->finRadius -= 128;
310
if (++self->timer == 24) {
311
self->timer = 0;
312
self->velocity.x = 0x400;
313
314
RSDK.PlaySfx(PhantomShinobi->sfxHit, false, 255);
315
self->state = PhantomShinobi_State_RetractFins;
316
}
317
318
PhantomShinobi_CheckPlayerCollisions();
319
}
320
321
void PhantomShinobi_State_RetractFins(void)
322
{
323
RSDK_THIS(PhantomShinobi);
324
325
self->velocity.x -= 16;
326
if (self->velocity.x < -0xC00)
327
self->velocity.x = -0xC00;
328
329
self->finRadius += self->velocity.x;
330
331
if (self->velocity.x < 0 && self->finRadius < 0x1600) {
332
self->finRadius = 0x1600;
333
334
if (++self->attackCount == 2) {
335
PhantomEgg_SetupWarpFX();
336
self->timer = 0;
337
self->state = PhantomShinobi_State_FinishedAttack;
338
}
339
else {
340
self->state = PhantomShinobi_State_AttackDelay;
341
}
342
}
343
344
PhantomShinobi_CheckPlayerCollisions();
345
}
346
347
void PhantomShinobi_State_FinishedAttack(void)
348
{
349
RSDK_THIS(PhantomShinobi);
350
351
if (self->finRadius > 0)
352
self->finRadius -= (self->finRadius >> 4);
353
354
if (++self->timer == 60) {
355
PhantomShinobi_ResetStates();
356
self->active = ACTIVE_NEVER;
357
}
358
}
359
360
bool32 PhantomShinobi_SfxCheck_ShinobiBlade(void)
361
{
362
foreach_active(PhantomShinobi, shinobi)
363
{
364
if (shinobi->state == PhantomShinobi_State_Moving) {
365
foreach_return true;
366
}
367
}
368
369
return false;
370
}
371
372
#if GAME_INCLUDE_EDITOR
373
void PhantomShinobi_EditorDraw(void)
374
{
375
RSDK_THIS(PhantomShinobi);
376
377
self->finRadius = 0x1600;
378
379
RSDK.SetSpriteAnimation(PhantomShinobi->aniFrames, 0, &self->bodyAnimator, false, 0);
380
RSDK.SetSpriteAnimation(PhantomShinobi->aniFrames, 0, &self->armAnimator, false, 1);
381
RSDK.SetSpriteAnimation(PhantomShinobi->aniFrames, 1, &self->finAnimator, false, 0);
382
383
PhantomShinobi_Draw();
384
}
385
386
void PhantomShinobi_EditorLoad(void) { PhantomShinobi->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomShinobi.bin", SCOPE_STAGE); }
387
#endif
388
389
void PhantomShinobi_Serialize(void) {}
390
391