/
mario_actions_cutscene.c
2754 lines (2374 loc) · 88.7 KB
/
mario_actions_cutscene.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <PR/ultratypes.h>
#include "prevent_bss_reordering.h"
#include "sm64.h"
#include "area.h"
#include "audio/external.h"
#include "behavior_data.h"
#include "camera.h"
#include "dialog_ids.h"
#include "engine/behavior_script.h"
#include "engine/graph_node.h"
#include "engine/math_util.h"
#include "engine/surface_collision.h"
#include "game_init.h"
#include "gfx_dimensions.h"
#include "ingame_menu.h"
#include "interaction.h"
#include "level_table.h"
#include "level_update.h"
#include "mario.h"
#include "mario_actions_cutscene.h"
#include "mario_actions_moving.h"
#include "mario_step.h"
#include "moving_texture.h"
#include "object_helpers.h"
#include "object_list_processor.h"
#include "save_file.h"
#include "seq_ids.h"
#include "sound_init.h"
#include "rumble_init.h"
static struct Object *sIntroWarpPipeObj;
static struct Object *sEndPeachObj;
static struct Object *sEndRightToadObj;
static struct Object *sEndLeftToadObj;
static struct Object *sEndJumboStarObj;
static UNUSED s32 sUnused;
static s16 sEndPeachAnimation;
static s16 sEndToadAnims[2];
static Vp sEndCutsceneVp = { { { 640, 480, 511, 0 }, { 640, 480, 511, 0 } } };
static struct CreditsEntry *sDispCreditsEntry = NULL;
// related to peach gfx?
static s8 D_8032CBE4 = 0;
static s8 D_8032CBE8 = 0;
static s8 D_8032CBEC[7] = { 2, 3, 2, 1, 2, 3, 2 };
static u8 sStarsNeededForDialog[] = { 1, 3, 8, 30, 50, 70 };
/**
* Data for the jumbo star cutscene. It specifies the flight path after triple
* jumping. Each entry is one keyframe.
* The first number is playback speed, 1000 is the maximum and means it lasts
* 1 frame. 20 means that it lasts 1000/20 = 50 frames.
* Speed 0 marks the last keyframe. Since the cubic spline looks 3 keyframes
* ahead, there should be at least 2 more entries afterwards.
* The last three numbers of each entry are x, y and z coordinates of points
* that define the curve.
*/
static Vec4s sJumboStarKeyframes[27] = {
{ 20, 0, 678, -2916 }, { 30, 0, 680, -3500 }, { 40, 1000, 700, -4000 },
{ 50, 2500, 750, -3500 }, { 50, 3500, 800, -2000 }, { 50, 4000, 850, 0 },
{ 50, 3500, 900, 2000 }, { 50, 2000, 950, 3500 }, { 50, 0, 1000, 4000 },
{ 50, -2000, 1050, 3500 }, { 50, -3500, 1100, 2000 }, { 50, -4000, 1150, 0 },
{ 50, -3500, 1200, -2000 }, { 50, -2000, 1250, -3500 }, { 50, 0, 1300, -4000 },
{ 50, 2000, 1350, -3500 }, { 50, 3500, 1400, -2000 }, { 50, 4000, 1450, 0 },
{ 50, 3500, 1500, 2000 }, { 50, 2000, 1600, 3500 }, { 50, 0, 1700, 4000 },
{ 50, -2000, 1800, 3500 }, { 50, -3500, 1900, 2000 }, { 30, -4000, 2000, 0 },
{ 0, -3500, 2100, -2000 }, { 0, -2000, 2200, -3500 }, { 0, 0, 2300, -4000 },
};
/**
* get_credits_str_width: Calculate width of a Credits String
* Loop over each character in a credits string and increment the length. If the
* character is a space, increment by 4; otherwise increment by 7. Once the next
* character is a null character (equal to 0), stop counting the length since
* that's the end of the string.
*/
s32 get_credits_str_width(char *str) {
u32 c;
s32 length = 0;
while ((c = *str++) != 0) {
length += (c == ' ' ? 4 : 7);
}
return length;
}
#define CREDIT_TEXT_MARGIN_X ((s32)(GFX_DIMENSIONS_ASPECT_RATIO * 21))
#define CREDIT_TEXT_X_LEFT GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(CREDIT_TEXT_MARGIN_X)
#define CREDIT_TEXT_X_RIGHT GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(CREDIT_TEXT_MARGIN_X)
/**
* print_displaying_credits_entry: Print the current displaying Credits Entry
* Called in render_game. This function checks if sDispCreditsEntry points to a
* credits entry (see act_credits_cutscene), and if so, display it. The reason
* this is called every frame in render_game is because the credits need to
* display on top of everything else.
* To print a credits entry, we take the first character of the first string,
* subtract the value of the 0 character, and use that as the number of lines to
* print, excluding the title. Then, we print the title (after advancing the
* pointer by 1) at X 28, Y either 28 or 172, depending on the print at bottom
* flag. Finally, we print each line using the number of lines previously stored
* as a counter, and increase the Y value by either the constant 16 (JP only) or
* by the value of lineHeight.
*/
void print_displaying_credits_entry(void) {
char **currStrPtr;
char *titleStr;
s16 numLines;
s16 strY;
#ifndef VERSION_JP
s16 lineHeight;
#endif
if (sDispCreditsEntry != NULL) {
currStrPtr = (char **) sDispCreditsEntry->unk0C;
titleStr = *currStrPtr++;
numLines = *titleStr++ - '0';
strY = (sDispCreditsEntry->unk02 & 0x20 ? 28 : 172) + (numLines == 1) * 16;
#ifndef VERSION_JP
lineHeight = 16;
#endif
dl_rgba16_begin_cutscene_msg_fade();
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY, titleStr);
#ifndef VERSION_JP
switch (numLines) {
case 4:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 24, *currStrPtr++);
numLines = 2;
lineHeight = 24;
break;
case 5:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++);
numLines = 3;
break;
#ifdef VERSION_EU
case 6:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++);
numLines = 3;
break;
case 7:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++);
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++);
numLines = 3;
break;
#endif
}
#endif
while (numLines-- > 0) {
print_credits_str_ascii(CREDIT_TEXT_X_RIGHT - get_credits_str_width(*currStrPtr), strY, *currStrPtr);
#ifdef VERSION_JP
strY += 16;
#else
strY += lineHeight;
#endif
currStrPtr++;
}
dl_rgba16_stop_cutscene_msg_fade();
sDispCreditsEntry = NULL;
}
}
void bhv_end_peach_loop(void) {
cur_obj_init_animation_with_sound(sEndPeachAnimation);
if (cur_obj_check_if_near_animation_end()) {
// anims: 0-3, 4, 5, 6-8, 9, 10, 11
if (sEndPeachAnimation < 3 || sEndPeachAnimation == 6 || sEndPeachAnimation == 7) {
sEndPeachAnimation++;
}
}
}
void bhv_end_toad_loop(void) {
s32 toadAnimIndex = (gCurrentObject->oPosX >= 0.0f);
cur_obj_init_animation_with_sound(sEndToadAnims[toadAnimIndex]);
if (cur_obj_check_if_near_animation_end()) {
// 0-1, 2-3, 4, 5, 6, 7
if (sEndToadAnims[toadAnimIndex] == 0 || sEndToadAnims[toadAnimIndex] == 2) {
sEndToadAnims[toadAnimIndex]++;
}
}
}
// Geo switch case function for controlling Peach's eye state.
s32 geo_switch_peach_eyes(s32 run, struct GraphNode *node, UNUSED s32 a2) {
struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node;
s16 timer;
if (run == TRUE) {
if (D_8032CBE4 == 0) {
timer = (gAreaUpdateCounter + 0x20) >> 1 & 0x1F;
if (timer < 7) {
switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBEC[timer];
} else {
switchCase->selectedCase = D_8032CBE8 * 4 + 1;
}
} else {
switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBE4 - 1;
}
}
return 0;
}
// unused
UNUSED static void stub_is_textbox_active(u16 *arg) {
if (get_dialog_id() == DIALOG_NONE) {
*arg = 0;
}
}
/**
* get_star_collection_dialog: Determine what dialog should show when Mario
* collects a star.
* Determines if Mario has collected enough stars to get a dialog for it, and
* if so, return the dialog ID. Otherwise, return 0. A dialog is returned if
* numStars has reached a milestone and prevNumStarsForDialog has not reached it.
*/
s32 get_star_collection_dialog(struct MarioState *m) {
s32 i;
s32 dialogID = 0;
s32 numStarsRequired;
for (i = 0; i < ARRAY_COUNT(sStarsNeededForDialog); i++) {
numStarsRequired = sStarsNeededForDialog[i];
if (m->prevNumStarsForDialog < numStarsRequired && m->numStars >= numStarsRequired) {
dialogID = i + DIALOG_141;
break;
}
}
m->prevNumStarsForDialog = m->numStars;
return dialogID;
}
// save menu handler
void handle_save_menu(struct MarioState *m) {
s32 dialogID;
// wait for the menu to show up
if (is_anim_past_end(m) && gSaveOptSelectIndex != MENU_OPT_NONE) {
// save and continue / save and quit
if (gSaveOptSelectIndex == MENU_OPT_SAVE_AND_CONTINUE || gSaveOptSelectIndex == MENU_OPT_SAVE_AND_QUIT) {
save_file_do_save(gCurrSaveFileNum - 1);
if (gSaveOptSelectIndex == MENU_OPT_SAVE_AND_QUIT) {
fade_into_special_warp(-2, 0); // reset game
}
}
// not quitting
if (gSaveOptSelectIndex != MENU_OPT_SAVE_AND_QUIT) {
disable_time_stop();
m->faceAngle[1] += 0x8000;
// figure out what dialog to show, if we should
dialogID = get_star_collection_dialog(m);
if (dialogID) {
play_peachs_jingle();
// look up for dialog
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID);
} else {
set_mario_action(m, ACT_IDLE, 0);
}
}
}
}
/**
* spawn_obj_at_mario_rel_yaw: Spawns object at Mario with relative yaw.
* Spawns object with given behavior and model and copies over Mario's position
* and yaw plus relative yaw.
*/
struct Object *spawn_obj_at_mario_rel_yaw(struct MarioState *m, s32 model, const BehaviorScript *behavior, s16 relYaw) {
struct Object *o = spawn_object(m->marioObj, model, behavior);
o->oFaceAngleYaw = m->faceAngle[1] + relYaw;
o->oPosX = m->pos[0];
o->oPosY = m->pos[1];
o->oPosZ = m->pos[2];
return o;
}
/**
* cutscene_take_cap_off: Put Mario's cap on.
* Clears "cap on head" flag, sets "cap in hand" flag, plays sound
* SOUND_ACTION_UNKNOWN43D.
*/
void cutscene_take_cap_off(struct MarioState *m) {
m->flags &= ~MARIO_CAP_ON_HEAD;
m->flags |= MARIO_CAP_IN_HAND;
play_sound(SOUND_ACTION_UNKNOWN43D, m->marioObj->header.gfx.cameraToObject);
}
/**
* cutscene_put_cap_on: Put Mario's cap on.
* Clears "cap in hand" flag, sets "cap on head" flag, plays sound
* SOUND_ACTION_UNKNOWN43E.
*/
void cutscene_put_cap_on(struct MarioState *m) {
m->flags &= ~MARIO_CAP_IN_HAND;
m->flags |= MARIO_CAP_ON_HEAD;
play_sound(SOUND_ACTION_UNKNOWN43E, m->marioObj->header.gfx.cameraToObject);
}
/**
* mario_ready_to_speak: Determine if Mario is able to speak to a NPC
* The following conditions must be met in order for Mario to be considered
* ready to speak.
* 1: Mario's action must be in the stationary or moving action groups, or if
* not, he must be in the "waiting for dialog" state.
* 2: Mario mat not be riding a shell or be invulnerable.
* 3: Mario must not be in first person mode.
*/
s32 mario_ready_to_speak(void) {
u32 actionGroup = gMarioState->action & ACT_GROUP_MASK;
s32 isReadyToSpeak = FALSE;
if ((gMarioState->action == ACT_WAITING_FOR_DIALOG || actionGroup == ACT_GROUP_STATIONARY
|| actionGroup == ACT_GROUP_MOVING)
&& (!(gMarioState->action & (ACT_FLAG_RIDING_SHELL | ACT_FLAG_INVULNERABLE))
&& gMarioState->action != ACT_FIRST_PERSON)) {
isReadyToSpeak = TRUE;
}
return isReadyToSpeak;
}
// (can) place Mario in dialog?
// initiate dialog?
// return values:
// 0 = not in dialog
// 1 = starting dialog
// 2 = speaking
s32 set_mario_npc_dialog(s32 actionArg) {
s32 dialogState = MARIO_DIALOG_STATUS_NONE;
// in dialog
if (gMarioState->action == ACT_READING_NPC_DIALOG) {
if (gMarioState->actionState < 8) {
dialogState = MARIO_DIALOG_STATUS_START; // starting dialog
}
if (gMarioState->actionState == 8) {
if (actionArg == MARIO_DIALOG_STOP) {
gMarioState->actionState++; // exit dialog
} else {
dialogState = MARIO_DIALOG_STATUS_SPEAK;
}
}
} else if (actionArg != 0 && mario_ready_to_speak()) {
gMarioState->usedObj = gCurrentObject;
set_mario_action(gMarioState, ACT_READING_NPC_DIALOG, actionArg);
dialogState = MARIO_DIALOG_STATUS_START; // starting dialog
}
return dialogState;
}
// actionargs:
// 1 : no head turn
// 2 : look up
// 3 : look down
// actionstate values:
// 0 - 7: looking toward npc
// 8: in dialog
// 9 - 22: looking away from npc
// 23: end
s32 act_reading_npc_dialog(struct MarioState *m) {
s32 headTurnAmount = 0;
s16 angleToNPC;
if (m->actionArg == MARIO_DIALOG_LOOK_UP) {
headTurnAmount = -1024;
}
if (m->actionArg == MARIO_DIALOG_LOOK_DOWN) {
headTurnAmount = 384;
}
if (m->actionState < 8) {
// turn to NPC
angleToNPC = mario_obj_angle_to_object(m, m->usedObj);
m->faceAngle[1] =
angleToNPC - approach_s32((angleToNPC - m->faceAngle[1]) << 16 >> 16, 0, 2048, 2048);
// turn head to npc
m->actionTimer += headTurnAmount;
// set animation
set_mario_animation(m, m->heldObj == NULL ? MARIO_ANIM_FIRST_PERSON
: MARIO_ANIM_IDLE_WITH_LIGHT_OBJ);
} else if (m->actionState >= 9 && m->actionState < 17) {
// look back from facing NPC
m->actionTimer -= headTurnAmount;
} else if (m->actionState == 23) {
if (m->flags & MARIO_CAP_IN_HAND) {
set_mario_action(m, ACT_PUTTING_ON_CAP, 0);
} else {
set_mario_action(m, m->heldObj == NULL ? ACT_IDLE : ACT_HOLD_IDLE, 0);
}
}
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
if (m->actionState != 8) {
m->actionState++;
}
return FALSE;
}
// puts Mario in a state where he's waiting for (npc) dialog; doesn't do much
s32 act_waiting_for_dialog(struct MarioState *m) {
set_mario_animation(m, m->heldObj == NULL ? MARIO_ANIM_FIRST_PERSON
: MARIO_ANIM_IDLE_WITH_LIGHT_OBJ);
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
return FALSE;
}
// makes Mario disappear and triggers warp
s32 act_disappeared(struct MarioState *m) {
set_mario_animation(m, MARIO_ANIM_A_POSE);
stop_and_set_height_to_floor(m);
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
if (m->actionArg) {
m->actionArg--;
if ((m->actionArg & 0xFFFF) == 0) {
level_trigger_warp(m, m->actionArg >> 16);
}
}
return FALSE;
}
s32 act_reading_automatic_dialog(struct MarioState *m) {
u32 actionArg;
m->actionState++;
if (m->actionState == 2) {
enable_time_stop();
}
if (m->actionState < 9) {
set_mario_animation(m, m->prevAction == ACT_STAR_DANCE_WATER ? MARIO_ANIM_WATER_IDLE
: MARIO_ANIM_FIRST_PERSON);
// always look up for automatic dialogs
m->actionTimer -= 1024;
} else {
// set Mario dialog
if (m->actionState == 9) {
actionArg = m->actionArg;
if (GET_HIGH_U16_OF_32(actionArg) == 0) {
create_dialog_box(GET_LOW_U16_OF_32(actionArg));
} else {
create_dialog_box_with_var(GET_HIGH_U16_OF_32(actionArg), GET_LOW_U16_OF_32(actionArg));
}
}
// wait until dialog is done
else if (m->actionState == 10) {
if (get_dialog_id() >= 0) {
m->actionState--;
}
}
// look back down
else if (m->actionState < 19) {
m->actionTimer += 1024;
}
// finished action
else if (m->actionState == 25) {
disable_time_stop();
if (gNeverEnteredCastle) {
gNeverEnteredCastle = FALSE;
play_cutscene_music(SEQUENCE_ARGS(0, SEQ_LEVEL_INSIDE_CASTLE));
}
if (m->prevAction == ACT_STAR_DANCE_WATER) {
set_mario_action(m, ACT_WATER_IDLE, 0); // 100c star?
} else {
// make Mario walk into door after star dialog
set_mario_action(m, m->prevAction == ACT_UNLOCKING_STAR_DOOR ? ACT_WALKING : ACT_IDLE,
0);
}
}
}
// apply head turn
vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
return FALSE;
}
s32 act_reading_sign(struct MarioState *m) {
struct Object *marioObj = m->marioObj;
play_sound_if_no_flag(m, SOUND_ACTION_READ_SIGN, MARIO_ACTION_SOUND_PLAYED);
switch (m->actionState) {
// start dialog
case 0:
trigger_cutscene_dialog(1);
enable_time_stop();
// reading sign
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
m->actionState = 1;
// intentional fall through
// turn toward sign
case 1:
m->faceAngle[1] += marioObj->oMarioPoleUnk108 / 11;
m->pos[0] += marioObj->oMarioReadingSignDPosX / 11.0f;
m->pos[2] += marioObj->oMarioReadingSignDPosZ / 11.0f;
// create the text box
if (m->actionTimer++ == 10) {
create_dialog_inverted_box(m->usedObj->oBehParams2ndByte);
m->actionState = 2;
}
break;
// in dialog
case 2:
// dialog finished
if (gCamera->cutscene == 0) {
disable_time_stop();
set_mario_action(m, ACT_IDLE, 0);
}
break;
}
vec3f_copy(marioObj->header.gfx.pos, m->pos);
vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
return FALSE;
}
s32 act_debug_free_move(struct MarioState *m) {
struct Surface *surf;
f32 floorHeight;
Vec3f pos;
f32 speed;
u32 action;
// integer immediates, generates convert instructions for some reason
speed = gPlayer1Controller->buttonDown & B_BUTTON ? 4 : 1;
if (gPlayer1Controller->buttonDown & L_TRIG) {
speed = 0.01f;
}
set_mario_animation(m, MARIO_ANIM_A_POSE);
vec3f_copy(pos, m->pos);
if (gPlayer1Controller->buttonDown & U_JPAD) {
pos[1] += 16.0f * speed;
}
if (gPlayer1Controller->buttonDown & D_JPAD) {
pos[1] -= 16.0f * speed;
}
if (m->intendedMag > 0) {
pos[0] += 32.0f * speed * sins(m->intendedYaw);
pos[2] += 32.0f * speed * coss(m->intendedYaw);
}
resolve_and_return_wall_collisions(pos, 60.0f, 50.0f);
floorHeight = find_floor(pos[0], pos[1], pos[2], &surf);
if (surf != NULL) {
if (pos[1] < floorHeight) {
pos[1] = floorHeight;
}
vec3f_copy(m->pos, pos);
}
m->faceAngle[1] = m->intendedYaw;
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
if (gPlayer1Controller->buttonPressed == A_BUTTON) {
if (m->pos[1] <= m->waterLevel - 100) {
action = ACT_WATER_IDLE;
} else {
action = ACT_IDLE;
}
set_mario_action(m, action, 0);
}
return FALSE;
}
void general_star_dance_handler(struct MarioState *m, s32 isInWater) {
s32 dialogID;
if (m->actionState == 0) {
switch (++m->actionTimer) {
case 1:
spawn_object(m->marioObj, MODEL_STAR, bhvCelebrationStar);
disable_background_sound();
if (m->actionArg & 1) {
play_course_clear();
} else {
if (gCurrLevelNum == LEVEL_BOWSER_1 || gCurrLevelNum == LEVEL_BOWSER_2) {
play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_KEY), 0);
} else {
play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_STAR), 0);
}
}
break;
case 42:
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
break;
case 80:
if (!(m->actionArg & 1)) {
level_trigger_warp(m, WARP_OP_STAR_EXIT);
} else {
enable_time_stop();
create_dialog_box_with_response(gLastCompletedStarNum == 7 ? DIALOG_013 : DIALOG_014);
m->actionState = 1;
}
break;
}
} else if (m->actionState == 1 && gDialogResponse != DIALOG_RESPONSE_NONE) {
if (gDialogResponse == DIALOG_RESPONSE_YES) {
save_file_do_save(gCurrSaveFileNum - 1);
}
m->actionState = 2;
} else if (m->actionState == 2 && is_anim_at_end(m)) {
disable_time_stop();
enable_background_sound();
dialogID = get_star_collection_dialog(m);
if (dialogID) {
// look up for dialog
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID);
} else {
set_mario_action(m, isInWater ? ACT_WATER_IDLE : ACT_IDLE, 0);
}
}
}
s32 act_star_dance(struct MarioState *m) {
m->faceAngle[1] = m->area->camera->yaw;
set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_STAR_DANCE
: MARIO_ANIM_STAR_DANCE);
general_star_dance_handler(m, 0);
if (m->actionState != 2 && m->actionTimer >= 40) {
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
}
stop_and_set_height_to_floor(m);
return FALSE;
}
s32 act_star_dance_water(struct MarioState *m) {
m->faceAngle[1] = m->area->camera->yaw;
set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_WATER_STAR_DANCE
: MARIO_ANIM_WATER_STAR_DANCE);
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
general_star_dance_handler(m, 1);
if (m->actionState != 2 && m->actionTimer >= 62) {
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
}
return FALSE;
}
s32 act_fall_after_star_grab(struct MarioState *m) {
if (m->pos[1] < m->waterLevel - 130) {
play_sound(SOUND_ACTION_UNKNOWN430, m->marioObj->header.gfx.cameraToObject);
m->particleFlags |= PARTICLE_WATER_SPLASH;
return set_mario_action(m, ACT_STAR_DANCE_WATER, m->actionArg);
}
if (perform_air_step(m, 1) == AIR_STEP_LANDED) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
set_mario_action(m, m->actionArg & 1 ? ACT_STAR_DANCE_NO_EXIT : ACT_STAR_DANCE_EXIT,
m->actionArg);
}
set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);
return FALSE;
}
s32 common_death_handler(struct MarioState *m, s32 animation, s32 frameToDeathWarp) {
s32 animFrame = set_mario_animation(m, animation);
if (animFrame == frameToDeathWarp) {
level_trigger_warp(m, WARP_OP_DEATH);
}
m->marioBodyState->eyeState = MARIO_EYES_DEAD;
stop_and_set_height_to_floor(m);
return animFrame;
}
s32 act_standing_death(struct MarioState *m) {
if (m->input & INPUT_IN_POISON_GAS) {
return set_mario_action(m, ACT_SUFFOCATION, 0);
}
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
common_death_handler(m, MARIO_ANIM_DYING_FALL_OVER, 80);
if (m->marioObj->header.gfx.animInfo.animFrame == 77) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
}
return FALSE;
}
s32 act_electrocution(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
common_death_handler(m, MARIO_ANIM_ELECTROCUTION, 43);
return FALSE;
}
s32 act_suffocation(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
common_death_handler(m, MARIO_ANIM_SUFFOCATING, 86);
return FALSE;
}
s32 act_death_on_back(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
if (common_death_handler(m, MARIO_ANIM_DYING_ON_BACK, 54) == 40) {
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
}
return FALSE;
}
s32 act_death_on_stomach(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
if (common_death_handler(m, MARIO_ANIM_DYING_ON_STOMACH, 37) == 37) {
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
}
return FALSE;
}
s32 act_quicksand_death(struct MarioState *m) {
if (m->actionState == 0) {
set_mario_animation(m, MARIO_ANIM_DYING_IN_QUICKSAND);
set_anim_to_frame(m, 60);
m->actionState = 1;
}
if (m->actionState == 1) {
if (m->quicksandDepth >= 100.0f) {
play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED);
}
if ((m->quicksandDepth += 5.0f) >= 180.0f) {
level_trigger_warp(m, WARP_OP_DEATH);
m->actionState = 2;
}
}
stationary_ground_step(m);
play_sound(SOUND_MOVING_QUICKSAND_DEATH, m->marioObj->header.gfx.cameraToObject);
return FALSE;
}
s32 act_eaten_by_bubba(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
set_mario_animation(m, MARIO_ANIM_A_POSE);
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
m->health = 0xFF;
if (m->actionTimer++ == 60) {
level_trigger_warp(m, WARP_OP_DEATH);
}
return FALSE;
}
// set animation and forwardVel; when perform_air_step returns AIR_STEP_LANDED,
// set the new action
s32 launch_mario_until_land(struct MarioState *m, s32 endAction, s32 animation, f32 forwardVel) {
s32 airStepLanded;
mario_set_forward_vel(m, forwardVel);
set_mario_animation(m, animation);
airStepLanded = (perform_air_step(m, 0) == AIR_STEP_LANDED);
if (airStepLanded) {
set_mario_action(m, endAction, 0);
}
return airStepLanded;
}
s32 act_unlocking_key_door(struct MarioState *m) {
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
m->pos[0] = m->usedObj->oPosX + coss(m->faceAngle[1]) * 75.0f;
m->pos[2] = m->usedObj->oPosZ + sins(m->faceAngle[1]) * 75.0f;
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
if (m->actionTimer == 0) {
spawn_obj_at_mario_rel_yaw(m, MODEL_BOWSER_KEY_CUTSCENE, bhvBowserKeyUnlockDoor, 0);
set_mario_animation(m, MARIO_ANIM_UNLOCK_DOOR);
}
switch (m->marioObj->header.gfx.animInfo.animFrame) {
case 79:
play_sound(SOUND_GENERAL_DOOR_INSERT_KEY, m->marioObj->header.gfx.cameraToObject);
break;
case 111:
play_sound(SOUND_GENERAL_DOOR_TURN_KEY, m->marioObj->header.gfx.cameraToObject);
break;
}
update_mario_pos_for_anim(m);
stop_and_set_height_to_floor(m);
if (is_anim_at_end(m)) {
if (m->usedObj->oBehParams >> 24 == 1) {
save_file_set_flags(SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR);
save_file_clear_flags(SAVE_FLAG_HAVE_KEY_2);
} else {
save_file_set_flags(SAVE_FLAG_UNLOCKED_BASEMENT_DOOR);
save_file_clear_flags(SAVE_FLAG_HAVE_KEY_1);
}
set_mario_action(m, ACT_WALKING, 0);
}
m->actionTimer++;
return FALSE;
}
s32 act_unlocking_star_door(struct MarioState *m) {
switch (m->actionState) {
case 0:
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
m->marioObj->oMarioReadingSignDPosX = m->pos[0];
m->marioObj->oMarioReadingSignDPosZ = m->pos[2];
set_mario_animation(m, MARIO_ANIM_SUMMON_STAR);
m->actionState++;
break;
case 1:
if (is_anim_at_end(m)) {
spawn_object(m->marioObj, MODEL_STAR, bhvUnlockDoorStar);
m->actionState++;
}
break;
case 2:
if (m->actionTimer++ == 70) {
set_mario_animation(m, MARIO_ANIM_RETURN_STAR_APPROACH_DOOR);
m->actionState++;
}
break;
case 3:
if (is_anim_at_end(m)) {
save_file_set_flags(get_door_save_file_flag(m->usedObj));
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_038);
}
break;
}
m->pos[0] = m->marioObj->oMarioReadingSignDPosX;
m->pos[2] = m->marioObj->oMarioReadingSignDPosZ;
update_mario_pos_for_anim(m);
stop_and_set_height_to_floor(m);
return FALSE;
}
s32 act_entering_star_door(struct MarioState *m) {
f32 targetDX;
f32 targetDZ;
s16 targetAngle;
if (m->actionTimer++ == 0) {
m->interactObj->oInteractStatus = INT_STATUS_UNK16;
// ~30 degrees / 1/12 rot
targetAngle = m->usedObj->oMoveAngleYaw + 0x1555;
if (m->actionArg & 2) {
targetAngle += 0x5556; // ~120 degrees / 1/3 rot (total 150d / 5/12)
}
// targetDX and targetDZ are the offsets to add to Mario's position to
// have Mario stand 150 units in front of the door
targetDX = m->usedObj->oPosX + 150.0f * sins(targetAngle) - m->pos[0];
targetDZ = m->usedObj->oPosZ + 150.0f * coss(targetAngle) - m->pos[2];
m->marioObj->oMarioReadingSignDPosX = targetDX / 20.0f;
m->marioObj->oMarioReadingSignDPosZ = targetDZ / 20.0f;
m->faceAngle[1] = atan2s(targetDZ, targetDX);
}
// set Mario's animation
if (m->actionTimer < 15) {
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
}
// go through door? for 20 frames
else if (m->actionTimer < 35) {
m->pos[0] += m->marioObj->oMarioReadingSignDPosX;
m->pos[2] += m->marioObj->oMarioReadingSignDPosZ;
set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, 0x00028000);
}
else {
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
m->pos[0] += 12.0f * sins(m->faceAngle[1]);
m->pos[2] += 12.0f * coss(m->faceAngle[1]);
set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, 0x00028000);
}
stop_and_set_height_to_floor(m);
if (m->actionTimer == 48) {
set_mario_action(m, ACT_IDLE, 0);
}
return FALSE;
}
s32 act_going_through_door(struct MarioState *m) {
if (m->actionTimer == 0) {
if (m->actionArg & 1) {
m->interactObj->oInteractStatus = INT_STATUS_UNK16;
set_mario_animation(m, MARIO_ANIM_PULL_DOOR_WALK_IN);
} else {
m->interactObj->oInteractStatus = INT_STATUS_UNK17;
set_mario_animation(m, MARIO_ANIM_PUSH_DOOR_WALK_IN);
}
}
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
m->pos[0] = m->usedObj->oPosX;
m->pos[2] = m->usedObj->oPosZ;
update_mario_pos_for_anim(m);
stop_and_set_height_to_floor(m);
if (m->actionArg & 4) {
if (m->actionTimer == 16) {
level_trigger_warp(m, WARP_OP_WARP_DOOR);
}
} else if (is_anim_at_end(m)) {
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
set_mario_action(m, ACT_IDLE, 0);
}
m->actionTimer++;
return FALSE;
}
s32 act_warp_door_spawn(struct MarioState *m) {
if (m->actionState == 0) {
m->actionState = 1;
if (m->actionArg & 1) {
m->usedObj->oInteractStatus = INT_STATUS_UNK18;
} else {
m->usedObj->oInteractStatus = INT_STATUS_UNK19;
}
} else if (m->usedObj->oAction == 0) {
if (gNeverEnteredCastle == TRUE && gCurrLevelNum == LEVEL_CASTLE) {
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_021);
} else {
set_mario_action(m, ACT_IDLE, 0);
}
}
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
stop_and_set_height_to_floor(m);
return FALSE;
}
s32 act_emerge_from_pipe(struct MarioState *m) {
struct Object *marioObj = m->marioObj;
if (m->actionTimer++ < 11) {
marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
return FALSE;
}
marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
if (gCurrLevelNum == LEVEL_THI) {
if (gCurrAreaIndex == 2) {
play_sound_if_no_flag(m, SOUND_MENU_EXIT_PIPE, MARIO_ACTION_SOUND_PLAYED);
} else {
play_sound_if_no_flag(m, SOUND_MENU_ENTER_PIPE, MARIO_ACTION_SOUND_PLAYED);
}
}
if (launch_mario_until_land(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) {
mario_set_forward_vel(m, 0.0f);
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
}
return FALSE;
}
s32 act_spawn_spin_airborne(struct MarioState *m) {
// entered water, exit action
if (m->pos[1] < m->waterLevel - 100) {