sm64coopdx/src/game/level_update.c
2024-07-14 11:37:48 -04:00

2001 lines
70 KiB
C

#include <ultra64.h>
#include <stdbool.h>
#include <time.h>
#include "sm64.h"
#include "seq_ids.h"
#include "dialog_ids.h"
#include "audio/external.h"
#include "level_update.h"
#include "game_init.h"
#include "level_update.h"
#include "main.h"
#include "engine/math_util.h"
#include "engine/graph_node.h"
#include "area.h"
#include "save_file.h"
#include "sound_init.h"
#include "mario.h"
#include "camera.h"
#include "object_list_processor.h"
#include "ingame_menu.h"
#include "obj_behaviors.h"
#include "object_helpers.h"
#include "save_file.h"
#include "hardcoded.h"
#include "debug_course.h"
#ifdef VERSION_EU
#include "memory.h"
#include "eu_translation.h"
#include "segment_symbols.h"
#endif
#include "level_table.h"
#include "course_table.h"
#include "../../include/libc/stdlib.h"
#include "rumble_init.h"
#include "game/interaction.h"
#include "menu/intro_geo.h"
#include "pc/pc_main.h"
#include "pc/cliopts.h"
#include "pc/configfile.h"
#include "pc/network/network.h"
#include "pc/djui/djui.h"
#include "pc/lua/smlua_hooks.h"
#include "pc/mods/mods.h"
#include "pc/nametags.h"
#include "game/screen_transition.h"
#include "engine/level_script.h"
#define MENU_LEVEL_MIN 0
#define MENU_LEVEL_MAX 17
struct SavedWarpValues gReceiveWarp = { 0 };
extern s8 sReceivedLoadedActNum;
u8 gRejectInstantWarp = 0;
u16 gFanFareDebounce = 0;
s16 gChangeLevel = -1;
s16 gChangeLevelTransition = -1;
s16 gChangeActNum = -1;
s16 gDelayedInitSound = -1;
static bool sFirstCastleGroundsMenu = true;
bool gIsDemoActive = false;
static u16 gDemoCountdown = 0;
static int sDemoNumber = -1;
static bool sCancelNextActSelector = false;
// TODO: Make these ifdefs better
const char *credits01[] = { "1GAME DIRECTOR", "SHIGERU MIYAMOTO" };
const char *credits02[] = { "2ASSISTANT DIRECTORS", "YOSHIAKI KOIZUMI", "TAKASHI TEZUKA" };
const char *credits03[] = { "2SYSTEM PROGRAMMERS", "YASUNARI NISHIDA", "YOSHINORI TANIMOTO" };
const char *credits04[] = { "3PROGRAMMERS", "HAJIME YAJIMA", "DAIKI IWAMOTO", "TOSHIO IWAWAKI" };
#if defined(VERSION_JP) || defined(VERSION_SH)
const char *credits05[] = { "1CAMERA PROGRAMMER", "TAKUMI KAWAGOE" };
const char *credits06[] = { "1MARIO FACE PROGRAMMER", "GILES GODDARD" };
const char *credits07[] = { "2COURSE DIRECTORS", "YOICHI YAMADA", "YASUHISA YAMAMURA" };
const char *credits08[] = { "2COURSE DESIGNERS", "KENTA USUI", "NAOKI MORI" };
const char *credits09[] = { "3COURSE DESIGNERS", "YOSHIKI HARUHANA", "MAKOTO MIYANAGA",
"KATSUHIKO KANNO" };
const char *credits10[] = { "1SOUND COMPOSER", "KOJI KONDO" };
#ifdef VERSION_SH
const char *credits11[] = { "4SOUND EFFECTS", "SOUND PROGRAMMER", "YOJI INAGAKI", "HIDEAKI SHIMIZU" };
const char *credits12[] = { "23D ANIMATORS", "YOSHIAKI KOIZUMI", "SATORU TAKIZAWA" };
const char *credits13[] = { "1CG DESIGNER", "MASANAO ARIMOTO" };
const char *credits14[] = { "3TECHNICAL SUPPORT", "TAKAO SAWANO", "HIROHITO YOSHIMOTO", "HIROTO YADA" };
const char *credits15[] = { "1TECHNICAL SUPPORT", "SGI. 64PROJECT STAFF" };
const char *credits16[] = { "2PROGRESS MANAGEMENT", "KIMIYOSHI FUKUI", "KEIZO KATO" };
const char *credits17[] = { "4MARIO VOICE", "PEACH VOICE", "CHARLES MARTINET", "LESLIE SWAN" };
const char *credits18[] = { "3SPECIAL THANKS TO", "JYOHO KAIHATUBU", "ALL NINTENDO",
"MARIO CLUB STAFF" };
const char *credits19[] = { "1PRODUCER", "SHIGERU MIYAMOTO" };
const char *credits20[] = { "1EXECUTIVE PRODUCER", "HIROSHI YAMAUCHI" };
#else // VERSION_JP
const char *credits11[] = { "1SOUND EFFECTS", "YOJI INAGAKI" };
const char *credits12[] = { "1SOUND PROGRAMMER", "HIDEAKI SHIMIZU" };
const char *credits13[] = { "23D ANIMATORS", "YOSHIAKI KOIZUMI", "SATORU TAKIZAWA" };
const char *credits14[] = { "1CG DESIGNER", "MASANAO ARIMOTO" };
const char *credits15[] = { "3TECHNICAL SUPPORT", "TAKAO SAWANO", "HIROHITO YOSHIMOTO", "HIROTO YADA" };
const char *credits16[] = { "1TECHNICAL SUPPORT", "SGI. 64PROJECT STAFF" };
const char *credits17[] = { "2PROGRESS MANAGEMENT", "KIMIYOSHI FUKUI", "KEIZO KATO" };
const char *credits18[] = { "3SPECIAL THANKS TO", "JYOHO KAIHATUBU", "ALL NINTENDO",
"MARIO CLUB STAFF" };
const char *credits19[] = { "1PRODUCER", "SHIGERU MIYAMOTO" };
const char *credits20[] = { "1EXECUTIVE PRODUCER", "HIROSHI YAMAUCHI" };
#endif
#else // VERSION_US || VERSION_EU
const char *credits05[] = {
"4CAMERA PROGRAMMER", "MARIO FACE PROGRAMMER", "TAKUMI KAWAGOE", "GILES GODDARD"
}; // US combines camera programmer and Mario face programmer
const char *credits06[] = { "2COURSE DIRECTORS", "YOICHI YAMADA", "YASUHISA YAMAMURA" };
const char *credits07[] = { "2COURSE DESIGNERS", "KENTA USUI", "NAOKI MORI" };
const char *credits08[] = { "3COURSE DESIGNERS", "YOSHIKI HARUHANA", "MAKOTO MIYANAGA",
"KATSUHIKO KANNO" };
#ifdef VERSION_US
const char *credits09[] = { "1SOUND COMPOSER", "KOJI KONDO" };
const char *credits10[] = { "4SOUND EFFECTS", "SOUND PROGRAMMER", "YOJI INAGAKI",
"HIDEAKI SHIMIZU" }; // as well as sound effects and sound programmer
const char *credits11[] = { "23-D ANIMATORS", "YOSHIAKI KOIZUMI", "SATORU TAKIZAWA" };
const char *credits12[] = { "1ADDITIONAL GRAPHICS", "MASANAO ARIMOTO" };
const char *credits13[] = { "3TECHNICAL SUPPORT", "TAKAO SAWANO", "HIROHITO YOSHIMOTO", "HIROTO YADA" };
const char *credits14[] = { "1TECHNICAL SUPPORT", "SGI N64 PROJECT STAFF" };
const char *credits15[] = { "2PROGRESS MANAGEMENT", "KIMIYOSHI FUKUI", "KEIZO KATO" };
const char *credits16[] = { "5SCREEN TEXT WRITER", "TRANSLATION", "LESLIE SWAN", "MINA AKINO",
"HIRO YAMADA" }; // ...in order to make room for these 2 new lines
#else // VERSION_EU
const char *credits09[] = { "7SOUND COMPOSER", "SOUND EFFECTS", "SOUND PROGRAMMER", "KOJI KONDO",
"YOJI INAGAKI", "HIDEAKI SHIMIZU" };
const char *credits10[] = { "63-D ANIMATORS", "ADDITIONAL GRAPHICS", "YOSHIAKI KOIZUMI", "SATORU TAKIZAWA",
"MASANAO ARIMOTO" };
const char *credits11[] = { "3TECHNICAL SUPPORT", "TAKAO SAWANO", "HIROHITO YOSHIMOTO", "HIROTO YADA" };
const char *credits12[] = { "1TECHNICAL SUPPORT", "SGI N64 PROJECT STAFF" };
const char *credits13[] = { "2PROGRESS MANAGEMENT", "KIMIYOSHI FUKUI", "KEIZO KATO" };
const char *credits14[] = { "5SCREEN TEXT WRITER", "ENGLISH TRANSLATION", "LESLIE SWAN", "MINA AKINO",
"HIRO YAMADA" };
const char *credits15[] = { "4SCREEN TEXT WRITER", "FRENCH TRANSLATION", "JULIEN BARDAKOFF",
"KENJI HARAGUCHI" };
const char *credits16[] = { "4SCREEN TEXT WRITER", "GERMAN TRANSLATION", "THOMAS GOERG",
"THOMAS SPINDLER" };
#endif
const char *credits17[] = { "4MARIO VOICE", "PEACH VOICE", "CHARLES MARTINET", "LESLIE SWAN" };
const char *credits18[] = { "3SPECIAL THANKS TO", "EAD STAFF", "ALL NINTENDO PERSONNEL",
#ifdef VERSION_US
"MARIO CLUB STAFF" };
#else // VERSION_EU
"SUPER MARIO CLUB STAFF" };
#endif
const char *credits19[] = { "1PRODUCER", "SHIGERU MIYAMOTO" };
const char *credits20[] = { "1EXECUTIVE PRODUCER", "HIROSHI YAMAUCHI" };
#endif
struct CreditsEntry sCreditsSequence[] = {
{ LEVEL_CASTLE_GROUNDS, 1, 1, -128, { 0, 8000, 0 }, NULL },
{ LEVEL_BOB, 1, 1, 117, { 713, 3918, -3889 }, credits01 },
{ LEVEL_WF, 1, 50, 46, { 347, 5376, 326 }, credits02 },
{ LEVEL_JRB, 1, 18, 22, { 3800, -4840, 2727 }, credits03 },
{ LEVEL_CCM, 2, 34, 25, { -5464, 6656, -6575 }, credits04 },
{ LEVEL_BBH, 1, 1, 60, { 257, 1922, 2580 }, credits05 },
{ LEVEL_HMC, 1, -15, 123, { -6469, 1616, -6054 }, credits06 },
{ LEVEL_THI, 3, 17, -32, { 508, 1024, 1942 }, credits07 },
{ LEVEL_LLL, 2, 33, 124, { -73, 82, -1467 }, credits08 },
{ LEVEL_SSL, 1, 65, 98, { -5906, 1024, -2576 }, credits09 },
{ LEVEL_DDD, 1, 50, 47, { -4884, -4607, -272 }, credits10 },
{ LEVEL_SL, 1, 17, -34, { 1925, 3328, 563 }, credits11 },
{ LEVEL_WDW, 1, 33, 105, { -537, 1850, 1818 }, credits12 },
{ LEVEL_TTM, 1, 2, -33, { 2613, 313, 1074 }, credits13 },
{ LEVEL_THI, 1, 51, 54, { -2609, 512, 856 }, credits14 },
{ LEVEL_TTC, 1, 17, -72, { -1304, -71, -967 }, credits15 },
{ LEVEL_RR, 1, 33, 64, { 1565, 1024, -148 }, credits16 },
{ LEVEL_SA, 1, 1, 24, { -1050, -1330, -1559 }, credits17 },
{ LEVEL_COTMC, 1, 49, -16, { -254, 415, -6045 }, credits18 },
{ LEVEL_DDD, 2, -111, -64, { 3948, 1185, -104 }, credits19 },
{ LEVEL_CCM, 1, 33, 31, { 3169, -4607, 5240 }, credits20 },
{ LEVEL_CASTLE_GROUNDS, 1, 1, -128, { 0, 906, -1200 }, NULL },
{ LEVEL_NONE, 0, 1, 0, { 0, 0, 0 }, NULL },
};
struct MarioState gMarioStates[MAX_PLAYERS] = { 0 };
struct HudDisplay gHudDisplay;
s16 sCurrPlayMode;
u16 D_80339ECA;
s16 sTransitionTimer;
void (*sTransitionUpdate)(s16 *);
struct WarpDest sWarpDest;
s16 D_80339EE0;
s16 sDelayedWarpOp;
s16 sDelayedWarpTimer;
s16 sSourceWarpNodeId;
s32 sDelayedWarpArg;
#if defined(VERSION_EU) || defined(VERSION_SH)
s16 unusedEULevelUpdateBss1;
#endif
s8 sTimerRunning;
bool gNeverEnteredCastle;
struct MarioState *gMarioState = &gMarioStates[0];
u8 unused1[4] = { 0 };
s8 sWarpCheckpointActive = FALSE;
u8 unused3[4];
u8 unused4[2];
u32 gControlTimerStartNat = 0;
u32 gControlTimerStopNat = 0;
u8 level_control_timer_running(void) {
return sTimerRunning;
}
u16 level_control_timer(s32 timerOp) {
switch (timerOp) {
case TIMER_CONTROL_SHOW:
gHudDisplay.flags |= HUD_DISPLAY_FLAG_TIMER;
sTimerRunning = FALSE;
gHudDisplay.timer = 0;
gControlTimerStartNat = 0;
gControlTimerStopNat = 0;
break;
case TIMER_CONTROL_START:
if (!sTimerRunning) {
sTimerRunning = TRUE;
gControlTimerStartNat = gNetworkAreaTimer;
}
break;
case TIMER_CONTROL_STOP:
if (sTimerRunning) {
sTimerRunning = FALSE;
gControlTimerStopNat = gNetworkAreaTimer;
}
break;
case TIMER_CONTROL_HIDE:
gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_TIMER;
sTimerRunning = FALSE;
gHudDisplay.timer = 0;
gControlTimerStartNat = 0;
gControlTimerStopNat = 0;
break;
}
return gHudDisplay.timer;
}
u32 pressed_pause(void) {
if (gServerSettings.pauseAnywhere) {
if (get_dialog_id() < 0 && !gWarpTransition.isActive && sDelayedWarpOp == WARP_OP_NONE) {
return gPlayer1Controller->buttonPressed & START_BUTTON;
}
} else {
u32 dialogActive = get_dialog_id() >= 0;
u32 intangible = (gMarioState->action & ACT_FLAG_INTANGIBLE) != 0;
u32 firstPerson = gMarioState->action == ACT_FIRST_PERSON;
if (!intangible && !dialogActive && !firstPerson && !gWarpTransition.isActive && sDelayedWarpOp == WARP_OP_NONE) {
return (gPlayer1Controller->buttonPressed & START_BUTTON);
}
}
return FALSE;
}
void set_play_mode(s16 playMode) {
sCurrPlayMode = playMode;
D_80339ECA = 0;
}
void warp_special(s32 arg) {
if (arg != 0 && arg != SPECIAL_WARP_CAKE && arg != SPECIAL_WARP_GODDARD && arg != SPECIAL_WARP_GODDARD_GAMEOVER && arg != SPECIAL_WARP_TITLE && arg != SPECIAL_WARP_LEVEL_SELECT) {
LOG_ERROR("Invalid parameter value for warp_special: Expected 0, SPECIAL_WARP_CAKE, SPECIAL_WARP_GODDARD, SPECIAL_WARP_GODDARD_GAMEOVER, SPECIAL_WARP_TITLE, or SPECIAL_WARP_LEVEL_SELECT");
return;
}
sCurrPlayMode = PLAY_MODE_CHANGE_LEVEL;
D_80339ECA = 0;
D_80339EE0 = arg;
}
void fade_into_special_warp(u32 arg, u32 color) {
if (color != 0) {
color = 0xFF;
}
fadeout_music(190);
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x10, color, color, color);
level_set_transition(16, NULL);
warp_special(arg);
}
void stub_level_update_1(void) {
}
void load_level_init_text(u32 arg) {
s32 gotAchievement;
u32 dialogID = gCurrentArea->dialog[arg];
if (dialogID == gBehaviorValues.dialogs.VanishCourseDialog) {
gotAchievement = save_file_get_flags() & SAVE_FLAG_HAVE_VANISH_CAP;
} else if (dialogID == gBehaviorValues.dialogs.MetalCourseDialog) {
gotAchievement = save_file_get_flags() & SAVE_FLAG_HAVE_METAL_CAP;
} else if (dialogID == gBehaviorValues.dialogs.WingCourseDialog) {
gotAchievement = save_file_get_flags() & SAVE_FLAG_HAVE_WING_CAP;
} else if (dialogID == 255) {
gotAchievement = TRUE;
} else {
gotAchievement = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
}
if (!gotAchievement) {
//level_set_transition(-1, NULL);
create_dialog_box(dialogID);
}
}
void init_door_warp(struct SpawnInfo *spawnInfo, u32 arg1) {
if (arg1 & 0x00000002) {
spawnInfo->startAngle[1] += 0x8000;
}
spawnInfo->startPos[0] += 300.0f * sins(spawnInfo->startAngle[1]);
spawnInfo->startPos[2] += 300.0f * coss(spawnInfo->startAngle[1]);
}
void set_mario_initial_cap_powerup(struct MarioState *m) {
u32 capCourseIndex = gCurrCourseNum - COURSE_CAP_COURSES;
switch (capCourseIndex) {
case COURSE_COTMC - COURSE_CAP_COURSES:
m->flags |= MARIO_METAL_CAP | MARIO_CAP_ON_HEAD;
m->capTimer = gLevelValues.metalCapDurationCotmc;
break;
case COURSE_TOTWC - COURSE_CAP_COURSES:
m->flags |= MARIO_WING_CAP | MARIO_CAP_ON_HEAD;
m->capTimer = gLevelValues.wingCapDurationTotwc;
break;
case COURSE_VCUTM - COURSE_CAP_COURSES:
m->flags |= MARIO_VANISH_CAP | MARIO_CAP_ON_HEAD;
m->capTimer = gLevelValues.vanishCapDurationVcutm;
break;
}
}
void set_mario_initial_action(struct MarioState *m, u32 spawnType, u32 actionArg) {
switch (spawnType) {
case MARIO_SPAWN_DOOR_WARP:
set_mario_action(m, ACT_WARP_DOOR_SPAWN, actionArg);
break;
case MARIO_SPAWN_UNKNOWN_02:
set_mario_action(m, ACT_IDLE, 0);
break;
case MARIO_SPAWN_UNKNOWN_03:
set_mario_action(m, ACT_EMERGE_FROM_PIPE, 0);
break;
case MARIO_SPAWN_TELEPORT:
set_mario_action(m, ACT_TELEPORT_FADE_IN, 0);
break;
case MARIO_SPAWN_INSTANT_ACTIVE:
set_mario_action(m, ACT_IDLE, 0);
break;
case MARIO_SPAWN_AIRBORNE:
set_mario_action(m, ACT_SPAWN_NO_SPIN_AIRBORNE, 0);
break;
case MARIO_SPAWN_HARD_AIR_KNOCKBACK:
set_mario_action(m, ACT_HARD_BACKWARD_AIR_KB, 0);
break;
case MARIO_SPAWN_SPIN_AIRBORNE_CIRCLE:
set_mario_action(m, ACT_SPAWN_SPIN_AIRBORNE, 0);
break;
case MARIO_SPAWN_DEATH:
set_mario_action(m, ACT_FALLING_DEATH_EXIT, 0);
break;
case MARIO_SPAWN_SPIN_AIRBORNE:
set_mario_action(m, ACT_SPAWN_SPIN_AIRBORNE, 0);
break;
case MARIO_SPAWN_FLYING:
set_mario_action(m, ACT_FLYING, 2);
break;
case MARIO_SPAWN_SWIMMING:
set_mario_action(m, ACT_WATER_IDLE, 1);
break;
case MARIO_SPAWN_PAINTING_STAR_COLLECT:
set_mario_action(m, ACT_EXIT_AIRBORNE, 0);
break;
case MARIO_SPAWN_PAINTING_DEATH:
set_mario_action(m, ACT_DEATH_EXIT, 0);
break;
case MARIO_SPAWN_AIRBORNE_STAR_COLLECT:
set_mario_action(m, ACT_FALLING_EXIT_AIRBORNE, 0);
break;
case MARIO_SPAWN_AIRBORNE_DEATH:
set_mario_action(m, ACT_UNUSED_DEATH_EXIT, 0);
break;
case MARIO_SPAWN_LAUNCH_STAR_COLLECT:
set_mario_action(m, ACT_SPECIAL_EXIT_AIRBORNE, 0);
break;
case MARIO_SPAWN_LAUNCH_DEATH:
set_mario_action(m, ACT_SPECIAL_DEATH_EXIT, 0);
break;
}
set_mario_initial_cap_powerup(m);
}
void init_mario_after_warp(void) {
struct ObjectWarpNode *spawnNode = area_get_warp_node(sWarpDest.nodeId);
if (spawnNode == NULL) { LOG_ERROR("Failed to find spawn node: %u", sWarpDest.nodeId); }
if (spawnNode == NULL || spawnNode->object == NULL) { spawnNode = area_get_warp_node(0xFA); }
if (spawnNode == NULL || spawnNode->object == NULL) { spawnNode = area_get_warp_node(0x00); }
if (spawnNode == NULL || spawnNode->object == NULL) { spawnNode = area_get_any_warp_node(); }
if (spawnNode == NULL || spawnNode->object == NULL) { return; }
u32 marioSpawnType = get_mario_spawn_type(spawnNode->object);
if (gMarioState && gMarioState->action != ACT_UNINITIALIZED) {
for (s32 i = 0; i < MAX_PLAYERS; i++) {
gPlayerSpawnInfos[i].startPos[0] = (s16) spawnNode->object->oPosX;
gPlayerSpawnInfos[i].startPos[1] = (s16) spawnNode->object->oPosY;
gPlayerSpawnInfos[i].startPos[2] = (s16) spawnNode->object->oPosZ;
gPlayerSpawnInfos[i].startAngle[0] = 0;
gPlayerSpawnInfos[i].startAngle[1] = spawnNode->object->oMoveAngleYaw;
gPlayerSpawnInfos[i].startAngle[2] = 0;
if (marioSpawnType == MARIO_SPAWN_DOOR_WARP) {
init_door_warp(&gPlayerSpawnInfos[i], sWarpDest.arg);
}
if (sWarpDest.type == WARP_TYPE_CHANGE_LEVEL || sWarpDest.type == WARP_TYPE_CHANGE_AREA) {
gPlayerSpawnInfos[i].areaIndex = sWarpDest.areaIdx;
if (i == 0) { load_mario_area(); }
}
// enforce bubble on area change
if (gServerSettings.bubbleDeath) {
if (i == 0 && gMarioStates[i].numLives == -1) {
mario_set_bubbled(&gMarioStates[i]);
gMarioStates[i].health = 0xFF;
}
}
}
init_mario();
set_mario_initial_action(gMarioState, marioSpawnType, sWarpDest.arg);
// remove offset from local mario during warps
if (sWarpDest.type == WARP_TYPE_SAME_AREA && marioSpawnType != MARIO_SPAWN_DOOR_WARP) {
gMarioState[0].pos[0] = (s16)spawnNode->object->oPosX;
gMarioState[0].pos[1] = (s16)spawnNode->object->oPosY;
gMarioState[0].pos[2] = (s16)spawnNode->object->oPosZ;
if (gMarioState[0].marioObj != NULL) {
gMarioState[0].marioObj->oPosX = spawnNode->object->oPosX;
gMarioState[0].marioObj->oPosY = spawnNode->object->oPosY;
gMarioState[0].marioObj->oPosZ = spawnNode->object->oPosZ;
}
}
gMarioState->interactObj = spawnNode->object;
gMarioState->usedObj = spawnNode->object;
}
if (gCurrentArea) {
reset_camera(gCurrentArea->camera);
}
sWarpDest.type = WARP_TYPE_NOT_WARPING;
sDelayedWarpOp = WARP_OP_NONE;
switch (marioSpawnType) {
case MARIO_SPAWN_UNKNOWN_03:
play_transition(WARP_TRANSITION_FADE_FROM_STAR, 0x10, 0x00, 0x00, 0x00);
break;
case MARIO_SPAWN_DOOR_WARP:
play_transition(WARP_TRANSITION_FADE_FROM_CIRCLE, 0x10, 0x00, 0x00, 0x00);
break;
case MARIO_SPAWN_TELEPORT:
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x14, 0xFF, 0xFF, 0xFF);
break;
case MARIO_SPAWN_SPIN_AIRBORNE:
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x1A, 0xFF, 0xFF, 0xFF);
break;
case MARIO_SPAWN_SPIN_AIRBORNE_CIRCLE:
play_transition(WARP_TRANSITION_FADE_FROM_CIRCLE, 0x10, 0x00, 0x00, 0x00);
break;
case MARIO_SPAWN_UNKNOWN_27:
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x10, 0x00, 0x00, 0x00);
break;
default:
play_transition(WARP_TRANSITION_FADE_FROM_STAR, 0x10, 0x00, 0x00, 0x00);
break;
}
if (gCurrDemoInput == NULL && gMarioState) {
if (gCurrentArea) {
set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0);
}
if (gMarioState->flags & MARIO_METAL_CAP) {
play_cap_music(SEQUENCE_ARGS(4, SEQ_EVENT_METAL_CAP));
}
if (gMarioState->flags & (MARIO_VANISH_CAP | MARIO_WING_CAP)) {
play_cap_music(SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP));
}
#ifndef VERSION_JP
if (gCurrLevelNum == LEVEL_BOB
&& get_current_background_music() != SEQUENCE_ARGS(4, SEQ_LEVEL_SLIDE)
&& sTimerRunning) {
play_music(SEQ_PLAYER_LEVEL, SEQUENCE_ARGS(4, SEQ_LEVEL_SLIDE), 0);
}
#endif
if (sWarpDest.levelNum == LEVEL_CASTLE && sWarpDest.areaIdx == 1
#ifndef VERSION_JP
&& (sWarpDest.nodeId == 31 || sWarpDest.nodeId == 32)
#else
&& sWarpDest.nodeId == 31
#endif
)
play_sound(SOUND_MENU_MARIO_CASTLE_WARP, gGlobalSoundSource);
#ifndef VERSION_JP
if (sWarpDest.levelNum == LEVEL_CASTLE_GROUNDS && sWarpDest.areaIdx == 1
&& (sWarpDest.nodeId == 7 || sWarpDest.nodeId == 10 || sWarpDest.nodeId == 20
|| sWarpDest.nodeId == 30)) {
play_sound(SOUND_MENU_MARIO_CASTLE_WARP, gGlobalSoundSource);
}
#endif
}
if (gNetworkPlayerLocal != NULL) {
network_player_update_course_level(gNetworkPlayerLocal, gCurrCourseNum, gCurrActStarNum, gCurrLevelNum, gCurrAreaIndex);
}
if (gMarioState && gMarioState->health <= 0x110) {
gMarioState->health = 0x880;
}
if (gMarioState) {
gMarioState->skipWarpInteractionsTimer = 30;
}
smlua_call_event_hooks(HOOK_ON_WARP);
}
// used for warps inside one level
void warp_area(void) {
if (sWarpDest.type != WARP_TYPE_NOT_WARPING) {
if (sWarpDest.type == WARP_TYPE_CHANGE_AREA) {
level_control_timer(TIMER_CONTROL_HIDE);
unload_mario_area();
load_area(sWarpDest.areaIdx);
}
init_mario_after_warp();
}
}
// used for warps between levels
void warp_level(void) {
gCurrLevelNum = sWarpDest.levelNum;
level_control_timer(TIMER_CONTROL_HIDE);
load_area(sWarpDest.areaIdx);
init_mario_after_warp();
}
void warp_credits(void) {
s32 marioAction;
switch (sWarpDest.nodeId) {
case WARP_NODE_CREDITS_START:
marioAction = ACT_END_PEACH_CUTSCENE;
break;
case WARP_NODE_CREDITS_NEXT:
marioAction = ACT_CREDITS_CUTSCENE;
break;
case WARP_NODE_CREDITS_END:
marioAction = ACT_END_WAVING_CUTSCENE;
break;
}
gCurrLevelNum = sWarpDest.levelNum;
load_area(sWarpDest.areaIdx);
if (gCurrCreditsEntry == NULL) {
gCurrCreditsEntry = &sCreditsSequence[0];
}
if ((gCurrCreditsEntry != NULL) && (gCurrCreditsEntry->levelNum == gLevelValues.skipCreditsAt)) {
lvl_skip_credits();
return;
}
for (s32 i = 0; i < MAX_PLAYERS; i++) {
vec3s_set(gPlayerSpawnInfos[i].startPos, gCurrCreditsEntry->marioPos[0],
gCurrCreditsEntry->marioPos[1], gCurrCreditsEntry->marioPos[2]);
vec3s_set(gPlayerSpawnInfos[i].startAngle, 0, 0x100 * gCurrCreditsEntry->marioAngle, 0);
gPlayerSpawnInfos[i].areaIndex = sWarpDest.areaIdx;
}
load_mario_area();
init_mario();
set_mario_action(gMarioState, marioAction, 0);
if (gCurrentArea) {
reset_camera(gCurrentArea->camera);
}
sWarpDest.type = WARP_TYPE_NOT_WARPING;
sDelayedWarpOp = WARP_OP_NONE;
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x14, 0x00, 0x00, 0x00);
if ((gCurrCreditsEntry == NULL || gCurrCreditsEntry == sCreditsSequence) && !gDjuiInMainMenu) {
if (gCurrentArea) {
set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0);
}
}
}
void check_instant_warp(void) {
if (!gCurrentArea) { return; }
s16 cameraAngle;
struct Surface *floor;
if (gRejectInstantWarp > 0) {
gRejectInstantWarp--;
}
if (gCurrLevelNum == LEVEL_CASTLE
&& save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1) >= gLevelValues.infiniteStairsRequirement) {
return;
}
if ((floor = gMarioStates[0].floor) != NULL) {
s32 index = floor->type - SURFACE_INSTANT_WARP_1B;
if (index >= INSTANT_WARP_INDEX_START && index < INSTANT_WARP_INDEX_STOP && gCurrentArea->instantWarps != NULL) {
struct InstantWarp *warp = &gCurrentArea->instantWarps[index];
if (warp->id != 0) {
if (gRejectInstantWarp > 0) {
vec3f_copy(gMarioStates[0].pos, gMarioStates[0].nonInstantWarpPos);
//vec3f_mul(gMarioStates[0].vel, -0.8f);
return;
}
mario_drop_held_object(&gMarioStates[0]);
u8 changeOfArea = (gCurrAreaIndex != warp->area);
gMarioStates[0].pos[0] += warp->displacement[0];
gMarioStates[0].pos[1] += warp->displacement[1];
gMarioStates[0].pos[2] += warp->displacement[2];
vec3f_copy(gMarioStates[0].nonInstantWarpPos, gMarioStates[0].pos);
if (changeOfArea) {
for (s32 i = 0; i < MAX_PLAYERS; i++) {
gMarioStates[i].marioObj->oIntangibleTimer = 30;
}
}
gMarioStates[0].marioObj->oPosX = gMarioStates[0].pos[0];
gMarioStates[0].marioObj->oPosY = gMarioStates[0].pos[1];
gMarioStates[0].marioObj->oPosZ = gMarioStates[0].pos[2];
cameraAngle = gMarioStates[0].area->camera->yaw;
change_area(warp->area);
for (s32 i = 0; i < MAX_PLAYERS; i++) {
gMarioStates[i].area = gCurrentArea;
}
warp_camera(warp->displacement[0], warp->displacement[1], warp->displacement[2]);
skip_camera_interpolation();
gMarioStates[0].area->camera->yaw = cameraAngle;
return;
}
}
}
vec3f_copy(gMarioStates[0].nonInstantWarpPos, gMarioStates[0].pos);
}
s16 music_changed_through_warp(s16 arg) {
if (arg == 0) {
return false;
}
struct ObjectWarpNode *warpNode = area_get_warp_node(arg);
if (!warpNode) { return FALSE; }
s16 levelNum = warpNode->node.destLevel & 0x7F;
#if BUGFIX_KOOPA_RACE_MUSIC
s16 destArea = warpNode->node.destArea;
s16 val4 = TRUE;
s16 sp2C;
if (levelNum == LEVEL_BOB && levelNum == gCurrLevelNum && destArea == gCurrAreaIndex) {
sp2C = get_current_background_music();
if (sp2C == SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP | SEQ_VARIATION)
|| sp2C == SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP)) {
val4 = 0;
}
} else {
u16 val8 = gAreas[destArea].musicParam;
u16 val6 = gAreas[destArea].musicParam2;
val4 = levelNum == gCurrLevelNum && val8 == gCurrentArea->musicParam
&& val6 == gCurrentArea->musicParam2;
if (get_current_background_music() != val6) {
val4 = FALSE;
}
}
return val4;
#else
u16 val8 = gAreas[warpNode->node.destArea].musicParam;
u16 val6 = gAreas[warpNode->node.destArea].musicParam2;
s16 val4 = levelNum == gCurrLevelNum && val8 == gCurrentArea->musicParam
&& val6 == gCurrentArea->musicParam2;
if (get_current_background_music() != val6) {
val4 = FALSE;
}
return val4;
#endif
}
/**
* Set the current warp type and destination level/area/node.
*/
void initiate_warp(s16 destLevel, s16 destArea, s16 destWarpNode, s32 arg3) {
if (destWarpNode >= WARP_NODE_CREDITS_MIN) {
sWarpDest.type = WARP_TYPE_CHANGE_LEVEL;
} else if (destLevel != gCurrLevelNum) {
sWarpDest.type = WARP_TYPE_CHANGE_LEVEL;
} else if (destArea != gCurrentArea->index) {
sWarpDest.type = WARP_TYPE_CHANGE_AREA;
} else {
sWarpDest.type = WARP_TYPE_SAME_AREA;
}
sWarpDest.levelNum = destLevel;
sWarpDest.areaIdx = destArea;
sWarpDest.nodeId = destWarpNode;
sWarpDest.arg = arg3;
}
/**
* Check if Mario is above and close to a painting warp floor, and return the
* corresponding warp node.
*/
struct WarpNode *get_painting_warp_node(void) {
if (!gMarioState || !gMarioState->floor || !gCurrentArea || !gCurrentArea->paintingWarpNodes) { return NULL; }
struct WarpNode *warpNode = NULL;
s32 paintingIndex = gMarioState->floor->type - SURFACE_PAINTING_WARP_D3;
if (paintingIndex >= PAINTING_WARP_INDEX_START && paintingIndex < PAINTING_WARP_INDEX_END) {
if (paintingIndex < PAINTING_WARP_INDEX_FA
|| gMarioState->pos[1] - gMarioState->floorHeight < 80.0f) {
warpNode = &gCurrentArea->paintingWarpNodes[paintingIndex];
}
}
return warpNode;
}
static void initiate_painting_warp_node(struct WarpNode *pWarpNode) {
struct WarpNode warpNode = *pWarpNode;
if (!(warpNode.destLevel & 0x80)) {
sWarpCheckpointActive = check_warp_checkpoint(&warpNode);
}
initiate_warp(warpNode.destLevel & 0x7F, warpNode.destArea, warpNode.destNode, 0);
check_if_should_set_warp_checkpoint(&warpNode);
extern s16 gMenuMode;
if (gMenuMode == -1) {
play_transition_after_delay(WARP_TRANSITION_FADE_INTO_COLOR, 30, 255, 255, 255, 45);
}
level_set_transition(74, basic_update);
play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
fadeout_music(398);
queue_rumble_data(80, 70);
func_sh_8024C89C(1);
}
/**
* Check is Mario has entered a painting, and if so, initiate a warp.
*/
void initiate_painting_warp(s16 paintingIndex) {
if (gCurrentArea && gCurrentArea->paintingWarpNodes && gMarioState && gMarioState->floor && paintingIndex >= -1 && paintingIndex < MAX_PAINTING_WARP_NODES) {
struct WarpNode *pWarpNode = paintingIndex == -1 ? get_painting_warp_node() : &gCurrentArea->paintingWarpNodes[paintingIndex];
if (pWarpNode != NULL) {
if (gMarioState->action & ACT_FLAG_INTANGIBLE) {
play_painting_eject_sound();
} else if (pWarpNode->id != 0) {
initiate_painting_warp_node(pWarpNode);
set_mario_action(gMarioState, ACT_DISAPPEARED, 0);
gMarioState->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
fadeout_music(398);
#ifdef VERSION_SH
queue_rumble_data(80, 70);
func_sh_8024C89C(1);
#endif
}
}
}
}
void verify_warp(struct MarioState *m, bool killMario) {
if (area_get_warp_node(sSourceWarpNodeId) != NULL) { return; }
if (area_get_warp_node(WARP_NODE_DEATH) == NULL) {
dynos_warp_to_start_level();
return;
}
if (!killMario) {
sSourceWarpNodeId = WARP_NODE_DEATH;
return;
}
m->numLives--;
if (m->numLives < 0) {
sDelayedWarpOp = WARP_OP_GAME_OVER;
} else {
sSourceWarpNodeId = WARP_NODE_DEATH;
}
}
/**
* If there is not already a delayed warp, schedule one. The source node is
* based on the warp operation and sometimes Mario's used object.
* Return the time left until the delayed warp is initiated.
*/
s16 level_trigger_warp(struct MarioState *m, s32 warpOp) {
// only allow for local player
if (m != &gMarioStates[0]) { return 0; }
s32 val04 = TRUE;
if (sDelayedWarpOp == WARP_OP_NONE) {
m->invincTimer = -1;
sDelayedWarpArg = 0;
sDelayedWarpOp = warpOp;
switch (warpOp) {
case WARP_OP_DEMO_NEXT:
case WARP_OP_DEMO_END: sDelayedWarpTimer = 20; // Must be one line to match on -O2
val04 = FALSE;
if (!gDjuiInMainMenu) {
sSourceWarpNodeId = WARP_NODE_F0;
gSavedCourseNum = COURSE_NONE;
play_transition(WARP_TRANSITION_FADE_INTO_STAR, 0x14, 0x00, 0x00, 0x00);
} else {
stop_demo(NULL);
}
break;
case WARP_OP_CREDITS_END:
sDelayedWarpTimer = 60;
sSourceWarpNodeId = WARP_NODE_F0;
verify_warp(m, false);
val04 = FALSE;
gSavedCourseNum = COURSE_NONE;
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x3C, 0x00, 0x00, 0x00);
break;
case WARP_OP_STAR_EXIT:
sDelayedWarpTimer = 32;
sSourceWarpNodeId = WARP_NODE_F0;
verify_warp(m, false);
gSavedCourseNum = COURSE_NONE;
play_transition(WARP_TRANSITION_FADE_INTO_MARIO, 0x20, 0x00, 0x00, 0x00);
break;
case WARP_OP_DEATH:
m->numLives--;
if (m->numLives <= -1) {
sDelayedWarpOp = WARP_OP_GAME_OVER;
}
sDelayedWarpTimer = 48;
sSourceWarpNodeId = WARP_NODE_DEATH;
play_transition(WARP_TRANSITION_FADE_INTO_BOWSER, 0x30, 0x00, 0x00, 0x00);
play_sound(SOUND_MENU_BOWSER_LAUGH, gGlobalSoundSource);
break;
case WARP_OP_EXIT:
sSourceWarpNodeId = WARP_NODE_DEATH;
sDelayedWarpTimer = 20;
play_transition(WARP_TRANSITION_FADE_INTO_CIRCLE, 0x14, 0x00, 0x00, 0x00);
break;
case WARP_OP_WARP_FLOOR:
sSourceWarpNodeId = WARP_NODE_WARP_FLOOR;
verify_warp(m, true);
sDelayedWarpTimer = 20;
play_transition(WARP_TRANSITION_FADE_INTO_CIRCLE, 0x14, 0x00, 0x00, 0x00);
break;
case WARP_OP_LOOK_UP: // enter totwc
sDelayedWarpTimer = 30;
sSourceWarpNodeId = WARP_NODE_F2;
verify_warp(m, false);
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x1E, 0xFF, 0xFF, 0xFF);
#ifndef VERSION_JP
play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
#endif
break;
case WARP_OP_SPIN_SHRINK: // bbh enter
if (m->usedObj == NULL) { break; }
sDelayedWarpTimer = 30;
sSourceWarpNodeId = (m->usedObj->oBehParams & 0x00FF0000) >> 16;
verify_warp(m, false);
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x1E, 0xFF, 0xFF, 0xFF);
break;
case WARP_OP_TELEPORT:
if (m->usedObj == NULL) { break; }
sDelayedWarpTimer = 20;
sSourceWarpNodeId = (m->usedObj->oBehParams & 0x00FF0000) >> 16;
verify_warp(m, false);
val04 = !music_changed_through_warp(sSourceWarpNodeId);
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x14, 0xFF, 0xFF, 0xFF);
break;
case WARP_OP_WARP_DOOR:
if (m->usedObj == NULL) { break; }
sDelayedWarpTimer = 20;
sDelayedWarpArg = m->actionArg;
sSourceWarpNodeId = (m->usedObj->oBehParams & 0x00FF0000) >> 16;
verify_warp(m, false);
val04 = !music_changed_through_warp(sSourceWarpNodeId);
play_transition(WARP_TRANSITION_FADE_INTO_CIRCLE, 0x14, 0x00, 0x00, 0x00);
break;
case WARP_OP_WARP_OBJECT:
if (m->usedObj == NULL) { break; }
sDelayedWarpTimer = 20;
sSourceWarpNodeId = (m->usedObj->oBehParams & 0x00FF0000) >> 16;
verify_warp(m, false);
val04 = !music_changed_through_warp(sSourceWarpNodeId);
play_transition(WARP_TRANSITION_FADE_INTO_STAR, 0x14, 0x00, 0x00, 0x00);
break;
case WARP_OP_CREDITS_START:
sDelayedWarpTimer = 30;
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x1E, 0x00, 0x00, 0x00);
break;
case WARP_OP_CREDITS_NEXT:
if (gCurrCreditsEntry == NULL) { gCurrCreditsEntry = &sCreditsSequence[0]; }
if (gCurrCreditsEntry == &sCreditsSequence[0]) {
sDelayedWarpTimer = gDjuiInMainMenu ? 1 : 60;
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x3C, 0x00, 0x00, 0x00);
} else {
sDelayedWarpTimer = 20;
play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 0x14, 0x00, 0x00, 0x00);
}
val04 = FALSE;
break;
}
if (val04 && gCurrDemoInput == NULL) {
fadeout_music((3 * sDelayedWarpTimer / 2) * 8 - 2);
}
}
return sDelayedWarpTimer;
}
/**
* If a delayed warp is ready, initiate it.
*/
void initiate_delayed_warp(void) {
struct ObjectWarpNode *warpNode;
s32 destWarpNode;
if (sDelayedWarpOp != WARP_OP_NONE && --sDelayedWarpTimer == 0) {
reset_dialog_render_state();
if (gDebugLevelSelect && (sDelayedWarpOp & WARP_OP_TRIGGERS_LEVEL_SELECT)) {
warp_special(SPECIAL_WARP_LEVEL_SELECT);
} else if (gCurrDemoInput != NULL) {
if (sDelayedWarpOp == WARP_OP_DEMO_END) {
warp_special(SPECIAL_WARP_TITLE);
} else {
warp_special(SPECIAL_WARP_GODDARD);
}
} else {
switch (sDelayedWarpOp) {
case WARP_OP_GAME_OVER:
gChangeLevel = gLevelValues.entryLevel;
gMarioStates[0].numLives = 4;
gMarioStates[0].health = 0x880;
break;
case WARP_OP_CREDITS_END:
warp_special(SPECIAL_WARP_CAKE);
sound_banks_enable(SEQ_PLAYER_SFX,
SOUND_BANKS_ALL & ~SOUND_BANKS_DISABLED_AFTER_CREDITS);
break;
case WARP_OP_DEMO_NEXT:
if (!gDjuiInMainMenu) {
warp_special(SPECIAL_WARP_GODDARD);
}
break;
case WARP_OP_CREDITS_START:
gCurrCreditsEntry = &sCreditsSequence[0];
if ((gCurrCreditsEntry != NULL) && (gCurrCreditsEntry->levelNum == gLevelValues.skipCreditsAt)) {
lvl_skip_credits();
} else if (gCurrCreditsEntry != NULL) {
// instance players in the credits
gCurrActStarNum = 99;
gCurrActNum = 99;
initiate_warp(gCurrCreditsEntry->levelNum, gCurrCreditsEntry->areaIndex,
WARP_NODE_CREDITS_START, 0);
}
break;
case WARP_OP_CREDITS_NEXT:
if (gCurrCreditsEntry == NULL) { gCurrCreditsEntry = &sCreditsSequence[0]; }
sound_banks_disable(SEQ_PLAYER_SFX, gDjuiInMainMenu ? SOUND_BANKS_ALL & ~(1 << SOUND_BANK_MENU) : SOUND_BANKS_ALL);
gCurrCreditsEntry += 1;
if ((gCurrCreditsEntry != NULL) && (gCurrCreditsEntry->levelNum == gLevelValues.skipCreditsAt)) {
lvl_skip_credits();
} else if (gCurrCreditsEntry != NULL) {
gCurrActNum = gCurrCreditsEntry->unk02 & 0x07;
if (gCurrCreditsEntry->levelNum == LEVEL_CASTLE_GROUNDS && gDjuiInMainMenu) {
gCurrCreditsEntry = &sCreditsSequence[1];
destWarpNode = WARP_NODE_CREDITS_NEXT;
} else if ((gCurrCreditsEntry + 1)->levelNum == LEVEL_NONE) {
destWarpNode = WARP_NODE_CREDITS_END;
} else {
destWarpNode = WARP_NODE_CREDITS_NEXT;
}
initiate_warp(gCurrCreditsEntry->levelNum, gCurrCreditsEntry->areaIndex,
destWarpNode, 0);
}
break;
default:
warpNode = area_get_warp_node(sSourceWarpNodeId);
if (warpNode != NULL) {
initiate_warp(warpNode->node.destLevel & 0x7F, warpNode->node.destArea,
warpNode->node.destNode, sDelayedWarpArg);
check_if_should_set_warp_checkpoint(&warpNode->node);
if (sWarpDest.type != WARP_TYPE_CHANGE_LEVEL) {
level_set_transition(2, NULL);
}
}
break;
}
}
}
}
void update_hud_values(void) {
if (gCurrCreditsEntry == NULL) {
s16 numHealthWedges = gMarioState->health > 0 ? MIN(gMarioState->health >> 8, 8) : 0;
if (gCurrCourseNum >= COURSE_MIN) {
gHudDisplay.flags |= HUD_DISPLAY_FLAG_COIN_COUNT;
} else {
gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_COIN_COUNT;
}
if (gHudDisplay.coins < gMarioState->numCoins) {
if (gGlobalTimer & 0x00000001) {
u32 coinSound;
if (gMarioState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
coinSound = SOUND_GENERAL_COIN_WATER;
} else {
coinSound = SOUND_GENERAL_COIN;
}
gHudDisplay.coins += 1;
play_sound(coinSound, gMarioState->marioObj->header.gfx.cameraToObject);
if (gServerSettings.stayInLevelAfterStar > 0 && gCurrCourseNum != COURSE_NONE) {
// retain vanilla behavior
if (gLevelValues.numCoinsToLife == 50) {
if (gHudDisplay.coins == 50 || gHudDisplay.coins == 100 || gHudDisplay.coins == 150) {
gMarioState->numLives++;
play_sound(SOUND_GENERAL_COLLECT_1UP, gGlobalSoundSource);
}
} else {
if (gHudDisplay.coins % gLevelValues.numCoinsToLife == 0 && gHudDisplay.coins > 0) {
gMarioState->numLives++;
play_sound(SOUND_GENERAL_COLLECT_1UP, gGlobalSoundSource);
}
}
}
}
}
if (gMarioState->numLives > gLevelValues.maxLives) {
gMarioState->numLives = gLevelValues.maxLives;
}
#if BUGFIX_MAX_LIVES
if (gMarioState->numCoins > gLevelValues.maxCoins) {
gMarioState->numCoins = gLevelValues.maxCoins;
}
if (gHudDisplay.coins > gLevelValues.maxCoins) {
gHudDisplay.coins = gLevelValues.maxCoins;
}
#else
if (gMarioState->numCoins > gLevelValues.maxCoins) {
gMarioState->numLives = (s8) gLevelValues.maxCoins;
}
#endif
gHudDisplay.stars = gMarioState->numStars;
gHudDisplay.lives = gMarioState->numLives;
gHudDisplay.keys = gMarioState->numKeys;
if (numHealthWedges > gHudDisplay.wedges && !gDjuiInMainMenu) {
play_sound(SOUND_MENU_POWER_METER, gGlobalSoundSource);
}
gHudDisplay.wedges = numHealthWedges;
if (gMarioState->hurtCounter > 0) {
gHudDisplay.flags |= HUD_DISPLAY_FLAG_EMPHASIZE_POWER;
} else {
gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_EMPHASIZE_POWER;
}
}
}
/**
* Update objects, HUD, and camera. This update function excludes things like
* endless staircase, warps, pausing, etc. This is used when entering a painting,
* presumably to allow painting and camera updating while avoiding triggering the
* warp twice.
*/
void basic_update(UNUSED s16 *arg) {
area_update_objects();
update_hud_values();
if (gCurrentArea != NULL) {
update_camera(gCurrentArea->camera);
}
}
bool find_demo_number(void) {
switch (gCurrLevelNum) {
case LEVEL_BOWSER_1:
sDemoNumber = 0;
return true;
case LEVEL_WF:
sDemoNumber = 1;
return true;
case LEVEL_CCM:
sDemoNumber = 2;
return true;
case LEVEL_BBH:
sDemoNumber = 3;
return true;
case LEVEL_JRB:
sDemoNumber = 4;
return true;
case LEVEL_HMC:
sDemoNumber = 5;
return true;
case LEVEL_PSS:
sDemoNumber = 6;
return true;
default:
sDemoNumber = -1;
}
return false;
}
static void start_demo(void) {
if (gIsDemoActive) {
gIsDemoActive = false;
} else {
gIsDemoActive = true;
if (find_demo_number()) {
gChangeLevel = gCurrLevelNum;
}
if (sDemoNumber >= 0 && sDemoNumber <= 6) {
gCurrDemoInput = NULL;
alloc_anim_dma_table(&gDemo, gDemoInputs, gDemoTargetAnim);
load_patchable_table(&gDemo, sDemoNumber, false);
gCurrDemoInput = ((struct DemoInput *) gDemo.targetAnim);
} else {
gIsDemoActive = false;
}
}
}
void stop_demo(UNUSED struct DjuiBase* caller) {
if (gIsDemoActive) {
gIsDemoActive = false;
gCurrDemoInput = NULL;
gChangeLevel = gCurrLevelNum;
gDemoCountdown = 0;
if (gDjuiInMainMenu || gNetworkType == NT_NONE) {
update_menu_level();
}
}
}
int gPressedStart = 0;
s32 play_mode_normal(void) {
if (!gDjuiInMainMenu) {
if (gCurrDemoInput != NULL) {
print_intro_text();
if (gPlayer1Controller->buttonPressed & END_DEMO) {
level_trigger_warp(gMarioState, gCurrLevelNum == LEVEL_PSS ? WARP_OP_DEMO_END : WARP_OP_DEMO_NEXT);
} else if (!gWarpTransition.isActive && sDelayedWarpOp == WARP_OP_NONE && (gPlayer1Controller->buttonPressed & START_BUTTON)) {
gPressedStart = 1;
level_trigger_warp(gMarioState, WARP_OP_DEMO_NEXT);
}
}
} else {
if (gDjuiInMainMenu &&
!configMenuStaffRoll &&
gCurrDemoInput == NULL &&
configMenuDemos &&
!gDjuiInPlayerMenu &&
(++gDemoCountdown) == PRESS_START_DEMO_TIMER &&
(find_demo_number() && (sDemoNumber <= 6 && sDemoNumber > -1)) &&
gNetworkType == NT_NONE) {
start_demo();
}
if (((gCurrDemoInput != NULL) &&
(gPlayer1Controller->buttonPressed & END_DEMO || !gIsDemoActive || !gDjuiInMainMenu || gNetworkType != NT_NONE || gDjuiInPlayerMenu)) ||
(gCurrDemoInput == NULL && gIsDemoActive)) {
gPlayer1Controller->buttonPressed &= ~END_DEMO;
stop_demo(NULL);
}
}
warp_area();
check_instant_warp();
if (sTimerRunning) {
gHudDisplay.timer = gNetworkAreaTimer - gControlTimerStartNat;
if (gHudDisplay.timer >= 17999) {
gHudDisplay.timer = 17999;
}
} else if (gControlTimerStopNat > 0) {
gHudDisplay.timer = gControlTimerStopNat - gControlTimerStartNat;
if (gHudDisplay.timer >= 17999) {
gHudDisplay.timer = 17999;
}
}
area_update_objects();
update_hud_values();
if (gCurrentArea != NULL) {
update_camera(gCurrentArea->camera);
}
initiate_painting_warp(-1);
initiate_delayed_warp();
// If either initiate_painting_warp or initiate_delayed_warp initiated a
// warp, change play mode accordingly.
if (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED) {
if (gCurrCreditsEntry != NULL && gCurrCreditsEntry != &sCreditsSequence[0]) {
// special case for credit warps
if (sWarpDest.type == WARP_TYPE_CHANGE_LEVEL) {
set_play_mode(PLAY_MODE_CHANGE_LEVEL);
} else if (sTransitionTimer != 0) {
set_play_mode(PLAY_MODE_CHANGE_AREA);
}
} else if (!gReceiveWarp.received) {
if (sWarpDest.type == WARP_TYPE_CHANGE_LEVEL) {
set_play_mode(PLAY_MODE_CHANGE_LEVEL);
} else if (sTransitionTimer != 0) {
set_play_mode(PLAY_MODE_CHANGE_AREA);
} else if (sCurrPlayMode == PLAY_MODE_NORMAL && pressed_pause()) {
lower_background_noise(1);
cancel_rumble();
gCameraMovementFlags |= CAM_MOVE_PAUSE_SCREEN;
set_play_mode(PLAY_MODE_PAUSED);
}
}
}
return 0;
}
s32 play_mode_paused(void) {
if (gPauseScreenMode == 0) {
set_menu_mode(RENDER_PAUSE_SCREEN);
} else if (gPauseScreenMode == 1) {
raise_background_noise(1);
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
set_play_mode(PLAY_MODE_NORMAL);
} else if (gPauseScreenMode == 2) {
level_trigger_warp(&gMarioStates[0], WARP_OP_EXIT);
set_play_mode(PLAY_MODE_NORMAL);
} else if (gPauseScreenMode == 3) {
// Exit level
if (gDebugLevelSelect) {
fade_into_special_warp(-9, 1);
} else {
initiate_warp(gLevelValues.exitCastleLevel, gLevelValues.exitCastleArea, gLevelValues.exitCastleWarpNode, 0);
fade_into_special_warp(0, 0);
gSavedCourseNum = COURSE_NONE;
}
set_play_mode(PLAY_MODE_CHANGE_LEVEL);
} /* else if (gPauseScreenMode == 4) {
// We should only be getting "int 4" to here
initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0);
fade_into_special_warp(0, 0);
game_exit();
}*/
if (!gLevelValues.zoomOutCameraOnPause || !network_check_singleplayer_pause()) {
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
}
return 0;
}
/**
* Debug mode that lets you frame advance by pressing D-pad down. Unfortunately
* it uses the pause camera, making it basically unusable in most levels.
*/
s32 play_mode_frame_advance(void) {
if (gPlayer1Controller->buttonPressed & D_JPAD) {
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
play_mode_normal();
} else if (gPlayer1Controller->buttonPressed & START_BUTTON) {
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
raise_background_noise(1);
set_play_mode(PLAY_MODE_NORMAL);
} else {
gCameraMovementFlags |= CAM_MOVE_PAUSE_SCREEN;
}
return 0;
}
/**
* Set the transition, which is a period of time after the warp is initiated
* but before it actually occurs. If updateFunction is not NULL, it will be
* called each frame during the transition.
*/
void level_set_transition(s16 length, void (*updateFunction)(s16 *)) {
sTransitionTimer = length;
sTransitionUpdate = updateFunction;
}
/**
* Play the transition and then return to normal play mode.
*/
s32 play_mode_change_area(void) {
// fade out all players
for (s32 i = 0; i < MAX_PLAYERS; i++) {
gNetworkPlayers[i].fadeOpacity = 0;
}
//! This maybe was supposed to be sTransitionTimer == -1? sTransitionUpdate
// is never set to -1.
if (sTransitionUpdate == (void (*)(s16 *)) - 1) {
update_camera(gCurrentArea->camera);
} else if (sTransitionUpdate != NULL) {
sTransitionUpdate(&sTransitionTimer);
}
if (sTransitionTimer > 0) {
sTransitionTimer -= 1;
}
if (sTransitionTimer == 0) {
sTransitionUpdate = NULL;
set_play_mode(PLAY_MODE_NORMAL);
}
return 0;
}
/**
* Play the transition and then return to normal play mode.
*/
s32 play_mode_change_level(void) {
// fade out all players
for (s32 i = 0; i < MAX_PLAYERS; i++) {
gNetworkPlayers[i].fadeOpacity = 0;
}
if (sTransitionUpdate != NULL) {
sTransitionUpdate(&sTransitionTimer);
}
if (--sTransitionTimer == -1) {
gHudDisplay.flags = HUD_DISPLAY_NONE;
sTransitionTimer = 0;
sTransitionUpdate = NULL;
if (gCurrActStarNum != 99) {
gCurrActStarNum = 0;
}
if (sWarpDest.type != WARP_TYPE_NOT_WARPING) {
return sWarpDest.levelNum;
} else {
return D_80339EE0;
}
}
return 0;
}
/**
* Unused play mode. Doesn't call transition update and doesn't reset transition at the end.
*/
UNUSED static s32 play_mode_unused(void) {
if (--sTransitionTimer == -1) {
gHudDisplay.flags = HUD_DISPLAY_NONE;
if (sWarpDest.type != WARP_TYPE_NOT_WARPING) {
return sWarpDest.levelNum;
} else {
return D_80339EE0;
}
}
return 0;
}
void update_menu_level(void) {
// figure out level
s32 curLevel = 0;
switch (configMenuLevel) {
case 0: curLevel = LEVEL_CASTLE_GROUNDS; break;
case 1: curLevel = LEVEL_BOB; break;
case 2: curLevel = LEVEL_WF; break;
case 3: curLevel = LEVEL_WMOTR; break;
case 4: curLevel = LEVEL_JRB; break;
case 5: curLevel = LEVEL_SSL; break;
case 6: curLevel = LEVEL_TTM; break;
case 7: curLevel = LEVEL_SL; break;
case 8: curLevel = LEVEL_BBH; break;
case 9: curLevel = LEVEL_LLL; break;
case 10: curLevel = LEVEL_THI; break;
case 11: curLevel = LEVEL_HMC; break;
case 12: curLevel = LEVEL_CCM; break;
case 13: curLevel = LEVEL_RR; break;
case 14: curLevel = LEVEL_BITDW; break;
case 15: curLevel = LEVEL_PSS; break;
case 16: curLevel = LEVEL_TTC; break;
case 17: curLevel = LEVEL_WDW; break;
default: curLevel = LEVEL_CASTLE_GROUNDS; break;
}
// figure out music
stop_cap_music();
if (!configMenuSound || configMenuStaffRoll || curLevel == LEVEL_CASTLE_GROUNDS) {
reset_volume();
sound_banks_disable(SEQ_PLAYER_SFX, SOUND_BANKS_BACKGROUND);
set_background_music(0, SEQ_MENU_TITLE_SCREEN, 0);
} else {
reset_volume();
sound_banks_disable(SEQ_PLAYER_SFX, SOUND_BANKS_BACKGROUND);
if (gCurrentArea != NULL) {
set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0);
}
}
if (configMenuStaffRoll) {
return;
} else {
gCurrCreditsEntry = NULL;
}
// warp to level
if (gCurrLevelNum != curLevel) {
if (gIsDemoActive) {
stop_demo(NULL);
}
gChangeLevel = curLevel;
gChangeActNum = 6;
gDemoCountdown = 0;
}
if (gIsDemoActive) { return; }
if (gCurrAreaIndex != 2 && gCurrLevelNum == LEVEL_THI) {
sWarpDest.type = WARP_TYPE_CHANGE_AREA;
sWarpDest.areaIdx = 2;
sWarpDest.nodeId = 0x0A;
}
struct Object *o;
// set mario/camera pos
switch (gCurrLevelNum) {
case LEVEL_CASTLE_GROUNDS:
if (gMarioState->action != ACT_INTRO_CUTSCENE && gMarioState->prevAction != ACT_INTRO_CUTSCENE) {
vec3f_set(gMarioState->pos, -1328, 260, 4664);
vec3f_set(gLakituState.curPos, -1328, 390, 6064);
gMarioState->faceAngle[1] = 0;
gLakituState.nextYaw = gMarioState->faceAngle[1] + 0x8000;
}
break;
case LEVEL_BOB:
vec3f_set(gMarioState->pos, 7008, 864, 1943);
vec3f_set(gLakituState.curPos, 7909, 1064, 2843);
gMarioState->faceAngle[1] = 0x2000;
// delete all goombas as they interfere with the main menu
o = find_object_with_behavior(bhvGoomba);
if (o != NULL) {
obj_mark_for_deletion(o);
}
break;
case LEVEL_WF:
vec3f_set(gMarioState->pos, -2904, 2560, -327);
vec3f_set(gLakituState.curPos, -4504, 2760, -777);
gMarioState->faceAngle[1] = -15536;
break;
case LEVEL_WMOTR:
vec3f_set(gMarioState->pos, 3548, -2738, 4663);
vec3f_set(gLakituState.curPos, 3548, -2438, 6063);
gMarioState->faceAngle[1] = 0;
break;
case LEVEL_JRB:
vec3f_set(gMarioState->pos, 3639, 1536, 6202);
vec3f_set(gLakituState.curPos, 5039, 1736, 6402);
break;
case LEVEL_SSL:
vec3f_set(gMarioState->pos, -2048, 256, 961);
vec3f_set(gLakituState.curPos, -2048, 356, 2461);
gMarioState->faceAngle[1] = 0;
break;
case LEVEL_TTM:
vec3f_set(gMarioState->pos, 2488, 1460, 2011);
vec3f_set(gLakituState.curPos, 3488, 1763, 3411);
gMarioState->faceAngle[1] = 0x1000;
break;
case LEVEL_SL:
vec3f_set(gMarioState->pos, 5494, 1024, 443);
vec3f_set(gLakituState.curPos, 6994, 1124, 443);
gMarioState->faceAngle[1] = 0x4000;
break;
case LEVEL_BBH:
vec3f_set(gMarioState->pos, 666, -204, 5303);
vec3f_set(gLakituState.curPos, 666, -204, 6803);
gMarioState->faceAngle[1] = 0;
// delete all scuttlebugs as they interfere with the main menu
o = find_object_with_behavior(bhvScuttlebug);
if (o != NULL) {
obj_mark_for_deletion(o);
}
break;
case LEVEL_LLL:
vec3f_set(gMarioState->pos, -2376, 638, 956);
vec3f_set(gLakituState.curPos, -3576, 938, 1576);
gMarioState->faceAngle[1] = -0x2800;
break;
case LEVEL_THI:
vec3f_set(gMarioState->pos, -1010, 341, -324);
vec3f_set(gLakituState.curPos, -2246, 431, -324);
gMarioState->faceAngle[1] = -0x4000;
// delete all goombas as they interfere with the main menu
o = find_object_with_behavior(bhvGoomba);
if (o != NULL) {
obj_mark_for_deletion(o);
}
break;
case LEVEL_HMC:
vec3f_set(gMarioState->pos, -3600, -4279, 3616);
vec3f_set(gLakituState.curPos, -6000, -2938, 600);
gMarioState->faceAngle[1] = -0x6000;
break;
case LEVEL_CCM:
vec3f_set(gMarioState->pos, -1127, -3580, 6162);
vec3f_set(gLakituState.curPos, -1330, -2830, 9099);
gMarioState->faceAngle[1] = -0x1000;
break;
case LEVEL_RR:
vec3f_set(gMarioState->pos, 1418, 3167, -2349);
vec3f_set(gLakituState.curPos, -1518, 4567, -4549);
gMarioState->faceAngle[1] = -0x6000;
break;
case LEVEL_BITDW:
vec3f_set(gMarioState->pos, -4507, 1126, -285);
vec3f_set(gLakituState.curPos, -2507, 2126, -285);
break;
case LEVEL_PSS:
vec3f_set(gMarioState->pos, -4729, -3057, -3025);
vec3f_set(gLakituState.curPos, -2729, -1557, -5025);
gMarioState->faceAngle[1] = 0x5000;
break;
case LEVEL_TTC:
vec3f_set(gMarioState->pos, -645, 0, -750);
vec3f_set(gLakituState.curPos, 2500, 570, -240);
gMarioState->faceAngle[1] = 0x2000;
break;
case LEVEL_WDW:
vec3f_set(gMarioState->pos, -2684, 3328, 3000);
vec3f_set(gLakituState.curPos, -4002, 4000, 4622);
gMarioState->faceAngle[1] = -0x1C34;
break;
}
gMarioState->health = 0x880;
// reset input
gMarioState->input = 0;
gMarioState->intendedMag = 0;
gMarioState->controller->buttonDown = 0;
gMarioState->controller->buttonPressed = 0;
gMarioState->controller->rawStickX = 0;
gMarioState->controller->rawStickY = 0;
gMarioState->controller->stickX = 0;
gMarioState->controller->stickY = 0;
gMarioState->controller->extStickX = 0;
gMarioState->controller->extStickY = 0;
gMarioState->controller->stickMag = 0;
}
s32 update_level(void) {
// update main menu level
if (gDjuiInMainMenu) {
update_menu_level();
}
sCancelNextActSelector = gDjuiInMainMenu;
if (gFanFareDebounce > 0) { gFanFareDebounce--; }
s32 changeLevel = 0;
if (gChangeLevel != -1) {
gHudDisplay.flags = HUD_DISPLAY_NONE;
sTransitionTimer = 0;
sTransitionUpdate = NULL;
changeLevel = gChangeLevel;
gChangeLevel = -1;
gChangeLevelTransition = -1;
return changeLevel;
} else if (!gWarpTransition.isActive && sDelayedWarpOp == WARP_OP_NONE && gChangeLevelTransition != -1) {
gHudDisplay.flags = HUD_DISPLAY_NONE;
sTransitionTimer = 0;
sTransitionUpdate = NULL;
changeLevel = gChangeLevelTransition;
gChangeLevel = -1;
gChangeLevelTransition = -1;
return changeLevel;
}
if (gCurrentArea != NULL) {
gCurrentArea->localAreaTimer++;
}
switch (sCurrPlayMode) {
case PLAY_MODE_NORMAL:
changeLevel = play_mode_normal();
break;
case PLAY_MODE_PAUSED:
if (!network_check_singleplayer_pause()) {
changeLevel = play_mode_normal();
}
if (sCurrPlayMode == PLAY_MODE_PAUSED) {
changeLevel = play_mode_paused();
}
break;
case PLAY_MODE_CHANGE_AREA:
changeLevel = play_mode_change_area();
break;
case PLAY_MODE_CHANGE_LEVEL:
changeLevel = play_mode_change_level();
break;
case PLAY_MODE_FRAME_ADVANCE:
changeLevel = play_mode_frame_advance();
break;
}
if (changeLevel) {
reset_volume();
enable_background_sound();
}
return changeLevel;
}
s32 init_level(void) {
sync_objects_clear();
reset_dialog_render_state();
s32 val4 = 0;
set_play_mode(PLAY_MODE_NORMAL);
sDelayedWarpOp = WARP_OP_NONE;
sTransitionTimer = 0;
D_80339EE0 = 0;
for (int i = 0; i < 8; i++) {
gSpawnedStarDefault[i] = 0;
gSpawnedStarNLE[i] = 0;
gSpawnedStarRed[i] = 0;
gSpawnedStarHidden[i] = 0;
}
gSpawnedStarDefaultCount = 0;
gSpawnedStarNLECount = 0;
gSpawnedStarRedCount = 0;
gSpawnedStarHiddenCount = 0;
if (gCurrCreditsEntry == NULL) {
gHudDisplay.flags = HUD_DISPLAY_DEFAULT;
} else {
gHudDisplay.flags = HUD_DISPLAY_NONE;
}
sTimerRunning = FALSE;
if (sWarpDest.type != WARP_TYPE_NOT_WARPING) {
if (sWarpDest.nodeId >= WARP_NODE_CREDITS_MIN) {
warp_credits();
} else {
warp_level();
}
} else {
if (gPlayerSpawnInfos[0].areaIndex >= 0) {
load_mario_area();
init_mario();
}
if (gCurrentArea != NULL) {
reset_camera(gCurrentArea->camera);
if (gCurrDemoInput != NULL) {
set_mario_action(gMarioState, ACT_IDLE, 0);
} else if (!gDebugLevelSelect) {
if (gMarioState && gMarioState->action != ACT_UNINITIALIZED) {
bool skipIntro = (gNetworkType == NT_NONE || gServerSettings.skipIntro != 0);
if (gDjuiInMainMenu && gNetworkType == NT_NONE) {
// pick random main menu level
if (configMenuRandom) {
srand(time(0));
int randLevel = rand() % (MENU_LEVEL_MAX - MENU_LEVEL_MIN) + 1;
configMenuLevel = randLevel;
}
if (configMenuStaffRoll) {
gMarioState->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
warp_credits();
level_trigger_warp(gMarioState, WARP_OP_CREDITS_NEXT);
sFirstCastleGroundsMenu = false;
} else {
if (configMenuLevel == 0 && sFirstCastleGroundsMenu) {
set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 7);
sFirstCastleGroundsMenu = false;
} else {
set_mario_action(gMarioState, ACT_IDLE, 0);
}
}
} else if (skipIntro || save_file_exists(gCurrSaveFileNum - 1) || gDjuiInMainMenu) {
set_mario_action(gMarioState, ACT_IDLE, 0);
} else {
set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0);
val4 = 1;
}
}
}
}
if (val4 != 0) {
play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 0x5A, 0xFF, 0xFF, 0xFF);
} else {
play_transition(WARP_TRANSITION_FADE_FROM_STAR, 0x10, 0xFF, 0xFF, 0xFF);
}
if (gCurrDemoInput == NULL && gCurrentArea) {
set_background_music(gCurrentArea->musicParam, gCurrentArea->musicParam2, 0);
}
}
if (gCurrDemoInput == NULL) {
cancel_rumble();
}
if (gMarioState && gMarioState->action == ACT_INTRO_CUTSCENE) {
sound_banks_disable(SEQ_PLAYER_SFX, SOUND_BANKS_DISABLED_DURING_INTRO_CUTSCENE);
}
if (gNetworkPlayerLocal != NULL) {
network_player_update_course_level(gNetworkPlayerLocal, gCurrCourseNum, gCurrActStarNum, gCurrLevelNum, gCurrAreaIndex);
}
smlua_call_event_hooks(HOOK_ON_LEVEL_INIT);
// clear texture 1 on level init -- can linger and corrupt textures otherwise
extern u8 gGfxPcResetTex1;
gGfxPcResetTex1 = 1;
// reset nametags
nametags_reset();
if (gDelayedInitSound >= 0) {
play_character_sound(&gMarioStates[0], gDelayedInitSound);
gDelayedInitSound = -1;
}
return 1;
}
/**
* Initialize the current level if initOrUpdate is 0, or update the level if it is 1.
*/
s32 lvl_init_or_update(s16 initOrUpdate, UNUSED s32 unused) {
s32 result = 0;
switch (initOrUpdate) {
case 0:
result = init_level();
break;
case 1:
result = update_level();
break;
}
return result;
}
s32 lvl_init_from_save_file(UNUSED s16 arg0, s16 levelNum) {
#ifdef VERSION_EU
s16 var = eu_get_language();
switch (var) {
case LANGUAGE_ENGLISH:
load_segment_decompress(0x19, _translation_en_mio0SegmentRomStart,
_translation_en_mio0SegmentRomEnd);
break;
case LANGUAGE_FRENCH:
load_segment_decompress(0x19, _translation_fr_mio0SegmentRomStart,
_translation_fr_mio0SegmentRomEnd);
break;
case LANGUAGE_GERMAN:
load_segment_decompress(0x19, _translation_de_mio0SegmentRomStart,
_translation_de_mio0SegmentRomEnd);
break;
}
#endif
sWarpDest.type = WARP_TYPE_NOT_WARPING;
sDelayedWarpOp = WARP_OP_NONE;
gNeverEnteredCastle = !save_file_exists(gCurrSaveFileNum - 1) && (gServerSettings.skipIntro == 0);
if (gNetworkType == NT_NONE) { gNeverEnteredCastle = true; }
gCurrLevelNum = levelNum;
gCurrCourseNum = COURSE_NONE;
gSavedCourseNum = COURSE_NONE;
gCurrCreditsEntry = NULL;
gMarioStates[0].specialTripleJump = FALSE;
init_mario_from_save_file();
disable_warp_checkpoint();
save_file_move_cap_to_default_location();
select_mario_cam_mode();
set_yoshi_as_not_dead();
return levelNum;
}
s32 lvl_set_current_level(UNUSED s16 arg0, s16 levelNum) {
s32 warpCheckpointActive = sWarpCheckpointActive;
sWarpCheckpointActive = FALSE;
gCurrLevelNum = levelNum;
gCurrCourseNum = get_level_course_num(levelNum);
bool foundHook = false;
bool hookUseActSelect = false;
smlua_call_event_hooks_use_act_select(HOOK_USE_ACT_SELECT, levelNum, &foundHook, &hookUseActSelect);
if (!foundHook || !hookUseActSelect) {
if (gCurrDemoInput != NULL || gCurrCreditsEntry != NULL || gCurrCourseNum == COURSE_NONE) {
return 0;
}
}
if (gCurrLevelNum != LEVEL_BOWSER_1 && gCurrLevelNum != LEVEL_BOWSER_2
&& gCurrLevelNum != LEVEL_BOWSER_3) {
gMarioState->numCoins = 0;
gHudDisplay.coins = 0;
gCurrCourseStarFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
}
if (gSavedCourseNum != gCurrCourseNum) {
gSavedCourseNum = gCurrCourseNum;
nop_change_course();
disable_warp_checkpoint();
}
if (gDjuiInMainMenu) {
return 0;
}
if (foundHook) {
return hookUseActSelect;
}
if (gLevelValues.disableActs) {
return 0;
}
if (gCurrCourseNum > COURSE_STAGES_MAX || warpCheckpointActive) {
return 0;
}
if (gDebugLevelSelect && !gShowProfiler) {
return 0;
}
if (sCancelNextActSelector) {
sCancelNextActSelector = false;
return 0;
}
return 1;
}
/**
* Play the "thank you so much for to playing my game" sound.
*/
s32 lvl_play_the_end_screen_sound(UNUSED s16 arg0, UNUSED s32 arg1) {
play_sound(SOUND_MENU_THANK_YOU_PLAYING_MY_GAME, gGlobalSoundSource);
return 1;
}
s32 lvl_exiting_credits(UNUSED s16 arg0, UNUSED s32 arg1) {
gCurrActStarNum = 0;
gCurrActNum = 0;
return 1;
}
void fake_lvl_init_from_save_file(void) {
sWarpDest.type = WARP_TYPE_NOT_WARPING;
sDelayedWarpOp = WARP_OP_NONE;
gNeverEnteredCastle = !save_file_exists(gCurrSaveFileNum - 1) && (gServerSettings.skipIntro == 0);
if (gNetworkType == NT_NONE) { gNeverEnteredCastle = true; }
gCurrCreditsEntry = NULL;
gMarioStates[0].specialTripleJump = false;
init_mario_from_save_file();
disable_warp_checkpoint();
save_file_move_cap_to_default_location();
select_mario_cam_mode();
set_yoshi_as_not_dead();
fadeout_music(30);
gChangeLevel = gLevelValues.entryLevel;
}
void lvl_skip_credits(void) {
gCurrCreditsEntry = NULL;
gCurrActStarNum = 0;
gCurrActNum = 0;
gChangeLevel = gLevelValues.entryLevel;
gMarioStates[0].health = 0x880;
play_transition(0x09, 0x14, 0x00, 0x00, 0x00);
}