sm64coopdx/src/game/mario_actions_cutscene.c
MysterD 906ea3345e Players turn into bubbles when they die
Player life counters are separate.
When one player dies they lose a life and are turned into a bubble.
If the other player pops it, they are alive again.
If all players are bubbled, they get kicked out of the level.
If the bubbled player ran out of lives, they can not come back to life
until the level is over.
Whenever a level change happens, everyone's life count is set to a
minimum of two.
No game overs.

Took heavy inspiration from Kaze Emanuar
2020-09-05 18:10:55 -07:00

2791 lines
91 KiB
C

#include <PR/ultratypes.h>
#include "prevent_bss_reordering.h"
#include "sm64.h"
#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"
#include "game_init.h"
#include "gfx_dimensions.h"
#include "ingame_menu.h"
#include "interaction.h"
#include "level_table.h"
#include "level_update.h"
#include "mario.h"
#include "mario_actions_moving.h"
#include "mario_step.h"
#include "moving_texture.h"
#include "object_helpers.h"
#include "object_list_processor.h"
#include "save_file.h"
#include "seq_ids.h"
#include "sound_init.h"
#include "thread6.h"
#include "../../include/libc/stdlib.h"
#include "pc/pc_main.h"
// TODO: put this elsewhere
enum SaveOption { SAVE_OPT_SAVE_AND_CONTINUE = 1, SAVE_OPT_SAVE_AND_QUIT, SAVE_OPT_SAVE_EXIT_GAME, SAVE_OPT_CONTINUE_DONT_SAVE };
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 };
static BehaviorScript* localDialogNPCBehavior = NULL;
/**
* Data for the jumbo star cutscene. It specifies the flight path after triple
* jumping. Each entry is one keyframe.
* The first number is playback speed, 1000 is the maximum and means it lasts
* 1 frame. 20 means that it lasts 1000/20 = 50 frames.
* Speed 0 marks the last keyframe. Since the cubic spline looks 3 keyframes
* ahead, there should be at least 2 more entries afterwards.
* The last three numbers of each entry are x, y and z coordinates of points
* that define the curve.
*/
static Vec4s sJumboStarKeyframes[27] = {
{ 20, 0, 678, -2916 }, { 30, 0, 680, -3500 }, { 40, 1000, 700, -4000 },
{ 50, 2500, 750, -3500 }, { 50, 3500, 800, -2000 }, { 50, 4000, 850, 0 },
{ 50, 3500, 900, 2000 }, { 50, 2000, 950, 3500 }, { 50, 0, 1000, 4000 },
{ 50, -2000, 1050, 3500 }, { 50, -3500, 1100, 2000 }, { 50, -4000, 1150, 0 },
{ 50, -3500, 1200, -2000 }, { 50, -2000, 1250, -3500 }, { 50, 0, 1300, -4000 },
{ 50, 2000, 1350, -3500 }, { 50, 3500, 1400, -2000 }, { 50, 4000, 1450, 0 },
{ 50, 3500, 1500, 2000 }, { 50, 2000, 1600, 3500 }, { 50, 0, 1700, 4000 },
{ 50, -2000, 1800, 3500 }, { 50, -3500, 1900, 2000 }, { 30, -4000, 2000, 0 },
{ 0, -3500, 2100, -2000 }, { 0, -2000, 2200, -3500 }, { 0, 0, 2300, -4000 },
};
/**
* get_credits_str_width: Calculate width of a Credits String
* Loop over each character in a credits string and increment the length. If the
* character is a space, increment by 4; otherwise increment by 7. Once the next
* character is a null character (equal to 0), stop counting the length since
* that's the end of the string.
*/
s32 get_credits_str_width(char *str) {
u32 c;
s32 length = 0;
while ((c = *str++) != 0) {
length += (c == ' ' ? 4 : 7);
}
return length;
}
#define CREDIT_TEXT_MARGIN_X ((s32)(GFX_DIMENSIONS_ASPECT_RATIO * 21))
#define CREDIT_TEXT_X_LEFT GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(CREDIT_TEXT_MARGIN_X)
#define CREDIT_TEXT_X_RIGHT GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(CREDIT_TEXT_MARGIN_X)
/**
* print_displaying_credits_entry: Print the current displaying Credits Entry
* Called in render_game. This function checks if sDispCreditsEntry points to a
* credits entry (see act_credits_cutscene), and if so, display it. The reason
* this is called every frame in render_game is because the credits need to
* display on top of everything else.
* To print a credits entry, we take the first character of the first string,
* subtract the value of the 0 character, and use that as the number of lines to
* print, excluding the title. Then, we print the title (after advancing the
* pointer by 1) at X 28, Y either 28 or 172, depending on the print at bottom
* flag. Finally, we print each line using the number of lines previously stored
* as a counter, and increase the Y value by either the constant 16 (JP only) or
* by the value of lineHeight.
*/
void print_displaying_credits_entry(void) {
char **currStrPtr;
char *titleStr;
s16 numLines;
s16 strY;
#ifndef VERSION_JP
s16 lineHeight;
#endif
if (sDispCreditsEntry != NULL) {
currStrPtr = (char **) sDispCreditsEntry->unk0C;
titleStr = *currStrPtr++;
numLines = *titleStr++ - '0';
strY = (sDispCreditsEntry->unk02 & 0x20 ? 28 : 172) + (numLines == 1) * 16;
#ifndef VERSION_JP
lineHeight = 16;
#endif
dl_rgba16_begin_cutscene_msg_fade();
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY, titleStr);
#ifndef VERSION_JP
switch (numLines) {
case 4:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 24, *currStrPtr++);
numLines = 2;
lineHeight = 24;
break;
case 5:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++);
numLines = 3;
break;
#ifdef VERSION_EU
case 6:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++);
numLines = 3;
break;
case 7:
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++);
print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++);
numLines = 3;
break;
#endif
}
#endif
while (numLines-- > 0) {
print_credits_str_ascii(CREDIT_TEXT_X_RIGHT - get_credits_str_width(*currStrPtr), strY, *currStrPtr);
#ifdef VERSION_JP
strY += 16;
#else
strY += lineHeight;
#endif
currStrPtr++;
}
dl_rgba16_stop_cutscene_msg_fade();
sDispCreditsEntry = NULL;
}
}
void bhv_end_peach_loop(void) {
cur_obj_init_animation_with_sound(sEndPeachAnimation);
if (cur_obj_check_if_near_animation_end()) {
// anims: 0-3, 4, 5, 6-8, 9, 10, 11
if (sEndPeachAnimation < 3 || sEndPeachAnimation == 6 || sEndPeachAnimation == 7) {
sEndPeachAnimation++;
}
}
}
void bhv_end_toad_loop(void) {
s32 toadAnimIndex = (gCurrentObject->oPosX >= 0.0f);
cur_obj_init_animation_with_sound(sEndToadAnims[toadAnimIndex]);
if (cur_obj_check_if_near_animation_end()) {
// 0-1, 2-3, 4, 5, 6, 7
if (sEndToadAnims[toadAnimIndex] == 0 || sEndToadAnims[toadAnimIndex] == 2) {
sEndToadAnims[toadAnimIndex]++;
}
}
}
// Geo switch case function for controlling Peach's eye state.
s32 geo_switch_peach_eyes(s32 run, struct GraphNode *node, UNUSED s32 a2) {
struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node;
s16 timer;
if (run == TRUE) {
if (D_8032CBE4 == 0) {
timer = (gAreaUpdateCounter + 0x20) >> 1 & 0x1F;
if (timer < 7) {
switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBEC[timer];
} else {
switchCase->selectedCase = D_8032CBE8 * 4 + 1;
}
} else {
switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBE4 - 1;
}
}
return 0;
}
// unused
static void stub_is_textbox_active(u16 *a0) {
if (get_dialog_id() == -1) {
*a0 = 0;
}
}
/**
* get_star_collection_dialog: Determine what dialog should show when Mario
* collects a star.
* Determines if Mario has collected enough stars to get a dialog for it, and
* if so, return the dialog ID. Otherwise, return 0. A dialog is returned if
* numStars has reached a milestone and prevNumStarsForDialog has not reached it.
*/
s32 get_star_collection_dialog(struct MarioState *m) {
s32 i;
s32 dialogID = 0;
s32 numStarsRequired;
for (i = 0; i < 6; i++) {
numStarsRequired = sStarsNeededForDialog[i];
if (m->prevNumStarsForDialog < numStarsRequired && m->numStars >= numStarsRequired) {
dialogID = i + DIALOG_141;
break;
}
}
m->prevNumStarsForDialog = m->numStars;
return dialogID;
}
// save menu handler
void handle_save_menu(struct MarioState *m) {
s32 dialogID;
// wait for the menu to show up
if (is_anim_past_end(m) && gSaveOptSelectIndex != 0) {
// save and continue / save and quit
if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_CONTINUE || gSaveOptSelectIndex == SAVE_OPT_SAVE_EXIT_GAME || gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) {
save_file_do_save(gCurrSaveFileNum - 1);
if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) {
fade_into_special_warp(-2, 0); // reset game
} else if (gSaveOptSelectIndex == SAVE_OPT_SAVE_EXIT_GAME) {
//initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0);
fade_into_special_warp(0, 0);
game_exit();
}
}
// not quitting
if (gSaveOptSelectIndex != SAVE_OPT_SAVE_EXIT_GAME) {
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);
}
}
}
}
/**
* spawn_obj_at_mario_rel_yaw: Spawns object at Mario with relative yaw.
* Spawns object with given behavior and model and copies over Mario's position
* and yaw plus relative yaw.
*/
struct Object *spawn_obj_at_mario_rel_yaw(struct MarioState *m, s32 model, const BehaviorScript *behavior, s16 relYaw) {
struct Object *o = spawn_object(m->marioObj, model, behavior);
o->oFaceAngleYaw = m->faceAngle[1] + relYaw;
o->oPosX = m->pos[0];
o->oPosY = m->pos[1];
o->oPosZ = m->pos[2];
return o;
}
/**
* cutscene_take_cap_off: Put Mario's cap on.
* Clears "cap on head" flag, sets "cap in hand" flag, plays sound
* SOUND_ACTION_UNKNOWN43D.
*/
void cutscene_take_cap_off(struct MarioState *m) {
m->flags &= ~MARIO_CAP_ON_HEAD;
m->flags |= MARIO_CAP_IN_HAND;
play_sound(SOUND_ACTION_UNKNOWN43D, m->marioObj->header.gfx.cameraToObject);
}
/**
* cutscene_put_cap_on: Put Mario's cap on.
* Clears "cap in hand" flag, sets "cap on head" flag, plays sound
* SOUND_ACTION_UNKNOWN43E.
*/
void cutscene_put_cap_on(struct MarioState *m) {
m->flags &= ~MARIO_CAP_IN_HAND;
m->flags |= MARIO_CAP_ON_HEAD;
play_sound(SOUND_ACTION_UNKNOWN43E, m->marioObj->header.gfx.cameraToObject);
}
/**
* mario_ready_to_speak: Determine if Mario is able to speak to a NPC
* The following conditions must be met in order for Mario to be considered
* ready to speak.
* 1: Mario's action must be in the stationary or moving action groups, or if
* not, he must be in the "waiting for dialog" state.
* 2: Mario mat not be riding a shell or be invulnerable.
* 3: Mario must not be in first person mode.
*/
s32 mario_ready_to_speak(struct MarioState* m) {
u32 actionGroup = m->action & ACT_GROUP_MASK;
s32 isReadyToSpeak = FALSE;
if ((m->action == ACT_WAITING_FOR_DIALOG || actionGroup == ACT_GROUP_STATIONARY
|| actionGroup == ACT_GROUP_MOVING)
&& (!(m->action & (ACT_FLAG_RIDING_SHELL | ACT_FLAG_INVULNERABLE))
&& m->action != ACT_FIRST_PERSON)) {
isReadyToSpeak = TRUE;
}
return isReadyToSpeak;
}
// (can) place Mario in dialog?
// initiate dialog?
// return values:
// 0 = not in dialog
// 1 = starting dialog
// 2 = speaking
s32 set_mario_npc_dialog(struct MarioState* m, s32 actionArg) {
s32 dialogState = 0;
if (m->playerIndex == 0 && actionArg == 0) {
localDialogNPCBehavior = NULL;
continueDialogFunction = NULL;
continueDialogFunctionObject = NULL;
}
// in dialog
if (m->action == ACT_READING_NPC_DIALOG) {
if (m->actionState < 8) {
dialogState = 1; // starting dialog
}
if (m->actionState == 8) {
if (actionArg == 0) {
m->actionState++; // exit dialog
} else {
dialogState = 2;
}
}
} else if (actionArg != 0 && mario_ready_to_speak(m)) {
m->usedObj = gCurrentObject;
set_mario_action(m, ACT_READING_NPC_DIALOG, actionArg);
if (m->playerIndex == 0) { localDialogNPCBehavior = (BehaviorScript*)m->usedObj->behavior; }
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;
if (m->playerIndex == 0) {
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) {
set_mario_npc_dialog(m, 0);
}
}
if (m->actionArg == 2) {
headTurnAmount = -1024;
}
if (m->actionArg == 3) {
headTurnAmount = 384;
}
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;
}
// set animation
set_mario_animation(m, m->heldObj == NULL ? MARIO_ANIM_FIRST_PERSON
: MARIO_ANIM_IDLE_WITH_LIGHT_OBJ);
} else if (m->actionState >= 9 && m->actionState < 17) {
// look back from facing NPC
m->actionTimer -= headTurnAmount;
} else if (m->playerIndex == 0 && m->actionState == 23) {
if (m->flags & MARIO_CAP_IN_HAND) {
set_mario_action(m, ACT_PUTTING_ON_CAP, 0);
} else {
set_mario_action(m, m->heldObj == NULL ? ACT_IDLE : ACT_HOLD_IDLE, 0);
}
}
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
if (m->playerIndex == 0 && m->actionState != 8) {
m->actionState++;
}
return FALSE;
}
// puts Mario in a state where he's waiting for (npc) dialog; doesn't do much
s32 act_waiting_for_dialog(struct MarioState *m) {
set_mario_animation(m,
m->heldObj == NULL ? MARIO_ANIM_FIRST_PERSON : MARIO_ANIM_IDLE_WITH_LIGHT_OBJ);
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
return FALSE;
}
// makes Mario disappear and triggers warp
s32 act_disappeared(struct MarioState *m) {
set_mario_animation(m, MARIO_ANIM_A_POSE);
stop_and_set_height_to_floor(m);
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
if (m->actionArg) {
m->actionArg--;
if ((m->actionArg & 0xFFFF) == 0) {
level_trigger_warp(m, m->actionArg >> 16);
}
}
return FALSE;
}
s32 act_reading_automatic_dialog(struct MarioState *m) {
u32 actionArg;
m->actionState++;
if (m->actionState == 2) {
//enable_time_stop();
}
if (m->actionState < 9) {
set_mario_animation(m, m->prevAction == ACT_STAR_DANCE_WATER ? MARIO_ANIM_WATER_IDLE
: MARIO_ANIM_FIRST_PERSON);
// always look up for automatic dialogs
m->actionTimer -= 1024;
} else {
// set Mario dialog
if (m->actionState == 9) {
// 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));
}
}
}
// wait until dialog is done
else if (m->actionState == 10) {
if (get_dialog_id() >= 0) {
m->actionState--;
}
}
// look back down
else if (m->actionState < 19) {
m->actionTimer += 1024;
}
// finished action
else if (m->actionState == 25) {
disable_time_stop();
if (gShouldNotPlayCastleMusic) {
gShouldNotPlayCastleMusic = FALSE;
play_cutscene_music(SEQUENCE_ARGS(0, SEQ_LEVEL_INSIDE_CASTLE));
}
if (m->prevAction == ACT_STAR_DANCE_WATER) {
set_mario_action(m, ACT_WATER_IDLE, 0); // 100c star?
} else {
// make Mario walk into door after star dialog
set_mario_action(m, m->prevAction == ACT_UNLOCKING_STAR_DOOR ? ACT_WALKING : ACT_IDLE,
0);
}
}
}
// apply head turn
vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0);
return FALSE;
}
s32 act_reading_sign(struct MarioState *m) {
struct Object *marioObj = m->marioObj;
if (m->playerIndex != 0) {
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
return FALSE;
}
play_sound_if_no_flag(m, SOUND_ACTION_READ_SIGN, MARIO_ACTION_SOUND_PLAYED);
switch (m->actionState) {
// start dialog
case 0:
if (m == &gMarioStates[0]) {
trigger_cutscene_dialog(1);
}
//enable_time_stop();
// reading sign
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
m->actionState = 1;
// intentional fall through
// turn toward sign
case 1:
m->faceAngle[1] += marioObj->oMarioPoleUnk108 / 11;
m->pos[0] += marioObj->oMarioReadingSignDPosX / 11.0f;
m->pos[2] += marioObj->oMarioReadingSignDPosZ / 11.0f;
// create the text box
if (m->actionTimer++ == 10) {
if (m == &gMarioStates[0]) {
create_dialog_inverted_box(m->usedObj->oBehParams2ndByte);
}
m->actionState = 2;
}
break;
// in dialog
case 2:
// dialog finished
if (gCamera->cutscene == 0) {
disable_time_stop();
set_mario_action(m, ACT_IDLE, 0);
}
break;
}
vec3f_copy(marioObj->header.gfx.pos, m->pos);
vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
return FALSE;
}
// 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;
if (gPlayer1Controller->buttonDown & L_TRIG) {
speed = 0.01f;
}
set_mario_animation(m, MARIO_ANIM_A_POSE);
vec3f_copy(pos, m->pos);
if (gPlayer1Controller->buttonDown & U_JPAD) {
pos[1] += 16.0f * speed;
}
if (gPlayer1Controller->buttonDown & D_JPAD) {
pos[1] -= 16.0f * speed;
}
if (m->intendedMag > 0) {
pos[0] += 32.0f * speed * sins(m->intendedYaw);
pos[2] += 32.0f * speed * coss(m->intendedYaw);
}
resolve_and_return_wall_collisions(pos, 60.0f, 50.0f);
floorHeight = find_floor(pos[0], pos[1], pos[2], &surf);
if (surf != NULL) {
if (pos[1] < floorHeight) {
pos[1] = floorHeight;
}
vec3f_copy(m->pos, pos);
}
m->faceAngle[1] = m->intendedYaw;
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
if (gPlayer1Controller->buttonPressed == A_BUTTON) {
if (m->pos[1] <= m->waterLevel - 100) {
action = ACT_WATER_IDLE;
} else {
action = ACT_IDLE;
}
set_mario_action(m, action, 0);
}
return FALSE;
}
// 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);
disable_background_sound();
if (m->actionArg & 1) {
play_course_clear();
} else {
if (gCurrLevelNum == LEVEL_BOWSER_1 || gCurrLevelNum == LEVEL_BOWSER_2) {
play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_KEY), 0);
} else {
play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_STAR), 0);
}
}
break;
case 42:
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
break;
case 80:
if ((m->actionArg & 1) == 0) {
level_trigger_warp(m, WARP_OP_STAR_EXIT);
} else {
//enable_time_stop();
create_dialog_box_with_response(gLastCompletedStarNum == 7 ? DIALOG_013 : DIALOG_014);
m->actionState = 1;
}
break;
}
} else if (m->actionState == 1 && gDialogResponse) {
if (gDialogResponse == 1) {
save_file_do_save(gCurrSaveFileNum - 1);
}
m->actionState = 2;
} else if (m->actionState == 2 && is_anim_at_end(m)) {
disable_time_stop();
enable_background_sound();
dialogID = get_star_collection_dialog(m);
if (dialogID != 0) {
// look up for dialog
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID);
} else {
set_mario_action(m, isInWater ? ACT_WATER_IDLE : ACT_IDLE, 0);
}
}
}
s32 act_star_dance(struct MarioState *m) {
m->faceAngle[1] = m->area->camera->yaw;
set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_STAR_DANCE
: MARIO_ANIM_STAR_DANCE);
general_star_dance_handler(m, 0);
if (m->actionState != 2 && m->actionTimer >= 40) {
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
}
stop_and_set_height_to_floor(m);
return FALSE;
}
s32 act_star_dance_water(struct MarioState *m) {
m->faceAngle[1] = m->area->camera->yaw;
set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_WATER_STAR_DANCE
: MARIO_ANIM_WATER_STAR_DANCE);
vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
general_star_dance_handler(m, 1);
if (m->actionState != 2 && m->actionTimer >= 62) {
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
}
return FALSE;
}
s32 act_fall_after_star_grab(struct MarioState *m) {
if (m->pos[1] < m->waterLevel - 130) {
play_sound(SOUND_ACTION_UNKNOWN430, m->marioObj->header.gfx.cameraToObject);
m->particleFlags |= PARTICLE_WATER_SPLASH;
return set_mario_action(m, ACT_STAR_DANCE_WATER, m->actionArg);
}
if (perform_air_step(m, 1) == AIR_STEP_LANDED) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
set_mario_action(m, m->actionArg & 1 ? ACT_STAR_DANCE_NO_EXIT : ACT_STAR_DANCE_EXIT,
m->actionArg);
}
set_mario_animation(m, MARIO_ANIM_GENERAL_FALL);
return FALSE;
}
// general death hander
s32 common_death_handler(struct MarioState *m, s32 animation, s32 frameToDeathWarp) {
s32 animFrame = set_mario_animation(m, animation);
if (animFrame == frameToDeathWarp) {
//level_trigger_warp(m, WARP_OP_DEATH);
mario_set_bubbled(m);
}
m->marioBodyState->eyeState = MARIO_EYES_DEAD;
stop_and_set_height_to_floor(m);
return animFrame;
}
s32 act_standing_death(struct MarioState *m) {
if (m->input & INPUT_IN_POISON_GAS) {
return set_mario_action(m, ACT_SUFFOCATION, 0);
}
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
common_death_handler(m, MARIO_ANIM_DYING_FALL_OVER, 80);
if (m->marioObj->header.gfx.unk38.animFrame == 77) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
}
return FALSE;
}
s32 act_electrocution(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
common_death_handler(m, MARIO_ANIM_ELECTROCUTION, 43);
return FALSE;
}
s32 act_suffocation(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
common_death_handler(m, MARIO_ANIM_SUFFOCATING, 86);
return FALSE;
}
s32 act_death_on_back(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
if (common_death_handler(m, MARIO_ANIM_DYING_ON_BACK, 54) == 40) {
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
}
return FALSE;
}
s32 act_death_on_stomach(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
if (common_death_handler(m, MARIO_ANIM_DYING_ON_STOMACH, 37) == 37) {
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND);
}
return FALSE;
}
s32 act_quicksand_death(struct MarioState *m) {
if (m->actionState == 0) {
set_mario_animation(m, MARIO_ANIM_DYING_IN_QUICKSAND);
set_anim_to_frame(m, 60);
m->actionState = 1;
}
if (m->actionState == 1) {
if (m->quicksandDepth >= 100.0f) {
play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED);
}
if ((m->quicksandDepth += 5.0f) >= 180.0f) {
level_trigger_warp(m, WARP_OP_DEATH);
m->actionState = 2;
}
}
stationary_ground_step(m);
play_sound(SOUND_MOVING_QUICKSAND_DEATH, m->marioObj->header.gfx.cameraToObject);
return FALSE;
}
s32 act_eaten_by_bubba(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED);
set_mario_animation(m, MARIO_ANIM_A_POSE);
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
if (m != &gMarioStates[0]) {
// never kill remote marios
m->health = 0x100;
} else {
m->health = 0xFF;
}
if (m->actionTimer++ == 60) {
//level_trigger_warp(m, WARP_OP_DEATH);
mario_set_bubbled(m);
}
return FALSE;
}
// set animation and forwardVel; when perform_air_step returns AIR_STEP_LANDED,
// set the new action
s32 launch_mario_until_land(struct MarioState *m, s32 endAction, s32 animation, f32 forwardVel) {
s32 airStepLanded;
mario_set_forward_vel(m, forwardVel);
set_mario_animation(m, animation);
airStepLanded = (perform_air_step(m, 0) == AIR_STEP_LANDED);
if (airStepLanded) {
set_mario_action(m, endAction, 0);
}
return airStepLanded;
}
s32 act_unlocking_key_door(struct MarioState *m) {
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
m->pos[0] = m->usedObj->oPosX + coss(m->faceAngle[1]) * 75.0f;
m->pos[2] = m->usedObj->oPosZ + sins(m->faceAngle[1]) * 75.0f;
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
if (m->actionTimer == 0) {
spawn_obj_at_mario_rel_yaw(m, MODEL_BOWSER_KEY_CUTSCENE, bhvBowserKeyUnlockDoor, 0);
set_mario_animation(m, MARIO_ANIM_UNLOCK_DOOR);
}
switch (m->marioObj->header.gfx.unk38.animFrame) {
case 79:
play_sound(SOUND_GENERAL_DOOR_INSERT_KEY, m->marioObj->header.gfx.cameraToObject);
break;
case 111:
play_sound(SOUND_GENERAL_DOOR_TURN_KEY, m->marioObj->header.gfx.cameraToObject);
break;
}
update_mario_pos_for_anim(m);
stop_and_set_height_to_floor(m);
if (is_anim_at_end(m)) {
if (m->usedObj->oBehParams >> 24 == 1) {
save_file_set_flags(SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR);
save_file_clear_flags(SAVE_FLAG_HAVE_KEY_2);
} else {
save_file_set_flags(SAVE_FLAG_UNLOCKED_BASEMENT_DOOR);
save_file_clear_flags(SAVE_FLAG_HAVE_KEY_1);
}
set_mario_action(m, ACT_WALKING, 0);
}
m->actionTimer++;
return FALSE;
}
s32 act_unlocking_star_door(struct MarioState *m) {
switch (m->actionState) {
case 0:
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
m->marioObj->oMarioReadingSignDPosX = m->pos[0];
m->marioObj->oMarioReadingSignDPosZ = m->pos[2];
set_mario_animation(m, MARIO_ANIM_SUMMON_STAR);
m->actionState++;
break;
case 1:
if (is_anim_at_end(m)) {
spawn_object(m->marioObj, MODEL_STAR, bhvUnlockDoorStar);
m->actionState++;
}
break;
case 2:
if (m->actionTimer++ == 70) {
set_mario_animation(m, MARIO_ANIM_RETURN_STAR_APPROACH_DOOR);
m->actionState++;
}
break;
case 3:
if (is_anim_at_end(m)) {
save_file_set_flags(get_door_save_file_flag(m->usedObj));
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_038);
}
break;
}
m->pos[0] = m->marioObj->oMarioReadingSignDPosX;
m->pos[2] = m->marioObj->oMarioReadingSignDPosZ;
update_mario_pos_for_anim(m);
stop_and_set_height_to_floor(m);
return FALSE;
}
// 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;
if (m->actionArg & 2) {
targetAngle += 0x5556; // ~120 degrees / 1/3 rot (total 150d / 5/12)
}
// targetDX and targetDZ are the offsets to add to Mario's position to
// have Mario stand 150 units in front of the door
targetDX = m->usedObj->oPosX + 150.0f * sins(targetAngle) - m->pos[0];
targetDZ = m->usedObj->oPosZ + 150.0f * coss(targetAngle) - m->pos[2];
m->marioObj->oMarioReadingSignDPosX = targetDX / 20.0f;
m->marioObj->oMarioReadingSignDPosZ = targetDZ / 20.0f;
m->faceAngle[1] = atan2s(targetDZ, targetDX);
}
// set Mario's animation
if (m->actionTimer < 15) {
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
}
// go through door? for 20 frames
else if (m->actionTimer < 35) {
m->pos[0] += m->marioObj->oMarioReadingSignDPosX;
m->pos[2] += m->marioObj->oMarioReadingSignDPosZ;
set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, 0x00028000);
}
else {
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
m->pos[0] += 12.0f * sins(m->faceAngle[1]);
m->pos[2] += 12.0f * coss(m->faceAngle[1]);
set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, 0x00028000);
}
stop_and_set_height_to_floor(m);
if (m->actionTimer == 48) {
set_mario_action(m, ACT_IDLE, 0);
}
return FALSE;
}
s32 act_going_through_door(struct MarioState *m) {
if (m->actionTimer == 0) {
if (m->actionArg & 1) {
m->interactObj->oInteractStatus = 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);
}
}
if (m->usedObj != NULL) {
m->faceAngle[1] = m->usedObj->oMoveAngleYaw;
m->pos[0] = m->usedObj->oPosX;
m->pos[2] = m->usedObj->oPosZ;
}
update_mario_pos_for_anim(m);
stop_and_set_height_to_floor(m);
if (m->actionArg & 4) {
if (m->actionTimer == 16) {
level_trigger_warp(m, WARP_OP_WARP_DOOR);
}
} else {
if (is_anim_at_end(m)) {
if (m->actionArg & 2) {
m->faceAngle[1] += 0x8000;
}
set_mario_action(m, ACT_IDLE, 0);
}
}
m->actionTimer++;
return FALSE;
}
s32 act_warp_door_spawn(struct MarioState *m) {
if (m->actionState == 0) {
m->actionState = 1;
if (m->usedObj != NULL) {
if (m->actionArg & 1) {
m->usedObj->oInteractStatus = 0x00040000;
}
else {
m->usedObj->oInteractStatus = 0x00080000;
}
}
} else if (m->usedObj == NULL || m->usedObj->oAction == 0) {
if (gShouldNotPlayCastleMusic == TRUE && gCurrLevelNum == LEVEL_CASTLE) {
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_021);
} else {
set_mario_action(m, ACT_IDLE, 0);
}
}
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
stop_and_set_height_to_floor(m);
return FALSE;
}
s32 act_emerge_from_pipe(struct MarioState *m) {
struct Object *marioObj = m->marioObj;
if (m->actionTimer++ < 11) {
marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
return FALSE;
}
marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
if (gCurrLevelNum == LEVEL_THI) {
if (gCurrAreaIndex == 2) {
play_sound_if_no_flag(m, SOUND_MENU_EXIT_PIPE, MARIO_ACTION_SOUND_PLAYED);
} else {
play_sound_if_no_flag(m, SOUND_MENU_ENTER_PIPE, MARIO_ACTION_SOUND_PLAYED);
}
}
if (launch_mario_until_land(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) {
mario_set_forward_vel(m, 0.0f);
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
}
return FALSE;
}
s32 act_spawn_spin_airborne(struct MarioState *m) {
// entered water, exit action
if (m->pos[1] < m->waterLevel - 100) {
load_level_init_text(0);
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) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
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) {
if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) { // first anim frame
play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject);
}
}
// 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)) {
load_level_init_text(0);
set_mario_action(m, ACT_IDLE, 0);
}
return FALSE;
}
/**
* act_exit_airborne: Jump out of a level after collecting a Power Star (no
** 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++
&& launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, -32.0f)) {
// heal Mario
m->healCounter = 31;
}
// 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) {
if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, 0.0f)) {
// heal Mario
m->healCounter = 31;
}
// rotate Mario to face away from the entrance
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);
play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING);
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);
// only allow for local player
if (m == &gMarioStates[0] && is_anim_past_end(m)) {
if (gLastCompletedCourseNum != COURSE_BITDW
&& gLastCompletedCourseNum != COURSE_BITFS) {
//enable_time_stop();
}
set_menu_mode(RENDER_COURSE_DONE_SCREEN);
gSaveOptSelectIndex = 0;
m->actionState = 3; // star exit with cap
if (!(m->flags & MARIO_CAP_ON_HEAD)) {
m->actionState = 2; // star exit without cap
}
if (gLastCompletedCourseNum == COURSE_BITDW
|| gLastCompletedCourseNum == COURSE_BITFS) {
m->actionState = 1; // key exit
}
}
break;
// key exit
case 1:
animFrame = set_mario_animation(m, MARIO_ANIM_THROW_CATCH_KEY);
switch (animFrame) {
case -1:
spawn_obj_at_mario_rel_yaw(m, MODEL_BOWSER_KEY_CUTSCENE, bhvBowserKeyCourseExit, -32768);
//! fall through
case 67:
play_sound(SOUND_ACTION_KEY_SWISH, m->marioObj->header.gfx.cameraToObject);
//! fall through
case 83:
play_sound(SOUND_ACTION_PAT_BACK, m->marioObj->header.gfx.cameraToObject);
//! fall through
case 111:
play_sound(SOUND_ACTION_UNKNOWN45C, m->marioObj->header.gfx.cameraToObject);
// no break
}
// only allow for local player
if (m == &gMarioStates[0]) { handle_save_menu(m); }
break;
// exit without cap
case 2:
animFrame = set_mario_animation(m, MARIO_ANIM_MISSING_CAP);
if ((animFrame >= 18 && animFrame < 55) || (animFrame >= 112 && animFrame < 134)) {
m->marioBodyState->handState = MARIO_HAND_OPEN;
}
if (!(animFrame < 109) && animFrame < 154) {
m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED;
}
// only allow for local player
if (m == &gMarioStates[0]) { handle_save_menu(m); }
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:
play_sound(SOUND_ACTION_BRUSH_HAIR, m->marioObj->header.gfx.cameraToObject);
break;
case 82:
cutscene_put_cap_on(m);
break;
}
// only allow for local player
if (m == &gMarioStates[0]) { handle_save_menu(m); }
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);
#endif
queue_rumble_data_mario(m, 5, 80);
//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);
#endif
queue_rumble_data_mario(m, 5, 80);
//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;
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
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)) {
// heal Mario
m->healCounter = 31;
m->actionArg = 1;
}
m->particleFlags |= PARTICLE_SPARKLES;
// rotate Mario to face away from the entrance
marioObj->header.gfx.angle[1] += 0x8000;
// show Mario
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)) {
queue_rumble_data_mario(m, 5, 80);
//m->numLives--;
m->healCounter = 31;
}
// show Mario
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);
if (m->pos[1] < m->waterLevel - 100) {
set_water_plunge_action(m);
}
return FALSE;
}
s32 act_spawn_no_spin_landing(struct MarioState *m) {
play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING);
set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);
stop_and_set_height_to_floor(m);
if (is_anim_at_end(m)) {
load_level_init_text(0);
set_mario_action(m, ACT_IDLE, 0);
}
return FALSE;
}
s32 act_bbh_enter_spin(struct MarioState *m) {
f32 floorDist;
f32 scale;
f32 cageDX = 0;
f32 cageDZ = 0;
f32 cageDist;
f32 forwardVel;
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];
}
cageDist = sqrtf(cageDX * cageDX + cageDZ * cageDZ);
if (cageDist > 20.0f) {
forwardVel = 10.0f;
} else {
forwardVel = cageDist / 2.0f;
}
if (forwardVel < 0.5f) {
forwardVel = 0.0f;
}
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);
if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) {
play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject);
}
m->flags &= ~MARIO_UNKNOWN_08;
perform_air_step(m, 0);
if (m->vel[1] <= 0) {
m->actionState = 2;
}
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);
queue_rumble_data_mario(m, 15, 80);
m->actionState = 4;
}
if (m->actionState == 2) {
if (m->marioObj->header.gfx.unk38.animFrame == 0) {
m->actionState = 3;
}
} else {
play_sound_if_no_flag(m, SOUND_ACTION_SHRINK_INTO_BBH, MARIO_ACTION_SOUND_PLAYED);
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;
play_mario_action_sound(
m, m->flags & MARIO_METAL_CAP ? SOUND_ACTION_METAL_JUMP : SOUND_ACTION_TERRAIN_JUMP, 1);
play_mario_jump_sound(m);
if (m->usedObj == NULL) {
m->usedObj = cur_obj_nearest_object_with_behavior(bhvBooCage);
}
if (m->actionState == 0 && m->usedObj != NULL) {
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);
if (m->vel[1] <= 0.0f) {
set_mario_action(m, ACT_BBH_ENTER_SPIN, 0);
}
return FALSE;
}
s32 act_teleport_fade_out(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED);
set_mario_animation(m, m->prevAction == ACT_CROUCHING ? MARIO_ANIM_CROUCHING
: MARIO_ANIM_FIRST_PERSON);
if (m->actionTimer == 0) {
queue_rumble_data_mario(m, 30, 70);
func_sh_8024C89C(2);
}
m->flags |= MARIO_TELEPORTING;
if (m->actionTimer < 32) {
m->fadeWarpOpacity = (-m->actionTimer << 3) + 0xF8;
}
if (m->actionTimer++ == 20) {
if (m == &gMarioStates[0]) {
// only do for local player
level_trigger_warp(m, WARP_OP_TELEPORT);
}
}
stop_and_set_height_to_floor(m);
return FALSE;
}
s32 act_teleport_fade_in(struct MarioState *m) {
play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED);
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
if (m->actionTimer == 0) {
queue_rumble_data_mario(m, 30, 70);
func_sh_8024C89C(2);
}
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.
if (m->playerIndex == 0 && m->area->camera->mode != CAMERA_MODE_WATER_SURFACE) {
set_camera_mode(m->area->camera, CAMERA_MODE_WATER_SURFACE, 1);
}
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) {
play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED);
play_sound(SOUND_MOVING_SHOCKED, m->marioObj->header.gfx.cameraToObject);
set_camera_shake_from_hit(SHAKE_SHOCK);
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) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
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) {
UNUSED s32 pad;
f32 squishAmount;
f32 spaceUnderCeil;
s16 surfAngle;
s32 underSteepSurf = FALSE; // seems to be responsible for setting velocity?
if ((spaceUnderCeil = m->ceilHeight - m->floorHeight) < 0) {
spaceUnderCeil = 0;
}
switch (m->actionState) {
case 0:
if (spaceUnderCeil > 160.0f) {
m->squishTimer = 0;
// 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;
}
}
m->squishTimer = 0xFF;
if (spaceUnderCeil >= 10.1f) {
// Mario becomes a pancake
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;
play_sound_if_no_flag(m, SOUND_MARIO_ATTACKED, MARIO_MARIO_SOUND_PLAYED);
}
// 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);
queue_rumble_data_mario(m, 10, 80);
m->actionState = 1;
}
break;
case 1:
if (spaceUnderCeil >= 30.0f) {
m->actionState = 2;
}
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);
drop_and_set_mario_action(m, ACT_DEATH_ON_BACK, 0);
m->squishTimer = 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;
}
}
// squished for more than 10 seconds, so kill Mario
if (m->actionArg++ > 300) {
// 0 units of health
if (m != &gMarioStates[0]) {
// never kill remote marios
m->health = 0x100;
} else {
m->health = 0xFF;
}
m->hurtCounter = 0;
//level_trigger_warp(m, WARP_OP_DEATH);
// woosh, he's gone!
//set_mario_action(m, ACT_DISAPPEARED, 0);
mario_set_bubbled(m);
}
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);
if (animFrame == 0) {
//enable_time_stop();
}
if (animFrame == 28) {
cutscene_put_cap_on(m);
}
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);
if (animFrame == -1) {
play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_STUCK_IN_GROUND, 1);
} else if (animFrame == unstuckFrame) {
queue_rumble_data_mario(m, 5, 80);
play_sound_and_spawn_particles(m, SOUND_ACTION_UNSTUCK_FROM_GROUND, 1);
} else if (animFrame == target2 || animFrame == target3) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
}
if (is_anim_at_end(m)) {
set_mario_action(m, endAction, 0);
}
}
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;
}
/**
* advance_cutscene_step: Advances the current step in the current cutscene.
* 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;
m->statusForCamera->cameraEvent = CAM_EVENT_START_INTRO;
m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
advance_cutscene_step(m);
}
#ifdef VERSION_EU
#define TIMER_SPAWN_PIPE 47
#else
#define TIMER_SPAWN_PIPE 37
#endif
static void intro_cutscene_peach_lakitu_scene(struct MarioState *m) {
if ((s16) m->statusForCamera->cameraEvent != CAM_EVENT_START_INTRO) {
if (m->actionTimer++ == TIMER_SPAWN_PIPE) {
sIntroWarpPipeObj =
spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_CASTLE_GROUNDS_WARP_PIPE,
bhvStaticObject, -1328, 60, 4664, 0, 180, 0);
advance_cutscene_step(m);
}
}
}
#undef TIMER_SPAWN_PIPE
#ifdef VERSION_EU
#define TIMER_RAISE_PIPE 28
#else
#define TIMER_RAISE_PIPE 38
#endif
static void intro_cutscene_raise_pipe(struct MarioState *m) {
sIntroWarpPipeObj->oPosY = camera_approach_f32_symmetric(sIntroWarpPipeObj->oPosY, 260.0f, 10.0f);
if (m->actionTimer == 0) {
play_sound(SOUND_MENU_EXIT_PIPE, sIntroWarpPipeObj->header.gfx.cameraToObject);
}
if (m->actionTimer++ == TIMER_RAISE_PIPE) {
m->vel[1] = 60.0f;
advance_cutscene_step(m);
}
}
#undef TIMER_RAISE_PIPE
static void intro_cutscene_jump_out_of_pipe(struct MarioState *m) {
if (m->actionTimer == 25) {
gHudDisplay.flags = HUD_DISPLAY_DEFAULT;
}
if (m->actionTimer++ >= 118) {
m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
#ifdef VERSION_EU
// For some reason these calls were swapped.
play_sound_if_no_flag(m, SOUND_ACTION_HIT_3, MARIO_ACTION_SOUND_PLAYED);
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
#else
play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED);
#ifndef VERSION_JP
play_sound_if_no_flag(m, SOUND_ACTION_HIT_3, MARIO_ACTION_SOUND_PLAYED);
#endif
#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);
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
#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);
if (is_anim_at_end(m)) {
advance_cutscene_step(m);
}
stop_and_set_height_to_floor(m);
}
static void intro_cutscene_lower_pipe(struct MarioState *m) {
if (m->actionTimer++ == 0) {
play_sound(SOUND_MENU_ENTER_PIPE, sIntroWarpPipeObj->header.gfx.cameraToObject);
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
}
sIntroWarpPipeObj->oPosY -= 5.0f;
if (sIntroWarpPipeObj->oPosY <= 50.0f) {
obj_mark_for_deletion(sIntroWarpPipeObj);
advance_cutscene_step(m);
}
stop_and_set_height_to_floor(m);
}
static void intro_cutscene_set_mario_to_idle(struct MarioState *m) {
if (gCamera->cutscene == 0) {
if (m->playerIndex == 0) {
gCameraMovementFlags &= ~CAM_MOVE_C_UP_MODE;
}
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;
}
// jumbo star cutscene: Mario lands after grabbing the jumbo star
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));
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
m->actionState++;
}
} else {
set_mario_animation(m, MARIO_ANIM_GENERAL_LAND);
if (is_anim_at_end(m)) {
m->statusForCamera->cameraEvent = CAM_EVENT_START_GRAND_STAR;
advance_cutscene_step(m);
}
}
}
// jumbo star cutscene: Mario takes off
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)) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
m->actionState++;
}
} else {
animFrame = set_mario_animation(m, MARIO_ANIM_FINAL_BOWSER_WING_CAP_TAKE_OFF);
if (animFrame == 3 || animFrame == 28 || animFrame == 60) {
play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_JUMP, 1);
}
if (animFrame >= 3) {
marioObj->rawData.asF32[0x22] -= 32.0f;
}
switch (animFrame) {
case 3:
play_sound(SOUND_MARIO_YAH_WAH_HOO + (gAudioRandom % 3 << 16),
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;
if (is_anim_past_end(m)) {
advance_cutscene_step(m);
}
}
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;
}
// jumbo star cutscene: Mario flying
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)) {
// does this twice
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;
if (m->actionTimer++ == 500) {
level_trigger_warp(m, WARP_OP_CREDITS_START);
}
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;
}
void generate_yellow_sparkles(s16 x, s16 y, s16 z, f32 radius) {
static s32 sSparkleGenTheta = 0;
static s32 sSparkleGenPhi = 0;
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;
}
// not sure what this does, returns the height of the floor.
// (animation related?)
static f32 end_obj_set_visual_pos(struct Object *o) {
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);
}
// make Mario fall and soften wing cap gravity
static void end_peach_cutscene_mario_falling(struct MarioState *m) {
if (m->actionTimer == 1) {
m->statusForCamera->cameraEvent = CAM_EVENT_START_ENDING;
}
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) {
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
advance_cutscene_step(m);
}
}
// set Mario on the ground, wait and spawn the jumbo star outside the castle.
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);
obj_scale(sEndJumboStarObj, 3.0);
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);
if (m->actionState == 0 && is_anim_past_end(m)) {
m->actionState++;
}
if (m->actionTimer == 90) {
play_cutscene_music(SEQUENCE_ARGS(0, SEQ_EVENT_CUTSCENE_ENDING));
}
if (m->actionTimer == 255) {
advance_cutscene_step(m);
}
sEndJumboStarObj->oFaceAngleYaw += 0x0400;
generate_yellow_sparkles(0, 2528, -1800, 250.0f);
play_sound(SOUND_AIR_PEACH_TWINKLE, sEndJumboStarObj->header.gfx.cameraToObject);
}
#ifdef VERSION_EU
#define TIMER_FADE_IN_PEACH 201
#define TIMER_DESCEND_PEACH 280
#else
#define TIMER_FADE_IN_PEACH 276
#define TIMER_DESCEND_PEACH 355
#endif
// 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) {
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
}
if (m->actionTimer == 44) {
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 192, 255, 255, 255);
}
if (m->actionTimer == 40) {
obj_mark_for_deletion(sEndJumboStarObj);
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;
}
if (m->actionTimer >= TIMER_FADE_IN_PEACH) {
sEndPeachObj->oOpacity = camera_approach_f32_symmetric(sEndPeachObj->oOpacity, 255.0f, 2.0f);
}
if (m->actionTimer >= 40) {
generate_yellow_sparkles(0, 2628, -1300, 150.0f);
}
if (m->actionTimer == TIMER_DESCEND_PEACH) {
advance_cutscene_step(m);
}
// probably added sounds later and missed the previous >= 40 check
if (m->actionTimer >= 40) {
play_sound(SOUND_AIR_PEACH_TWINKLE, sEndPeachObj->header.gfx.cameraToObject);
}
}
#ifdef VERSION_EU
#define TIMER_RUN_TO_PEACH 531
#else
#define TIMER_RUN_TO_PEACH 584
#endif
// 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) {
if (m->actionState < 60) {
m->actionState += 5;
}
} else {
if (m->actionState >= 27) {
m->actionState -= 2;
}
set_mario_animation(m, MARIO_ANIM_CREDITS_RETURN_FROM_LOOK_UP);
}
if ((sEndPeachObj->oPosY -= m->actionState / 10) <= 907.0f) {
sEndPeachObj->oPosY = 906.0f;
}
play_sound(SOUND_AIR_PEACH_TWINKLE, sEndPeachObj->header.gfx.cameraToObject);
if (m->actionTimer >= TIMER_RUN_TO_PEACH) {
advance_cutscene_step(m);
}
}
#undef TIMER_RUN_TO_PEACH
// Mario runs to peach
static void end_peach_cutscene_run_to_peach(struct MarioState *m) {
struct Surface *surf;
if (m->actionTimer == 22) {
sEndPeachAnimation = 5;
}
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);
play_step_sound(m, 9, 45);
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) {
if (animFrame == 8) {
cutscene_take_cap_off(m);
}
if (is_anim_at_end(m)) {
m->actionState++;
}
}
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
func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40);
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
play_sound(SOUND_PEACH_POWER_OF_THE_STARS, sEndPeachObj->header.gfx.cameraToObject);
#endif
break;
case 480:
advance_cutscene_step(m);
break;
}
}
#ifdef VERSION_EU
#define TIMER_SOMETHING_SPECIAL 150
#define TIMER_PEACH_KISS 260
#else
#define TIMER_SOMETHING_SPECIAL 130
#define TIMER_PEACH_KISS 200
#endif
// 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
play_sound(SOUND_PEACH_THANKS_TO_YOU, sEndPeachObj->header.gfx.cameraToObject);
#endif
break;
case 45:
D_8032CBE8 = 1;
break;
case 75:
set_cutscene_message(160, 227, 3, 30);
#ifndef VERSION_JP
play_sound(SOUND_PEACH_THANK_YOU_MARIO, sEndPeachObj->header.gfx.cameraToObject);
#endif
break;
case TIMER_SOMETHING_SPECIAL:
set_cutscene_message(160, 227, 4, 40);
#ifndef VERSION_JP
play_sound(SOUND_PEACH_SOMETHING_SPECIAL, sEndPeachObj->header.gfx.cameraToObject);
#endif
break;
case TIMER_PEACH_KISS:
advance_cutscene_step(m);
break;
}
}
#undef TIMER_SOMETHING_SPECIAL
#undef TIMER_PEACH_KISS
// 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,
};
static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) {
sEndPeachAnimation = 10;
if (m->actionTimer >= 90) {
m->marioBodyState->eyeState =
m->actionTimer < 110 ? sMarioBlinkOverride[m->actionTimer - 90] : MARIO_EYES_HALF_CLOSED;
}
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);
if (animFrame == 77) {
cutscene_put_cap_on(m);
}
if (animFrame == 88) {
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
}
if (animFrame >= 98) {
m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN;
}
if (m->actionTimer < 52) {
m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED;
}
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
sequence_player_unlower(SEQ_PLAYER_LEVEL, 60);
#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);
sEndPeachObj->oPosY = end_obj_set_visual_pos(sEndPeachObj);
sEndRightToadObj->oPosY = end_obj_set_visual_pos(sEndRightToadObj);
sEndLeftToadObj->oPosY = end_obj_set_visual_pos(sEndLeftToadObj);
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
play_sound(SOUND_PEACH_BAKE_A_CAKE, sEndPeachObj->header.gfx.cameraToObject);
#endif
break;
case 55:
set_cutscene_message(160, 227, 6, 40);
break;
case 130:
set_cutscene_message(160, 227, 7, 50);
#ifndef VERSION_JP
play_sound(SOUND_PEACH_FOR_MARIO, sEndPeachObj->header.gfx.cameraToObject);
#endif
break;
}
if (m->actionTimer == 350) {
advance_cutscene_step(m);
}
}
// "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);
m->marioObj->header.gfx.pos[1] = end_obj_set_visual_pos(m->marioObj);
if (m->actionState == 0 && is_anim_past_end(m)) {
m->actionState = 1;
}
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;
override_viewport_and_clip(NULL, &sEndCutsceneVp, 0, 0, 0);
return FALSE;
}
#ifdef VERSION_EU
#define TIMER_CREDITS_SHOW 51
#define TIMER_CREDITS_PROGRESS 80
#define TIMER_CREDITS_WARP 160
#else
#define TIMER_CREDITS_SHOW 61
#define TIMER_CREDITS_PROGRESS 90
#define TIMER_CREDITS_WARP 200
#endif
static s32 act_credits_cutscene(struct MarioState *m) {
s32 width;
s32 height;
m->statusForCamera->cameraEvent = CAM_EVENT_START_CREDITS;
// checks if Mario is underwater (JRB, DDD, SA, etc.)
if (m->pos[1] < m->waterLevel - 100) {
if (m->playerIndex == 0 && m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) {
set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1);
}
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);
m->particleFlags |= PARTICLE_BUBBLE;
} else {
set_mario_animation(m, MARIO_ANIM_FIRST_PERSON);
if (m->actionTimer > 0) {
stop_and_set_height_to_floor(m);
}
}
if (m->actionTimer >= TIMER_CREDITS_SHOW) {
if (m->actionState < 40) {
m->actionState += 2;
}
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;
override_viewport_and_clip(&sEndCutsceneVp, 0, 0, 0, 0);
}
if (m->actionTimer == TIMER_CREDITS_PROGRESS) {
reset_cutscene_msg_fade();
}
if (m->actionTimer >= TIMER_CREDITS_PROGRESS) {
sDispCreditsEntry = gCurrCreditsEntry;
}
if (m->actionTimer++ == TIMER_CREDITS_WARP) {
level_trigger_warp(m, 24);
}
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) {
m->statusForCamera->cameraEvent = CAM_EVENT_START_END_WAVING;
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;
if (m->actionTimer++ == 300) {
level_trigger_warp(m, WARP_OP_CREDITS_END);
}
return FALSE;
}
static s32 check_for_instant_quicksand(struct MarioState *m) {
if (m != &gMarioStates[0]) {
// never kill remote marios
return FALSE;
}
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;
if (check_for_instant_quicksand(m)) {
return TRUE;
}
/* 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) {
if (m->input & INPUT_IN_WATER) {
m->particleFlags |= PARTICLE_IDLE_WATER_WAVE;
}
}
return cancel;
}