Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/behaviors/bowser.inc.c
7861 views
1
/**
2
* Behavior for Bowser and it's actions (Tail, Flame, Body)
3
*/
4
5
// Bowser's Tail
6
7
/**
8
* Checks whenever the Bowser and his tail should be intangible or not
9
* By default it starts tangible
10
*/
11
void bowser_tail_anchor_act_default(void) {
12
struct Object *bowser = o->parentObj;
13
cur_obj_become_tangible();
14
cur_obj_scale(1.0f);
15
16
if (bowser->oAction == BOWSER_ACT_TILT_LAVA_PLATFORM) {
17
// Bowser cannot be touched when he tilts BITFS platform
18
bowser->oIntangibleTimer = -1;
19
} else if (obj_check_if_collided_with_object(o, gMarioObject)) {
20
// When Mario collides his tail, it now gets
21
// intangible so he can grab it through
22
bowser->oIntangibleTimer = 0;
23
o->oAction = BOWSER_ACT_TAIL_TOUCHED_MARIO;
24
} else {
25
bowser->oIntangibleTimer = -1;
26
}
27
}
28
29
/**
30
* While Bowser get's thrown, wait 30 frames then
31
* return to the default tail action check
32
*/
33
void bowser_tail_anchor_thrown(void) {
34
if (o->oTimer > 30) {
35
o->oAction = BOWSER_ACT_TAIL_DEFAULT;
36
}
37
}
38
39
/**
40
* Makes the tail intangible so Mario can grab it
41
*/
42
void bowser_tail_anchor_act_touched_mario(void) {
43
// Return to main action when Bowser tilts BITFS platform
44
if (o->parentObj->oAction == BOWSER_ACT_TILT_LAVA_PLATFORM) {
45
o->parentObj->oIntangibleTimer = -1;
46
o->oAction = BOWSER_ACT_TAIL_DEFAULT;
47
}
48
cur_obj_become_intangible();
49
}
50
51
void (*sBowserTailAnchorActions[])(void) = {
52
bowser_tail_anchor_act_default,
53
bowser_tail_anchor_thrown,
54
bowser_tail_anchor_act_touched_mario,
55
};
56
57
/**
58
* Bowser's tail main loop
59
*/
60
void bhv_bowser_tail_anchor_loop(void) {
61
// Call its actions
62
cur_obj_call_action_function(sBowserTailAnchorActions);
63
// Position the tail
64
o->oParentRelativePosX = 90.0f;
65
66
// Make it intangible while Bowser is dead
67
if (o->parentObj->oAction == BOWSER_ACT_DEAD) {
68
o->parentObj->oIntangibleTimer = -1;
69
}
70
71
o->oInteractStatus = 0;
72
}
73
74
// Bowser's Flame
75
76
/**
77
* Bowser's Flame spawn main loop
78
*/
79
void bhv_bowser_flame_spawn_loop(void) {
80
struct Object *bowser = o->parentObj;
81
s32 animFrame;
82
f32 posX;
83
f32 posZ;
84
f32 cossYaw = coss(bowser->oMoveAngleYaw);
85
f32 sinsYaw = sins(bowser->oMoveAngleYaw);
86
s16 *data = segmented_to_virtual(dBowserFlamesOrientationValues);
87
88
// Check for Bowser breathing animation
89
if (bowser->oSoundStateID == BOWSER_ANIM_BREATH) {
90
91
// Start counting anim frames then reset it when it ends
92
animFrame = bowser->header.gfx.animInfo.animFrame + 1.0f;
93
if (bowser->header.gfx.animInfo.curAnim->loopEnd == animFrame) {
94
animFrame = 0;
95
}
96
97
// Bowser is breathing, play sound and adjust flame position
98
// each animFrame based off the orientantion data
99
if (animFrame > 45 && animFrame < 85) {
100
cur_obj_play_sound_1(SOUND_AIR_BOWSER_SPIT_FIRE);
101
posX = data[5 * animFrame];
102
posZ = data[5 * animFrame + 2];
103
o->oPosX = bowser->oPosX + (posZ * sinsYaw + posX * cossYaw);
104
o->oPosY = bowser->oPosY + data[5 * animFrame + 1];
105
o->oPosZ = bowser->oPosZ + (posZ * cossYaw - posX * sinsYaw);
106
o->oMoveAnglePitch = data[5 * animFrame + 4] + 0xC00;
107
o->oMoveAngleYaw = data[5 * animFrame + 3] + (s16) bowser->oMoveAngleYaw;
108
// Spawns the flames on a non-odd animFrame value
109
if (!(animFrame & 1)) {
110
spawn_object(o, MODEL_RED_FLAME, bhvFlameMovingForwardGrowing);
111
}
112
}
113
}
114
}
115
116
/**
117
* Bowser's Body main loop
118
*/
119
void bhv_bowser_body_anchor_loop(void) {
120
// Copy position and angles from Bowser itself
121
obj_copy_pos_and_angle(o, o->parentObj);
122
// If Bowser is dead, set interaction type to text
123
// so that he can be ready to speak his dialog
124
if (o->parentObj->oAction == BOWSER_ACT_DEAD) {
125
#if BUGFIX_BOWSER_COLLIDE_BITS_DEAD
126
// Clear interaction type at the last sub action in BITS
127
// Fixes collision coliding after defeating him
128
if (o->parentObj->oSubAction == BOWSER_SUB_ACT_DEAD_FINAL_END_OVER) {
129
o->oInteractType = 0;
130
} else {
131
o->oInteractType = INTERACT_TEXT;
132
}
133
#else
134
o->oInteractType = INTERACT_TEXT;
135
#endif
136
} else {
137
// Do damage if Mario touches Bowser
138
o->oInteractType = INTERACT_DAMAGE;
139
// Make body intangible while is transparent
140
// in BITFS (Teleporting)
141
if (o->parentObj->oOpacity < 100) {
142
cur_obj_become_intangible();
143
} else {
144
cur_obj_become_tangible();
145
}
146
}
147
// Make body intangible while Bowser is getting grabbed
148
if (o->parentObj->oHeldState != HELD_FREE) {
149
cur_obj_become_intangible();
150
}
151
o->oInteractStatus = 0;
152
}
153
154
/**
155
* Bowser's shockwave attack, spawns only in BITS
156
*/
157
s32 bowser_spawn_shockwave(void) {
158
struct Object *wave;
159
if (o->oBehParams2ndByte == BOWSER_BP_BITS) {
160
wave = spawn_object(o, MODEL_BOWSER_WAVE, bhvBowserShockWave);
161
wave->oPosY = o->oFloorHeight;
162
return TRUE;
163
}
164
return FALSE;
165
}
166
167
/**
168
* Misc effects that Bowser plays when he lands with drastic actions
169
* Plays step sound, spawns particles and changes camera event
170
*/
171
void bowser_bounce_effects(s32 *timer) {
172
if (o->oMoveFlags & OBJ_MOVE_LANDED) {
173
(*timer)++;
174
if (*timer < 4) {
175
cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_THROW_BOUNCE);
176
spawn_mist_particles_variable(0, 0, 60.0f);
177
cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK);
178
}
179
}
180
}
181
182
/**
183
* Makes Bowser look up and walk on an specific animation frame
184
* Returns TRUE if the animation is almost over
185
*/
186
s32 bowser_set_anim_look_up_and_walk(void) {
187
cur_obj_init_animation_with_sound(BOWSER_ANIM_LOOK_UP_START_WALK);
188
if (cur_obj_check_anim_frame(21)) {
189
o->oForwardVel = 3.0f;
190
}
191
if (cur_obj_check_if_near_animation_end()) {
192
return TRUE;
193
} else {
194
return FALSE;
195
}
196
}
197
198
/**
199
* Makes Bowser do a slow gait (or slow walk)
200
* Returns TRUE if the animation is almost over
201
*/
202
s32 bowser_set_anim_slow_gait(void) {
203
o->oForwardVel = 3.0f;
204
cur_obj_init_animation_with_sound(BOWSER_ANIM_SLOW_GAIT);
205
if (cur_obj_check_if_near_animation_end()) {
206
return TRUE;
207
} else {
208
return FALSE;
209
}
210
}
211
212
/**
213
* Makes Bowser look down and stop on an specific animation frame
214
* Returns TRUE if the animation is almost over
215
*/
216
s32 bowser_set_anim_look_down_stop_walk(void) {
217
cur_obj_init_animation_with_sound(BOWSER_ANIM_LOOK_DOWN_STOP_WALK);
218
if (cur_obj_check_anim_frame(20)) {
219
o->oForwardVel = 0.0f;
220
}
221
if (cur_obj_check_if_near_animation_end()) {
222
return TRUE;
223
} else {
224
return FALSE;
225
}
226
}
227
228
229
/**
230
* Set Bowser an action depending of the CamAct value
231
* CamAct changes value on the cutscene itself (cutscene_bowser_arena)
232
*/
233
void bowser_init_camera_actions(void) {
234
if (o->oBowserCamAct == BOWSER_CAM_ACT_IDLE) {
235
o->oAction = BOWSER_ACT_WAIT;
236
} else if (o->oBowserCamAct == BOWSER_CAM_ACT_WALK) {
237
o->oAction = BOWSER_ACT_INTRO_WALK;
238
// Start with a big jump in BITFS to do a platform tilt
239
} else if (o->oBehParams2ndByte == BOWSER_BP_BITFS) {
240
o->oAction = BOWSER_ACT_BIG_JUMP;
241
} else {
242
o->oAction = BOWSER_ACT_DEFAULT;
243
}
244
}
245
246
/**
247
* Bowser's idle action that plays when he is initialized
248
* or the CamAct gets in idle mode
249
*/
250
void bowser_act_wait(void) {
251
o->oForwardVel = 0.0f;
252
cur_obj_init_animation_with_sound(BOWSER_ANIM_IDLE);
253
bowser_init_camera_actions();
254
}
255
256
/**
257
* Bowser's cutscene walk that last a few seconds to introduce itself
258
* Do subactions until the animation ends, then go to next subaction
259
*/
260
void bowser_act_intro_walk(void) {
261
// First look up and walk
262
if (o->oSubAction == 0) {
263
if (bowser_set_anim_look_up_and_walk()) {
264
o->oSubAction++;
265
}
266
// Then slowly walk
267
} else if (o->oSubAction == 1) {
268
if (bowser_set_anim_slow_gait()) {
269
o->oSubAction++;
270
}
271
// And finally stop, and set to wait mode
272
} else if (bowser_set_anim_look_down_stop_walk()) {
273
if (o->oBowserCamAct == BOWSER_CAM_ACT_WALK) {
274
o->oBowserCamAct = BOWSER_CAM_ACT_IDLE;
275
}
276
bowser_init_camera_actions();
277
}
278
}
279
280
/**
281
* List of actions to debug Bowser
282
*/
283
s8 sBowserDebugActions[] = {
284
BOWSER_ACT_CHARGE_MARIO,
285
BOWSER_ACT_SPIT_FIRE_INTO_SKY,
286
BOWSER_ACT_SPIT_FIRE_ONTO_FLOOR,
287
BOWSER_ACT_HIT_MINE,
288
BOWSER_ACT_BIG_JUMP,
289
BOWSER_ACT_WALK_TO_MARIO,
290
BOWSER_ACT_BREATH_FIRE,
291
BOWSER_ACT_DEAD,
292
BOWSER_ACT_DANCE,
293
BOWSER_ACT_TELEPORT,
294
BOWSER_ACT_QUICK_JUMP,
295
BOWSER_ACT_TILT_LAVA_PLATFORM,
296
BOWSER_ACT_DANCE,
297
BOWSER_ACT_DANCE,
298
BOWSER_ACT_DANCE,
299
BOWSER_ACT_DANCE,
300
};
301
302
/**
303
* Debug function that allows to change Bowser's actions (most of them)
304
*/
305
UNUSED static void bowser_debug_actions(void) {
306
if (gDebugInfo[5][1] != 0) {
307
o->oAction = sBowserDebugActions[gDebugInfo[5][2] & 0xf];
308
gDebugInfo[5][1] = 0;
309
}
310
}
311
312
/**
313
* Set actions (and attacks) for Bowser in "Bowser in the Dark World"
314
*/
315
void bowser_bitdw_actions(void) {
316
// Generate random float
317
f32 rand = random_float();
318
// Set attacks when Bowser Reacts
319
if (o->oBowserIsReacting == FALSE) {
320
if (o->oBowserStatus & BOWSER_STATUS_ANGLE_MARIO) {
321
if (o->oDistanceToMario < 1500.0f) {
322
o->oAction = BOWSER_ACT_BREATH_FIRE; // nearby
323
} else {
324
o->oAction = BOWSER_ACT_QUICK_JUMP; // far away
325
}
326
} else {
327
// Keep walking
328
o->oAction = BOWSER_ACT_WALK_TO_MARIO;
329
}
330
o->oBowserIsReacting++;
331
} else {
332
o->oBowserIsReacting = FALSE;
333
// Set starting Bowser level actions, randomly he can also start
334
// dancing after the introduction
335
#ifndef VERSION_JP
336
if (!gCurrDemoInput) { // demo check because entry exits post JP
337
if (rand < 0.1) {
338
o->oAction = BOWSER_ACT_DANCE; // 10% chance
339
} else {
340
o->oAction = BOWSER_ACT_WALK_TO_MARIO; // common
341
}
342
} else {
343
o->oAction = BOWSER_ACT_WALK_TO_MARIO;
344
}
345
#else
346
if (rand < 0.1) {
347
o->oAction = BOWSER_ACT_DANCE; // 10% chance
348
} else {
349
o->oAction = BOWSER_ACT_WALK_TO_MARIO; // common
350
}
351
#endif
352
}
353
}
354
355
/**
356
* Set actions (and attacks) for Bowser in "Bowser in the Fire Sea"
357
*/
358
void bowser_bitfs_actions(void) {
359
// Generate random float
360
f32 rand = random_float();
361
// Set attacks when Bowser Reacts
362
if (o->oBowserIsReacting == FALSE) {
363
if (o->oBowserStatus & BOWSER_STATUS_ANGLE_MARIO) {
364
if (o->oDistanceToMario < 1300.0f) { // nearby
365
if (rand < 0.5) { // 50% chance
366
o->oAction = BOWSER_ACT_TELEPORT;
367
} else {
368
o->oAction = BOWSER_ACT_SPIT_FIRE_ONTO_FLOOR;
369
}
370
} else { // far away
371
o->oAction = BOWSER_ACT_CHARGE_MARIO;
372
if (500.0f < o->oBowserDistToCentre && o->oBowserDistToCentre < 1500.0f
373
&& rand < 0.5) { // 50% chance
374
o->oAction = BOWSER_ACT_BIG_JUMP;
375
}
376
}
377
} else {
378
// Keep walking
379
o->oAction = BOWSER_ACT_WALK_TO_MARIO;
380
}
381
o->oBowserIsReacting++;
382
} else {
383
// Keep walking
384
o->oBowserIsReacting = FALSE;
385
o->oAction = BOWSER_ACT_WALK_TO_MARIO;
386
}
387
}
388
389
/**
390
* List of actions (and attacks) for "Bowser in the Sky"
391
*/
392
void bowser_bits_action_list(void) {
393
f32 rand = random_float();
394
if (o->oBowserStatus & BOWSER_STATUS_ANGLE_MARIO) {
395
if (o->oDistanceToMario < 1000.0f) { // nearby
396
if (rand < 0.4) {
397
o->oAction = BOWSER_ACT_SPIT_FIRE_ONTO_FLOOR; // 40% chance
398
} else if (rand < 0.8) {
399
o->oAction = BOWSER_ACT_SPIT_FIRE_INTO_SKY; // 80% chance
400
} else {
401
o->oAction = BOWSER_ACT_BREATH_FIRE;
402
} // far away
403
} else if (rand < 0.5) {
404
o->oAction = BOWSER_ACT_BIG_JUMP; // 50% chance
405
} else {
406
o->oAction = BOWSER_ACT_CHARGE_MARIO;
407
}
408
} else {
409
// Keep walking
410
o->oAction = BOWSER_ACT_WALK_TO_MARIO;
411
}
412
}
413
414
/**
415
* Sets big jump action, not much to say
416
* Never gets called since oBowserBitsJustJump is always FALSE
417
*/
418
void bowser_set_act_big_jump(void) {
419
o->oAction = BOWSER_ACT_BIG_JUMP;
420
}
421
422
/**
423
* Set actions (and attacks) for Bowser in "Bowser in the Sky"
424
*/
425
void bowser_bits_actions(void) {
426
switch (o->oBowserIsReacting) {
427
case FALSE:
428
// oBowserBitsJustJump never changes value,
429
// so its always FALSE, maybe a debug define
430
if (o->oBowserBitsJustJump == FALSE) {
431
bowser_bits_action_list();
432
} else {
433
bowser_set_act_big_jump();
434
}
435
o->oBowserIsReacting = TRUE;
436
break;
437
case TRUE:
438
o->oBowserIsReacting = FALSE;
439
o->oAction = BOWSER_ACT_WALK_TO_MARIO;
440
break;
441
}
442
}
443
444
/**
445
* Reset Bowser position and speed if he wasn't able to land properly on stage
446
*/
447
#if BUGFIX_BOWSER_FALLEN_OFF_STAGE
448
void bowser_reset_fallen_off_stage(void) {
449
if (o->oVelY < 0 && o->oPosY < (o->oHomeY - 300.0f)) {
450
o->oPosX = o->oPosZ = 0;
451
o->oPosY = o->oHomeY + 2000.0f;
452
o->oVelY = 0;
453
o->oForwardVel = 0;
454
}
455
}
456
#endif
457
458
/**
459
* Unused, makes Bowser be in idle and after it returns to default action
460
*/
461
void bowser_act_idle(void) {
462
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_IDLE)) {
463
o->oAction = BOWSER_ACT_DEFAULT;
464
}
465
}
466
467
/**
468
* Default Bowser act that doesn't last very long
469
*/
470
void bowser_act_default(void) {
471
// Set eye state
472
o->oBowserEyesShut = FALSE;
473
// Set idle animation
474
cur_obj_init_animation_with_sound(BOWSER_ANIM_IDLE);
475
// Stop him still
476
o->oAngleVelYaw = 0;
477
o->oForwardVel = 0.0f;
478
o->oVelY = 0.0f;
479
// Set level specific actions
480
if (o->oBehParams2ndByte == BOWSER_BP_BITDW) {
481
bowser_bitdw_actions();
482
} else if (o->oBehParams2ndByte == BOWSER_BP_BITFS) {
483
bowser_bitfs_actions();
484
} else { // BOWSER_BP_BITS
485
bowser_bits_actions();
486
}
487
}
488
489
/**
490
* Makes Bowser play breath animation and sound effect
491
* The actual breath attack is in bhv_bowser_flame_spawn_loop
492
* called as a child obj behavior in Bowser
493
*/
494
void bowser_act_breath_fire(void) {
495
o->oForwardVel = 0.0f;
496
if (o->oTimer == 0) {
497
cur_obj_play_sound_2(SOUND_OBJ_BOWSER_INHALING);
498
}
499
// Init animation and return to default act after it ends
500
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_BREATH)) {
501
o->oAction = BOWSER_ACT_DEFAULT;
502
}
503
}
504
505
/**
506
* Makes Bowser walk towards Mario
507
*/
508
void bowser_act_walk_to_mario(void) {
509
UNUSED s32 facing; // is Bowser facing Mario?
510
s16 turnSpeed;
511
s16 angleFromMario = abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario);
512
513
// Set turning speed depending of the health
514
// Also special case for BITFS
515
if (o->oBehParams2ndByte == BOWSER_BP_BITFS) {
516
turnSpeed = 0x400;
517
} else if (o->oHealth > 2) {
518
turnSpeed = 0x400;
519
} else if (o->oHealth == 2) {
520
turnSpeed = 0x300;
521
} else { // 1 health (BITFS-BITS)
522
turnSpeed = 0x200;
523
}
524
facing = cur_obj_rotate_yaw_toward(o->oAngleToMario, turnSpeed);
525
if (o->oSubAction == 0) {
526
o->oBowserTimer = 0;
527
// Start walking
528
if (bowser_set_anim_look_up_and_walk()) {
529
o->oSubAction++;
530
}
531
} else if (o->oSubAction == 1) {
532
// Keep walking slowly
533
if (bowser_set_anim_slow_gait()) {
534
o->oBowserTimer++;
535
// Reset fire sky status
536
if (o->oBowserStatus & BOWSER_STATUS_FIRE_SKY) {
537
if (o->oBowserTimer > 4) {
538
o->oBowserStatus &= ~BOWSER_STATUS_FIRE_SKY;
539
}
540
// Do subaction below if angles is less than 0x2000
541
} else if (angleFromMario < 0x2000) {
542
o->oSubAction++;
543
}
544
}
545
// Stop walking and set to default action
546
} else if (bowser_set_anim_look_down_stop_walk()) {
547
o->oAction = BOWSER_ACT_DEFAULT;
548
}
549
}
550
551
/**
552
* Makes Bowser teleport while invisible
553
*/
554
void bowser_act_teleport(void) {
555
switch (o->oSubAction) {
556
// Set opacity target to invisible and become intangible
557
case BOWSER_SUB_ACT_TELEPORT_START:
558
cur_obj_become_intangible();
559
o->oBowserTargetOpacity = 0;
560
o->oBowserTimer = 30; // set timer value
561
// Play sound effect
562
if (o->oTimer == 0) {
563
cur_obj_play_sound_2(SOUND_OBJ2_BOWSER_TELEPORT);
564
}
565
// Bowser is invisible, move angle to face Mario
566
if (o->oOpacity == 0) {
567
o->oSubAction++;
568
o->oMoveAngleYaw = o->oAngleToMario;
569
}
570
break;
571
case BOWSER_SUB_ACT_TELEPORT_MOVE:
572
// reduce timer and set velocity teleport while at it
573
if (o->oBowserTimer--) {
574
o->oForwardVel = 100.0f;
575
}
576
else {
577
o->oSubAction = BOWSER_SUB_ACT_TELEPORT_STOP;
578
o->oMoveAngleYaw = o->oAngleToMario; // update angle
579
}
580
//
581
if (abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario) > 0x4000) {
582
if (o->oDistanceToMario > 500.0f) {
583
o->oSubAction = BOWSER_SUB_ACT_TELEPORT_STOP;
584
o->oMoveAngleYaw = o->oAngleToMario; // update angle
585
cur_obj_play_sound_2(SOUND_OBJ2_BOWSER_TELEPORT);
586
}
587
}
588
break;
589
// Set opacity target to visible and become tangible
590
case BOWSER_SUB_ACT_TELEPORT_STOP:
591
o->oForwardVel = 0.0f; // reset velocity
592
o->oBowserTargetOpacity = 0xFF;
593
// Set to default action once visible
594
if (o->oOpacity == 0xFF) {
595
o->oAction = BOWSER_ACT_DEFAULT;
596
}
597
cur_obj_become_tangible();
598
break;
599
}
600
}
601
602
/**
603
* Makes Bowser do a fire split into the sky
604
*/
605
void bowser_act_spit_fire_into_sky(void) {
606
s32 frame;
607
// Play animation
608
cur_obj_init_animation_with_sound(BOWSER_ANIM_BREATH_UP);
609
// Set frames
610
frame = o->header.gfx.animInfo.animFrame;
611
// Spawn flames in the middle of the animation
612
if (frame > 24 && frame < 36) {
613
cur_obj_play_sound_1(SOUND_AIR_BOWSER_SPIT_FIRE);
614
if (frame == 35) { // Spawns Blue flames at this frame
615
spawn_object_relative(1, 0, 0x190, 0x64, o, MODEL_RED_FLAME, bhvBlueBowserFlame);
616
} else { // Spawns Red flames
617
spawn_object_relative(0, 0, 0x190, 0x64, o, MODEL_RED_FLAME, bhvBlueBowserFlame);
618
}
619
}
620
// Return to default act once the animation is over
621
if (cur_obj_check_if_near_animation_end()) {
622
o->oAction = BOWSER_ACT_DEFAULT;
623
}
624
// Set fire sky status
625
o->oBowserStatus |= BOWSER_STATUS_FIRE_SKY;
626
}
627
628
/**
629
* Flips Bowser back on stage if he hits a mine with more than 1 health
630
*/
631
void bowser_act_hit_mine(void) {
632
// Similar vel values from bowser_fly_back_dead
633
if (o->oTimer == 0) {
634
o->oForwardVel = -400.0f;
635
o->oVelY = 100.0f;
636
o->oMoveAngleYaw = o->oBowserAngleToCentre + 0x8000;
637
o->oBowserEyesShut = TRUE; // close eyes
638
}
639
// Play flip animation
640
if (o->oSubAction == BOWSER_SUB_ACT_HIT_MINE_START) {
641
cur_obj_init_animation_with_sound(BOWSER_ANIM_FLIP);
642
o->oSubAction++;
643
o->oBowserTimer = 0;
644
// Play flip animation again, extend it and play bounce effects
645
} else if (o->oSubAction == BOWSER_SUB_ACT_HIT_MINE_FALL) {
646
cur_obj_init_animation_with_sound(BOWSER_ANIM_FLIP);
647
cur_obj_extend_animation_if_at_end();
648
bowser_bounce_effects(&o->oBowserTimer);
649
// Reset vel and stand up
650
if (o->oBowserTimer > 2) {
651
cur_obj_init_animation_with_sound(BOWSER_ANIM_STAND_UP_FROM_FLIP);
652
o->oVelY = 0.0f;
653
o->oForwardVel = 0.0f;
654
o->oSubAction++;
655
}
656
// Play these actions once he is stand up
657
} else if (o->oSubAction == BOWSER_SUB_ACT_HIT_MINE_STOP) {
658
if (cur_obj_check_if_near_animation_end()) {
659
// Makes Bowser dance at one health (in BITS)
660
if (o->oHealth == 1) {
661
o->oAction = BOWSER_ACT_DANCE;
662
} else {
663
o->oAction = BOWSER_ACT_DEFAULT;
664
}
665
o->oBowserEyesShut = FALSE; // open eyes
666
}
667
} else {
668
}
669
}
670
671
/**
672
* Makes Bowser do his jump start animation
673
* Returns TRUE on the middle of the jump
674
*/
675
s32 bowser_set_anim_jump(void) {
676
cur_obj_init_animation_with_sound(BOWSER_ANIM_JUMP_START);
677
if (cur_obj_check_anim_frame(11)) {
678
return TRUE;
679
} else {
680
return FALSE;
681
}
682
}
683
684
/**
685
* Reset speed, play jump stop animation and do attacks in BITDW
686
* Returns TRUE when Bowser lands
687
*/
688
s32 bowser_land(void) {
689
if (o->oMoveFlags & OBJ_MOVE_LANDED) {
690
o->oForwardVel = 0;
691
o->oVelY = 0;
692
spawn_mist_particles_variable(0, 0, 60.0f);
693
cur_obj_init_animation_with_sound(BOWSER_ANIM_JUMP_STOP);
694
o->header.gfx.animInfo.animFrame = 0;
695
cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_JUMP);
696
// Set status attacks in BITDW since the other levels
697
// have different attacks defined
698
if (o->oBehParams2ndByte == BOWSER_BP_BITDW) {
699
if (o->oDistanceToMario < 850.0f) {
700
gMarioObject->oInteractStatus |= INT_STATUS_MARIO_KNOCKBACK_DMG;
701
} else {
702
gMarioObject->oInteractStatus |= INT_STATUS_MARIO_STUNNED;
703
}
704
}
705
return TRUE;
706
} else {
707
return FALSE;
708
}
709
}
710
711
/**
712
* Makes Bowser do a second hop speed only in BITS
713
*/
714
void bowser_short_second_hop(void) {
715
if (o->oBehParams2ndByte == BOWSER_BP_BITS && o->oBowserStatus & BOWSER_STATUS_BIG_JUMP) {
716
if (o->oBowserDistToCentre > 1000.0f) {
717
o->oForwardVel = 60.0f;
718
}
719
}
720
}
721
722
/**
723
* Makes Bowser do a big jump
724
*/
725
void bowser_act_big_jump(void) {
726
UNUSED s32 unused;
727
if (o->oSubAction == 0) {
728
// Set jump animation
729
if (bowser_set_anim_jump()) {
730
// Set vel depending of the stage and status
731
if (o->oBehParams2ndByte == BOWSER_BP_BITS && o->oBowserStatus & BOWSER_STATUS_BIG_JUMP) {
732
o->oVelY = 70.0f;
733
} else {
734
o->oVelY = 80.0f;
735
}
736
o->oBowserTimer = 0;
737
bowser_short_second_hop();
738
o->oSubAction++;
739
}
740
} else if (o->oSubAction == 1) {
741
#if BUGFIX_BOWSER_FALLEN_OFF_STAGE
742
// Reset Bowser back on stage in BITS if he doesn't land properly
743
if (o->oBehParams2ndByte == BOWSER_BP_BITS && o->oBowserStatus & BOWSER_STATUS_BIG_JUMP) {
744
bowser_reset_fallen_off_stage();
745
}
746
#endif
747
// Land on stage, reset status jump and velocity
748
if (bowser_land()) {
749
o->oBowserStatus &= ~BOWSER_STATUS_BIG_JUMP;
750
o->oForwardVel = 0.0f;
751
o->oSubAction++;
752
// Spawn shockwave (BITS only) if is not on a platform
753
bowser_spawn_shockwave();
754
// Tilt platform in BITFS
755
if (o->oBehParams2ndByte == BOWSER_BP_BITFS) {
756
o->oAction = BOWSER_ACT_TILT_LAVA_PLATFORM;
757
}
758
} else {
759
}
760
// Set to default action when the animation is over
761
} else if (cur_obj_check_if_near_animation_end()) {
762
o->oAction = BOWSER_ACT_DEFAULT;
763
}
764
}
765
766
/**
767
* Fixed values for the quick jump action
768
*/
769
s16 sBowserVelYAir[] = { 60 };
770
s16 sBowserFVelAir[] = { 50 };
771
772
/**
773
* Makes Bowser do a "quick" jump in BITDW
774
*/
775
void bowser_act_quick_jump(void) {
776
f32 velY = sBowserVelYAir[0];
777
f32 fVel = sBowserFVelAir[0];
778
if (o->oSubAction == 0) {
779
// Set fixed val positions while jumping
780
if (bowser_set_anim_jump()) {
781
o->oVelY = velY;
782
o->oForwardVel = fVel;
783
o->oBowserTimer = 0;
784
o->oSubAction++;
785
}
786
// Lands then quickly returns to default action
787
} else if (o->oSubAction == 1) {
788
if (bowser_land()) {
789
o->oSubAction++;
790
}
791
} else if (cur_obj_check_if_near_animation_end()) {
792
o->oAction = BOWSER_ACT_DEFAULT;
793
}
794
}
795
796
/**
797
* Makes Bowser moving around if he is on an edge floor
798
*/
799
void bowser_act_hit_edge(void) {
800
// Reset speed and timer
801
o->oForwardVel = 0.0f;
802
if (o->oTimer == 0) {
803
o->oBowserTimer = 0;
804
}
805
switch (o->oSubAction) {
806
case 0:
807
// Move on the edge
808
cur_obj_init_animation_with_sound(BOWSER_ANIM_EDGE_MOVE);
809
if (cur_obj_check_if_near_animation_end()) {
810
o->oBowserTimer++;
811
}
812
if (o->oBowserTimer > 0) {
813
o->oSubAction++;
814
}
815
break;
816
case 1:
817
// Stop moving on the edge
818
cur_obj_init_animation_with_sound(BOWSER_ANIM_EDGE_STOP);
819
// Turn around once the animation ends
820
if (cur_obj_check_if_near_animation_end()) {
821
o->oAction = BOWSER_ACT_TURN_FROM_EDGE;
822
}
823
break;
824
}
825
}
826
827
/**
828
* Makes Bowser do a fire split attack
829
*/
830
void bowser_act_spit_fire_onto_floor(void) {
831
// Set fixed rand value if Mario is low health
832
if (gHudDisplay.wedges < 4) {
833
o->oBowserRandSplitFloor = 3;
834
} else {
835
o->oBowserRandSplitFloor = random_float() * 3.0f + 1.0f;
836
}
837
838
// Play animation and split fire at a specific frame
839
cur_obj_init_animation_with_sound(BOWSER_ANIM_BREATH_QUICK);
840
if (cur_obj_check_anim_frame(5)) {
841
obj_spit_fire(0, 200, 180, 7.0f, MODEL_RED_FLAME, 30.0f, 10.0f, 0x1000);
842
}
843
// Use subaction as a timer when the animation is over
844
if (cur_obj_check_if_near_animation_end()) {
845
o->oSubAction++;
846
}
847
// Return to default act once we get past rand value
848
if (o->oSubAction >= o->oBowserRandSplitFloor) {
849
o->oAction = BOWSER_ACT_DEFAULT;
850
}
851
}
852
853
/**
854
* Turns around Bowser from an specific yaw angle
855
* Returns TRUE once the timer is bigger than the time set
856
*/
857
s32 bowser_turn_on_timer(s32 time, s16 yaw) {
858
if (o->oSubAction == 0) {
859
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_LOOK_UP_START_WALK)) {
860
o->oSubAction++;
861
}
862
} else if (o->oSubAction == 1) {
863
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_LOOK_DOWN_STOP_WALK)) {
864
o->oSubAction++;
865
}
866
} else {
867
cur_obj_init_animation_with_sound(BOWSER_ANIM_IDLE);
868
}
869
o->oForwardVel = 0.0f;
870
o->oMoveAngleYaw += yaw;
871
if (o->oTimer >= time) {
872
return TRUE;
873
} else {
874
return FALSE;
875
}
876
}
877
878
/**
879
* Makes Bowser turn around after hitting the edge
880
*/
881
void bowser_act_turn_from_edge(void) {
882
if (bowser_turn_on_timer(63, 0x200)) {
883
o->oAction = BOWSER_ACT_DEFAULT;
884
}
885
}
886
887
/**
888
* Makes Bowser charge (run) to Mario
889
*/
890
void bowser_act_charge_mario(void) {
891
s32 time;
892
// Reset Speed to prepare charge
893
if (o->oTimer == 0) {
894
o->oForwardVel = 0.0f;
895
}
896
897
switch (o->oSubAction) {
898
case BOWSER_SUB_ACT_CHARGE_START:
899
// Start running
900
o->oBowserTimer = 0;
901
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_RUN_START)) {
902
o->oSubAction = BOWSER_SUB_ACT_CHARGE_RUN;
903
}
904
break;
905
case BOWSER_SUB_ACT_CHARGE_RUN:
906
// Set speed to run
907
o->oForwardVel = 50.0f;
908
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_RUN)) {
909
o->oBowserTimer++;
910
// Split if 6 timer frames has passed
911
if (o->oBowserTimer >= 6) {
912
o->oSubAction = BOWSER_SUB_ACT_CHARGE_SLIP;
913
}
914
// Slip if Mario has a differentiable angle and 2 timer frames has passed
915
if (o->oBowserTimer >= 2) {
916
if (abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw) > 0x2000) {
917
o->oSubAction = BOWSER_SUB_ACT_CHARGE_SLIP;
918
}
919
}
920
}
921
// Rotate to Mario
922
cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x200);
923
break;
924
case BOWSER_SUB_ACT_CHARGE_SLIP:
925
// Spawn smoke puff while slipping
926
o->oBowserTimer = 0;
927
cur_obj_init_animation_with_sound(BOWSER_ANIM_RUN_SLIP);
928
spawn_object_relative_with_scale(0, 100, -50, 0, 3.0f, o, MODEL_SMOKE, bhvWhitePuffSmoke2);
929
spawn_object_relative_with_scale(0, -100, -50, 0, 3.0f, o, MODEL_SMOKE,
930
bhvWhitePuffSmoke2);
931
// End Charge once Bowser stops running
932
if (approach_f32_signed(&o->oForwardVel, 0, -1.0f)) {
933
o->oSubAction = BOWSER_SUB_ACT_CHARGE_END;
934
}
935
cur_obj_extend_animation_if_at_end();
936
break;
937
case BOWSER_SUB_ACT_CHARGE_END:
938
// Stop running
939
o->oForwardVel = 0.0f;
940
cur_obj_init_animation_with_sound(BOWSER_ANIM_RUN_STOP);
941
if (cur_obj_check_if_near_animation_end()) {
942
// Set time delay to go to default action
943
if (o->oBehParams2ndByte == BOWSER_BP_BITS) {
944
time = 10;
945
} else {
946
time = 30;
947
}
948
if (o->oBowserTimer > time) {
949
o->oAction = BOWSER_ACT_DEFAULT;
950
}
951
o->oBowserTimer++;
952
}
953
cur_obj_extend_animation_if_at_end();
954
break;
955
}
956
// Bowser is close to falling so set hit edge action
957
if (o->oMoveFlags & OBJ_MOVE_HIT_EDGE) {
958
o->oAction = BOWSER_ACT_HIT_EDGE;
959
}
960
}
961
962
/**
963
* Checks if Bowser hits a mine from a distance, returns TRUE if so
964
*/
965
s32 bowser_check_hit_mine(void) {
966
struct Object *mine;
967
f32 dist;
968
969
mine = cur_obj_find_nearest_object_with_behavior(bhvBowserBomb, &dist);
970
if (mine != NULL && dist < 800.0f) {
971
mine->oInteractStatus |= INT_STATUS_HIT_MINE;
972
return TRUE;
973
}
974
975
return FALSE;
976
}
977
978
/**
979
* Bowser's thrown act that gets called after Mario releases him
980
*/
981
void bowser_act_thrown(void) {
982
UNUSED s32 unused;
983
// Keep Bowser's timer at 0 unless he lands
984
if (o->oTimer < 2)
985
o->oBowserTimer = 0;
986
if (o->oSubAction == 0) {
987
// Play shake animations and do bounce effects
988
cur_obj_init_animation_with_sound(BOWSER_ANIM_SHAKING);
989
bowser_bounce_effects(&o->oBowserTimer);
990
// Reset speed when he moves on ground
991
if (o->oMoveFlags & OBJ_MOVE_ON_GROUND) {
992
o->oForwardVel = 0.0f;
993
o->oSubAction++; // stops this current subaction
994
}
995
// Stand up and after play, set to default act
996
} else if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_STAND_UP))
997
o->oAction = BOWSER_ACT_DEFAULT;
998
// Hit mine check, reduce health and set specific action depending of it
999
if (bowser_check_hit_mine()) {
1000
o->oHealth--;
1001
if (o->oHealth <= 0) {
1002
o->oAction = BOWSER_ACT_DEAD;
1003
} else {
1004
o->oAction = BOWSER_ACT_HIT_MINE;
1005
}
1006
}
1007
}
1008
1009
/**
1010
* Set Bowser invisible and stops him (after falling)
1011
*/
1012
void bowser_set_goal_invisible(void) {
1013
o->oBowserTargetOpacity = 0;
1014
if (o->oOpacity == 0) {
1015
o->oForwardVel = 0.0f;
1016
o->oVelY = 0.0f;
1017
o->oPosY = o->oHomeY - 1000.0f;
1018
}
1019
}
1020
1021
/**
1022
* Makes Bowser jump back on stage after falling
1023
*/
1024
void bowser_act_jump_onto_stage(void) {
1025
s32 onDynamicFloor;
1026
UNUSED s32 unused;
1027
struct Surface *floor = o->oFloor;
1028
1029
// Set dynamic floor check (Object platforms)
1030
if (floor != NULL && floor->flags & SURFACE_FLAG_DYNAMIC) {
1031
onDynamicFloor = TRUE;
1032
} else {
1033
onDynamicFloor = FALSE;
1034
}
1035
// Set status Jump
1036
o->oBowserStatus |= BOWSER_STATUS_BIG_JUMP;
1037
1038
switch (o->oSubAction) {
1039
// Stops Bowser and makes him invisible
1040
case BOWSER_SUB_ACT_JUMP_ON_STAGE_IDLE:
1041
if (o->oTimer == 0) {
1042
o->oFaceAnglePitch = 0;
1043
o->oFaceAngleRoll = 0;
1044
} //? missing else
1045
o->oFaceAnglePitch += 0x800;
1046
o->oFaceAngleRoll += 0x800;
1047
if (!(o->oFaceAnglePitch & 0xFFFF)) {
1048
o->oSubAction++;
1049
}
1050
bowser_set_goal_invisible();
1051
break;
1052
// Start jump animation and make him visible after an animation frame
1053
case BOWSER_SUB_ACT_JUMP_ON_STAGE_START:
1054
cur_obj_init_animation_with_sound(BOWSER_ANIM_JUMP_START);
1055
if (cur_obj_check_anim_frame(11)) {
1056
o->oMoveAngleYaw = o->oBowserAngleToCentre;
1057
o->oVelY = 150.0f;
1058
o->oBowserTargetOpacity = 0xFF;
1059
o->oBowserTimer = 0;
1060
o->oSubAction++;
1061
} else {
1062
bowser_set_goal_invisible();
1063
}
1064
break;
1065
// Approach him back on stage
1066
case BOWSER_SUB_ACT_JUMP_ON_STAGE_LAND:
1067
if (o->oPosY > o->oHomeY) {
1068
o->oDragStrength = 0.0f;
1069
if (o->oBowserDistToCentre < 2500.0f) {
1070
if (absf(o->oFloorHeight - o->oHomeY) < 100.0f) {
1071
approach_f32_signed(&o->oForwardVel, 0, -5.0f);
1072
} else {
1073
cur_obj_forward_vel_approach_upward(150.0f, 2.0f);
1074
}
1075
} else
1076
cur_obj_forward_vel_approach_upward(150.0f, 2.0f);
1077
}
1078
// Land on stage
1079
if (bowser_land()) {
1080
o->oDragStrength = 10.0f;
1081
o->oSubAction++;
1082
// Spawn shockwave (BITS only) if is not on a platform
1083
if (onDynamicFloor == FALSE) {
1084
bowser_spawn_shockwave();
1085
// If is on a dynamic floor in BITS, then jump
1086
// because of the falling platform
1087
} else if (o->oBehParams2ndByte == BOWSER_BP_BITS) {
1088
o->oAction = BOWSER_ACT_BIG_JUMP;
1089
}
1090
// If is on a dynamic floor in BITFS, then tilt platform
1091
if (o->oBehParams2ndByte == BOWSER_BP_BITFS) {
1092
o->oAction = BOWSER_ACT_TILT_LAVA_PLATFORM;
1093
}
1094
}
1095
// Reset him back on stage if he still didn't landed yet
1096
// Post-JP made this check as a separate function
1097
#if BUGFIX_BOWSER_FALLEN_OFF_STAGE
1098
bowser_reset_fallen_off_stage();
1099
#else
1100
if (o->oVelY < 0.0f && o->oPosY < o->oHomeY - 300.0f) {
1101
o->oPosZ = 0.0f, o->oPosX = o->oPosZ;
1102
o->oPosY = o->oHomeY + 2000.0f;
1103
o->oVelY = 0.0f;
1104
}
1105
#endif
1106
break;
1107
// Bowser landed, so reset action after he's done jumping
1108
case BOWSER_SUB_ACT_JUMP_ON_STAGE_STOP:
1109
if (cur_obj_check_if_near_animation_end()) {
1110
o->oAction = BOWSER_ACT_DEFAULT;
1111
o->oBowserStatus &= ~BOWSER_STATUS_BIG_JUMP;
1112
cur_obj_extend_animation_if_at_end();
1113
}
1114
break;
1115
}
1116
print_debug_bottom_up("sp %d", o->oForwardVel);
1117
}
1118
1119
/**
1120
* The frames of the Bowser's timer on which to play a "stomp" sound
1121
*/
1122
s8 sBowserDanceStepNoises[] = { 24, 42, 60, -1 };
1123
1124
/**
1125
* Makes Bowser's dance as a "taunt"
1126
*/
1127
void bowser_act_dance(void) {
1128
// Play a stomp sound effect on certain frames
1129
if (is_item_in_array(o->oTimer, sBowserDanceStepNoises)) {
1130
cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK);
1131
}
1132
// Play dance animation and after that return to default action
1133
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_DANCE)) {
1134
o->oAction = BOWSER_ACT_DEFAULT;
1135
}
1136
}
1137
1138
/**
1139
* Spawn collectable that Bowser spawns after despawning
1140
* Spawns a Key in BITDW/BITFS or Grand Star in BITS
1141
*/
1142
void bowser_spawn_collectable(void) {
1143
if (o->oBehParams2ndByte == BOWSER_BP_BITS) {
1144
gSecondCameraFocus = spawn_object(o, MODEL_STAR, bhvGrandStar);
1145
} else {
1146
gSecondCameraFocus = spawn_object(o, MODEL_BOWSER_KEY, bhvBowserKey);
1147
cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_KEY);
1148
}
1149
gSecondCameraFocus->oAngleVelYaw = o->oAngleVelYaw;
1150
}
1151
1152
/**
1153
* Makes Bowser fly back on stage defeated
1154
*/
1155
void bowser_fly_back_dead(void) {
1156
cur_obj_init_animation_with_sound(BOWSER_ANIM_FLIP_DOWN);
1157
// More knockback in BITS
1158
if (o->oBehParams2ndByte == BOWSER_BP_BITS) {
1159
o->oForwardVel = -400.0f;
1160
} else {
1161
o->oForwardVel = -200.0f;
1162
}
1163
o->oVelY = 100.0f;
1164
o->oMoveAngleYaw = o->oBowserAngleToCentre + 0x8000;
1165
o->oBowserTimer = 0;
1166
o->oSubAction++; // BOWSER_SUB_ACT_DEAD_BOUNCE
1167
}
1168
1169
/**
1170
* Plays bounce effects after landing upside down
1171
*/
1172
void bowser_dead_bounce(void) {
1173
o->oBowserEyesShut = TRUE; // close eyes
1174
bowser_bounce_effects(&o->oBowserTimer);
1175
if (o->oMoveFlags & OBJ_MOVE_LANDED) {
1176
cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK);
1177
}
1178
if (o->oMoveFlags & OBJ_MOVE_ON_GROUND) {
1179
o->oForwardVel = 0.0f;
1180
o->oSubAction++; // BOWSER_SUB_ACT_DEAD_WAIT
1181
}
1182
}
1183
1184
/**
1185
* Wait for Mario to get close while Bowser is defeated
1186
* Returns TRUE if he is close enough
1187
*/
1188
s32 bowser_dead_wait_for_mario(void) {
1189
s32 ret = FALSE;
1190
cur_obj_become_intangible();
1191
if (cur_obj_init_animation_and_check_if_near_end(BOWSER_ANIM_LAY_DOWN) && o->oDistanceToMario < 700.0f
1192
&& abs_angle_diff(gMarioObject->oMoveAngleYaw, o->oAngleToMario) > 0x6000) {
1193
ret = TRUE;
1194
}
1195
cur_obj_extend_animation_if_at_end();
1196
o->oBowserTimer = 0;
1197
return ret;
1198
}
1199
1200
/**
1201
* Makes Bowser twirl up by changing his scale
1202
* Returns TRUE once done
1203
*/
1204
s32 bowser_dead_twirl_up(void) {
1205
s32 ret = FALSE;
1206
// Set angle rotation once he has low X scale value
1207
if (o->header.gfx.scale[0] < 0.8) {
1208
o->oAngleVelYaw += 0x80;
1209
}
1210
// Slowly scale down his X and Z value
1211
if (o->header.gfx.scale[0] > 0.2) {
1212
o->header.gfx.scale[0] = o->header.gfx.scale[0] - 0.02;
1213
o->header.gfx.scale[2] = o->header.gfx.scale[2] - 0.02;
1214
} else {
1215
// Now scale down his Y value (and send Bowser up)
1216
o->header.gfx.scale[1] = o->header.gfx.scale[1] - 0.01;
1217
o->oVelY = 20.0f;
1218
o->oGravity = 0.0f;
1219
}
1220
// At half Y scale value, he is high enough, so we are done
1221
if (o->header.gfx.scale[1] < 0.5) {
1222
ret = TRUE;
1223
}
1224
// Copy angle rotation to moving rotation
1225
o->oMoveAngleYaw += o->oAngleVelYaw;
1226
// Slowly fade out
1227
if (o->oOpacity >= 3) {
1228
o->oOpacity -= 2;
1229
}
1230
return ret;
1231
}
1232
1233
/**
1234
* Hides Bowser after his death sequence is done
1235
*/
1236
void bowser_dead_hide(void) {
1237
cur_obj_scale(0);
1238
o->oForwardVel = 0;
1239
o->oVelY = 0;
1240
o->oGravity = 0;
1241
}
1242
1243
/**
1244
* Dialog values that are set on each stage Bowser's is defeated
1245
*/
1246
s16 sBowserDefeatedDialogText[3] = { DIALOG_119, DIALOG_120, DIALOG_121 };
1247
1248
/**
1249
* Bowser's dead sequence that plays in BITDW/BITFS
1250
* Returns TRUE once done
1251
*/
1252
s32 bowser_dead_default_stage_ending(void) {
1253
s32 ret = FALSE;
1254
if (o->oBowserTimer < 2) {
1255
// Lower music volume
1256
if (o->oBowserTimer == 0) {
1257
seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40);
1258
o->oBowserTimer++;
1259
}
1260
// Play Bowser defeated dialog
1261
if (cur_obj_update_dialog(MARIO_DIALOG_LOOK_UP,
1262
(DIALOG_FLAG_TEXT_DEFAULT | DIALOG_FLAG_TIME_STOP_ENABLED),
1263
sBowserDefeatedDialogText[o->oBehParams2ndByte], 0)) {
1264
// Dialog is done, fade out music and play explode sound effect
1265
o->oBowserTimer++;
1266
cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_EXPLODE);
1267
seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60);
1268
seq_player_fade_out(SEQ_PLAYER_LEVEL, 1);
1269
}
1270
// Hide Bowser and spawn collectable once done twirling
1271
} else if (bowser_dead_twirl_up()) {
1272
bowser_dead_hide();
1273
spawn_triangle_break_particles(20, MODEL_YELLOW_COIN, 1.0f, 0);
1274
bowser_spawn_collectable();
1275
set_mario_npc_dialog(MARIO_DIALOG_STOP);
1276
ret = TRUE;
1277
1278
if (configGreenDemon > 1) {
1279
spawn_object_relative(0, 0, 256, 0, gMarioObject, MODEL_1UP, bhvHidden1upInPole);
1280
}
1281
}
1282
return ret;
1283
}
1284
1285
/**
1286
* Bowser's dead sequence that plays in BITS
1287
* Returns TRUE once done
1288
*/
1289
s32 bowser_dead_final_stage_ending(void) {
1290
UNUSED s32 unused;
1291
s32 ret = FALSE;
1292
s32 dialogID;
1293
if (o->oBowserTimer < 2) {
1294
// Set dialog whenever you have 120 stars or not
1295
if (gHudDisplay.stars < 120) {
1296
dialogID = DIALOG_121;
1297
} else {
1298
dialogID = DIALOG_163;
1299
}
1300
// Lower music volume
1301
if (o->oBowserTimer == 0) {
1302
seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40);
1303
o->oBowserTimer++;
1304
}
1305
// Play Bowser defeated dialog
1306
if (cur_obj_update_dialog(MARIO_DIALOG_LOOK_UP,
1307
(DIALOG_FLAG_TEXT_DEFAULT | DIALOG_FLAG_TIME_STOP_ENABLED), dialogID, 0)) {
1308
// Dialog is done, fade out music and spawn grand star
1309
cur_obj_set_model(MODEL_BOWSER_NO_SHADOW);
1310
seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60);
1311
seq_player_fade_out(SEQ_PLAYER_LEVEL, 1);
1312
bowser_spawn_collectable();
1313
o->oBowserTimer++;
1314
}
1315
// Slowly fade him out
1316
} else if (o->oOpacity > 4)
1317
o->oOpacity -= 4;
1318
else {
1319
// And at last, hide him
1320
bowser_dead_hide();
1321
ret = TRUE;
1322
1323
if (configGreenDemon > 1) {
1324
spawn_object_relative(0, 0, 256, 0, gMarioObject, MODEL_1UP, bhvGreenDemon);
1325
}
1326
}
1327
return ret;
1328
}
1329
1330
/**
1331
* Bowser's dead action, plays when he has no health left
1332
* This action is divided in subaction functions
1333
*/
1334
void bowser_act_dead(void) {
1335
switch (o->oSubAction) {
1336
case BOWSER_SUB_ACT_DEAD_FLY_BACK:
1337
bowser_fly_back_dead();
1338
break;
1339
case BOWSER_SUB_ACT_DEAD_BOUNCE:
1340
bowser_dead_bounce();
1341
break;
1342
case BOWSER_SUB_ACT_DEAD_WAIT:
1343
// Check if Mario is close to Bowser
1344
if (bowser_dead_wait_for_mario()) {
1345
o->oBowserTimer = 0;
1346
// Set different (final) subaction in BITS
1347
// Non-BITS Bowser uses default subaction and sets dithering
1348
if (o->oBehParams2ndByte == BOWSER_BP_BITS) {
1349
o->oSubAction = BOWSER_SUB_ACT_DEAD_FINAL_END;
1350
} else {
1351
o->activeFlags |= ACTIVE_FLAG_DITHERED_ALPHA;
1352
o->oSubAction++; // BOWSER_SUB_ACT_DEAD_DEFAULT_END
1353
}
1354
}
1355
break;
1356
case BOWSER_SUB_ACT_DEAD_DEFAULT_END:
1357
if (bowser_dead_default_stage_ending()) {
1358
o->oSubAction++; // BOWSER_SUB_ACT_DEAD_DEFAULT_END_OVER
1359
}
1360
break;
1361
case BOWSER_SUB_ACT_DEAD_DEFAULT_END_OVER:
1362
break;
1363
case BOWSER_SUB_ACT_DEAD_FINAL_END:
1364
if (bowser_dead_final_stage_ending()) {
1365
o->oSubAction++; // BOWSER_SUB_ACT_DEAD_FINAL_END_OVER
1366
}
1367
break;
1368
case BOWSER_SUB_ACT_DEAD_FINAL_END_OVER:
1369
break;
1370
}
1371
}
1372
1373
/**
1374
* Sets values for the BITFS platform to tilt
1375
*/
1376
void bowser_tilt_platform(struct Object *platform, s16 angSpeed) {
1377
s16 angle;
1378
angle = o->oBowserAngleToCentre + 0x8000;
1379
platform->oAngleVelPitch = coss(angle) * angSpeed;
1380
platform->oAngleVelRoll = -sins(angle) * angSpeed;
1381
}
1382
1383
/**
1384
* Struct for the BITFS tilt platform
1385
*/
1386
struct BowserTiltPlatformInfo {
1387
// Flag value to make sure platform moves smoothly
1388
// 0 = Don't move
1389
// 1 = Move angle behind Bowser
1390
// -1 = Move angle in front of Bowser
1391
s16 flag;
1392
// Sets platform's tilt angle speed (pattern: positive then negative)
1393
s16 angSpeed;
1394
// Sets how much time the platform can tilt, increases each move
1395
s16 time;
1396
};
1397
1398
/**
1399
* Data for the BITFS tilt Platform
1400
*/
1401
struct BowserTiltPlatformInfo sBowsertiltPlatformData[] = {
1402
{ 1, 10, 40 },
1403
{ 0, 0, 74 },
1404
{ -1, -10, 114 },
1405
{ 1, -20, 134 },
1406
{ -1, 20, 154 },
1407
{ 1, 40, 164 },
1408
{ -1, -40, 174 },
1409
{ 1, -80, 179 },
1410
{ -1, 80, 184 },
1411
{ 1, 160, 186 },
1412
{ -1, -160, 186 },
1413
{ 1, 0, 0 },
1414
};
1415
1416
/**
1417
* Makes the platform in BITFS tilt from left to right
1418
*/
1419
void bowser_act_tilt_lava_platform(void) {
1420
// Set platform object
1421
struct Object *platform = cur_obj_nearest_object_with_behavior(bhvTiltingBowserLavaPlatform);
1422
UNUSED s16 angle = o->oBowserAngleToCentre + 0x8000;
1423
s16 angSpeed;
1424
UNUSED s32 unused;
1425
s32 i;
1426
s32 isNotTilting;
1427
// If there's not platform, return to default action
1428
if (platform == NULL) {
1429
o->oAction = BOWSER_ACT_DEFAULT;
1430
} else {
1431
i = 0;
1432
isNotTilting = TRUE;
1433
// Active platform tilting if the timer is not 0
1434
while (sBowsertiltPlatformData[i].time != 0) {
1435
// Move if the time values is more than the timer
1436
if (o->oTimer < sBowsertiltPlatformData[i].time) {
1437
// Set angle speed
1438
angSpeed = sBowsertiltPlatformData[i].angSpeed;
1439
// Move angle behind Bowser
1440
if (sBowsertiltPlatformData[i].flag > 0) {
1441
angSpeed = (sBowsertiltPlatformData[i].time - o->oTimer - 1) * angSpeed;
1442
} else { // Move angle in front of Bowser
1443
angSpeed = (o->oTimer - sBowsertiltPlatformData[i - 1].time) * angSpeed;
1444
}
1445
// Set angle values to the platform
1446
bowser_tilt_platform(platform, angSpeed);
1447
// Play sound effect
1448
if (angSpeed != 0) {
1449
play_sound(SOUND_ENV_MOVING_BIG_PLATFORM, platform->header.gfx.cameraToObject);
1450
}
1451
isNotTilting = FALSE;
1452
break;
1453
}
1454
i++;
1455
}
1456
// If Bowser is done tilting, reset platform angles
1457
// and set Bowser to default action
1458
if (isNotTilting) {
1459
o->oAction = BOWSER_ACT_DEFAULT;
1460
platform->oAngleVelPitch = 0;
1461
platform->oAngleVelRoll = 0;
1462
platform->oFaceAnglePitch = 0;
1463
platform->oFaceAngleRoll = 0;
1464
}
1465
}
1466
// Extend "idle" animation
1467
cur_obj_extend_animation_if_at_end();
1468
}
1469
1470
/**
1471
* Check if Bowser is offstage from a large distance or landed on a lethal floor
1472
*/
1473
s32 bowser_check_fallen_off_stage(void) {
1474
if (o->oAction != BOWSER_ACT_JUMP_ONTO_STAGE && o->oAction != BOWSER_ACT_TILT_LAVA_PLATFORM) {
1475
if (o->oPosY < o->oHomeY - 1000.0f)
1476
return TRUE;
1477
if (o->oMoveFlags & OBJ_MOVE_LANDED) {
1478
// Check for Fire Sea
1479
if (o->oFloorType == SURFACE_BURNING) {
1480
return TRUE;
1481
}
1482
// Check for Dark World - Sky
1483
if (o->oFloorType == SURFACE_DEATH_PLANE) {
1484
return TRUE;
1485
}
1486
}
1487
}
1488
return FALSE;
1489
}
1490
1491
/**
1492
* Set Bowser's actions
1493
*/
1494
void (*sBowserActions[])(void) = {
1495
bowser_act_default,
1496
bowser_act_thrown,
1497
bowser_act_jump_onto_stage,
1498
bowser_act_dance,
1499
bowser_act_dead,
1500
bowser_act_wait,
1501
bowser_act_intro_walk,
1502
bowser_act_charge_mario,
1503
bowser_act_spit_fire_into_sky,
1504
bowser_act_spit_fire_onto_floor,
1505
bowser_act_hit_edge,
1506
bowser_act_turn_from_edge,
1507
bowser_act_hit_mine,
1508
bowser_act_big_jump,
1509
bowser_act_walk_to_mario,
1510
bowser_act_breath_fire,
1511
bowser_act_teleport,
1512
bowser_act_quick_jump,
1513
bowser_act_idle,
1514
bowser_act_tilt_lava_platform
1515
};
1516
1517
/**
1518
* Set Bowser's sound animations
1519
*/
1520
struct SoundState sBowserSoundStates[] = {
1521
{ 0, 0, 0, NO_SOUND },
1522
{ 0, 0, 0, NO_SOUND },
1523
{ 0, 0, 0, NO_SOUND },
1524
{ 0, 0, 0, NO_SOUND },
1525
{ 0, 0, 0, NO_SOUND },
1526
{ 0, 0, 0, NO_SOUND },
1527
{ 0, 0, 0, NO_SOUND },
1528
{ 0, 0, 0, NO_SOUND },
1529
{ 1, 0, -1, SOUND_OBJ_BOWSER_WALK },
1530
{ 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR },
1531
{ 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR },
1532
{ 0, 0, 0, NO_SOUND },
1533
{ 0, 0, 0, NO_SOUND },
1534
{ 1, 20, 40, SOUND_OBJ_BOWSER_WALK },
1535
{ 1, 20, -1, SOUND_OBJ_BOWSER_WALK },
1536
{ 1, 20, 40, SOUND_OBJ_BOWSER_WALK },
1537
{ 1, 0, -1, SOUND_OBJ_BOWSER_TAIL_PICKUP },
1538
{ 1, 0, -1, SOUND_OBJ_BOWSER_DEFEATED },
1539
{ 1, 8, -1, SOUND_OBJ_BOWSER_WALK },
1540
{ 1, 8, 17, SOUND_OBJ_BOWSER_WALK },
1541
{ 1, 8, -10, SOUND_OBJ_BOWSER_WALK },
1542
{ 0, 0, 0, NO_SOUND },
1543
{ 1, 5, -1, SOUND_OBJ_FLAME_BLOWN },
1544
{ 0, 0, 0, NO_SOUND },
1545
{ 0, 0, 0, NO_SOUND },
1546
{ 1, 0, -1, SOUND_OBJ_BOWSER_TAIL_PICKUP },
1547
{ 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR },
1548
};
1549
1550
/**
1551
* Set whenever Bowser should have rainbow light or not on each stage
1552
*/
1553
s8 sBowserRainbowLight[] = { FALSE, FALSE, TRUE };
1554
1555
/**
1556
* Set how much health Bowser has on each stage
1557
*/
1558
s8 sBowserHealth[] = { 1, 1, 3 };
1559
s8 sBowserHealth_hard[4] = { 3, 4, 5 };
1560
1561
/**
1562
* Update Bowser's actions when he's hands free
1563
*/
1564
void bowser_free_update(void) {
1565
struct Surface *floor;
1566
struct Object *platform;
1567
UNUSED f32 floorHeight;
1568
1569
// Platform displacement check (for BITFS)
1570
if ((platform = o->platform) != NULL) {
1571
apply_platform_displacement(FALSE, platform);
1572
}
1573
// Reset grabbed status
1574
o->oBowserGrabbedStatus = BOWSER_GRAB_STATUS_NONE;
1575
// Update positions and actions (default action)
1576
cur_obj_update_floor_and_walls();
1577
cur_obj_call_action_function(sBowserActions);
1578
cur_obj_move_standard(-78);
1579
// Jump on stage if Bowser has fallen off
1580
if (bowser_check_fallen_off_stage()) {
1581
o->oAction = BOWSER_ACT_JUMP_ONTO_STAGE;
1582
}
1583
// Check floor height and platform
1584
floorHeight = find_floor(o->oPosX, o->oPosY, o->oPosZ, &floor);
1585
if ((floor != NULL) && (floor->object != NULL)) {
1586
o->platform = floor->object;
1587
} else {
1588
o->platform = NULL;
1589
}
1590
// Sound states for Bowser Animations
1591
exec_anim_sound_state(sBowserSoundStates);
1592
}
1593
1594
/**
1595
* Update Bowser's actions when he's getting held
1596
*/
1597
void bowser_held_update(void) {
1598
// Reset fire sky status and make him intangible
1599
o->oBowserStatus &= ~BOWSER_STATUS_FIRE_SKY;
1600
cur_obj_become_intangible();
1601
switch (o->oBowserGrabbedStatus) {
1602
// Play pickup sound, start grabbed animation, and set throw action
1603
// Throw action won't be played until he's actually released
1604
case BOWSER_GRAB_STATUS_NONE:
1605
cur_obj_play_sound_2(SOUND_OBJ_BOWSER_TAIL_PICKUP);
1606
cur_obj_unrender_set_action_and_anim(BOWSER_ANIM_GRABBED, BOWSER_ACT_THROWN);
1607
o->oBowserGrabbedStatus++;
1608
break;
1609
// After the grabbed animation ends, play shaking animation in a loop
1610
case BOWSER_GRAB_STATUS_GRABBED:
1611
if (cur_obj_check_if_near_animation_end()) {
1612
cur_obj_init_animation_with_sound(BOWSER_ANIM_SHAKING);
1613
o->oBowserGrabbedStatus++;
1614
}
1615
break;
1616
case BOWSER_GRAB_STATUS_HOLDING:
1617
break;
1618
}
1619
// Reset move flags
1620
o->oMoveFlags = 0;
1621
// Copy angle values from Mario
1622
o->oBowserHeldAnglePitch = gMarioObject->oMoveAnglePitch;
1623
o->oBowserHeldAngleVelYaw = gMarioObject->oAngleVelYaw;
1624
o->oMoveAngleYaw = gMarioObject->oMoveAngleYaw;
1625
}
1626
1627
/**
1628
* Update Bowser's actions when he's thrown and dropped
1629
*/
1630
void bowser_thrown_dropped_update(void) {
1631
f32 swingSpd;
1632
// Reset grabbed status
1633
o->oBowserGrabbedStatus = BOWSER_GRAB_STATUS_NONE;
1634
// Set throw action and vel values
1635
cur_obj_get_thrown_or_placed(1.0f, 1.0f, BOWSER_ACT_THROWN);
1636
// Set swing speed based of angle
1637
swingSpd = o->oBowserHeldAngleVelYaw / 3000.0 * (configEasyBowserThrows ? 140.0f : 70.0f);
1638
// If less than 0, reduce speed
1639
if (swingSpd < 0.0f) {
1640
swingSpd = -swingSpd;
1641
}
1642
// If more than 90, increase speed
1643
if (swingSpd > 90.0f) {
1644
swingSpd *= 2.5;
1645
}
1646
// Set distance speed when throwing
1647
o->oForwardVel = coss(o->oBowserHeldAnglePitch) * swingSpd;
1648
o->oVelY = -sins(o->oBowserHeldAnglePitch) * swingSpd;
1649
cur_obj_become_intangible();
1650
1651
// Reset timer and subactions
1652
o->prevObj->oAction = BOWSER_ACT_TAIL_THROWN; // prevObj is Bowser's Tail
1653
o->prevObj->oTimer = 0;
1654
o->prevObj->oSubAction = 0; //! Tail doesn't have sub actions
1655
1656
o->oTimer = 0;
1657
o->oSubAction = 0;
1658
}
1659
1660
/**
1661
* Bowser's main loop
1662
*/
1663
void bhv_bowser_loop(void) {
1664
s16 angleToMario; // AngleToMario from Bowser's perspective
1665
s16 angleToCentre; // AngleToCentre from Bowser's perspective
1666
1667
// Set distance/angle values
1668
o->oBowserDistToCentre = sqrtf(o->oPosX * o->oPosX + o->oPosZ * o->oPosZ);
1669
o->oBowserAngleToCentre = atan2s(0.0f - o->oPosZ, 0.0f - o->oPosX);
1670
angleToMario = abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario);
1671
angleToCentre = abs_angle_diff(o->oMoveAngleYaw, o->oBowserAngleToCentre);
1672
1673
// Reset Status
1674
o->oBowserStatus &= ~0xFF;
1675
1676
// Set bitflag status for distance/angle values
1677
// Only the first one is used
1678
if (angleToMario < 0x2000) {
1679
o->oBowserStatus |= BOWSER_STATUS_ANGLE_MARIO;
1680
}
1681
if (angleToCentre < 0x3800) {
1682
o->oBowserStatus |= BOWSER_STATUS_ANGLE_CENTRE; // unused
1683
}
1684
if (o->oBowserDistToCentre < 1000.0f) {
1685
o->oBowserStatus |= BOWSER_STATUS_DIST_CENTRE; // unused
1686
}
1687
if (o->oDistanceToMario < 850.0f) {
1688
o->oBowserStatus |= BOWSER_STATUS_DIST_MARIO; // unused
1689
}
1690
1691
// Update Held state actions
1692
switch (o->oHeldState) {
1693
case HELD_FREE:
1694
bowser_free_update();
1695
break;
1696
case HELD_HELD:
1697
bowser_held_update();
1698
break;
1699
case HELD_THROWN:
1700
bowser_thrown_dropped_update();
1701
break;
1702
case HELD_DROPPED:
1703
bowser_thrown_dropped_update();
1704
break;
1705
}
1706
// Adjust model to the floor
1707
cur_obj_align_gfx_with_floor();
1708
1709
// Adjust opacity (when not dead)
1710
// Mostly for the teleport action in BITFS
1711
if (o->oAction != BOWSER_ACT_DEAD) {
1712
if (o->oBowserTargetOpacity != o->oOpacity) {
1713
// increase opacity when oBowserTargetOpacity is 0xFF
1714
if (o->oBowserTargetOpacity > o->oOpacity) {
1715
o->oOpacity += 20;
1716
if (o->oOpacity >= 0x100) {
1717
o->oOpacity = 0xFF;
1718
}
1719
// reduce opacity when oBowserTargetOpacity is 0
1720
} else {
1721
o->oOpacity -= 20;
1722
if (o->oOpacity < 0) {
1723
o->oOpacity = 0;
1724
}
1725
}
1726
}
1727
}
1728
}
1729
1730
/**
1731
* Bowser's initial values and actions
1732
*/
1733
void bhv_bowser_init(void) {
1734
s32 level;
1735
// Set "reaction" value
1736
// It goes true when Bowser is a non-walking state
1737
o->oBowserIsReacting = TRUE;
1738
// Set no transparency opacity
1739
o->oOpacity = 0xFF;
1740
o->oBowserTargetOpacity = 0xFF;
1741
// Set Bowser B-param depending of the stage
1742
if (gCurrLevelNum == LEVEL_BOWSER_2) {
1743
level = BOWSER_BP_BITFS;
1744
} else if (gCurrLevelNum == LEVEL_BOWSER_3) {
1745
level = BOWSER_BP_BITS;
1746
} else { // LEVEL_BOWSER_1
1747
level = BOWSER_BP_BITDW;
1748
}
1749
o->oBehParams2ndByte = level;
1750
// Set health and rainbow light depending of the level
1751
o->oBowserRainbowLight = sBowserRainbowLight[level];
1752
if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) {
1753
o->oHealth = sBowserHealth_hard[level];
1754
}
1755
else {
1756
o->oHealth = sBowserHealth[level];
1757
}
1758
// Start camera event, this event is not defined so maybe
1759
// the "start arena" cutscene was originally called this way
1760
cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_INIT);
1761
o->oAction = BOWSER_ACT_WAIT;
1762
// Set eyes status
1763
o->oBowserEyesTimer = 0;
1764
o->oBowserEyesShut = FALSE;
1765
}
1766
1767
Gfx *geo_update_body_rot_from_parent(s32 callContext, UNUSED struct GraphNode *node, Mat4 mtx) {
1768
Mat4 mtx2;
1769
struct Object *obj;
1770
1771
if (callContext == GEO_CONTEXT_RENDER) {
1772
obj = (struct Object *) gCurGraphNodeObject;
1773
if (obj->prevObj != NULL) {
1774
create_transformation_from_matrices(mtx2, mtx, *gCurGraphNodeCamera->matrixPtr);
1775
obj_update_pos_from_parent_transformation(mtx2, obj->prevObj);
1776
obj_set_gfx_pos_from_pos(obj->prevObj);
1777
}
1778
}
1779
return NULL;
1780
}
1781
1782
/**
1783
* Bowser's eyes Geo-Switch-Case IDs, defined from Mario's POV
1784
*/
1785
enum BowserEyesGSCId
1786
{
1787
/*0x00*/ BOWSER_EYES_OPEN,
1788
/*0x01*/ BOWSER_EYES_HALF_CLOSED,
1789
/*0x02*/ BOWSER_EYES_CLOSED,
1790
/*0x03*/ BOWSER_EYES_LEFT,
1791
/*0x04*/ BOWSER_EYES_FAR_LEFT,
1792
/*0x05*/ BOWSER_EYES_RIGHT,
1793
/*0x06*/ BOWSER_EYES_FAR_RIGHT,
1794
/*0x07*/ BOWSER_EYES_DERP, // unused
1795
/*0x08*/ BOWSER_EYES_CROSS, // unused
1796
/*0x08*/ BOWSER_EYES_RESET // set eyes back to open
1797
};
1798
1799
/**
1800
* Controls Bowser's eye open stage, including blinking and look directions
1801
*/
1802
void bowser_open_eye_switch(struct Object *obj, struct GraphNodeSwitchCase *switchCase) {
1803
s32 eyeCase;
1804
s16 angleFromMario;
1805
angleFromMario = abs_angle_diff(obj->oMoveAngleYaw, obj->oAngleToMario);
1806
eyeCase = switchCase->selectedCase;
1807
switch (eyeCase) {
1808
case BOWSER_EYES_OPEN:
1809
// Mario is in Bowser's field of view
1810
if (angleFromMario > 0x2000) {
1811
if (obj->oAngleVelYaw > 0)
1812
switchCase->selectedCase = BOWSER_EYES_RIGHT;
1813
if (obj->oAngleVelYaw < 0)
1814
switchCase->selectedCase = BOWSER_EYES_LEFT;
1815
}
1816
// Half close, start blinking
1817
if (obj->oBowserEyesTimer > 50)
1818
switchCase->selectedCase = BOWSER_EYES_HALF_CLOSED;
1819
break;
1820
case BOWSER_EYES_HALF_CLOSED:
1821
// Close, blinking
1822
if (obj->oBowserEyesTimer > 2)
1823
switchCase->selectedCase = BOWSER_EYES_CLOSED;
1824
break;
1825
case BOWSER_EYES_CLOSED:
1826
// Reset blinking
1827
if (obj->oBowserEyesTimer > 2)
1828
switchCase->selectedCase = BOWSER_EYES_RESET;
1829
break;
1830
case BOWSER_EYES_RESET:
1831
// Open, no longer blinking
1832
if (obj->oBowserEyesTimer > 2)
1833
switchCase->selectedCase = BOWSER_EYES_OPEN;
1834
break;
1835
case BOWSER_EYES_RIGHT:
1836
// Look more on the right if angle didn't change
1837
// Otherwise, look at the center (open)
1838
if (obj->oBowserEyesTimer > 2) {
1839
switchCase->selectedCase = BOWSER_EYES_FAR_RIGHT;
1840
if (obj->oAngleVelYaw <= 0)
1841
switchCase->selectedCase = BOWSER_EYES_OPEN;
1842
}
1843
break;
1844
case BOWSER_EYES_FAR_RIGHT:
1845
// Look close right if angle was drastically changed
1846
if (obj->oAngleVelYaw <= 0)
1847
switchCase->selectedCase = BOWSER_EYES_RIGHT;
1848
break;
1849
case BOWSER_EYES_LEFT:
1850
// Look more on the left if angle didn't change
1851
// Otherwise, look at the center (open)
1852
if (obj->oBowserEyesTimer > 2) {
1853
switchCase->selectedCase = BOWSER_EYES_FAR_LEFT;
1854
if (obj->oAngleVelYaw >= 0)
1855
switchCase->selectedCase = BOWSER_EYES_OPEN;
1856
}
1857
break;
1858
case BOWSER_EYES_FAR_LEFT:
1859
// Look close left if angle was drastically changed
1860
if (obj->oAngleVelYaw >= 0)
1861
switchCase->selectedCase = BOWSER_EYES_LEFT;
1862
break;
1863
default:
1864
switchCase->selectedCase = BOWSER_EYES_OPEN;
1865
}
1866
// Reset timer if eye case has changed
1867
if (switchCase->selectedCase != eyeCase) {
1868
obj->oBowserEyesTimer = -1;
1869
}
1870
}
1871
1872
/**
1873
* Geo switch for controlling the state of Bowser's eye direction and open/closed
1874
* state. Checks whether oBowserEyesShut is TRUE and closes eyes if so and processes
1875
* direction otherwise.
1876
*/
1877
Gfx *geo_switch_bowser_eyes(s32 callContext, struct GraphNode *node, UNUSED Mat4 *mtx) {
1878
UNUSED s16 eyeShut;
1879
UNUSED s32 unused;
1880
struct Object *obj = (struct Object *) gCurGraphNodeObject;
1881
struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node;
1882
if (callContext == GEO_CONTEXT_RENDER) {
1883
if (gCurGraphNodeHeldObject != NULL)
1884
obj = gCurGraphNodeHeldObject->objNode;
1885
switch (eyeShut = obj->oBowserEyesShut) {
1886
case FALSE: // eyes open, handle eye looking direction
1887
bowser_open_eye_switch(obj, switchCase);
1888
break;
1889
case TRUE: // eyes closed, blinking
1890
switchCase->selectedCase = BOWSER_EYES_CLOSED;
1891
break;
1892
}
1893
obj->oBowserEyesTimer++;
1894
}
1895
return NULL;
1896
}
1897
1898
/**
1899
* Geo switch that sets Bowser's Rainbow coloring (in BITS)
1900
*/
1901
Gfx *geo_bits_bowser_coloring(s32 callContext, struct GraphNode *node, UNUSED s32 context) {
1902
Gfx *gfxHead = NULL;
1903
Gfx *gfx;
1904
struct Object *obj;
1905
struct GraphNodeGenerated *graphNode;
1906
1907
if (callContext == GEO_CONTEXT_RENDER) {
1908
obj = (struct Object *) gCurGraphNodeObject;
1909
graphNode = (struct GraphNodeGenerated *) node;
1910
if (gCurGraphNodeHeldObject != 0) {
1911
obj = gCurGraphNodeHeldObject->objNode;
1912
}
1913
// Set layers if object is transparent or not
1914
if (obj->oOpacity == 0xFF) {
1915
graphNode->fnNode.node.flags = (graphNode->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8);
1916
} else {
1917
graphNode->fnNode.node.flags = (graphNode->fnNode.node.flags & 0xFF) | (LAYER_TRANSPARENT << 8);
1918
}
1919
gfx = gfxHead = alloc_display_list(2 * sizeof(Gfx));
1920
// If TRUE, clear lighting to give rainbow color
1921
if (obj->oBowserRainbowLight != 0) {
1922
gSPClearGeometryMode(gfx++, G_LIGHTING);
1923
}
1924
gSPEndDisplayList(gfx);
1925
}
1926
return gfxHead;
1927
}
1928