Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/behaviors/boo.inc.c
7861 views
1
// boo.c.inc
2
3
static struct ObjectHitbox sBooGivingStarHitbox = {
4
/* interactType: */ 0,
5
/* downOffset: */ 0,
6
/* damageOrCoinValue: */ 3,
7
/* health: */ 3,
8
/* numLootCoins: */ 0,
9
/* radius: */ 140,
10
/* height: */ 80,
11
/* hurtboxRadius: */ 40,
12
/* hurtboxHeight: */ 60,
13
};
14
15
// Relative positions
16
static s16 sCourtyardBooTripletPositions[][3] = {
17
{0, 50, 0},
18
{210, 110, 210},
19
{-210, 70, -210}
20
};
21
22
static void boo_stop(void) {
23
o->oForwardVel = 0.0f;
24
o->oVelY = 0.0f;
25
o->oGravity = 0.0f;
26
}
27
28
void bhv_boo_init(void) {
29
o->oBooInitialMoveYaw = o->oMoveAngleYaw;
30
}
31
32
static s32 boo_should_be_stopped(void) {
33
if (cur_obj_has_behavior(bhvMerryGoRoundBigBoo) || cur_obj_has_behavior(bhvMerryGoRoundBoo)) {
34
if (!gMarioOnMerryGoRound) {
35
return TRUE;
36
} else {
37
return FALSE;
38
}
39
} else {
40
if (o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) {
41
return TRUE;
42
}
43
44
if (o->oRoom == 10) {
45
if (gTimeStopState & TIME_STOP_MARIO_OPENED_DOOR) {
46
return TRUE;
47
}
48
}
49
}
50
51
return FALSE;
52
}
53
54
static s32 boo_should_be_active(void) {
55
f32 activationRadius;
56
57
if (cur_obj_has_behavior(bhvBalconyBigBoo)) {
58
activationRadius = 5000.0f;
59
} else {
60
activationRadius = 1500.0f;
61
}
62
63
if (cur_obj_has_behavior(bhvMerryGoRoundBigBoo) || cur_obj_has_behavior(bhvMerryGoRoundBoo)) {
64
if (gMarioOnMerryGoRound == TRUE) {
65
return TRUE;
66
} else {
67
return FALSE;
68
}
69
} else if (o->oRoom == -1) {
70
if (o->oDistanceToMario < activationRadius) {
71
return TRUE;
72
}
73
} else if (!boo_should_be_stopped()) {
74
if (
75
o->oDistanceToMario < activationRadius &&
76
(o->oRoom == gMarioCurrentRoom || gMarioCurrentRoom == 0)
77
) {
78
return TRUE;
79
}
80
}
81
82
return FALSE;
83
}
84
85
void bhv_courtyard_boo_triplet_init(void) {
86
s32 i;
87
struct Object *boo;
88
89
if (gHudDisplay.stars < 12) {
90
obj_mark_for_deletion(o);
91
} else {
92
for (i = 0; i < 3; i++) {
93
boo = spawn_object_relative(
94
0x01,
95
sCourtyardBooTripletPositions[i][0],
96
sCourtyardBooTripletPositions[i][1],
97
sCourtyardBooTripletPositions[i][2],
98
o,
99
MODEL_BOO,
100
bhvGhostHuntBoo
101
);
102
103
boo->oMoveAngleYaw = random_u16();
104
}
105
}
106
}
107
108
static void boo_approach_target_opacity_and_update_scale(void) {
109
f32 scale;
110
111
if (o->oBooTargetOpacity != o->oOpacity) {
112
if (o->oBooTargetOpacity > o->oOpacity) {
113
o->oOpacity += 20;
114
115
if (o->oBooTargetOpacity < o->oOpacity) {
116
o->oOpacity = o->oBooTargetOpacity;
117
}
118
} else {
119
o->oOpacity -= 20;
120
121
if (o->oBooTargetOpacity > o->oOpacity) {
122
o->oOpacity = o->oBooTargetOpacity;
123
}
124
}
125
}
126
127
scale = (o->oOpacity/255.0f * 0.4 + 0.6) * o->oBooBaseScale;
128
obj_scale(o, scale); // why no cur_obj_scale? was cur_obj_scale written later?
129
}
130
131
static void boo_oscillate(s32 ignoreOpacity) {
132
o->oFaceAnglePitch = sins(o->oBooOscillationTimer) * 0x400;
133
134
if (o->oOpacity == 0xFF || ignoreOpacity == TRUE) {
135
o->header.gfx.scale[0] = sins(o->oBooOscillationTimer) * 0.08 + o->oBooBaseScale;
136
o->header.gfx.scale[1] = -sins(o->oBooOscillationTimer) * 0.08 + o->oBooBaseScale;
137
o->header.gfx.scale[2] = o->header.gfx.scale[0];
138
o->oGravity = sins(o->oBooOscillationTimer) * o->oBooBaseScale;
139
o->oBooOscillationTimer += 0x400;
140
}
141
}
142
143
static s32 boo_vanish_or_appear(void) {
144
s16 relativeAngleToMario = abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw);
145
s16 relativeMarioFaceAngle = abs_angle_diff(o->oMoveAngleYaw, gMarioObject->oFaceAngleYaw);
146
// magic?
147
s16 relativeAngleToMarioThreshhold = 0x1568;
148
s16 relativeMarioFaceAngleThreshhold = 0x6b58;
149
s32 doneAppearing = FALSE;
150
151
o->oVelY = 0.0f;
152
153
if (
154
relativeAngleToMario > relativeAngleToMarioThreshhold ||
155
relativeMarioFaceAngle < relativeMarioFaceAngleThreshhold
156
) {
157
if (o->oOpacity == 40) {
158
o->oBooTargetOpacity = 255;
159
cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_LONG);
160
}
161
162
if (o->oOpacity > 180) {
163
doneAppearing = TRUE;
164
}
165
} else if (o->oOpacity == 255) {
166
o->oBooTargetOpacity = 40;
167
}
168
169
return doneAppearing;
170
}
171
172
static void boo_set_move_yaw_for_during_hit(s32 hurt) {
173
cur_obj_become_intangible();
174
175
o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
176
o->oBooMoveYawBeforeHit = (f32) o->oMoveAngleYaw;
177
178
if (hurt != FALSE) {
179
o->oBooMoveYawDuringHit = gMarioObject->oMoveAngleYaw;
180
} else if (coss((s16)o->oMoveAngleYaw - (s16)o->oAngleToMario) < 0.0f) {
181
o->oBooMoveYawDuringHit = o->oMoveAngleYaw;
182
} else {
183
o->oBooMoveYawDuringHit = (s16)(o->oMoveAngleYaw + 0x8000);
184
}
185
}
186
187
static void boo_move_during_hit(s32 roll, f32 fVel) {
188
// Boos seem to have been supposed to oscillate up then down then back again
189
// when hit. However it seems the programmers forgot to scale the cosine,
190
// so the Y velocity goes from 1 to -1 and back to 1 over 32 frames.
191
// This is such a small change that the Y position only changes by 5 units.
192
// It's completely unnoticable in-game.
193
s32 oscillationVel = o->oTimer * 0x800 + 0x800;
194
195
o->oForwardVel = fVel;
196
o->oVelY = coss(oscillationVel) * (configBetterEnemies ? 8.0f : 1.0f);
197
o->oMoveAngleYaw = o->oBooMoveYawDuringHit;
198
199
if (roll != FALSE) {
200
o->oFaceAngleYaw += sBooHitRotations[o->oTimer];
201
o->oFaceAngleRoll += sBooHitRotations[o->oTimer];
202
}
203
}
204
205
static void big_boo_shake_after_hit(void) {
206
// Oscillate yaw
207
s32 oscillationVel = o->oTimer * 0x2000 - 0x3E000;
208
o->oFaceAngleYaw += coss(oscillationVel) * 0x400;
209
}
210
211
static void boo_reset_after_hit(void) {
212
o->oMoveAngleYaw = o->oBooMoveYawBeforeHit;
213
o->oFlags |= OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
214
o->oInteractStatus = 0;
215
}
216
217
// called iff boo/big boo/cage boo is in action 2, which only occurs if it was non-attack-ly interacted with/bounced on?
218
static s32 boo_update_after_bounced_on(f32 a0) {
219
boo_stop();
220
221
if (o->oTimer == 0) {
222
boo_set_move_yaw_for_during_hit(FALSE);
223
}
224
225
if (o->oTimer < 32) {
226
boo_move_during_hit(FALSE, sBooHitRotations[o->oTimer]/5000.0f * a0);
227
} else {
228
cur_obj_become_tangible();
229
boo_reset_after_hit();
230
o->oAction = 1;
231
return TRUE;
232
}
233
234
return FALSE;
235
}
236
237
// called iff big boo nonlethally hit
238
static s32 big_boo_update_during_nonlethal_hit(f32 a0) {
239
boo_stop();
240
241
if (o->oTimer == 0) {
242
boo_set_move_yaw_for_during_hit(TRUE);
243
}
244
245
if (o->oTimer < 32) {
246
boo_move_during_hit(TRUE, sBooHitRotations[o->oTimer]/5000.0f * a0);
247
} else if (o->oTimer < 48) {
248
big_boo_shake_after_hit();
249
} else {
250
cur_obj_become_tangible();
251
boo_reset_after_hit();
252
253
o->oAction = 1;
254
255
return TRUE;
256
}
257
258
return FALSE;
259
}
260
261
// called every frame once mario lethally hits the boo until the boo is deleted,
262
// returns whether death is complete
263
static s32 boo_update_during_death(void) {
264
struct Object *parentBigBoo;
265
266
if (o->oTimer == 0) {
267
o->oForwardVel = 40.0f;
268
o->oMoveAngleYaw = gMarioObject->oMoveAngleYaw;
269
o->oBooDeathStatus = BOO_DEATH_STATUS_DYING;
270
o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
271
} else {
272
if (o->oTimer == 5) {
273
o->oBooTargetOpacity = 0;
274
}
275
276
if (o->oTimer > 30 || o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
277
spawn_mist_particles();
278
o->oBooDeathStatus = BOO_DEATH_STATUS_DEAD;
279
280
if (o->oBooParentBigBoo != NULL) {
281
parentBigBoo = o->oBooParentBigBoo;
282
283
#ifndef VERSION_JP
284
if (!cur_obj_has_behavior(bhvBoo)) {
285
parentBigBoo->oBigBooNumMinionBoosKilled++;
286
}
287
#else
288
parentBigBoo->oBigBooNumMinionBoosKilled++;
289
#endif
290
}
291
292
return TRUE;
293
}
294
}
295
296
o->oVelY = 5.0f;
297
o->oFaceAngleRoll += 0x800;
298
o->oFaceAngleYaw += 0x800;
299
300
return FALSE;
301
}
302
303
static s32 obj_has_attack_type(u32 attackType) {
304
if ((o->oInteractStatus & INT_STATUS_ATTACK_MASK) == attackType) {
305
return TRUE;
306
} else {
307
return FALSE;
308
}
309
}
310
311
static s32 boo_get_attack_status(void) {
312
s32 attackStatus = BOO_NOT_ATTACKED;
313
314
if (o->oInteractStatus & INT_STATUS_INTERACTED) {
315
if ((o->oInteractStatus & INT_STATUS_WAS_ATTACKED) && !obj_has_attack_type(ATTACK_FROM_ABOVE)) {
316
cur_obj_become_intangible();
317
318
o->oInteractStatus = 0;
319
320
cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_SHORT);
321
322
attackStatus = BOO_ATTACKED;
323
} else {
324
cur_obj_play_sound_2(SOUND_OBJ_BOO_BOUNCE_TOP);
325
326
o->oInteractStatus = 0;
327
328
attackStatus = BOO_BOUNCED_ON;
329
}
330
}
331
332
return attackStatus;
333
}
334
335
// boo idle/chasing movement?
336
static void boo_chase_mario(f32 a0, s16 a1, f32 a2) {
337
f32 sp1C;
338
s16 sp1A;
339
340
if (boo_vanish_or_appear()) {
341
o->oInteractType = 0x8000;
342
343
if (cur_obj_lateral_dist_from_mario_to_home() > 1500.0f) {
344
sp1A = cur_obj_angle_to_home();
345
} else {
346
sp1A = o->oAngleToMario;
347
}
348
349
cur_obj_rotate_yaw_toward(sp1A, a1);
350
o->oVelY = 0.0f;
351
352
if (mario_is_in_air_action() == 0) {
353
sp1C = o->oPosY - gMarioObject->oPosY;
354
if (a0 < sp1C && sp1C < 500.0f) {
355
o->oVelY = increment_velocity_toward_range(o->oPosY, gMarioObject->oPosY + 50.0f, 10.f, 2.0f);
356
}
357
}
358
359
cur_obj_set_vel_from_mario_vel(10.0f - o->oBooNegatedAggressiveness, a2);
360
361
if (o->oForwardVel != 0.0f) {
362
boo_oscillate(FALSE);
363
}
364
} else {
365
o->oInteractType = 0;
366
// why is boo_stop not used here
367
o->oForwardVel = 0.0f;
368
o->oVelY = 0.0f;
369
o->oGravity = 0.0f;
370
}
371
}
372
373
static void boo_act_0(void) {
374
o->activeFlags |= ACTIVE_FLAG_MOVE_THROUGH_GRATE;
375
376
if (o->oBehParams2ndByte == 2) {
377
o->oRoom = 10;
378
}
379
380
cur_obj_set_pos_to_home();
381
o->oMoveAngleYaw = o->oBooInitialMoveYaw;
382
boo_stop();
383
384
o->oBooParentBigBoo = cur_obj_nearest_object_with_behavior(bhvGhostHuntBigBoo);
385
o->oBooBaseScale = 1.0f;
386
o->oBooTargetOpacity = 0xFF;
387
388
if (boo_should_be_active()) {
389
// Condition is met if the object is bhvBalconyBigBoo or bhvMerryGoRoundBoo
390
if (o->oBehParams2ndByte == 2) {
391
o->oBooParentBigBoo = NULL;
392
o->oAction = 5;
393
} else {
394
o->oAction = 1;
395
}
396
}
397
}
398
399
static void boo_act_5(void) {
400
if (o->oTimer < 30) {
401
o->oVelY = 0.0f;
402
o->oForwardVel = 13.0f;
403
boo_oscillate(FALSE);
404
o->oWallHitboxRadius = 0.0f;
405
} else {
406
o->oAction = 1;
407
o->oWallHitboxRadius = 30.0f;
408
}
409
}
410
411
static void boo_act_1(void) {
412
s32 attackStatus;
413
414
if (o->oTimer == 0) {
415
o->oBooNegatedAggressiveness = -random_float() * 5.0f;
416
o->oBooTurningSpeed = (s32)(random_float() * 128.0f);
417
}
418
419
boo_chase_mario(-100.0f, o->oBooTurningSpeed + 0x180, 0.5f);
420
attackStatus = boo_get_attack_status();
421
422
if (boo_should_be_stopped()) {
423
o->oAction = 0;
424
}
425
426
if (attackStatus == BOO_BOUNCED_ON) {
427
o->oAction = 2;
428
}
429
430
if (attackStatus == BOO_ATTACKED) {
431
o->oAction = 3;
432
}
433
434
if (attackStatus == BOO_ATTACKED) {
435
create_sound_spawner(SOUND_OBJ_DYING_ENEMY1);
436
}
437
}
438
439
static void boo_act_2(void) {
440
if (boo_update_after_bounced_on(20.0f)) {
441
o->oAction = 1;
442
}
443
}
444
445
static void boo_act_3(void) {
446
if (boo_update_during_death()) {
447
if (o->oBehParams2ndByte != 0) {
448
obj_mark_for_deletion(o);
449
} else {
450
o->oAction = 4;
451
cur_obj_disable();
452
}
453
}
454
}
455
456
// Called when a Go on a Ghost Hunt boo dies
457
static void boo_act_4(void) {
458
s32 dialogID;
459
460
// If there are no remaining "minion" boos, show the dialog of the Big Boo
461
if (cur_obj_nearest_object_with_behavior(bhvGhostHuntBoo) == NULL) {
462
dialogID = DIALOG_108;
463
gBooDialogueWasSaid = 0;
464
} else {
465
dialogID = DIALOG_107;
466
}
467
468
if (!gBooDialogueWasSaid || dialogID == DIALOG_108) {
469
if (cur_obj_update_dialog(MARIO_DIALOG_LOOK_UP, DIALOG_FLAG_TEXT_DEFAULT, dialogID, 0)) {
470
create_sound_spawner(SOUND_OBJ_DYING_ENEMY1);
471
obj_mark_for_deletion(o);
472
473
if (dialogID == DIALOG_108) { // If the Big Boo should spawn, play the jingle
474
play_puzzle_jingle();
475
}
476
else if (configBetterEnemies)
477
gBooDialogueWasSaid = 1;
478
}
479
}
480
}
481
482
static void (*sBooActions[])(void) = {
483
boo_act_0,
484
boo_act_1,
485
boo_act_2,
486
boo_act_3,
487
boo_act_4,
488
boo_act_5
489
};
490
491
void bhv_boo_loop(void) {
492
//PARTIAL_UPDATE
493
494
cur_obj_update_floor_and_walls();
495
cur_obj_call_action_function(sBooActions);
496
cur_obj_move_standard(78);
497
boo_approach_target_opacity_and_update_scale();
498
499
if (obj_has_behavior(o->parentObj, bhvMerryGoRoundBooManager)) {
500
if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) {
501
o->parentObj->oMerryGoRoundBooManagerNumBoosKilled++;
502
}
503
}
504
505
o->oInteractStatus = 0;
506
}
507
508
static void big_boo_act_0(void) {
509
if (cur_obj_has_behavior(bhvBalconyBigBoo)) {
510
obj_set_secondary_camera_focus();
511
// number of killed boos set > 5 so that boo always loads
512
// redundant? this is also done in behavior_data.s
513
o->oBigBooNumMinionBoosKilled = 10;
514
}
515
516
o->oBooParentBigBoo = NULL;
517
518
#ifndef VERSION_JP
519
if (boo_should_be_active() && gDebugInfo[5][0] + 5 <= o->oBigBooNumMinionBoosKilled) {
520
#else
521
if (boo_should_be_active() && o->oBigBooNumMinionBoosKilled >= 5) {
522
#endif
523
o->oAction = 1;
524
525
cur_obj_set_pos_to_home();
526
o->oMoveAngleYaw = o->oBooInitialMoveYaw;
527
528
cur_obj_unhide();
529
530
o->oBooTargetOpacity = 0xFF;
531
o->oBooBaseScale = 3.0f;
532
if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) {
533
o->oHealth = 5;
534
}
535
else {
536
o->oHealth = 3;
537
}
538
539
cur_obj_scale(3.0f);
540
cur_obj_become_tangible();
541
} else {
542
cur_obj_hide();
543
cur_obj_become_intangible();
544
boo_stop();
545
}
546
}
547
548
static void big_boo_act_1(void) {
549
s32 attackStatus;
550
s16 sp22;
551
f32 sp1C;
552
553
if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) {
554
if (o->oHealth == 3) {
555
sp22 = 0x300; sp1C = 0.8f;
556
} else if (o->oHealth == 2) {
557
sp22 = 0x360; sp1C = 1.0f;
558
} else {
559
sp22 = 0x420; sp1C = 1.2f;
560
}
561
}
562
else {
563
if (o->oHealth == 3) {
564
sp22 = 0x180; sp1C = 0.5f;
565
} else if (o->oHealth == 2) {
566
sp22 = 0x240; sp1C = 0.6f;
567
} else {
568
sp22 = 0x300; sp1C = 0.8f;
569
}
570
}
571
572
boo_chase_mario(-100.0f, sp22, sp1C);
573
574
attackStatus = boo_get_attack_status();
575
576
// redundant; this check is in boo_should_be_stopped
577
if (cur_obj_has_behavior(bhvMerryGoRoundBigBoo)) {
578
if (!gMarioOnMerryGoRound) {
579
o->oAction = 0;
580
}
581
} else if (boo_should_be_stopped()) {
582
o->oAction = 0;
583
}
584
585
if (attackStatus == BOO_BOUNCED_ON) {
586
o->oAction = 2;
587
}
588
589
if (attackStatus == BOO_ATTACKED) {
590
o->oAction = 3;
591
}
592
593
if (attackStatus == 1) {
594
create_sound_spawner(SOUND_OBJ_THWOMP);
595
}
596
}
597
598
static void big_boo_act_2(void) {
599
if (boo_update_after_bounced_on(20.0f)) {
600
o->oAction = 1;
601
}
602
}
603
604
static void big_boo_spawn_ghost_hunt_star(void) {
605
spawn_default_star(980.0f, 1100.0f, 250.0f);
606
}
607
608
static void big_boo_spawn_balcony_star(void) {
609
spawn_default_star(700.0f, 3200.0f, 1900.0f);
610
}
611
612
static void big_boo_spawn_merry_go_round_star(void) {
613
struct Object *merryGoRound;
614
615
spawn_default_star(-1600.0f, -2100.0f, 205.0f);
616
617
merryGoRound = cur_obj_nearest_object_with_behavior(bhvMerryGoRound);
618
619
if (merryGoRound != NULL) {
620
merryGoRound->oMerryGoRoundStopped = TRUE;
621
}
622
}
623
624
static void big_boo_act_3(void) {
625
if (o->oTimer == 0) {
626
o->oHealth--;
627
}
628
629
if (o->oHealth == 0) {
630
if (boo_update_during_death()) {
631
cur_obj_disable();
632
633
o->oAction = 4;
634
635
obj_set_angle(o, 0, 0, 0);
636
637
if (o->oBehParams2ndByte == 0) {
638
big_boo_spawn_ghost_hunt_star();
639
} else if (o->oBehParams2ndByte == 1) {
640
big_boo_spawn_merry_go_round_star();
641
} else {
642
big_boo_spawn_balcony_star();
643
}
644
}
645
} else {
646
if (o->oTimer == 0) {
647
spawn_mist_particles();
648
o->oBooBaseScale -= 0.5;
649
}
650
651
if (big_boo_update_during_nonlethal_hit(40.0f)) {
652
o->oAction = 1;
653
}
654
}
655
}
656
657
static void big_boo_act_4(void) {
658
#ifndef VERSION_JP
659
boo_stop();
660
#endif
661
662
if (o->oBehParams2ndByte == 0) {
663
obj_set_pos(o, 973, 0, 626);
664
665
if (o->oTimer > 60 && o->oDistanceToMario < 600.0f) {
666
obj_set_pos(o, 973, 0, 717);
667
668
spawn_object_relative(0, 0, 0, 0, o, MODEL_BBH_STAIRCASE_STEP, bhvBooStaircase);
669
spawn_object_relative(1, 0, 0, -200, o, MODEL_BBH_STAIRCASE_STEP, bhvBooStaircase);
670
spawn_object_relative(2, 0, 0, 200, o, MODEL_BBH_STAIRCASE_STEP, bhvBooStaircase);
671
672
obj_mark_for_deletion(o);
673
}
674
} else {
675
obj_mark_for_deletion(o);
676
}
677
}
678
679
static void (*sBooGivingStarActions[])(void) = {
680
big_boo_act_0,
681
big_boo_act_1,
682
big_boo_act_2,
683
big_boo_act_3,
684
big_boo_act_4
685
};
686
687
void bhv_big_boo_loop(void) {
688
//PARTIAL_UPDATE
689
690
obj_set_hitbox(o, &sBooGivingStarHitbox);
691
692
o->oGraphYOffset = o->oBooBaseScale * 60.0f;
693
694
cur_obj_update_floor_and_walls();
695
cur_obj_call_action_function(sBooGivingStarActions);
696
cur_obj_move_standard(78);
697
698
boo_approach_target_opacity_and_update_scale();
699
o->oInteractStatus = 0;
700
}
701
702
static void boo_with_cage_act_0(void) {
703
o->oBooParentBigBoo = NULL;
704
o->oBooTargetOpacity = 0xFF;
705
o->oBooBaseScale = 2.0f;
706
707
cur_obj_scale(2.0f);
708
cur_obj_become_tangible();
709
710
if (boo_should_be_active()) {
711
o->oAction = 1;
712
}
713
}
714
715
static void boo_with_cage_act_1(void) {
716
s32 attackStatus;
717
718
boo_chase_mario(100.0f, 512, 0.5f);
719
720
attackStatus = boo_get_attack_status();
721
722
if (boo_should_be_stopped()) {
723
o->oAction = 0;
724
}
725
726
if (attackStatus == BOO_BOUNCED_ON) {
727
o->oAction = 2;
728
}
729
730
if (attackStatus == BOO_ATTACKED) {
731
o->oAction = 3;
732
}
733
}
734
735
static void boo_with_cage_act_2(void) {
736
if (boo_update_after_bounced_on(20.0f)) {
737
o->oAction = 1;
738
}
739
}
740
741
static void boo_with_cage_act_3(void) {
742
if (boo_update_during_death()) {
743
obj_mark_for_deletion(o);
744
}
745
}
746
747
void bhv_boo_with_cage_init(void) {
748
struct Object* cage;
749
750
if (gHudDisplay.stars < 12) {
751
obj_mark_for_deletion(o);
752
} else {
753
cage = spawn_object(o, MODEL_HAUNTED_CAGE, bhvBooCage);
754
cage->oBehParams = o->oBehParams;
755
}
756
}
757
758
static void (*sBooWithCageActions[])(void) = {
759
boo_with_cage_act_0,
760
boo_with_cage_act_1,
761
boo_with_cage_act_2,
762
boo_with_cage_act_3
763
};
764
765
void bhv_boo_with_cage_loop(void)
766
{
767
//PARTIAL_UPDATE
768
769
cur_obj_update_floor_and_walls();
770
cur_obj_call_action_function(sBooWithCageActions);
771
cur_obj_move_standard(78);
772
773
boo_approach_target_opacity_and_update_scale();
774
o->oInteractStatus = 0;
775
}
776
777
void bhv_merry_go_round_boo_manager_loop(void) {
778
switch (o->oAction) {
779
case 0:
780
if (o->oDistanceToMario < 1000.0f) {
781
if (o->oMerryGoRoundBooManagerNumBoosKilled < 5) {
782
if (o->oMerryGoRoundBooManagerNumBoosSpawned != 5) {
783
if (o->oMerryGoRoundBooManagerNumBoosSpawned - o->oMerryGoRoundBooManagerNumBoosKilled < 2) {
784
spawn_object(o, MODEL_BOO, bhvMerryGoRoundBoo);
785
o->oMerryGoRoundBooManagerNumBoosSpawned++;
786
}
787
}
788
789
o->oAction++;
790
}
791
792
if (o->oMerryGoRoundBooManagerNumBoosKilled > 4) {
793
struct Object *boo = spawn_object(o, MODEL_BOO, bhvMerryGoRoundBigBoo);
794
obj_copy_behavior_params(boo, o);
795
796
o->oAction = 2;
797
798
#ifndef VERSION_JP
799
play_puzzle_jingle();
800
#else
801
play_sound(SOUND_GENERAL2_RIGHT_ANSWER, gGlobalSoundSource);
802
#endif
803
}
804
}
805
806
break;
807
case 1:
808
if (o->oTimer > 60) {
809
o->oAction = 0;
810
}
811
812
break;
813
case 2:
814
break;
815
}
816
}
817
818
void obj_set_secondary_camera_focus(void) {
819
gSecondCameraFocus = o;
820
}
821
822
void bhv_animated_texture_loop(void) {
823
cur_obj_set_pos_to_home_with_debug();
824
}
825
826
void bhv_boo_in_castle_loop(void) {
827
s16 targetAngle;
828
829
o->oBooBaseScale = 2.0f;
830
831
if (o->oAction == 0) {
832
cur_obj_hide();
833
834
if (gHudDisplay.stars < 12) {
835
obj_mark_for_deletion(o);
836
}
837
838
if (gMarioCurrentRoom == 1) {
839
o->oAction++;
840
}
841
} else if (o->oAction == 1) {
842
cur_obj_unhide();
843
844
o->oOpacity = 180;
845
846
if (o->oTimer == 0) {
847
cur_obj_scale(o->oBooBaseScale);
848
}
849
850
if (o->oDistanceToMario < 1000.0f) {
851
o->oAction++;
852
cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_LONG);
853
}
854
855
o->oForwardVel = 0.0f;
856
targetAngle = o->oAngleToMario;
857
} else {
858
cur_obj_forward_vel_approach_upward(32.0f, 1.0f);
859
860
o->oHomeX = -1000.0f;
861
o->oHomeZ = -9000.0f;
862
863
targetAngle = cur_obj_angle_to_home();
864
865
if (o->oPosZ < -5000.0f) {
866
if (o->oOpacity > 0) {
867
o->oOpacity -= 20;
868
} else {
869
o->oOpacity = 0;
870
}
871
}
872
873
if (o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) {
874
o->oAction = 1;
875
}
876
}
877
878
o->oVelY = 0.0f;
879
880
targetAngle = cur_obj_angle_to_home();
881
882
cur_obj_rotate_yaw_toward(targetAngle, 0x5A8);
883
boo_oscillate(TRUE);
884
cur_obj_move_using_fvel_and_gravity();
885
}
886
887
void bhv_boo_staircase(void) {
888
f32 targetY;
889
890
switch (o->oBehParams2ndByte) {
891
case 1:
892
targetY = 0.0f;
893
break;
894
case 0:
895
targetY = -206.0f;
896
break;
897
case 2:
898
targetY = -413.0f;
899
break;
900
}
901
902
switch(o->oAction) {
903
case 0:
904
o->oPosY = o->oHomeY - 620.0f;
905
o->oAction++;
906
// fallthrough
907
case 1:
908
o->oPosY += 8.0f;
909
cur_obj_play_sound_1(SOUND_ENV_ELEVATOR2);
910
911
if (o->oPosY > targetY) {
912
o->oPosY = targetY;
913
o->oAction++;
914
}
915
916
break;
917
case 2:
918
if (o->oTimer == 0) {
919
cur_obj_play_sound_2(SOUND_GENERAL_UNKNOWN4_LOWPRIO);
920
}
921
922
if (jiggle_bbh_stair(o->oTimer)) {
923
o->oAction++;
924
}
925
926
break;
927
case 3:
928
if (o->oTimer == 0 && o->oBehParams2ndByte == 1) {
929
play_puzzle_jingle();
930
}
931
932
break;
933
}
934
}
935