2020-06-02 16:44:34 +00:00
|
|
|
#include <PR/ultratypes.h>
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#include "prevent_bss_reordering.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "sm64.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "area.h"
|
|
|
|
#include "audio/data.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"
|
2020-04-03 18:57:26 +00:00
|
|
|
#include "game_init.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "gfx_dimensions.h"
|
|
|
|
#include "ingame_menu.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "interaction.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "level_table.h"
|
|
|
|
#include "level_update.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "mario.h"
|
|
|
|
#include "mario_actions_moving.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "mario_step.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "moving_texture.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "object_helpers.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
#include "object_list_processor.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "save_file.h"
|
|
|
|
#include "seq_ids.h"
|
|
|
|
#include "sound_init.h"
|
2020-04-03 18:57:26 +00:00
|
|
|
#include "thread6.h"
|
2020-05-11 10:58:33 +00:00
|
|
|
#include "../../include/libc/stdlib.h"
|
2020-06-02 16:44:34 +00:00
|
|
|
#include "pc/pc_main.h"
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
// TODO: put this elsewhere
|
2020-05-11 10:58:33 +00:00
|
|
|
enum SaveOption { SAVE_OPT_SAVE_AND_CONTINUE = 1, SAVE_OPT_SAVE_AND_QUIT, SAVE_OPT_SAVE_EXIT_GAME, SAVE_OPT_CONTINUE_DONT_SAVE };
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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[6] = { 1, 3, 8, 30, 50, 70 };
|
|
|
|
|
2020-08-27 06:29:40 +00:00
|
|
|
static BehaviorScript* localDialogNPCBehavior = NULL;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
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 },
|
|
|
|
};
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* get_credits_str_width: Calculate width of a Credits String
|
2019-08-25 04:46:40 +00:00
|
|
|
* 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;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
while ((c = *str++) != 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
length += (c == ' ' ? 4 : 7);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2020-05-07 18:21:22 +00:00
|
|
|
#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)
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* print_displaying_credits_entry: Print the current displaying Credits Entry
|
2019-08-25 04:46:40 +00:00
|
|
|
* 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
|
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
dl_rgba16_begin_cutscene_msg_fade();
|
2020-05-07 18:21:22 +00:00
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY, titleStr);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
#ifndef VERSION_JP
|
|
|
|
switch (numLines) {
|
|
|
|
case 4:
|
2020-05-07 18:21:22 +00:00
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 24, *currStrPtr++);
|
2019-08-25 04:46:40 +00:00
|
|
|
numLines = 2;
|
|
|
|
lineHeight = 24;
|
|
|
|
break;
|
|
|
|
case 5:
|
2020-05-07 18:21:22 +00:00
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++);
|
2019-08-25 04:46:40 +00:00
|
|
|
numLines = 3;
|
|
|
|
break;
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
case 6:
|
2020-05-07 18:21:22 +00:00
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++);
|
2019-08-25 04:46:40 +00:00
|
|
|
numLines = 3;
|
|
|
|
break;
|
|
|
|
case 7:
|
2020-05-07 18:21:22 +00:00
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++);
|
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++);
|
2019-08-25 04:46:40 +00:00
|
|
|
numLines = 3;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (numLines-- > 0) {
|
2020-05-07 18:21:22 +00:00
|
|
|
print_credits_str_ascii(CREDIT_TEXT_X_RIGHT - get_credits_str_width(*currStrPtr), strY, *currStrPtr);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
#ifdef VERSION_JP
|
|
|
|
strY += 16;
|
|
|
|
#else
|
|
|
|
strY += lineHeight;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
currStrPtr++;
|
|
|
|
}
|
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
dl_rgba16_stop_cutscene_msg_fade();
|
2019-08-25 04:46:40 +00:00
|
|
|
sDispCreditsEntry = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
void bhv_end_peach_loop(void) {
|
|
|
|
cur_obj_init_animation_with_sound(sEndPeachAnimation);
|
|
|
|
if (cur_obj_check_if_near_animation_end()) {
|
2019-08-25 04:46:40 +00:00
|
|
|
// anims: 0-3, 4, 5, 6-8, 9, 10, 11
|
2019-09-01 19:50:50 +00:00
|
|
|
if (sEndPeachAnimation < 3 || sEndPeachAnimation == 6 || sEndPeachAnimation == 7) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sEndPeachAnimation++;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
void bhv_end_toad_loop(void) {
|
2019-08-25 04:46:40 +00:00
|
|
|
s32 toadAnimIndex = (gCurrentObject->oPosX >= 0.0f);
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_init_animation_with_sound(sEndToadAnims[toadAnimIndex]);
|
|
|
|
if (cur_obj_check_if_near_animation_end()) {
|
2019-08-25 04:46:40 +00:00
|
|
|
// 0-1, 2-3, 4, 5, 6, 7
|
2019-09-01 19:50:50 +00:00
|
|
|
if (sEndToadAnims[toadAnimIndex] == 0 || sEndToadAnims[toadAnimIndex] == 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sEndToadAnims[toadAnimIndex]++;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (timer < 7) {
|
2019-08-25 04:46:40 +00:00
|
|
|
switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBEC[timer];
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
switchCase->selectedCase = D_8032CBE8 * 4 + 1;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBE4 - 1;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// unused
|
2020-03-02 03:42:52 +00:00
|
|
|
static void stub_is_textbox_active(u16 *a0) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (get_dialog_id() == -1) {
|
2019-08-25 04:46:40 +00:00
|
|
|
*a0 = 0;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* get_star_collection_dialog: Determine what dialog should show when Mario
|
2019-12-02 02:52:53 +00:00
|
|
|
* collects a star.
|
2019-08-25 04:46:40 +00:00
|
|
|
* Determines if Mario has collected enough stars to get a dialog for it, and
|
2020-06-02 16:44:34 +00:00
|
|
|
* 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.
|
2019-08-25 04:46:40 +00:00
|
|
|
*/
|
|
|
|
s32 get_star_collection_dialog(struct MarioState *m) {
|
|
|
|
s32 i;
|
|
|
|
s32 dialogID = 0;
|
|
|
|
s32 numStarsRequired;
|
|
|
|
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
numStarsRequired = sStarsNeededForDialog[i];
|
2020-06-02 16:44:34 +00:00
|
|
|
if (m->prevNumStarsForDialog < numStarsRequired && m->numStars >= numStarsRequired) {
|
2019-12-02 02:52:53 +00:00
|
|
|
dialogID = i + DIALOG_141;
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
m->prevNumStarsForDialog = m->numStars;
|
2019-08-25 04:46:40 +00:00
|
|
|
return dialogID;
|
|
|
|
}
|
|
|
|
|
|
|
|
// save menu handler
|
|
|
|
void handle_save_menu(struct MarioState *m) {
|
|
|
|
s32 dialogID;
|
|
|
|
// wait for the menu to show up
|
2019-10-05 19:08:05 +00:00
|
|
|
if (is_anim_past_end(m) && gSaveOptSelectIndex != 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
// save and continue / save and quit
|
2020-05-11 10:58:33 +00:00
|
|
|
if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_CONTINUE || gSaveOptSelectIndex == SAVE_OPT_SAVE_EXIT_GAME || gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) {
|
2019-08-25 04:46:40 +00:00
|
|
|
save_file_do_save(gCurrSaveFileNum - 1);
|
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) {
|
2020-03-02 03:42:52 +00:00
|
|
|
fade_into_special_warp(-2, 0); // reset game
|
2020-05-17 16:46:30 +00:00
|
|
|
} else if (gSaveOptSelectIndex == SAVE_OPT_SAVE_EXIT_GAME) {
|
|
|
|
//initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0);
|
|
|
|
fade_into_special_warp(0, 0);
|
|
|
|
game_exit();
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// not quitting
|
2020-05-11 10:58:33 +00:00
|
|
|
if (gSaveOptSelectIndex != SAVE_OPT_SAVE_EXIT_GAME) {
|
2019-08-25 04:46:40 +00:00
|
|
|
disable_time_stop();
|
|
|
|
m->faceAngle[1] += 0x8000;
|
|
|
|
// figure out what dialog to show, if we should
|
|
|
|
dialogID = get_star_collection_dialog(m);
|
|
|
|
if (dialogID != 0) {
|
|
|
|
play_peachs_jingle();
|
|
|
|
// look up for dialog
|
|
|
|
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID);
|
|
|
|
} else {
|
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* spawn_obj_at_mario_rel_yaw: Spawns object at Mario with relative yaw.
|
2019-08-25 04:46:40 +00:00
|
|
|
* Spawns object with given behavior and model and copies over Mario's position
|
|
|
|
* and yaw plus relative yaw.
|
|
|
|
*/
|
2019-11-03 19:36:27 +00:00
|
|
|
struct Object *spawn_obj_at_mario_rel_yaw(struct MarioState *m, s32 model, const BehaviorScript *behavior, s16 relYaw) {
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* cutscene_take_cap_off: Put Mario's cap on.
|
2019-08-25 04:46:40 +00:00
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* cutscene_put_cap_on: Put Mario's cap on.
|
2019-08-25 04:46:40 +00:00
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* mario_ready_to_speak: Determine if Mario is able to speak to a NPC
|
2019-08-25 04:46:40 +00:00
|
|
|
* 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.
|
|
|
|
*/
|
2020-08-09 00:13:11 +00:00
|
|
|
s32 mario_ready_to_speak(struct MarioState* m) {
|
|
|
|
u32 actionGroup = m->action & ACT_GROUP_MASK;
|
2019-08-25 04:46:40 +00:00
|
|
|
s32 isReadyToSpeak = FALSE;
|
|
|
|
|
2020-08-09 00:13:11 +00:00
|
|
|
if ((m->action == ACT_WAITING_FOR_DIALOG || actionGroup == ACT_GROUP_STATIONARY
|
2019-08-25 04:46:40 +00:00
|
|
|
|| actionGroup == ACT_GROUP_MOVING)
|
2020-08-09 00:13:11 +00:00
|
|
|
&& (!(m->action & (ACT_FLAG_RIDING_SHELL | ACT_FLAG_INVULNERABLE))
|
|
|
|
&& m->action != ACT_FIRST_PERSON)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
isReadyToSpeak = TRUE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return isReadyToSpeak;
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// (can) place Mario in dialog?
|
2019-08-25 04:46:40 +00:00
|
|
|
// initiate dialog?
|
|
|
|
// return values:
|
|
|
|
// 0 = not in dialog
|
|
|
|
// 1 = starting dialog
|
|
|
|
// 2 = speaking
|
2020-08-09 00:13:11 +00:00
|
|
|
s32 set_mario_npc_dialog(struct MarioState* m, s32 actionArg) {
|
2019-08-25 04:46:40 +00:00
|
|
|
s32 dialogState = 0;
|
|
|
|
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->playerIndex == 0 && actionArg == 0) {
|
|
|
|
localDialogNPCBehavior = NULL;
|
2020-09-01 04:10:34 +00:00
|
|
|
continueDialogFunction = NULL;
|
|
|
|
continueDialogFunctionObject = NULL;
|
2020-08-27 06:29:40 +00:00
|
|
|
}
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
// in dialog
|
2020-08-09 00:13:11 +00:00
|
|
|
if (m->action == ACT_READING_NPC_DIALOG) {
|
|
|
|
if (m->actionState < 8) {
|
2019-08-25 04:46:40 +00:00
|
|
|
dialogState = 1; // starting dialog
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2020-08-09 00:13:11 +00:00
|
|
|
if (m->actionState == 8) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (actionArg == 0) {
|
2020-08-09 00:13:11 +00:00
|
|
|
m->actionState++; // exit dialog
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
dialogState = 2;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
2020-08-09 00:13:11 +00:00
|
|
|
} else if (actionArg != 0 && mario_ready_to_speak(m)) {
|
|
|
|
m->usedObj = gCurrentObject;
|
|
|
|
set_mario_action(m, ACT_READING_NPC_DIALOG, actionArg);
|
2020-09-04 08:55:54 +00:00
|
|
|
if (m->playerIndex == 0) { localDialogNPCBehavior = (BehaviorScript*)m->usedObj->behavior; }
|
2019-08-25 04:46:40 +00:00
|
|
|
dialogState = 1; // 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;
|
|
|
|
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->playerIndex == 0) {
|
2020-09-01 04:10:34 +00:00
|
|
|
u8 continueDialogCallback = TRUE;
|
|
|
|
if (continueDialogFunction != NULL && continueDialogFunctionObject != NULL) {
|
|
|
|
struct Object* tmp = gCurrentObject;
|
|
|
|
gCurrentObject = continueDialogFunctionObject;
|
|
|
|
continueDialogCallback = continueDialogFunction();
|
|
|
|
gCurrentObject = tmp;
|
|
|
|
}
|
|
|
|
if (!continueDialogCallback || m->usedObj == NULL || m->usedObj->activeFlags == ACTIVE_FLAG_DEACTIVATED || m->usedObj->behavior != localDialogNPCBehavior) {
|
2020-08-27 06:29:40 +00:00
|
|
|
set_mario_npc_dialog(m, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionArg == 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
headTurnAmount = -1024;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (m->actionArg == 3) {
|
2019-08-25 04:46:40 +00:00
|
|
|
headTurnAmount = 384;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-09-02 01:10:00 +00:00
|
|
|
if (m->actionState < 8) {
|
|
|
|
if (m->usedObj != NULL) {
|
|
|
|
// 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;
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
// 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;
|
2020-09-02 01:10:00 +00:00
|
|
|
} else if (m->playerIndex == 0 && m->actionState == 23) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->flags & MARIO_CAP_IN_HAND) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_PUTTING_ON_CAP, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-12-02 02:52:53 +00:00
|
|
|
set_mario_action(m, m->heldObj == NULL ? ACT_IDLE : ACT_HOLD_IDLE, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
|
|
|
|
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
|
2020-02-03 05:51:26 +00:00
|
|
|
vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-09-02 01:10:00 +00:00
|
|
|
if (m->playerIndex == 0 && m->actionState != 8) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState++;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// puts Mario in a state where he's waiting for (npc) dialog; doesn't do much
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// makes Mario disappear and triggers warp
|
2019-08-25 04:46:40 +00:00
|
|
|
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--;
|
2019-09-01 19:50:50 +00:00
|
|
|
if ((m->actionArg & 0xFFFF) == 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, m->actionArg >> 16);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_reading_automatic_dialog(struct MarioState *m) {
|
|
|
|
u32 actionArg;
|
|
|
|
|
|
|
|
m->actionState++;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState == 2) {
|
2020-08-04 06:23:09 +00:00
|
|
|
//enable_time_stop();
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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 {
|
2020-06-02 16:44:34 +00:00
|
|
|
// set Mario dialog
|
2019-08-25 04:46:40 +00:00
|
|
|
if (m->actionState == 9) {
|
2020-08-04 06:23:09 +00:00
|
|
|
// only show dialog for local player
|
|
|
|
if (m == &gMarioStates[0]) {
|
|
|
|
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));
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
// wait until dialog is done
|
|
|
|
else if (m->actionState == 10) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (get_dialog_id() >= 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState--;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
// look back down
|
|
|
|
else if (m->actionState < 19) {
|
|
|
|
m->actionTimer += 1024;
|
|
|
|
}
|
|
|
|
// finished action
|
|
|
|
else if (m->actionState == 25) {
|
2020-08-11 01:10:04 +00:00
|
|
|
disable_time_stop();
|
2019-08-25 04:46:40 +00:00
|
|
|
if (gShouldNotPlayCastleMusic) {
|
|
|
|
gShouldNotPlayCastleMusic = FALSE;
|
|
|
|
play_cutscene_music(SEQUENCE_ARGS(0, SEQ_LEVEL_INSIDE_CASTLE));
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->prevAction == ACT_STAR_DANCE_WATER) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_WATER_IDLE, 0); // 100c star?
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2020-06-02 16:44:34 +00:00
|
|
|
// make Mario walk into door after star dialog
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, m->prevAction == ACT_UNLOCKING_STAR_DOOR ? ACT_WALKING : ACT_IDLE,
|
|
|
|
0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// apply head turn
|
2020-02-03 05:51:26 +00:00
|
|
|
vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_reading_sign(struct MarioState *m) {
|
2020-08-04 06:23:09 +00:00
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
struct Object *marioObj = m->marioObj;
|
|
|
|
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_ACTION_READ_SIGN, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (m->actionState) {
|
|
|
|
// start dialog
|
|
|
|
case 0:
|
2020-08-04 06:23:09 +00:00
|
|
|
if (m == &gMarioStates[0]) {
|
|
|
|
trigger_cutscene_dialog(1);
|
|
|
|
}
|
|
|
|
//enable_time_stop();
|
2019-08-25 04:46:40 +00:00
|
|
|
// 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) {
|
2020-08-04 06:23:09 +00:00
|
|
|
if (m == &gMarioStates[0]) {
|
|
|
|
create_dialog_inverted_box(m->usedObj->oBehParams2ndByte);
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// in dialog
|
|
|
|
case 2:
|
|
|
|
// dialog finished
|
2020-01-03 15:38:57 +00:00
|
|
|
if (gCamera->cutscene == 0) {
|
2020-08-11 01:10:04 +00:00
|
|
|
disable_time_stop();
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// debug free move action
|
|
|
|
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;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gPlayer1Controller->buttonDown & L_TRIG) {
|
2019-08-25 04:46:40 +00:00
|
|
|
speed = 0.01f;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
set_mario_animation(m, MARIO_ANIM_A_POSE);
|
|
|
|
vec3f_copy(pos, m->pos);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gPlayer1Controller->buttonDown & U_JPAD) {
|
2019-08-25 04:46:40 +00:00
|
|
|
pos[1] += 16.0f * speed;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (gPlayer1Controller->buttonDown & D_JPAD) {
|
2019-08-25 04:46:40 +00:00
|
|
|
pos[1] -= 16.0f * speed;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (pos[1] < floorHeight) {
|
2019-08-25 04:46:40 +00:00
|
|
|
pos[1] = floorHeight;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->pos[1] <= m->waterLevel - 100) {
|
2019-08-25 04:46:40 +00:00
|
|
|
action = ACT_WATER_IDLE;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
action = ACT_IDLE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, action, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// star dance handler
|
|
|
|
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);
|
2020-03-02 03:42:52 +00:00
|
|
|
disable_background_sound();
|
2019-08-25 04:46:40 +00:00
|
|
|
if (m->actionArg & 1) {
|
|
|
|
play_course_clear();
|
|
|
|
} else {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gCurrLevelNum == LEVEL_BOWSER_1 || gCurrLevelNum == LEVEL_BOWSER_2) {
|
2020-04-03 18:57:26 +00:00
|
|
|
play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_KEY), 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2020-04-03 18:57:26 +00:00
|
|
|
play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_STAR), 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 42:
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 80:
|
|
|
|
if ((m->actionArg & 1) == 0) {
|
|
|
|
level_trigger_warp(m, WARP_OP_STAR_EXIT);
|
|
|
|
} else {
|
2020-08-11 01:10:04 +00:00
|
|
|
//enable_time_stop();
|
2019-12-02 02:52:53 +00:00
|
|
|
create_dialog_box_with_response(gLastCompletedStarNum == 7 ? DIALOG_013 : DIALOG_014);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (m->actionState == 1 && gDialogResponse) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gDialogResponse == 1) {
|
2019-08-25 04:46:40 +00:00
|
|
|
save_file_do_save(gCurrSaveFileNum - 1);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 2;
|
|
|
|
} else if (m->actionState == 2 && is_anim_at_end(m)) {
|
|
|
|
disable_time_stop();
|
2020-03-02 03:42:52 +00:00
|
|
|
enable_background_sound();
|
2019-08-25 04:46:40 +00:00
|
|
|
dialogID = get_star_collection_dialog(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (dialogID != 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
// look up for dialog
|
|
|
|
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, isInWater ? ACT_WATER_IDLE : ACT_IDLE, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_star_dance(struct MarioState *m) {
|
2020-01-03 15:38:57 +00:00
|
|
|
m->faceAngle[1] = m->area->camera->yaw;
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_STAR_DANCE
|
|
|
|
: MARIO_ANIM_STAR_DANCE);
|
|
|
|
general_star_dance_handler(m, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState != 2 && m->actionTimer >= 40) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_star_dance_water(struct MarioState *m) {
|
2020-01-03 15:38:57 +00:00
|
|
|
m->faceAngle[1] = m->area->camera->yaw;
|
2019-08-25 04:46:40 +00:00
|
|
|
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);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState != 2 && m->actionTimer >= 62) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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);
|
2020-03-02 03:42:52 +00:00
|
|
|
m->particleFlags |= PARTICLE_WATER_SPLASH;
|
2019-08-25 04:46:40 +00:00
|
|
|
return set_mario_action(m, ACT_STAR_DANCE_WATER, m->actionArg);
|
|
|
|
}
|
|
|
|
if (perform_air_step(m, 1) == AIR_STEP_LANDED) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// general death hander
|
|
|
|
s32 common_death_handler(struct MarioState *m, s32 animation, s32 frameToDeathWarp) {
|
|
|
|
s32 animFrame = set_mario_animation(m, animation);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == frameToDeathWarp) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, WARP_OP_DEATH);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->eyeState = MARIO_EYES_DEAD;
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
return animFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_standing_death(struct MarioState *m) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->input & INPUT_IN_POISON_GAS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return set_mario_action(m, ACT_SUFFOCATION, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
common_death_handler(m, MARIO_ANIM_DYING_FALL_OVER, 80);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->marioObj->header.gfx.unk38.animFrame == 77) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_electrocution(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
common_death_handler(m, MARIO_ANIM_ELECTROCUTION, 43);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_suffocation(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
common_death_handler(m, MARIO_ANIM_SUFFOCATING, 86);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_death_on_back(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (common_death_handler(m, MARIO_ANIM_DYING_ON_BACK, 54) == 40) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_death_on_stomach(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (common_death_handler(m, MARIO_ANIM_DYING_ON_STOMACH, 37) == 37) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
if ((m->quicksandDepth += 5.0f) >= 180.0f) {
|
|
|
|
level_trigger_warp(m, WARP_OP_DEATH);
|
|
|
|
m->actionState = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stationary_ground_step(m);
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_MOVING_QUICKSAND_DEATH, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_eaten_by_bubba(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_A_POSE);
|
|
|
|
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
|
2020-08-07 01:45:39 +00:00
|
|
|
if (m != &gMarioStates[0]) {
|
|
|
|
// never kill remote marios
|
|
|
|
m->health = 0x100;
|
|
|
|
} else {
|
|
|
|
m->health = 0xFF;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer++ == 60) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, WARP_OP_DEATH);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (airStepLanded) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, endAction, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionArg & 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->faceAngle[1] += 0x8000;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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.unk38.animFrame) {
|
|
|
|
case 79:
|
2019-12-02 02:52:53 +00:00
|
|
|
play_sound(SOUND_GENERAL_DOOR_INSERT_KEY, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
case 111:
|
2019-12-02 02:52:53 +00:00
|
|
|
play_sound(SOUND_GENERAL_DOOR_TURN_KEY, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionArg & 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->faceAngle[1] += 0x8000;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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));
|
2019-12-02 02:52:53 +00:00
|
|
|
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_038);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// not sure what kind of door this is
|
|
|
|
s32 act_entering_star_door(struct MarioState *m) {
|
|
|
|
f32 targetDX;
|
|
|
|
f32 targetDZ;
|
|
|
|
s16 targetAngle;
|
|
|
|
|
|
|
|
if (m->actionTimer++ == 0) {
|
|
|
|
m->interactObj->oInteractStatus = 0x00010000;
|
|
|
|
|
|
|
|
// ~30 degrees / 1/12 rot
|
|
|
|
targetAngle = m->usedObj->oMoveAngleYaw + 0x1555;
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionArg & 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
targetAngle += 0x5556; // ~120 degrees / 1/3 rot (total 150d / 5/12)
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// targetDX and targetDZ are the offsets to add to Mario's position to
|
|
|
|
// have Mario stand 150 units in front of the door
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// set Mario's animation
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionArg & 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->faceAngle[1] += 0x8000;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 48) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_going_through_door(struct MarioState *m) {
|
|
|
|
if (m->actionTimer == 0) {
|
|
|
|
if (m->actionArg & 1) {
|
|
|
|
m->interactObj->oInteractStatus = 0x00010000;
|
|
|
|
set_mario_animation(m, MARIO_ANIM_PULL_DOOR_WALK_IN);
|
|
|
|
} else {
|
|
|
|
m->interactObj->oInteractStatus = 0x00020000;
|
|
|
|
set_mario_animation(m, MARIO_ANIM_PUSH_DOOR_WALK_IN);
|
|
|
|
}
|
|
|
|
}
|
2020-08-03 06:02:29 +00:00
|
|
|
if (m->usedObj != NULL) {
|
|
|
|
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
|
|
|
|
m->pos[0] = m->usedObj->oPosX;
|
|
|
|
m->pos[2] = m->usedObj->oPosZ;
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
update_mario_pos_for_anim(m);
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
|
|
|
|
if (m->actionArg & 4) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 16) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, WARP_OP_WARP_DOOR);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
|
|
|
if (is_anim_at_end(m)) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionArg & 2) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->faceAngle[1] += 0x8000;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
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;
|
2020-08-03 06:02:29 +00:00
|
|
|
if (m->usedObj != NULL) {
|
|
|
|
if (m->actionArg & 1) {
|
|
|
|
m->usedObj->oInteractStatus = 0x00040000;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m->usedObj->oInteractStatus = 0x00080000;
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2020-08-03 06:02:29 +00:00
|
|
|
} else if (m->usedObj == NULL || m->usedObj->oAction == 0) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gShouldNotPlayCastleMusic == TRUE && gCurrLevelNum == LEVEL_CASTLE) {
|
2019-12-02 02:52:53 +00:00
|
|
|
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_021);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-04-03 18:57:26 +00:00
|
|
|
if (gCurrLevelNum == LEVEL_THI) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gCurrAreaIndex == 2) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MENU_EXIT_PIPE, MARIO_ACTION_SOUND_PLAYED);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MENU_ENTER_PIPE, MARIO_ACTION_SOUND_PLAYED);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (launch_mario_until_land(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) {
|
|
|
|
mario_set_forward_vel(m, 0.0f);
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_spawn_spin_airborne(struct MarioState *m) {
|
|
|
|
// entered water, exit action
|
|
|
|
if (m->pos[1] < m->waterLevel - 100) {
|
2020-03-02 03:42:52 +00:00
|
|
|
load_level_init_text(0);
|
2019-08-25 04:46:40 +00:00
|
|
|
return set_water_plunge_action(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
// updates all velocity variables based on m->forwardVel
|
|
|
|
mario_set_forward_vel(m, m->forwardVel);
|
|
|
|
|
|
|
|
// landed on floor, play spawn land animation
|
|
|
|
if (perform_air_step(m, 0.0) == AIR_STEP_LANDED) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_SPAWN_SPIN_LANDING, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// is 300 units above floor, spin and play woosh sounds
|
|
|
|
if (m->actionState == 0 && m->pos[1] - m->floorHeight > 300.0f) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) { // first anim frame
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// under 300 units above floor, enter freefall animation
|
|
|
|
else {
|
|
|
|
m->actionState = 1;
|
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_spawn_spin_landing(struct MarioState *m) {
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);
|
|
|
|
if (is_anim_at_end(m)) {
|
2020-03-02 03:42:52 +00:00
|
|
|
load_level_init_text(0);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* act_exit_airborne: Jump out of a level after collecting a Power Star (no
|
2019-08-25 04:46:40 +00:00
|
|
|
** sparkles)
|
|
|
|
* Mario always faces a level entrance when he launches out of it, whether he
|
|
|
|
* died or he collected a star/key. Because of that, we need him to move away
|
|
|
|
* from the painting by setting his speed to -32.0f and have him face away from
|
|
|
|
* the painting by adding 0x8000 (180 deg) to his graphics angle. We also set
|
|
|
|
* his heal counter to 31 to restore 7.75 units of his health, and enable the
|
|
|
|
* particle flag that generates sparkles.
|
|
|
|
*/
|
|
|
|
s32 act_exit_airborne(struct MarioState *m) {
|
|
|
|
if (15 < m->actionTimer++
|
2019-09-01 19:50:50 +00:00
|
|
|
&& launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, -32.0f)) {
|
2020-06-02 16:44:34 +00:00
|
|
|
// heal Mario
|
2019-08-25 04:46:40 +00:00
|
|
|
m->healCounter = 31;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
// rotate him to face away from the entrance
|
|
|
|
m->marioObj->header.gfx.angle[1] += 0x8000;
|
|
|
|
m->particleFlags |= PARTICLE_SPARKLES;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_falling_exit_airborne(struct MarioState *m) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, 0.0f)) {
|
2020-06-02 16:44:34 +00:00
|
|
|
// heal Mario
|
2019-08-25 04:46:40 +00:00
|
|
|
m->healCounter = 31;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2020-06-02 16:44:34 +00:00
|
|
|
// rotate Mario to face away from the entrance
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioObj->header.gfx.angle[1] += 0x8000;
|
|
|
|
m->particleFlags |= PARTICLE_SPARKLES;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_exit_land_save_dialog(struct MarioState *m) {
|
|
|
|
s32 animFrame;
|
|
|
|
stationary_ground_step(m);
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
switch (m->actionState) {
|
|
|
|
// determine type of exit
|
|
|
|
case 0:
|
|
|
|
set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_GENERAL_LAND
|
|
|
|
: MARIO_ANIM_LAND_FROM_SINGLE_JUMP);
|
2020-08-09 15:23:53 +00:00
|
|
|
// only allow for local player
|
|
|
|
if (m == &gMarioStates[0] && is_anim_past_end(m)) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (gLastCompletedCourseNum != COURSE_BITDW
|
|
|
|
&& gLastCompletedCourseNum != COURSE_BITFS) {
|
2020-08-11 01:10:04 +00:00
|
|
|
//enable_time_stop();
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
set_menu_mode(RENDER_COURSE_DONE_SCREEN);
|
|
|
|
gSaveOptSelectIndex = 0;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
m->actionState = 3; // star exit with cap
|
2019-09-01 19:50:50 +00:00
|
|
|
if (!(m->flags & MARIO_CAP_ON_HEAD)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 2; // star exit without cap
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (gLastCompletedCourseNum == COURSE_BITDW
|
|
|
|
|| gLastCompletedCourseNum == COURSE_BITFS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 1; // key exit
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
// key exit
|
|
|
|
case 1:
|
|
|
|
animFrame = set_mario_animation(m, MARIO_ANIM_THROW_CATCH_KEY);
|
|
|
|
switch (animFrame) {
|
|
|
|
case -1:
|
2020-06-02 16:44:34 +00:00
|
|
|
spawn_obj_at_mario_rel_yaw(m, MODEL_BOWSER_KEY_CUTSCENE, bhvBowserKeyCourseExit, -32768);
|
2019-08-25 04:46:40 +00:00
|
|
|
//! fall through
|
|
|
|
case 67:
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound(SOUND_ACTION_KEY_SWISH, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
//! fall through
|
|
|
|
case 83:
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_ACTION_PAT_BACK, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
//! fall through
|
|
|
|
case 111:
|
|
|
|
play_sound(SOUND_ACTION_UNKNOWN45C, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
// no break
|
|
|
|
}
|
2020-08-09 15:23:53 +00:00
|
|
|
// only allow for local player
|
|
|
|
if (m == &gMarioStates[0]) { handle_save_menu(m); }
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
// exit without cap
|
|
|
|
case 2:
|
|
|
|
animFrame = set_mario_animation(m, MARIO_ANIM_MISSING_CAP);
|
2019-09-01 19:50:50 +00:00
|
|
|
if ((animFrame >= 18 && animFrame < 55) || (animFrame >= 112 && animFrame < 134)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->handState = MARIO_HAND_OPEN;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (!(animFrame < 109) && animFrame < 154) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-08-09 15:23:53 +00:00
|
|
|
// only allow for local player
|
|
|
|
if (m == &gMarioStates[0]) { handle_save_menu(m); }
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
// exit with cap
|
|
|
|
case 3:
|
|
|
|
animFrame = set_mario_animation(m, MARIO_ANIM_TAKE_CAP_OFF_THEN_ON);
|
|
|
|
switch (animFrame) {
|
|
|
|
case 12:
|
|
|
|
cutscene_take_cap_off(m);
|
|
|
|
break;
|
|
|
|
case 37:
|
|
|
|
// fall through
|
|
|
|
case 53:
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_ACTION_BRUSH_HAIR, m->marioObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
case 82:
|
|
|
|
cutscene_put_cap_on(m);
|
|
|
|
break;
|
|
|
|
}
|
2020-08-09 15:23:53 +00:00
|
|
|
// only allow for local player
|
|
|
|
if (m == &gMarioStates[0]) { handle_save_menu(m); }
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->marioObj->header.gfx.angle[1] += 0x8000;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_death_exit(struct MarioState *m) {
|
|
|
|
if (15 < m->actionTimer++
|
|
|
|
&& launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, -32.0f)) {
|
|
|
|
#ifdef VERSION_JP
|
|
|
|
play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
#else
|
|
|
|
play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject);
|
2020-04-03 18:57:26 +00:00
|
|
|
#endif
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 5, 80);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->numLives--;
|
|
|
|
// restore 7.75 units of health
|
|
|
|
m->healCounter = 31;
|
|
|
|
}
|
|
|
|
// one unit of health
|
|
|
|
m->health = 0x0100;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_unused_death_exit(struct MarioState *m) {
|
|
|
|
if (launch_mario_until_land(m, ACT_FREEFALL_LAND_STOP, MARIO_ANIM_GENERAL_FALL, 0.0f)) {
|
|
|
|
#ifdef VERSION_JP
|
|
|
|
play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
#else
|
|
|
|
play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
#endif
|
|
|
|
m->numLives--;
|
|
|
|
// restore 7.75 units of health
|
|
|
|
m->healCounter = 31;
|
|
|
|
}
|
|
|
|
// one unit of health
|
|
|
|
m->health = 0x0100;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_falling_death_exit(struct MarioState *m) {
|
|
|
|
if (launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, 0.0f)) {
|
|
|
|
#ifdef VERSION_JP
|
|
|
|
play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
#else
|
|
|
|
play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject);
|
2020-04-03 18:57:26 +00:00
|
|
|
#endif
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 5, 80);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->numLives--;
|
|
|
|
// restore 7.75 units of health
|
|
|
|
m->healCounter = 31;
|
|
|
|
}
|
|
|
|
// one unit of health
|
|
|
|
m->health = 0x0100;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// waits 11 frames before actually executing, also has reduced fvel
|
|
|
|
s32 act_special_exit_airborne(struct MarioState *m) {
|
|
|
|
struct Object *marioObj = m->marioObj;
|
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if (m->actionTimer++ < 11) {
|
|
|
|
marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_SINGLE_JUMP, -24.0f)) {
|
2020-06-02 16:44:34 +00:00
|
|
|
// heal Mario
|
2019-08-25 04:46:40 +00:00
|
|
|
m->healCounter = 31;
|
|
|
|
m->actionArg = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->particleFlags |= PARTICLE_SPARKLES;
|
2020-06-02 16:44:34 +00:00
|
|
|
// rotate Mario to face away from the entrance
|
2019-08-25 04:46:40 +00:00
|
|
|
marioObj->header.gfx.angle[1] += 0x8000;
|
2020-06-02 16:44:34 +00:00
|
|
|
// show Mario
|
2019-08-25 04:46:40 +00:00
|
|
|
marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_special_death_exit(struct MarioState *m) {
|
|
|
|
struct Object *marioObj = m->marioObj;
|
|
|
|
|
|
|
|
if (m->actionTimer++ < 11) {
|
|
|
|
marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (launch_mario_until_land(m, ACT_HARD_BACKWARD_GROUND_KB, MARIO_ANIM_BACKWARD_AIR_KB, -24.0f)) {
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 5, 80);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->numLives--;
|
|
|
|
m->healCounter = 31;
|
|
|
|
}
|
2020-06-02 16:44:34 +00:00
|
|
|
// show Mario
|
2019-08-25 04:46:40 +00:00
|
|
|
marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
|
|
|
|
// one unit of health
|
|
|
|
m->health = 0x0100;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_spawn_no_spin_airborne(struct MarioState *m) {
|
|
|
|
launch_mario_until_land(m, ACT_SPAWN_NO_SPIN_LANDING, MARIO_ANIM_GENERAL_FALL, 0.0f);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->pos[1] < m->waterLevel - 100) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_water_plunge_action(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_spawn_no_spin_landing(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
if (is_anim_at_end(m)) {
|
2020-03-02 03:42:52 +00:00
|
|
|
load_level_init_text(0);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_bbh_enter_spin(struct MarioState *m) {
|
|
|
|
f32 floorDist;
|
|
|
|
f32 scale;
|
2020-08-27 06:29:40 +00:00
|
|
|
f32 cageDX = 0;
|
|
|
|
f32 cageDZ = 0;
|
2019-08-25 04:46:40 +00:00
|
|
|
f32 cageDist;
|
|
|
|
f32 forwardVel;
|
|
|
|
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->usedObj == NULL) {
|
|
|
|
m->usedObj = cur_obj_nearest_object_with_behavior(bhvBooCage);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->usedObj != NULL) {
|
|
|
|
cageDX = m->usedObj->oPosX - m->pos[0];
|
|
|
|
cageDZ = m->usedObj->oPosZ - m->pos[2];
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
cageDist = sqrtf(cageDX * cageDX + cageDZ * cageDZ);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (cageDist > 20.0f) {
|
2019-08-25 04:46:40 +00:00
|
|
|
forwardVel = 10.0f;
|
2019-09-01 19:50:50 +00:00
|
|
|
} else {
|
2019-08-25 04:46:40 +00:00
|
|
|
forwardVel = cageDist / 2.0f;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (forwardVel < 0.5f) {
|
2019-08-25 04:46:40 +00:00
|
|
|
forwardVel = 0.0f;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (m->actionState) {
|
|
|
|
case 0:
|
|
|
|
floorDist = 512.0f - (m->pos[1] - m->floorHeight);
|
|
|
|
m->vel[1] = floorDist > 0 ? sqrtf(4.0f * floorDist + 1.0f) - 1.0f : 2.0f;
|
|
|
|
|
|
|
|
m->actionState = 1;
|
|
|
|
m->actionTimer = 100;
|
|
|
|
// fall through
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
m->faceAngle[1] = atan2s(cageDZ, cageDX);
|
|
|
|
mario_set_forward_vel(m, forwardVel);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) {
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
m->flags &= ~MARIO_UNKNOWN_08;
|
|
|
|
perform_air_step(m, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->vel[1] <= 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 2;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
// fall through
|
|
|
|
case 3:
|
|
|
|
m->faceAngle[1] = atan2s(cageDZ, cageDX);
|
|
|
|
mario_set_forward_vel(m, forwardVel);
|
|
|
|
m->flags &= ~MARIO_UNKNOWN_08;
|
|
|
|
if (perform_air_step(m, 0) == AIR_STEP_LANDED) {
|
|
|
|
level_trigger_warp(m, WARP_OP_UNKNOWN_02);
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 15, 80);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 4;
|
|
|
|
}
|
|
|
|
if (m->actionState == 2) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->marioObj->header.gfx.unk38.animFrame == 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 3;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_ACTION_SHRINK_INTO_BBH, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_DIVE);
|
|
|
|
m->marioObj->header.gfx.angle[0] = atan2s(m->forwardVel, -m->vel[1]);
|
|
|
|
}
|
|
|
|
m->squishTimer = 0xFF;
|
|
|
|
if (m->actionTimer >= 11) {
|
|
|
|
m->actionTimer -= 6;
|
|
|
|
scale = m->actionTimer / 100.0f;
|
|
|
|
vec3f_set(m->marioObj->header.gfx.scale, scale, scale, scale);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_bbh_enter_jump(struct MarioState *m) {
|
|
|
|
f32 cageDX;
|
|
|
|
f32 cageDZ;
|
|
|
|
f32 cageDist;
|
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_action_sound(
|
|
|
|
m, m->flags & MARIO_METAL_CAP ? SOUND_ACTION_METAL_JUMP : SOUND_ACTION_TERRAIN_JUMP, 1);
|
|
|
|
play_mario_jump_sound(m);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->usedObj == NULL) {
|
|
|
|
m->usedObj = cur_obj_nearest_object_with_behavior(bhvBooCage);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->actionState == 0 && m->usedObj != NULL) {
|
2019-08-25 04:46:40 +00:00
|
|
|
cageDX = m->usedObj->oPosX - m->pos[0];
|
|
|
|
cageDZ = m->usedObj->oPosZ - m->pos[2];
|
|
|
|
cageDist = sqrtf(cageDX * cageDX + cageDZ * cageDZ);
|
|
|
|
|
|
|
|
m->vel[1] = 60.0f;
|
|
|
|
m->faceAngle[1] = atan2s(cageDZ, cageDX);
|
|
|
|
mario_set_forward_vel(m, cageDist / 20.0f);
|
|
|
|
|
|
|
|
m->flags &= ~MARIO_UNKNOWN_08;
|
|
|
|
m->actionState = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_mario_animation(m, MARIO_ANIM_DOUBLE_JUMP_RISE);
|
|
|
|
perform_air_step(m, 0);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->vel[1] <= 0.0f) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_BBH_ENTER_SPIN, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_teleport_fade_out(struct MarioState *m) {
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, m->prevAction == ACT_CROUCHING ? MARIO_ANIM_CROUCHING
|
|
|
|
: MARIO_ANIM_FIRST_PERSON);
|
2020-06-02 16:44:34 +00:00
|
|
|
|
2020-04-03 18:57:26 +00:00
|
|
|
if (m->actionTimer == 0) {
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 30, 70);
|
2020-04-03 18:57:26 +00:00
|
|
|
func_sh_8024C89C(2);
|
|
|
|
}
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
m->flags |= MARIO_TELEPORTING;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer < 32) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->fadeWarpOpacity = (-m->actionTimer << 3) + 0xF8;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer++ == 20) {
|
2020-08-07 03:28:21 +00:00
|
|
|
if (m == &gMarioStates[0]) {
|
|
|
|
// only do for local player
|
|
|
|
level_trigger_warp(m, WARP_OP_TELEPORT);
|
|
|
|
}
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_teleport_fade_in(struct MarioState *m) {
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
|
|
|
|
|
2020-04-03 18:57:26 +00:00
|
|
|
if (m->actionTimer == 0) {
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 30, 70);
|
2020-04-03 18:57:26 +00:00
|
|
|
func_sh_8024C89C(2);
|
|
|
|
}
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
if (m->actionTimer < 32) {
|
|
|
|
m->flags |= MARIO_TELEPORTING;
|
|
|
|
m->fadeWarpOpacity = m->actionTimer << 3;
|
|
|
|
} else {
|
|
|
|
m->flags &= ~MARIO_TELEPORTING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->actionTimer++ == 32) {
|
|
|
|
if (m->pos[1] < m->waterLevel - 100) {
|
|
|
|
// Check if the camera is not underwater.
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->playerIndex == 0 && m->area->camera->mode != CAMERA_MODE_WATER_SURFACE) {
|
2020-01-03 15:38:57 +00:00
|
|
|
set_camera_mode(m->area->camera, CAMERA_MODE_WATER_SURFACE, 1);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_WATER_IDLE, 0);
|
|
|
|
} else {
|
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_shocked(struct MarioState *m) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED);
|
|
|
|
play_sound(SOUND_MOVING_SHOCKED, m->marioObj->header.gfx.cameraToObject);
|
2020-01-03 15:38:57 +00:00
|
|
|
set_camera_shake_from_hit(SHAKE_SHOCK);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if (set_mario_animation(m, MARIO_ANIM_SHOCKED) == 0) {
|
|
|
|
m->actionTimer++;
|
|
|
|
m->flags |= MARIO_METAL_SHOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->actionArg == 0) {
|
|
|
|
mario_set_forward_vel(m, 0.0f);
|
|
|
|
if (perform_air_step(m, 1) == AIR_STEP_LANDED) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionArg = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (m->actionTimer >= 6) {
|
|
|
|
m->invincTimer = 30;
|
|
|
|
set_mario_action(m, m->health < 0x0100 ? ACT_ELECTROCUTION : ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_squished(struct MarioState *m) {
|
2020-08-07 01:45:39 +00:00
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
UNUSED s32 pad;
|
|
|
|
f32 squishAmount;
|
|
|
|
f32 spaceUnderCeil;
|
|
|
|
s16 surfAngle;
|
|
|
|
s32 underSteepSurf = FALSE; // seems to be responsible for setting velocity?
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if ((spaceUnderCeil = m->ceilHeight - m->floorHeight) < 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
spaceUnderCeil = 0;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (m->actionState) {
|
|
|
|
case 0:
|
|
|
|
if (spaceUnderCeil > 160.0f) {
|
|
|
|
m->squishTimer = 0;
|
2020-08-07 01:45:39 +00:00
|
|
|
// prevent infinite loop for remote players
|
|
|
|
if (m == &gMarioStates[0]) {
|
|
|
|
return set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
} else {
|
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m->squishTimer = 0xFF;
|
|
|
|
|
|
|
|
if (spaceUnderCeil >= 10.1f) {
|
2020-06-02 16:44:34 +00:00
|
|
|
// Mario becomes a pancake
|
2019-08-25 04:46:40 +00:00
|
|
|
squishAmount = spaceUnderCeil / 160.0f;
|
|
|
|
vec3f_set(m->marioObj->header.gfx.scale, 2.0f - squishAmount, squishAmount,
|
|
|
|
2.0f - squishAmount);
|
|
|
|
} else {
|
|
|
|
if (!(m->flags & MARIO_METAL_CAP) && m->invincTimer == 0) {
|
|
|
|
// cap on: 3 units; cap off: 4.5 units
|
|
|
|
m->hurtCounter += m->flags & MARIO_CAP_ON_HEAD ? 12 : 18;
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_ATTACKED, MARIO_MARIO_SOUND_PLAYED);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Both of the 1.8's are really floats, but one of them has to
|
|
|
|
// be written as a double for this to match on EU.
|
|
|
|
vec3f_set(m->marioObj->header.gfx.scale, 1.8, 0.05f, 1.8f);
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 10, 80);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
2019-09-01 19:50:50 +00:00
|
|
|
if (spaceUnderCeil >= 30.0f) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 2;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
m->actionTimer++;
|
|
|
|
if (m->actionTimer >= 15) {
|
|
|
|
// 1 unit of health
|
|
|
|
if (m->health < 0x0100) {
|
|
|
|
level_trigger_warp(m, WARP_OP_DEATH);
|
|
|
|
// woosh, he's gone!
|
|
|
|
set_mario_action(m, ACT_DISAPPEARED, 0);
|
|
|
|
} else if (m->hurtCounter == 0) {
|
|
|
|
// un-squish animation
|
|
|
|
m->squishTimer = 30;
|
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// steep floor
|
|
|
|
if (m->floor != NULL && m->floor->normal.y < 0.5f) {
|
|
|
|
surfAngle = atan2s(m->floor->normal.z, m->floor->normal.x);
|
|
|
|
underSteepSurf = TRUE;
|
|
|
|
}
|
|
|
|
// steep ceiling
|
|
|
|
if (m->ceil != NULL && -0.5f < m->ceil->normal.y) {
|
|
|
|
surfAngle = atan2s(m->ceil->normal.z, m->ceil->normal.x);
|
|
|
|
underSteepSurf = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (underSteepSurf) {
|
|
|
|
m->vel[0] = sins(surfAngle) * 10.0f;
|
|
|
|
m->vel[2] = coss(surfAngle) * 10.0f;
|
|
|
|
m->vel[1] = 0;
|
|
|
|
|
|
|
|
// check if there's no floor 10 units away from the surface
|
|
|
|
if (perform_ground_step(m) == GROUND_STEP_LEFT_GROUND) {
|
|
|
|
// instant un-squish
|
|
|
|
m->squishTimer = 0;
|
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// squished for more than 10 seconds, so kill Mario
|
2019-08-25 04:46:40 +00:00
|
|
|
if (m->actionArg++ > 300) {
|
|
|
|
// 0 units of health
|
2020-08-07 01:45:39 +00:00
|
|
|
if (m != &gMarioStates[0]) {
|
|
|
|
// never kill remote marios
|
|
|
|
m->health = 0x100;
|
|
|
|
} else {
|
|
|
|
m->health = 0xFF;
|
|
|
|
}
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
m->hurtCounter = 0;
|
|
|
|
level_trigger_warp(m, WARP_OP_DEATH);
|
|
|
|
// woosh, he's gone!
|
|
|
|
set_mario_action(m, ACT_DISAPPEARED, 0);
|
|
|
|
}
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
set_mario_animation(m, MARIO_ANIM_A_POSE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_putting_on_cap(struct MarioState *m) {
|
|
|
|
s32 animFrame = set_mario_animation(m, MARIO_ANIM_PUT_CAP_ON);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == 0) {
|
2020-08-11 01:10:04 +00:00
|
|
|
//enable_time_stop();
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == 28) {
|
2019-08-25 04:46:40 +00:00
|
|
|
cutscene_put_cap_on(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if (is_anim_at_end(m)) {
|
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
disable_time_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
stationary_ground_step(m);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stuck_in_ground_handler(struct MarioState *m, s32 animation, s32 unstuckFrame, s32 target2,
|
|
|
|
s32 target3, s32 endAction) {
|
|
|
|
s32 animFrame = set_mario_animation(m, animation);
|
|
|
|
|
|
|
|
if (m->input & INPUT_A_PRESSED) {
|
|
|
|
m->actionTimer++;
|
|
|
|
if (m->actionTimer >= 5 && animFrame < unstuckFrame - 1) {
|
|
|
|
animFrame = unstuckFrame - 1;
|
|
|
|
set_anim_to_frame(m, animFrame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == -1) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_STUCK_IN_GROUND, 1);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (animFrame == unstuckFrame) {
|
2020-08-14 01:49:24 +00:00
|
|
|
queue_rumble_data_mario(m, 5, 80);
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound_and_spawn_particles(m, SOUND_ACTION_UNSTUCK_FROM_GROUND, 1);
|
2019-09-01 19:50:50 +00:00
|
|
|
} else if (animFrame == target2 || animFrame == target3) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (is_anim_at_end(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, endAction, 0);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_head_stuck_in_ground(struct MarioState *m) {
|
|
|
|
stuck_in_ground_handler(m, MARIO_ANIM_HEAD_STUCK_IN_GROUND, 96, 105, 135, ACT_IDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_butt_stuck_in_ground(struct MarioState *m) {
|
|
|
|
stuck_in_ground_handler(m, MARIO_ANIM_BOTTOM_STUCK_IN_GROUND, 127, 136, -2, ACT_GROUND_POUND_LAND);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 act_feet_stuck_in_ground(struct MarioState *m) {
|
|
|
|
stuck_in_ground_handler(m, MARIO_ANIM_LEGS_STUCK_IN_GROUND, 116, 129, -2, ACT_IDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
/**
|
|
|
|
* advance_cutscene_step: Advances the current step in the current cutscene.
|
2019-08-25 04:46:40 +00:00
|
|
|
* Resets action state and action timer, adds 1 to the action arg (responsible
|
|
|
|
* for keeping track of what step of the cutscene Mario is in.)
|
|
|
|
*/
|
|
|
|
static void advance_cutscene_step(struct MarioState *m) {
|
|
|
|
m->actionState = 0;
|
|
|
|
m->actionTimer = 0;
|
|
|
|
m->actionArg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intro_cutscene_hide_hud_and_mario(struct MarioState *m) {
|
|
|
|
gHudDisplay.flags = HUD_DISPLAY_NONE;
|
2020-01-03 15:38:57 +00:00
|
|
|
m->statusForCamera->cameraEvent = CAM_EVENT_START_INTRO;
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define TIMER_SPAWN_PIPE 47
|
|
|
|
#else
|
|
|
|
#define TIMER_SPAWN_PIPE 37
|
|
|
|
#endif
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
static void intro_cutscene_peach_lakitu_scene(struct MarioState *m) {
|
2020-01-03 15:38:57 +00:00
|
|
|
if ((s16) m->statusForCamera->cameraEvent != CAM_EVENT_START_INTRO) {
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer++ == TIMER_SPAWN_PIPE) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sIntroWarpPipeObj =
|
|
|
|
spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_CASTLE_GROUNDS_WARP_PIPE,
|
|
|
|
bhvStaticObject, -1328, 60, 4664, 0, 180, 0);
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 05:51:26 +00:00
|
|
|
#undef TIMER_SPAWN_PIPE
|
|
|
|
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define TIMER_RAISE_PIPE 28
|
|
|
|
#else
|
|
|
|
#define TIMER_RAISE_PIPE 38
|
|
|
|
#endif
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
static void intro_cutscene_raise_pipe(struct MarioState *m) {
|
|
|
|
sIntroWarpPipeObj->oPosY = camera_approach_f32_symmetric(sIntroWarpPipeObj->oPosY, 260.0f, 10.0f);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 0) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_MENU_EXIT_PIPE, sIntroWarpPipeObj->header.gfx.cameraToObject);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer++ == TIMER_RAISE_PIPE) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->vel[1] = 60.0f;
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 05:51:26 +00:00
|
|
|
#undef TIMER_RAISE_PIPE
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
static void intro_cutscene_jump_out_of_pipe(struct MarioState *m) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 25) {
|
2019-08-25 04:46:40 +00:00
|
|
|
gHudDisplay.flags = HUD_DISPLAY_DEFAULT;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if (m->actionTimer++ >= 118) {
|
|
|
|
m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
// For some reason these calls were swapped.
|
|
|
|
play_sound_if_no_flag(m, SOUND_ACTION_HIT_3, MARIO_ACTION_SOUND_PLAYED);
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
|
2020-02-03 05:51:26 +00:00
|
|
|
#else
|
|
|
|
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
|
|
|
|
#ifndef VERSION_JP
|
2019-11-03 19:36:27 +00:00
|
|
|
play_sound_if_no_flag(m, SOUND_ACTION_HIT_3, MARIO_ACTION_SOUND_PLAYED);
|
2020-02-03 05:51:26 +00:00
|
|
|
#endif
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP);
|
|
|
|
mario_set_forward_vel(m, 10.0f);
|
|
|
|
if (perform_air_step(m, 0) == AIR_STEP_LANDED) {
|
|
|
|
sound_banks_enable(2, 0x0330);
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
#ifndef VERSION_JP
|
|
|
|
play_sound(SOUND_MARIO_HAHA, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
#endif
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intro_cutscene_land_outside_pipe(struct MarioState *m) {
|
|
|
|
set_mario_animation(m, MARIO_ANIM_LAND_FROM_SINGLE_JUMP);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (is_anim_at_end(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intro_cutscene_lower_pipe(struct MarioState *m) {
|
|
|
|
if (m->actionTimer++ == 0) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_MENU_ENTER_PIPE, sIntroWarpPipeObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
|
|
|
|
}
|
|
|
|
|
|
|
|
sIntroWarpPipeObj->oPosY -= 5.0f;
|
|
|
|
if (sIntroWarpPipeObj->oPosY <= 50.0f) {
|
2020-03-02 03:42:52 +00:00
|
|
|
obj_mark_for_deletion(sIntroWarpPipeObj);
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void intro_cutscene_set_mario_to_idle(struct MarioState *m) {
|
2020-01-03 15:38:57 +00:00
|
|
|
if (gCamera->cutscene == 0) {
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->playerIndex == 0) {
|
|
|
|
gCameraMovementFlags &= ~CAM_MOVE_C_UP_MODE;
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_IDLE, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
enum {
|
|
|
|
INTRO_CUTSCENE_HIDE_HUD_AND_MARIO,
|
|
|
|
INTRO_CUTSCENE_PEACH_LAKITU_SCENE,
|
|
|
|
INTRO_CUTSCENE_RAISE_PIPE,
|
|
|
|
INTRO_CUTSCENE_JUMP_OUT_OF_PIPE,
|
|
|
|
INTRO_CUTSCENE_LAND_OUTSIDE_PIPE,
|
|
|
|
INTRO_CUTSCENE_LOWER_PIPE,
|
|
|
|
INTRO_CUTSCENE_SET_MARIO_TO_IDLE
|
|
|
|
};
|
|
|
|
|
|
|
|
static s32 act_intro_cutscene(struct MarioState *m) {
|
|
|
|
switch (m->actionArg) {
|
|
|
|
case INTRO_CUTSCENE_HIDE_HUD_AND_MARIO:
|
|
|
|
intro_cutscene_hide_hud_and_mario(m);
|
|
|
|
break;
|
|
|
|
case INTRO_CUTSCENE_PEACH_LAKITU_SCENE:
|
|
|
|
intro_cutscene_peach_lakitu_scene(m);
|
|
|
|
break;
|
|
|
|
case INTRO_CUTSCENE_RAISE_PIPE:
|
|
|
|
intro_cutscene_raise_pipe(m);
|
|
|
|
break;
|
|
|
|
case INTRO_CUTSCENE_JUMP_OUT_OF_PIPE:
|
|
|
|
intro_cutscene_jump_out_of_pipe(m);
|
|
|
|
break;
|
|
|
|
case INTRO_CUTSCENE_LAND_OUTSIDE_PIPE:
|
|
|
|
intro_cutscene_land_outside_pipe(m);
|
|
|
|
break;
|
|
|
|
case INTRO_CUTSCENE_LOWER_PIPE:
|
|
|
|
intro_cutscene_lower_pipe(m);
|
|
|
|
break;
|
|
|
|
case INTRO_CUTSCENE_SET_MARIO_TO_IDLE:
|
|
|
|
intro_cutscene_set_mario_to_idle(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// jumbo star cutscene: Mario lands after grabbing the jumbo star
|
2019-08-25 04:46:40 +00:00
|
|
|
static void jumbo_star_cutscene_falling(struct MarioState *m) {
|
|
|
|
if (m->actionState == 0) {
|
|
|
|
m->input |= INPUT_A_DOWN;
|
|
|
|
m->flags |= (MARIO_WING_CAP | MARIO_CAP_ON_HEAD);
|
|
|
|
|
|
|
|
m->faceAngle[1] = -0x8000;
|
|
|
|
m->pos[0] = 0.0f;
|
|
|
|
m->pos[2] = 0.0f;
|
|
|
|
|
|
|
|
mario_set_forward_vel(m, 0.0f);
|
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);
|
|
|
|
|
|
|
|
if (perform_air_step(m, 1) == AIR_STEP_LANDED) {
|
|
|
|
play_cutscene_music(SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_VICTORY));
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);
|
|
|
|
if (is_anim_at_end(m)) {
|
2020-01-03 15:38:57 +00:00
|
|
|
m->statusForCamera->cameraEvent = CAM_EVENT_START_GRAND_STAR;
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// jumbo star cutscene: Mario takes off
|
2019-08-25 04:46:40 +00:00
|
|
|
static s32 jumbo_star_cutscene_taking_off(struct MarioState *m) {
|
|
|
|
struct Object *marioObj = m->marioObj;
|
|
|
|
s32 animFrame;
|
|
|
|
|
|
|
|
if (m->actionState == 0) {
|
|
|
|
set_mario_animation(m, MARIO_ANIM_FINAL_BOWSER_RAISE_HAND_SPIN);
|
|
|
|
marioObj->rawData.asF32[0x22] = 0.0f;
|
|
|
|
|
|
|
|
if (is_anim_past_end(m)) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
animFrame = set_mario_animation(m, MARIO_ANIM_FINAL_BOWSER_WING_CAP_TAKE_OFF);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == 3 || animFrame == 28 || animFrame == 60) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_JUMP, 1);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (animFrame >= 3) {
|
2019-08-25 04:46:40 +00:00
|
|
|
marioObj->rawData.asF32[0x22] -= 32.0f;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (animFrame) {
|
|
|
|
case 3:
|
2019-12-02 02:52:53 +00:00
|
|
|
play_sound(SOUND_MARIO_YAH_WAH_HOO + (gAudioRandom % 3 << 16),
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioObj->header.gfx.cameraToObject);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 28:
|
|
|
|
play_sound(SOUND_MARIO_HOOHOO, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 60:
|
|
|
|
play_sound(SOUND_MARIO_YAHOO, m->marioObj->header.gfx.cameraToObject);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m->particleFlags |= PARTICLE_SPARKLES;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (is_anim_past_end(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vec3f_set(m->pos, 0.0f, 307.0, marioObj->rawData.asF32[0x22]);
|
|
|
|
update_mario_pos_for_anim(m);
|
|
|
|
vec3f_copy(marioObj->header.gfx.pos, m->pos);
|
|
|
|
vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
|
|
|
|
|
|
|
|
// not sure why they did this, probably was from being used to action
|
|
|
|
// functions
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// jumbo star cutscene: Mario flying
|
2019-08-25 04:46:40 +00:00
|
|
|
static s32 jumbo_star_cutscene_flying(struct MarioState *m) {
|
|
|
|
Vec3f targetPos;
|
|
|
|
UNUSED struct Object *marioObj = m->marioObj;
|
|
|
|
f32 targetDX;
|
|
|
|
f32 targetDY;
|
|
|
|
f32 targetDZ;
|
|
|
|
f32 targetHyp;
|
|
|
|
s16 targetAngle;
|
|
|
|
|
|
|
|
switch (m->actionState) {
|
|
|
|
case 0:
|
|
|
|
set_mario_animation(m, MARIO_ANIM_WING_CAP_FLY);
|
|
|
|
anim_spline_init(sJumboStarKeyframes);
|
|
|
|
m->actionState++;
|
|
|
|
// fall through
|
|
|
|
case 1:
|
|
|
|
if (anim_spline_poll(targetPos)) {
|
2020-06-02 16:44:34 +00:00
|
|
|
// does this twice
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_action(m, ACT_FREEFALL, 0);
|
|
|
|
m->actionState++;
|
|
|
|
} else {
|
|
|
|
targetDX = targetPos[0] - m->pos[0];
|
|
|
|
targetDY = targetPos[1] - m->pos[1];
|
|
|
|
targetDZ = targetPos[2] - m->pos[2];
|
|
|
|
targetHyp = sqrtf(targetDX * targetDX + targetDZ * targetDZ);
|
|
|
|
targetAngle = atan2s(targetDZ, targetDX);
|
|
|
|
|
|
|
|
vec3f_copy(m->pos, targetPos);
|
|
|
|
m->marioObj->header.gfx.angle[0] = -atan2s(targetHyp, targetDY);
|
|
|
|
m->marioObj->header.gfx.angle[1] = targetAngle;
|
|
|
|
m->marioObj->header.gfx.angle[2] = ((m->faceAngle[1] - targetAngle) << 16 >> 16) * 20;
|
|
|
|
m->faceAngle[1] = targetAngle;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
set_mario_action(m, ACT_FREEFALL, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->marioBodyState->handState = MARIO_HAND_RIGHT_OPEN;
|
|
|
|
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
|
|
|
|
m->particleFlags |= PARTICLE_SPARKLES;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer++ == 500) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, WARP_OP_CREDITS_START);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum { JUMBO_STAR_CUTSCENE_FALLING, JUMBO_STAR_CUTSCENE_TAKING_OFF, JUMBO_STAR_CUTSCENE_FLYING };
|
|
|
|
|
|
|
|
static s32 act_jumbo_star_cutscene(struct MarioState *m) {
|
|
|
|
switch (m->actionArg) {
|
|
|
|
case JUMBO_STAR_CUTSCENE_FALLING:
|
|
|
|
jumbo_star_cutscene_falling(m);
|
|
|
|
break;
|
|
|
|
case JUMBO_STAR_CUTSCENE_TAKING_OFF:
|
|
|
|
jumbo_star_cutscene_taking_off(m);
|
|
|
|
break;
|
|
|
|
case JUMBO_STAR_CUTSCENE_FLYING:
|
|
|
|
jumbo_star_cutscene_flying(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:57:26 +00:00
|
|
|
void generate_yellow_sparkles(s16 x, s16 y, s16 z, f32 radius) {
|
|
|
|
static s32 sSparkleGenTheta = 0;
|
|
|
|
static s32 sSparkleGenPhi = 0;
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
s16 offsetX = radius * coss(sSparkleGenTheta) * sins(sSparkleGenPhi);
|
|
|
|
s16 offsetY = radius * sins(sSparkleGenTheta);
|
|
|
|
s16 offsetZ = radius * coss(sSparkleGenTheta) * coss(sSparkleGenPhi);
|
|
|
|
|
|
|
|
spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_NONE, bhvSparkleSpawn, x + offsetX, y + offsetY,
|
|
|
|
z + offsetZ, 0, 0, 0);
|
|
|
|
|
|
|
|
//! copy paste error
|
|
|
|
offsetX = offsetX * 4 / 3;
|
|
|
|
offsetX = offsetY * 4 / 3;
|
|
|
|
offsetX = offsetZ * 4 / 3;
|
|
|
|
|
|
|
|
spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_NONE, bhvSparkleSpawn, x - offsetX, y - offsetY,
|
|
|
|
z - offsetZ, 0, 0, 0);
|
|
|
|
|
|
|
|
sSparkleGenTheta += 0x3800;
|
|
|
|
sSparkleGenPhi += 0x6000;
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// not sure what this does, returns the height of the floor.
|
|
|
|
// (animation related?)
|
2020-03-02 03:42:52 +00:00
|
|
|
static f32 end_obj_set_visual_pos(struct Object *o) {
|
2019-08-25 04:46:40 +00:00
|
|
|
struct Surface *surf;
|
|
|
|
Vec3s sp24;
|
|
|
|
f32 sp20;
|
|
|
|
f32 sp1C;
|
|
|
|
f32 sp18;
|
|
|
|
|
|
|
|
find_mario_anim_flags_and_translation(o, o->header.gfx.angle[1], sp24);
|
|
|
|
|
|
|
|
sp20 = o->header.gfx.pos[0] + sp24[0];
|
|
|
|
sp1C = o->header.gfx.pos[1] + 10.0f;
|
|
|
|
sp18 = o->header.gfx.pos[2] + sp24[2];
|
|
|
|
|
|
|
|
return find_floor(sp20, sp1C, sp18, &surf);
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// make Mario fall and soften wing cap gravity
|
2019-08-25 04:46:40 +00:00
|
|
|
static void end_peach_cutscene_mario_falling(struct MarioState *m) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 1) {
|
2020-01-03 15:38:57 +00:00
|
|
|
m->statusForCamera->cameraEvent = CAM_EVENT_START_ENDING;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
m->input |= INPUT_A_DOWN;
|
|
|
|
m->flags |= (MARIO_WING_CAP | MARIO_CAP_ON_HEAD);
|
|
|
|
|
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);
|
|
|
|
mario_set_forward_vel(m, 0.0f);
|
|
|
|
|
|
|
|
if (perform_air_step(m, 0) == AIR_STEP_LANDED) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// set Mario on the ground, wait and spawn the jumbo star outside the castle.
|
2019-08-25 04:46:40 +00:00
|
|
|
static void end_peach_cutscene_mario_landing(struct MarioState *m) {
|
|
|
|
set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
|
|
|
|
if (is_anim_at_end(m)) {
|
|
|
|
// make wing cap run out
|
|
|
|
m->capTimer = 60;
|
|
|
|
|
|
|
|
sEndJumboStarObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_STAR, bhvStaticObject, 0,
|
|
|
|
2528, -1800, 0, 0, 0);
|
2020-03-02 03:42:52 +00:00
|
|
|
obj_scale(sEndJumboStarObj, 3.0);
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// raise hand animation, lower hand animation, do some special effects
|
|
|
|
static void end_peach_cutscene_summon_jumbo_star(struct MarioState *m) {
|
|
|
|
set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_CREDITS_RAISE_HAND
|
|
|
|
: MARIO_ANIM_CREDITS_LOWER_HAND);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState == 0 && is_anim_past_end(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState++;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (m->actionTimer == 90) {
|
2019-08-25 04:46:40 +00:00
|
|
|
play_cutscene_music(SEQUENCE_ARGS(0, SEQ_EVENT_CUTSCENE_ENDING));
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (m->actionTimer == 255) {
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
sEndJumboStarObj->oFaceAngleYaw += 0x0400;
|
|
|
|
generate_yellow_sparkles(0, 2528, -1800, 250.0f);
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_AIR_PEACH_TWINKLE, sEndJumboStarObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define TIMER_FADE_IN_PEACH 201
|
|
|
|
#define TIMER_DESCEND_PEACH 280
|
2020-06-02 16:44:34 +00:00
|
|
|
#else
|
2020-02-03 05:51:26 +00:00
|
|
|
#define TIMER_FADE_IN_PEACH 276
|
|
|
|
#define TIMER_DESCEND_PEACH 355
|
|
|
|
#endif
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
// free peach from the stained glass window
|
|
|
|
static void end_peach_cutscene_spawn_peach(struct MarioState *m) {
|
|
|
|
if (m->actionTimer == 1) {
|
|
|
|
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 14, 255, 255, 255);
|
|
|
|
}
|
|
|
|
if (m->actionTimer == 2) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
if (m->actionTimer == 44) {
|
|
|
|
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 192, 255, 255, 255);
|
|
|
|
}
|
|
|
|
if (m->actionTimer == 40) {
|
2020-03-02 03:42:52 +00:00
|
|
|
obj_mark_for_deletion(sEndJumboStarObj);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
sEndPeachObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_PEACH, bhvEndPeach, 0, 2428,
|
|
|
|
-1300, 0, 0, 0);
|
|
|
|
gCutsceneFocus = sEndPeachObj;
|
|
|
|
|
|
|
|
sEndRightToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, 200,
|
|
|
|
906, -1290, 0, 0, 0);
|
|
|
|
|
|
|
|
sEndLeftToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, -200,
|
|
|
|
906, -1290, 0, 0, 0);
|
|
|
|
|
|
|
|
sEndPeachObj->oOpacity = 127;
|
|
|
|
sEndRightToadObj->oOpacity = 255;
|
|
|
|
sEndLeftToadObj->oOpacity = 255;
|
|
|
|
|
|
|
|
D_8032CBE4 = 4;
|
|
|
|
sEndPeachAnimation = 4;
|
|
|
|
|
|
|
|
sEndToadAnims[0] = 4;
|
|
|
|
sEndToadAnims[1] = 5;
|
|
|
|
}
|
2020-02-03 05:51:26 +00:00
|
|
|
|
|
|
|
if (m->actionTimer >= TIMER_FADE_IN_PEACH) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sEndPeachObj->oOpacity = camera_approach_f32_symmetric(sEndPeachObj->oOpacity, 255.0f, 2.0f);
|
|
|
|
}
|
|
|
|
if (m->actionTimer >= 40) {
|
|
|
|
generate_yellow_sparkles(0, 2628, -1300, 150.0f);
|
|
|
|
}
|
2020-02-03 05:51:26 +00:00
|
|
|
|
|
|
|
if (m->actionTimer == TIMER_DESCEND_PEACH) {
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
// probably added sounds later and missed the previous >= 40 check
|
|
|
|
if (m->actionTimer >= 40) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_AIR_PEACH_TWINKLE, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define TIMER_RUN_TO_PEACH 531
|
2020-06-02 16:44:34 +00:00
|
|
|
#else
|
2020-02-03 05:51:26 +00:00
|
|
|
#define TIMER_RUN_TO_PEACH 584
|
|
|
|
#endif
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
// descend peach
|
|
|
|
static void end_peach_cutscene_descend_peach(struct MarioState *m) {
|
|
|
|
generate_yellow_sparkles(0, sEndPeachObj->oPosY, -1300, 150.0f);
|
|
|
|
|
|
|
|
if (sEndPeachObj->oPosY >= 1300.0f) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState < 60) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState += 5;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState >= 27) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState -= 2;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_CREDITS_RETURN_FROM_LOOK_UP);
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if ((sEndPeachObj->oPosY -= m->actionState / 10) <= 907.0f) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sEndPeachObj->oPosY = 906.0f;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_AIR_PEACH_TWINKLE, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer >= TIMER_RUN_TO_PEACH) {
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#undef TIMER_RUN_TO_PEACH
|
|
|
|
|
2020-06-02 16:44:34 +00:00
|
|
|
// Mario runs to peach
|
2019-08-25 04:46:40 +00:00
|
|
|
static void end_peach_cutscene_run_to_peach(struct MarioState *m) {
|
|
|
|
struct Surface *surf;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 22) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sEndPeachAnimation = 5;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if ((m->pos[2] -= 20.0f) <= -1181.0f) {
|
|
|
|
m->pos[2] = -1180.0f;
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
m->pos[1] = find_floor(m->pos[0], m->pos[1], m->pos[2], &surf);
|
|
|
|
|
|
|
|
set_mario_anim_with_accel(m, MARIO_ANIM_RUNNING, 0x00080000);
|
2020-03-02 03:42:52 +00:00
|
|
|
play_step_sound(m, 9, 45);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
|
|
|
|
m->particleFlags |= PARTICLE_DUST;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dialog 1
|
|
|
|
// "Mario!"
|
|
|
|
// "The power of the Stars is restored to the castle..."
|
|
|
|
static void end_peach_cutscene_dialog_1(struct MarioState *m) {
|
|
|
|
s32 animFrame = set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_CREDITS_TAKE_OFF_CAP
|
|
|
|
: MARIO_ANIM_CREDITS_LOOK_UP);
|
|
|
|
|
|
|
|
if (m->actionState == 0) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == 8) {
|
2019-08-25 04:46:40 +00:00
|
|
|
cutscene_take_cap_off(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (is_anim_at_end(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState++;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (m->actionTimer) {
|
|
|
|
case 80:
|
|
|
|
sEndPeachAnimation = 6;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 81:
|
|
|
|
D_8032CBE4 = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 145:
|
|
|
|
D_8032CBE4 = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 228:
|
|
|
|
D_8032CBE4 = 1;
|
|
|
|
D_8032CBE8 = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 230:
|
|
|
|
set_cutscene_message(160, 227, 0, 30);
|
|
|
|
#ifndef VERSION_JP
|
2020-04-03 18:57:26 +00:00
|
|
|
func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40);
|
2019-08-25 04:46:40 +00:00
|
|
|
play_sound(SOUND_PEACH_MARIO, sEndPeachObj->header.gfx.cameraToObject);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 275:
|
|
|
|
D_8032CBE4 = 0;
|
|
|
|
D_8032CBE8 = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 290:
|
|
|
|
set_cutscene_message(160, 227, 1, 60);
|
|
|
|
#ifndef VERSION_JP
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_PEACH_POWER_OF_THE_STARS, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 480:
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define TIMER_SOMETHING_SPECIAL 150
|
|
|
|
#define TIMER_PEACH_KISS 260
|
2020-06-02 16:44:34 +00:00
|
|
|
#else
|
2020-02-03 05:51:26 +00:00
|
|
|
#define TIMER_SOMETHING_SPECIAL 130
|
|
|
|
#define TIMER_PEACH_KISS 200
|
|
|
|
#endif
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
// dialog 2
|
|
|
|
// "...and it's all thanks to you!"
|
|
|
|
// "Thank you Mario!"
|
|
|
|
// "We have to do something special for you..."
|
|
|
|
static void end_peach_cutscene_dialog_2(struct MarioState *m) {
|
|
|
|
sEndPeachAnimation = 9;
|
|
|
|
|
|
|
|
switch (m->actionTimer) {
|
|
|
|
case 29:
|
|
|
|
set_cutscene_message(160, 227, 2, 30);
|
|
|
|
#ifndef VERSION_JP
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_PEACH_THANKS_TO_YOU, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 45:
|
|
|
|
D_8032CBE8 = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 75:
|
|
|
|
set_cutscene_message(160, 227, 3, 30);
|
|
|
|
#ifndef VERSION_JP
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_PEACH_THANK_YOU_MARIO, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
case TIMER_SOMETHING_SPECIAL:
|
2019-08-25 04:46:40 +00:00
|
|
|
set_cutscene_message(160, 227, 4, 40);
|
|
|
|
#ifndef VERSION_JP
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_PEACH_SOMETHING_SPECIAL, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
case TIMER_PEACH_KISS:
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#undef TIMER_SOMETHING_SPECIAL
|
|
|
|
#undef TIMER_PEACH_KISS
|
|
|
|
|
2020-04-03 18:57:26 +00:00
|
|
|
// blink twice then have half-shut eyes (see end_peach_cutscene_kiss_from_peach)
|
|
|
|
static u8 sMarioBlinkOverride[20] = {
|
|
|
|
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
|
|
|
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN,
|
|
|
|
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
|
|
|
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN,
|
|
|
|
MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED,
|
|
|
|
};
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) {
|
|
|
|
sEndPeachAnimation = 10;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer >= 90) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->eyeState =
|
|
|
|
m->actionTimer < 110 ? sMarioBlinkOverride[m->actionTimer - 90] : MARIO_EYES_HALF_CLOSED;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (m->actionTimer) {
|
|
|
|
case 8:
|
|
|
|
D_8032CBE8 = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 10:
|
|
|
|
D_8032CBE4 = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 50:
|
|
|
|
D_8032CBE4 = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 75:
|
|
|
|
m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 76:
|
|
|
|
m->marioBodyState->eyeState = MARIO_EYES_CLOSED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 100:
|
|
|
|
D_8032CBE4 = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 136:
|
|
|
|
D_8032CBE4 = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 140:
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void end_peach_cutscene_star_dance(struct MarioState *m) {
|
|
|
|
s32 animFrame = set_mario_animation(m, MARIO_ANIM_CREDITS_PEACE_SIGN);
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (animFrame == 77) {
|
2019-08-25 04:46:40 +00:00
|
|
|
cutscene_put_cap_on(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (animFrame == 88) {
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
|
|
|
if (animFrame >= 98) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer < 52) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (m->actionTimer) {
|
|
|
|
case 70:
|
|
|
|
D_8032CBE4 = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 86:
|
|
|
|
D_8032CBE4 = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 90:
|
|
|
|
D_8032CBE4 = 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 120:
|
|
|
|
D_8032CBE4 = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 140:
|
|
|
|
#ifndef VERSION_JP
|
2020-04-03 18:57:26 +00:00
|
|
|
sequence_player_unlower(SEQ_PLAYER_LEVEL, 60);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
play_cutscene_music(SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_CREDITS));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 142:
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// dialog 3
|
|
|
|
// "Listen everybody"
|
|
|
|
// "let's bake a delicious cake..."
|
|
|
|
// "...for Mario..."
|
|
|
|
static void end_peach_cutscene_dialog_3(struct MarioState *m) {
|
|
|
|
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
sEndPeachObj->oPosY = end_obj_set_visual_pos(sEndPeachObj);
|
|
|
|
sEndRightToadObj->oPosY = end_obj_set_visual_pos(sEndRightToadObj);
|
|
|
|
sEndLeftToadObj->oPosY = end_obj_set_visual_pos(sEndLeftToadObj);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
switch (m->actionTimer) {
|
|
|
|
case 1:
|
|
|
|
sEndPeachAnimation = 0;
|
|
|
|
sEndToadAnims[0] = 0;
|
|
|
|
sEndToadAnims[1] = 2;
|
|
|
|
D_8032CBE8 = 1;
|
|
|
|
set_cutscene_message(160, 227, 5, 30);
|
|
|
|
#ifndef VERSION_JP
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_PEACH_BAKE_A_CAKE, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 55:
|
|
|
|
set_cutscene_message(160, 227, 6, 40);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 130:
|
|
|
|
set_cutscene_message(160, 227, 7, 50);
|
|
|
|
#ifndef VERSION_JP
|
2019-10-05 19:08:05 +00:00
|
|
|
play_sound(SOUND_PEACH_FOR_MARIO, sEndPeachObj->header.gfx.cameraToObject);
|
2019-08-25 04:46:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer == 350) {
|
2019-08-25 04:46:40 +00:00
|
|
|
advance_cutscene_step(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// "Mario!"
|
|
|
|
static void end_peach_cutscene_run_to_castle(struct MarioState *m) {
|
|
|
|
set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_CREDITS_START_WALK_LOOK_UP
|
|
|
|
: MARIO_ANIM_CREDITS_LOOK_BACK_THEN_RUN);
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
m->marioObj->header.gfx.pos[1] = end_obj_set_visual_pos(m->marioObj);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState == 0 && is_anim_past_end(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState = 1;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
if (m->actionTimer == 95) {
|
|
|
|
set_cutscene_message(160, 227, 0, 40);
|
|
|
|
#ifndef VERSION_JP
|
|
|
|
play_sound(SOUND_PEACH_MARIO2, sEndPeachObj->header.gfx.cameraToObject);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (m->actionTimer == 389) {
|
|
|
|
advance_cutscene_step(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void end_peach_cutscene_fade_out(struct MarioState *m) {
|
|
|
|
if (m->actionState == 0) {
|
|
|
|
level_trigger_warp(m, WARP_OP_CREDITS_NEXT);
|
|
|
|
gPaintingMarioYEntry = 1500.0f; // ensure medium water level in WDW credits cutscene
|
|
|
|
m->actionState = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum {
|
|
|
|
END_PEACH_CUTSCENE_MARIO_FALLING,
|
|
|
|
END_PEACH_CUTSCENE_MARIO_LANDING,
|
|
|
|
END_PEACH_CUTSCENE_SUMMON_JUMBO_STAR,
|
|
|
|
END_PEACH_CUTSCENE_SPAWN_PEACH,
|
|
|
|
END_PEACH_CUTSCENE_DESCEND_PEACH,
|
|
|
|
END_PEACH_CUTSCENE_RUN_TO_PEACH,
|
|
|
|
END_PEACH_CUTSCENE_DIALOG_1,
|
|
|
|
END_PEACH_CUTSCENE_DIALOG_2,
|
|
|
|
END_PEACH_CUTSCENE_KISS_FROM_PEACH,
|
|
|
|
END_PEACH_CUTSCENE_STAR_DANCE,
|
|
|
|
END_PEACH_CUTSCENE_DIALOG_3,
|
|
|
|
END_PEACH_CUTSCENE_RUN_TO_CASTLE,
|
|
|
|
END_PEACH_CUTSCENE_FADE_OUT
|
|
|
|
};
|
|
|
|
|
|
|
|
static s32 act_end_peach_cutscene(struct MarioState *m) {
|
|
|
|
switch (m->actionArg) {
|
|
|
|
case END_PEACH_CUTSCENE_MARIO_FALLING:
|
|
|
|
end_peach_cutscene_mario_falling(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_MARIO_LANDING:
|
|
|
|
end_peach_cutscene_mario_landing(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_SUMMON_JUMBO_STAR:
|
|
|
|
end_peach_cutscene_summon_jumbo_star(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_SPAWN_PEACH:
|
|
|
|
end_peach_cutscene_spawn_peach(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_DESCEND_PEACH:
|
|
|
|
end_peach_cutscene_descend_peach(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_RUN_TO_PEACH:
|
|
|
|
end_peach_cutscene_run_to_peach(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_DIALOG_1:
|
|
|
|
end_peach_cutscene_dialog_1(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_DIALOG_2:
|
|
|
|
end_peach_cutscene_dialog_2(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_KISS_FROM_PEACH:
|
|
|
|
end_peach_cutscene_kiss_from_peach(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_STAR_DANCE:
|
|
|
|
end_peach_cutscene_star_dance(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_DIALOG_3:
|
|
|
|
end_peach_cutscene_dialog_3(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_RUN_TO_CASTLE:
|
|
|
|
end_peach_cutscene_run_to_castle(m);
|
|
|
|
break;
|
|
|
|
case END_PEACH_CUTSCENE_FADE_OUT:
|
|
|
|
end_peach_cutscene_fade_out(m);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->actionTimer++;
|
|
|
|
|
|
|
|
sEndCutsceneVp.vp.vscale[0] = 640;
|
|
|
|
sEndCutsceneVp.vp.vscale[1] = 360;
|
|
|
|
sEndCutsceneVp.vp.vtrans[0] = 640;
|
|
|
|
sEndCutsceneVp.vp.vtrans[1] = 480;
|
2020-03-02 03:42:52 +00:00
|
|
|
override_viewport_and_clip(NULL, &sEndCutsceneVp, 0, 0, 0);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define TIMER_CREDITS_SHOW 51
|
|
|
|
#define TIMER_CREDITS_PROGRESS 80
|
|
|
|
#define TIMER_CREDITS_WARP 160
|
2020-06-02 16:44:34 +00:00
|
|
|
#else
|
2020-02-03 05:51:26 +00:00
|
|
|
#define TIMER_CREDITS_SHOW 61
|
|
|
|
#define TIMER_CREDITS_PROGRESS 90
|
|
|
|
#define TIMER_CREDITS_WARP 200
|
|
|
|
#endif
|
|
|
|
|
2019-08-25 04:46:40 +00:00
|
|
|
static s32 act_credits_cutscene(struct MarioState *m) {
|
|
|
|
s32 width;
|
|
|
|
s32 height;
|
|
|
|
|
2020-01-03 15:38:57 +00:00
|
|
|
m->statusForCamera->cameraEvent = CAM_EVENT_START_CREDITS;
|
2020-06-02 16:44:34 +00:00
|
|
|
// checks if Mario is underwater (JRB, DDD, SA, etc.)
|
2019-08-25 04:46:40 +00:00
|
|
|
if (m->pos[1] < m->waterLevel - 100) {
|
2020-08-27 06:29:40 +00:00
|
|
|
if (m->playerIndex == 0 && m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) {
|
2020-01-03 15:38:57 +00:00
|
|
|
set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
set_mario_animation(m, MARIO_ANIM_WATER_IDLE);
|
|
|
|
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
|
|
|
|
// will copy over roll and pitch, if set
|
|
|
|
vec3s_copy(m->marioObj->header.gfx.angle, m->faceAngle);
|
2020-04-03 18:57:26 +00:00
|
|
|
m->particleFlags |= PARTICLE_BUBBLE;
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
|
|
|
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer > 0) {
|
2019-08-25 04:46:40 +00:00
|
|
|
stop_and_set_height_to_floor(m);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer >= TIMER_CREDITS_SHOW) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionState < 40) {
|
2019-08-25 04:46:40 +00:00
|
|
|
m->actionState += 2;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
width = m->actionState * 640 / 100;
|
|
|
|
height = m->actionState * 480 / 100;
|
|
|
|
|
|
|
|
sEndCutsceneVp.vp.vscale[0] = 640 - width;
|
|
|
|
sEndCutsceneVp.vp.vscale[1] = 480 - height;
|
|
|
|
sEndCutsceneVp.vp.vtrans[0] =
|
|
|
|
(gCurrCreditsEntry->unk02 & 0x10 ? width : -width) * 56 / 100 + 640;
|
|
|
|
sEndCutsceneVp.vp.vtrans[1] =
|
|
|
|
(gCurrCreditsEntry->unk02 & 0x20 ? height : -height) * 66 / 100 + 480;
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
override_viewport_and_clip(&sEndCutsceneVp, 0, 0, 0, 0);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer == TIMER_CREDITS_PROGRESS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
reset_cutscene_msg_fade();
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer >= TIMER_CREDITS_PROGRESS) {
|
2019-08-25 04:46:40 +00:00
|
|
|
sDispCreditsEntry = gCurrCreditsEntry;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
if (m->actionTimer++ == TIMER_CREDITS_WARP) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, 24);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
m->marioObj->header.gfx.angle[1] += (gCurrCreditsEntry->unk02 & 0xC0) << 8;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static s32 act_end_waving_cutscene(struct MarioState *m) {
|
|
|
|
if (m->actionState == 0) {
|
2020-01-03 15:38:57 +00:00
|
|
|
m->statusForCamera->cameraEvent = CAM_EVENT_START_END_WAVING;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
sEndPeachObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_PEACH, bhvEndPeach, 60, 906,
|
|
|
|
-1180, 0, 0, 0);
|
|
|
|
|
|
|
|
sEndRightToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, 180,
|
|
|
|
906, -1170, 0, 0, 0);
|
|
|
|
|
|
|
|
sEndLeftToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, -180,
|
|
|
|
906, -1170, 0, 0, 0);
|
|
|
|
|
|
|
|
sEndPeachObj->oOpacity = 255;
|
|
|
|
sEndRightToadObj->oOpacity = 255;
|
|
|
|
sEndLeftToadObj->oOpacity = 255;
|
|
|
|
|
|
|
|
sEndPeachAnimation = 11;
|
|
|
|
sEndToadAnims[0] = 6;
|
|
|
|
sEndToadAnims[1] = 7;
|
|
|
|
|
|
|
|
m->actionState = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_mario_animation(m, MARIO_ANIM_CREDITS_WAVING);
|
|
|
|
stop_and_set_height_to_floor(m);
|
|
|
|
|
|
|
|
m->marioObj->header.gfx.angle[1] += 0x8000;
|
|
|
|
m->marioObj->header.gfx.pos[0] -= 60.0f;
|
|
|
|
m->marioBodyState->handState = MARIO_HAND_RIGHT_OPEN;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->actionTimer++ == 300) {
|
2019-08-25 04:46:40 +00:00
|
|
|
level_trigger_warp(m, WARP_OP_CREDITS_END);
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static s32 check_for_instant_quicksand(struct MarioState *m) {
|
2020-08-07 01:45:39 +00:00
|
|
|
if (m != &gMarioStates[0]) {
|
|
|
|
// never kill remote marios
|
|
|
|
return FALSE;
|
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
if (m->floor->type == SURFACE_INSTANT_QUICKSAND && m->action & ACT_FLAG_INVULNERABLE
|
|
|
|
&& m->action != ACT_QUICKSAND_DEATH) {
|
|
|
|
update_mario_sound_and_camera(m);
|
|
|
|
return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 mario_execute_cutscene_action(struct MarioState *m) {
|
|
|
|
s32 cancel;
|
|
|
|
|
2019-09-01 19:50:50 +00:00
|
|
|
if (check_for_instant_quicksand(m)) {
|
2019-08-25 04:46:40 +00:00
|
|
|
return TRUE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
/* clang-format off */
|
|
|
|
switch (m->action) {
|
|
|
|
case ACT_DISAPPEARED: cancel = act_disappeared(m); break;
|
|
|
|
case ACT_INTRO_CUTSCENE: cancel = act_intro_cutscene(m); break;
|
|
|
|
case ACT_STAR_DANCE_EXIT: cancel = act_star_dance(m); break;
|
|
|
|
case ACT_STAR_DANCE_NO_EXIT: cancel = act_star_dance(m); break;
|
|
|
|
case ACT_STAR_DANCE_WATER: cancel = act_star_dance_water(m); break;
|
|
|
|
case ACT_FALL_AFTER_STAR_GRAB: cancel = act_fall_after_star_grab(m); break;
|
|
|
|
case ACT_READING_AUTOMATIC_DIALOG: cancel = act_reading_automatic_dialog(m); break;
|
|
|
|
case ACT_READING_NPC_DIALOG: cancel = act_reading_npc_dialog(m); break;
|
|
|
|
case ACT_DEBUG_FREE_MOVE: cancel = act_debug_free_move(m); break;
|
|
|
|
case ACT_READING_SIGN: cancel = act_reading_sign(m); break;
|
|
|
|
case ACT_JUMBO_STAR_CUTSCENE: cancel = act_jumbo_star_cutscene(m); break;
|
|
|
|
case ACT_WAITING_FOR_DIALOG: cancel = act_waiting_for_dialog(m); break;
|
|
|
|
case ACT_STANDING_DEATH: cancel = act_standing_death(m); break;
|
|
|
|
case ACT_QUICKSAND_DEATH: cancel = act_quicksand_death(m); break;
|
|
|
|
case ACT_ELECTROCUTION: cancel = act_electrocution(m); break;
|
|
|
|
case ACT_SUFFOCATION: cancel = act_suffocation(m); break;
|
|
|
|
case ACT_DEATH_ON_STOMACH: cancel = act_death_on_stomach(m); break;
|
|
|
|
case ACT_DEATH_ON_BACK: cancel = act_death_on_back(m); break;
|
|
|
|
case ACT_EATEN_BY_BUBBA: cancel = act_eaten_by_bubba(m); break;
|
|
|
|
case ACT_END_PEACH_CUTSCENE: cancel = act_end_peach_cutscene(m); break;
|
|
|
|
case ACT_CREDITS_CUTSCENE: cancel = act_credits_cutscene(m); break;
|
|
|
|
case ACT_END_WAVING_CUTSCENE: cancel = act_end_waving_cutscene(m); break;
|
|
|
|
case ACT_PULLING_DOOR:
|
|
|
|
case ACT_PUSHING_DOOR: cancel = act_going_through_door(m); break;
|
|
|
|
case ACT_WARP_DOOR_SPAWN: cancel = act_warp_door_spawn(m); break;
|
|
|
|
case ACT_EMERGE_FROM_PIPE: cancel = act_emerge_from_pipe(m); break;
|
|
|
|
case ACT_SPAWN_SPIN_AIRBORNE: cancel = act_spawn_spin_airborne(m); break;
|
|
|
|
case ACT_SPAWN_SPIN_LANDING: cancel = act_spawn_spin_landing(m); break;
|
|
|
|
case ACT_EXIT_AIRBORNE: cancel = act_exit_airborne(m); break;
|
|
|
|
case ACT_EXIT_LAND_SAVE_DIALOG: cancel = act_exit_land_save_dialog(m); break;
|
|
|
|
case ACT_DEATH_EXIT: cancel = act_death_exit(m); break;
|
|
|
|
case ACT_UNUSED_DEATH_EXIT: cancel = act_unused_death_exit(m); break;
|
|
|
|
case ACT_FALLING_DEATH_EXIT: cancel = act_falling_death_exit(m); break;
|
|
|
|
case ACT_SPECIAL_EXIT_AIRBORNE: cancel = act_special_exit_airborne(m); break;
|
|
|
|
case ACT_SPECIAL_DEATH_EXIT: cancel = act_special_death_exit(m); break;
|
|
|
|
case ACT_FALLING_EXIT_AIRBORNE: cancel = act_falling_exit_airborne(m); break;
|
|
|
|
case ACT_UNLOCKING_KEY_DOOR: cancel = act_unlocking_key_door(m); break;
|
|
|
|
case ACT_UNLOCKING_STAR_DOOR: cancel = act_unlocking_star_door(m); break;
|
|
|
|
case ACT_ENTERING_STAR_DOOR: cancel = act_entering_star_door(m); break;
|
|
|
|
case ACT_SPAWN_NO_SPIN_AIRBORNE: cancel = act_spawn_no_spin_airborne(m); break;
|
|
|
|
case ACT_SPAWN_NO_SPIN_LANDING: cancel = act_spawn_no_spin_landing(m); break;
|
|
|
|
case ACT_BBH_ENTER_JUMP: cancel = act_bbh_enter_jump(m); break;
|
|
|
|
case ACT_BBH_ENTER_SPIN: cancel = act_bbh_enter_spin(m); break;
|
|
|
|
case ACT_TELEPORT_FADE_OUT: cancel = act_teleport_fade_out(m); break;
|
|
|
|
case ACT_TELEPORT_FADE_IN: cancel = act_teleport_fade_in(m); break;
|
|
|
|
case ACT_SHOCKED: cancel = act_shocked(m); break;
|
|
|
|
case ACT_SQUISHED: cancel = act_squished(m); break;
|
|
|
|
case ACT_HEAD_STUCK_IN_GROUND: cancel = act_head_stuck_in_ground(m); break;
|
|
|
|
case ACT_BUTT_STUCK_IN_GROUND: cancel = act_butt_stuck_in_ground(m); break;
|
|
|
|
case ACT_FEET_STUCK_IN_GROUND: cancel = act_feet_stuck_in_ground(m); break;
|
|
|
|
case ACT_PUTTING_ON_CAP: cancel = act_putting_on_cap(m); break;
|
|
|
|
}
|
|
|
|
/* clang-format on */
|
|
|
|
|
|
|
|
if (!cancel) {
|
2019-09-01 19:50:50 +00:00
|
|
|
if (m->input & INPUT_IN_WATER) {
|
2020-03-02 03:42:52 +00:00
|
|
|
m->particleFlags |= PARTICLE_IDLE_WATER_WAVE;
|
2019-09-01 19:50:50 +00:00
|
|
|
}
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cancel;
|
|
|
|
}
|