From 946f16329c4e39e3206408df8ce7bed7a9b2cf2f Mon Sep 17 00:00:00 2001 From: MysterD Date: Sun, 5 Jun 2022 21:55:31 -0700 Subject: [PATCH] Added ability to have completely custom levels that don't override anything --- autogen/convert_functions.py | 4 +- autogen/convert_structs.py | 1 + autogen/lua_definitions/functions.lua | 75 ++++++--- autogen/lua_definitions/structs.lua | 14 ++ data/dynos.c.h | 1 + data/dynos.cpp.h | 1 + data/dynos_c.cpp | 4 + data/dynos_level.cpp | 24 ++- data/dynos_mgr_lvl.cpp | 16 +- docs/lua/functions-4.md | 233 +++++++++++++++++--------- docs/lua/functions.md | 15 +- docs/lua/structs.md | 21 +++ src/audio/external.c | 65 +++++-- src/game/camera.c | 35 ++-- src/game/level_info.c | 17 +- src/game/level_update.c | 2 +- src/game/save_file.c | 16 +- src/game/save_file.h | 1 + src/menu/level_select_menu.c | 16 +- src/pc/chat_commands.c | 7 + src/pc/lua/smlua.c | 2 + src/pc/lua/smlua_cobject_autogen.c | 17 ++ src/pc/lua/smlua_cobject_autogen.h | 1 + src/pc/lua/smlua_functions_autogen.c | 157 +++++++++++------ src/pc/lua/utils/smlua_level_utils.c | 143 ++++++++++++++++ src/pc/lua/utils/smlua_level_utils.h | 29 ++++ src/pc/lua/utils/smlua_misc_utils.c | 18 -- src/pc/lua/utils/smlua_misc_utils.h | 5 - 28 files changed, 726 insertions(+), 214 deletions(-) create mode 100644 src/pc/lua/utils/smlua_level_utils.c create mode 100644 src/pc/lua/utils/smlua_level_utils.h diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index 8ab1615bd..b53d2ba13 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -42,6 +42,7 @@ in_files = [ "src/pc/lua/utils/smlua_model_utils.h", "src/pc/lua/utils/smlua_text_utils.h", "src/pc/lua/utils/smlua_audio_utils.h", + "src/pc/lua/utils/smlua_level_utils.h", "src/game/object_helpers.c", "src/game/obj_behaviors.c", "src/game/obj_behaviors_2.c", @@ -87,7 +88,8 @@ override_disallowed_functions = { "src/game/camera.h": [ "update_camera", "init_camera", "stub_camera", "^reset_camera", "move_point_along_spline" ], "src/game/behavior_actions.h": [ "bhv_dust_smoke_loop", "bhv_init_room" ], "src/pc/lua/utils/smlua_audio_utils.h": [ "smlua_audio_utils_override", "audio_custom_shutdown"], - "src/pc/djui/djui_hud_utils.h": [ "djui_hud_render_texture", "djui_hud_render_texture_raw" ], + "src/pc/djui/djui_hud_utils.h": [ "djui_hud_render_texture", "djui_hud_render_texture_raw" ], + "src/pc/lua/utils/smlua_level_utils.h": [ "smlua_level_util_reset" ], } lua_function_params = { diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py index d6f16b373..ef36c8e4b 100644 --- a/autogen/convert_structs.py +++ b/autogen/convert_structs.py @@ -18,6 +18,7 @@ in_files = [ 'src/pc/lua/utils/smlua_anim_utils.h', 'src/pc/lua/utils/smlua_misc_utils.h', 'src/pc/lua/utils/smlua_collision_utils.h', + 'src/pc/lua/utils/smlua_level_utils.h', 'src/game/spawn_sound.h', 'src/pc/network/network.h', 'src/game/hardcoded.h', diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 534575e1d..3a0aa8d9a 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -7330,6 +7330,56 @@ function smlua_collision_util_get(name) -- ... end +--- @param scriptEntryName string +--- @param courseNum integer +--- @param fullName string +--- @param shortName string +--- @param acousticReach integer +--- @param echoLevel1 integer +--- @param echoLevel2 integer +--- @param echoLevel3 integer +--- @return integer +function level_register(scriptEntryName, courseNum, fullName, shortName, acousticReach, echoLevel1, echoLevel2, echoLevel3) + -- ... +end + +--- @param levelNum integer +--- @return CustomLevelInfo +function smlua_level_util_get_info(levelNum) + -- ... +end + +--- @param shortName string +--- @return CustomLevelInfo +function smlua_level_util_get_info_from_short_name(shortName) + -- ... +end + +--- @param aDelay integer +--- @return boolean +function warp_exit_level(aDelay) + -- ... +end + +--- @return boolean +function warp_restart_level() + -- ... +end + +--- @param aLevel integer +--- @return boolean +function warp_to_castle(aLevel) + -- ... +end + +--- @param aLevel integer +--- @param aArea integer +--- @param aAct integer +--- @return boolean +function warp_to_level(aLevel, aArea, aAct) + -- ... +end + --- @param actFlags integer --- @return integer function allocate_mario_action(actFlags) @@ -7593,31 +7643,6 @@ function set_override_near(near) -- ... end ---- @param aDelay integer ---- @return boolean -function warp_exit_level(aDelay) - -- ... -end - ---- @return boolean -function warp_restart_level() - -- ... -end - ---- @param aLevel integer ---- @return boolean -function warp_to_castle(aLevel) - -- ... -end - ---- @param aLevel integer ---- @param aArea integer ---- @param aAct integer ---- @return boolean -function warp_to_level(aLevel, aArea, aAct) - -- ... -end - --- @param name string --- @return integer function smlua_model_util_get_id(name) diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index b89ee40cb..a96535a9c 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -327,6 +327,19 @@ --- @field public stickX number --- @field public stickY number +--- @class CustomLevelInfo +--- @field public acousticReach integer +--- @field public courseNum integer +--- @field public echoLevel1 integer +--- @field public echoLevel2 integer +--- @field public echoLevel3 integer +--- @field public fullName string +--- @field public levelNum integer +--- @field public next CustomLevelInfo +--- @field public script Pointer_LevelScript +--- @field public scriptEntryName string +--- @field public shortName string + --- @class Cutscene --- @field public duration integer @@ -1777,6 +1790,7 @@ --- @class Pointer_integer --- @class Pointer_Trajectory +--- @class Pointer_LevelScript --- @class Pointer_ObjectAnimPointer --- @class Pointer_Collision --- @class Pointer_BehaviorScript diff --git a/data/dynos.c.h b/data/dynos.c.h index 6408fbb95..fb415c05a 100644 --- a/data/dynos.c.h +++ b/data/dynos.c.h @@ -17,6 +17,7 @@ s32 dynos_tex_import(void **output, void *ptr, s32 tile, void *grapi, void **h void dynos_gfx_swap_animations(void *ptr); // -- warps -- // +LevelScript* dynos_get_level_script(char* scriptEntryName); bool dynos_warp_to_level(s32 aLevel, s32 aArea, s32 aAct); bool dynos_warp_restart_level(void); bool dynos_warp_exit_level(s32 aDelay); diff --git a/data/dynos.cpp.h b/data/dynos.cpp.h index b18ee9f59..fda42893d 100644 --- a/data/dynos.cpp.h +++ b/data/dynos.cpp.h @@ -815,6 +815,7 @@ void DynOS_Tex_ModShutdown(); // Array> &DynOS_Lvl_GetArray(); +LevelScript* DynOS_Lvl_GetScript(char* aScriptEntryName); void DynOS_Lvl_Activate(s32 modIndex, const SysPath &aFilePath, const char *aLevelName); GfxData* DynOS_Lvl_GetActiveGfx(void); const char* DynOS_Lvl_GetToken(u32 index); diff --git a/data/dynos_c.cpp b/data/dynos_c.cpp index c01d4ef31..e8e27f5cc 100644 --- a/data/dynos_c.cpp +++ b/data/dynos_c.cpp @@ -30,6 +30,10 @@ void dynos_gfx_swap_animations(void *ptr) { // -- warps -- // +LevelScript* dynos_get_level_script(char* scriptEntryName) { + return DynOS_Lvl_GetScript(scriptEntryName); +} + bool dynos_warp_to_level(s32 aLevel, s32 aArea, s32 aAct) { return DynOS_Warp_ToLevel(aLevel, aArea, aAct); } diff --git a/data/dynos_level.cpp b/data/dynos_level.cpp index 9c58f47cc..7998d3f77 100644 --- a/data/dynos_level.cpp +++ b/data/dynos_level.cpp @@ -3,6 +3,7 @@ extern "C" { #include "game/segment2.h" #include "game/save_file.h" #include "levels/scripts.h" +#include "pc/lua/utils/smlua_level_utils.h" } // @@ -198,7 +199,7 @@ static void DynOS_Level_Init() { for (s32 i = COURSE_MIN; i <= COURSE_MAX; ++i) { if (i == COURSE_CAKE_END) continue; for (s32 j = 1; j != LEVEL_COUNT; ++j) { - if (gLevelToCourseNumTable[j - 1] == i) { + if (get_level_course_num(j - 1) == i) { sDynosLevelList.Add(j); } } @@ -249,6 +250,12 @@ void DynOS_Level_Unoverride() { } const void *DynOS_Level_GetScript(s32 aLevel) { + if (aLevel >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel); + if (!info || !info->script) { return NULL; } + return info->script; + } + DynOS_Level_Init(); return sDynosLevelScripts[aLevel]; } @@ -844,6 +851,21 @@ static void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFuncti // s16 *DynOS_Level_GetWarp(s32 aLevel, s32 aArea, u8 aWarpId) { + if (aLevel >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel); + if (!info || !info->script) { return NULL; } + sDynosCurrentLevelNum = 1; + DynOS_Level_ParseScript(info->script, DynOS_Level_PreprocessScript); + for (const auto &_Warp : sDynosLevelWarps[1]) { + if (_Warp.mArea == aArea) { + if (_Warp.mId == aWarpId) { + return (s16 *) &_Warp; + } + } + } + return NULL; + } + DynOS_Level_Init(); for (const auto &_Warp : sDynosLevelWarps[aLevel]) { if (_Warp.mArea == aArea) { diff --git a/data/dynos_mgr_lvl.cpp b/data/dynos_mgr_lvl.cpp index e8659e37b..05abe5c2e 100644 --- a/data/dynos_mgr_lvl.cpp +++ b/data/dynos_mgr_lvl.cpp @@ -21,6 +21,19 @@ Array> &DynOS_Lvl_GetArray() { return sDynosCustomLevelScripts; } +LevelScript* DynOS_Lvl_GetScript(char* aScriptEntryName) { + auto& _CustomLevelScripts = DynOS_Lvl_GetArray(); + for (s32 i = 0; i < _CustomLevelScripts.Count(); ++i) { + auto& pair = _CustomLevelScripts[i]; + if (!strcmp(pair.first, aScriptEntryName)) { + auto& newScripts = pair.second->mLevelScripts; + auto& newScriptNode = newScripts[newScripts.Count() - 1]; + return newScriptNode->mData; + } + } + return NULL; +} + void DynOS_Lvl_ModShutdown() { DynOS_Level_Unoverride(); @@ -66,6 +79,7 @@ void DynOS_Lvl_Activate(s32 modIndex, const SysPath &aFilename, const char *aLev // Add to levels _CustomLevelScripts.Add({ levelName, _Node }); + DynOS_Tex_Valid(_Node); // Override vanilla script auto& newScripts = _Node->mLevelScripts; @@ -78,7 +92,6 @@ void DynOS_Lvl_Activate(s32 modIndex, const SysPath &aFilename, const char *aLev DynOS_Level_Override((void*)originalScript, newScriptNode->mData); _OverrideLevelScripts.Add({ originalScript, newScriptNode->mData, _Node}); - DynOS_Tex_Valid(_Node); } GfxData* DynOS_Lvl_GetActiveGfx(void) { @@ -170,3 +183,4 @@ void *DynOS_Lvl_Override(void *aCmd) { } return aCmd; } + diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md index 747050c5d..151f1041e 100644 --- a/docs/lua/functions-4.md +++ b/docs/lua/functions-4.md @@ -6073,6 +6073,159 @@
+--- +# functions from smlua_level_utils.h + +
+ + +## [level_register](#level_register) + +### Lua Example +`local integerValue = level_register(scriptEntryName, courseNum, fullName, shortName, acousticReach, echoLevel1, echoLevel2, echoLevel3)` + +### Parameters +| Field | Type | +| ----- | ---- | +| scriptEntryName | `string` | +| courseNum | `integer` | +| fullName | `string` | +| shortName | `string` | +| acousticReach | `integer` | +| echoLevel1 | `integer` | +| echoLevel2 | `integer` | +| echoLevel3 | `integer` | + +### Returns +- `integer` + +### C Prototype +`s16 level_register(const char* scriptEntryName, s16 courseNum, const char* fullName, const char* shortName, u32 acousticReach, u32 echoLevel1, u32 echoLevel2, u32 echoLevel3);` + +[:arrow_up_small:](#) + +
+ +## [smlua_level_util_get_info](#smlua_level_util_get_info) + +### Lua Example +`local CustomLevelInfoValue = smlua_level_util_get_info(levelNum)` + +### Parameters +| Field | Type | +| ----- | ---- | +| levelNum | `integer` | + +### Returns +[CustomLevelInfo](structs.md#CustomLevelInfo) + +### C Prototype +`struct CustomLevelInfo* smlua_level_util_get_info(s16 levelNum);` + +[:arrow_up_small:](#) + +
+ +## [smlua_level_util_get_info_from_short_name](#smlua_level_util_get_info_from_short_name) + +### Lua Example +`local CustomLevelInfoValue = smlua_level_util_get_info_from_short_name(shortName)` + +### Parameters +| Field | Type | +| ----- | ---- | +| shortName | `string` | + +### Returns +[CustomLevelInfo](structs.md#CustomLevelInfo) + +### C Prototype +`struct CustomLevelInfo* smlua_level_util_get_info_from_short_name(char* shortName);` + +[:arrow_up_small:](#) + +
+ +## [warp_exit_level](#warp_exit_level) + +### Lua Example +`local booleanValue = warp_exit_level(aDelay)` + +### Parameters +| Field | Type | +| ----- | ---- | +| aDelay | `integer` | + +### Returns +- `boolean` + +### C Prototype +`bool warp_exit_level(s32 aDelay);` + +[:arrow_up_small:](#) + +
+ +## [warp_restart_level](#warp_restart_level) + +### Lua Example +`local booleanValue = warp_restart_level()` + +### Parameters +- None + +### Returns +- `boolean` + +### C Prototype +`bool warp_restart_level(void);` + +[:arrow_up_small:](#) + +
+ +## [warp_to_castle](#warp_to_castle) + +### Lua Example +`local booleanValue = warp_to_castle(aLevel)` + +### Parameters +| Field | Type | +| ----- | ---- | +| aLevel | `integer` | + +### Returns +- `boolean` + +### C Prototype +`bool warp_to_castle(s32 aLevel);` + +[:arrow_up_small:](#) + +
+ +## [warp_to_level](#warp_to_level) + +### Lua Example +`local booleanValue = warp_to_level(aLevel, aArea, aAct)` + +### Parameters +| Field | Type | +| ----- | ---- | +| aLevel | `integer` | +| aArea | `integer` | +| aAct | `integer` | + +### Returns +- `boolean` + +### C Prototype +`bool warp_to_level(s32 aLevel, s32 aArea, s32 aAct);` + +[:arrow_up_small:](#) + +
+ --- # functions from smlua_misc_utils.h @@ -6941,86 +7094,6 @@
-## [warp_exit_level](#warp_exit_level) - -### Lua Example -`local booleanValue = warp_exit_level(aDelay)` - -### Parameters -| Field | Type | -| ----- | ---- | -| aDelay | `integer` | - -### Returns -- `boolean` - -### C Prototype -`bool warp_exit_level(s32 aDelay);` - -[:arrow_up_small:](#) - -
- -## [warp_restart_level](#warp_restart_level) - -### Lua Example -`local booleanValue = warp_restart_level()` - -### Parameters -- None - -### Returns -- `boolean` - -### C Prototype -`bool warp_restart_level(void);` - -[:arrow_up_small:](#) - -
- -## [warp_to_castle](#warp_to_castle) - -### Lua Example -`local booleanValue = warp_to_castle(aLevel)` - -### Parameters -| Field | Type | -| ----- | ---- | -| aLevel | `integer` | - -### Returns -- `boolean` - -### C Prototype -`bool warp_to_castle(s32 aLevel);` - -[:arrow_up_small:](#) - -
- -## [warp_to_level](#warp_to_level) - -### Lua Example -`local booleanValue = warp_to_level(aLevel, aArea, aAct)` - -### Parameters -| Field | Type | -| ----- | ---- | -| aLevel | `integer` | -| aArea | `integer` | -| aAct | `integer` | - -### Returns -- `boolean` - -### C Prototype -`bool warp_to_level(s32 aLevel, s32 aArea, s32 aAct);` - -[:arrow_up_small:](#) - -
- --- # functions from smlua_model_utils.h diff --git a/docs/lua/functions.md b/docs/lua/functions.md index cc8c1b410..929aee211 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1368,6 +1368,17 @@
+- smlua_level_utils.h + - [level_register](functions-4.md#level_register) + - [smlua_level_util_get_info](functions-4.md#smlua_level_util_get_info) + - [smlua_level_util_get_info_from_short_name](functions-4.md#smlua_level_util_get_info_from_short_name) + - [warp_exit_level](functions-4.md#warp_exit_level) + - [warp_restart_level](functions-4.md#warp_restart_level) + - [warp_to_castle](functions-4.md#warp_to_castle) + - [warp_to_level](functions-4.md#warp_to_level) + +
+ - smlua_misc_utils.h - [allocate_mario_action](functions-4.md#allocate_mario_action) - [camera_config_enable_analog_cam](functions-4.md#camera_config_enable_analog_cam) @@ -1413,10 +1424,6 @@ - [set_override_far](functions-4.md#set_override_far) - [set_override_fov](functions-4.md#set_override_fov) - [set_override_near](functions-4.md#set_override_near) - - [warp_exit_level](functions-4.md#warp_exit_level) - - [warp_restart_level](functions-4.md#warp_restart_level) - - [warp_to_castle](functions-4.md#warp_to_castle) - - [warp_to_level](functions-4.md#warp_to_level)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md index 3787f76d6..f48f902a2 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -16,6 +16,7 @@ - [ChainSegment](#ChainSegment) - [Character](#Character) - [Controller](#Controller) +- [CustomLevelInfo](#CustomLevelInfo) - [Cutscene](#Cutscene) - [CutsceneSplinePoint](#CutsceneSplinePoint) - [CutsceneVariable](#CutsceneVariable) @@ -506,6 +507,26 @@
+## [CustomLevelInfo](#CustomLevelInfo) + +| Field | Type | Access | +| ----- | ---- | ------ | +| acousticReach | `integer` | | +| courseNum | `integer` | | +| echoLevel1 | `integer` | | +| echoLevel2 | `integer` | | +| echoLevel3 | `integer` | | +| fullName | `string` | read-only | +| levelNum | `integer` | | +| next | [CustomLevelInfo](structs.md#CustomLevelInfo) | | +| script | `Pointer` <`LevelScript`> | read-only | +| scriptEntryName | `string` | read-only | +| shortName | `string` | read-only | + +[:arrow_up_small:](#) + +
+ ## [Cutscene](#Cutscene) | Field | Type | Access | diff --git a/src/audio/external.c b/src/audio/external.c index 821d73129..d310c7ffd 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -14,6 +14,7 @@ #include "seq_ids.h" #include "dialog_ids.h" #include "level_table.h" +#include "pc/lua/utils/smlua_level_utils.h" #if defined(VERSION_EU) || defined(VERSION_SH) #define EU_FLOAT(x) x##f @@ -511,6 +512,50 @@ void process_level_music_dynamics(void); static u8 begin_background_music_fade(u16 fadeDuration); void func_80320ED8(void); +static s16 get_level_dynamics(s16 levelNum, s16 index) { + if (levelNum < 0 || levelNum >= LEVEL_COUNT) { + return 0; + } + return sLevelDynamics[levelNum][index]; +} + +static u8 get_level_area_reverb(s16 levelNum, s16 index) { + if (levelNum >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(levelNum); + if (!info) { return 0x00; } + switch (index) { + case 0: return info->echoLevel1; break; + case 1: return info->echoLevel2; break; + case 2: return info->echoLevel3; break; + } + return 0x00; + } + + if (levelNum < 0 || levelNum >= LEVEL_COUNT) { + return 0x00; + } + + if (index < 0 || index >= 3) { + return 0x00; + } + + return sLevelAreaReverbs[levelNum][index]; + +} + +static u16 get_level_acoustic_reaches(s16 levelNum) { + if (levelNum >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(levelNum); + return (info ? info->acousticReach : 20000); + } + + if (levelNum < 0 || levelNum >= LEVEL_COUNT) { + return 20000; + } + + return sLevelAcousticReaches[levelNum]; +} + #ifndef VERSION_JP void unused_8031E4F0(void) { // This is a debug function which is almost entirely optimized away, @@ -1215,7 +1260,7 @@ static f32 get_sound_volume(u8 bank, u8 soundIndex, f32 volumeRange) { if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_NO_VOLUME_LOSS)) { #ifdef VERSION_JP // Intensity linearly lowers from 1 at the camera to 0 at maxSoundDistance - maxSoundDistance = sLevelAcousticReaches[gCurrLevelNum]; + maxSoundDistance = get_level_acoustic_reaches(gCurrLevelNum); if (maxSoundDistance < sSoundBanks[bank][soundIndex].distance) { intensity = 0.0f; } else { @@ -1227,7 +1272,7 @@ static f32 get_sound_volume(u8 bank, u8 soundIndex, f32 volumeRange) { if (sSoundBanks[bank][soundIndex].distance > AUDIO_MAX_DISTANCE) { intensity = 0.0f; } else { - maxSoundDistance = sLevelAcousticReaches[gCurrLevelNum] / div; + maxSoundDistance = get_level_acoustic_reaches(gCurrLevelNum) / div; if (maxSoundDistance < sSoundBanks[bank][soundIndex].distance) { intensity = ((AUDIO_MAX_DISTANCE - sSoundBanks[bank][soundIndex].distance) / (AUDIO_MAX_DISTANCE - maxSoundDistance)) @@ -1305,7 +1350,7 @@ static u8 get_sound_reverb(UNUSED u8 bank, UNUSED u8 soundIndex, u8 channelIndex // The volume-dependent value is 0 when volume is at maximum, and raises to // LOW_VOLUME_REVERB when the volume is 0 reverb = (u8)((u8) gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[5] - + sLevelAreaReverbs[level][area] + + get_level_area_reverb(level, area) + (US_FLOAT(1.0) - gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->volume) * LOW_VOLUME_REVERB); @@ -1867,12 +1912,12 @@ void process_level_music_dynamics(void) { sBackgroundMusicForDynamics = sCurrentBackgroundMusicSeqId; } - if (sBackgroundMusicForDynamics != sLevelDynamics[gCurrLevelNum][0]) { + if (sBackgroundMusicForDynamics != get_level_dynamics(gCurrLevelNum, 0)) { return; } - conditionBits = sLevelDynamics[gCurrLevelNum][1] & 0xff00; - musicDynIndex = (u8) sLevelDynamics[gCurrLevelNum][1] & 0xff; + conditionBits = get_level_dynamics(gCurrLevelNum, 1) & 0xff00; + musicDynIndex = (u8) get_level_dynamics(gCurrLevelNum, 1) & 0xff; i = 2; while (conditionBits & 0xff00) { j = 0; @@ -1880,7 +1925,7 @@ void process_level_music_dynamics(void) { bit = 0x8000; while (j < 8) { if (conditionBits & bit) { - conditionValues[condIndex] = sLevelDynamics[gCurrLevelNum][i++]; + conditionValues[condIndex] = get_level_dynamics(gCurrLevelNum, i++); conditionTypes[condIndex] = j; condIndex++; } @@ -1946,8 +1991,8 @@ void process_level_music_dynamics(void) { // The area matches. Break out of the loop. tempBits = 0; } else { - tempBits = sLevelDynamics[gCurrLevelNum][i] & 0xff00; - musicDynIndex = sLevelDynamics[gCurrLevelNum][i] & 0xff; + tempBits = get_level_dynamics(gCurrLevelNum, i) & 0xff00; + musicDynIndex = get_level_dynamics(gCurrLevelNum, i) & 0xff; i++; } @@ -2809,7 +2854,7 @@ f32 sound_get_level_intensity(f32 distance) { } f32 volumeRange = VOLUME_RANGE_UNK1; - f32 maxSoundDistance = sLevelAcousticReaches[gCurrLevelNum] / 2.0f; + f32 maxSoundDistance = get_level_acoustic_reaches(gCurrLevelNum) / 2.0f; if (maxSoundDistance < distance) { intensity = ((AUDIO_MAX_DISTANCE - distance) / (AUDIO_MAX_DISTANCE - maxSoundDistance)) * (1.0f - volumeRange); } else { diff --git a/src/game/camera.c b/src/game/camera.c index 9f9e068a6..52a5f715a 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -6610,6 +6610,14 @@ struct CutsceneSplinePoint sEndingLookAtSkyFocus[] = { { -1, 0, { 636, 2027, -415 } } }; +static struct CameraTrigger* get_camera_trigger(s16 levelNum) { + if (levelNum < 0 || levelNum >= LEVEL_COUNT + 2) { + return NULL; + } + + return sCameraTriggers[levelNum]; +} + /** * Activates any CameraTriggers that Mario is inside. * Then, applies area-specific processing to the camera, such as setting the default mode, or changing @@ -6638,40 +6646,41 @@ s16 camera_course_processing(struct Camera *c) { level = LEVEL_COUNT + 1; } - if (sCameraTriggers[level] != NULL) { + if (get_camera_trigger(level) != NULL) { b = 0; // Process positional triggers. // All triggered events are called, not just the first one. - while (sCameraTriggers[level][b].event != NULL) { + struct CameraTrigger* camTrigger = get_camera_trigger(level); + while (camTrigger[b].event != NULL) { // Check only the current area's triggers - if (sCameraTriggers[level][b].area == area) { + if (camTrigger[b].area == area) { // Copy the bounding box into center and bounds - vec3f_set(center, sCameraTriggers[level][b].centerX, - sCameraTriggers[level][b].centerY, - sCameraTriggers[level][b].centerZ); - vec3f_set(bounds, sCameraTriggers[level][b].boundsX, - sCameraTriggers[level][b].boundsY, - sCameraTriggers[level][b].boundsZ); + vec3f_set(center, camTrigger[b].centerX, + camTrigger[b].centerY, + camTrigger[b].centerZ); + vec3f_set(bounds, camTrigger[b].boundsX, + camTrigger[b].boundsY, + camTrigger[b].boundsZ); // Check if Mario is inside the bounds if (is_pos_in_bounds(sMarioCamState->pos, center, bounds, - sCameraTriggers[level][b].boundsYaw) == TRUE) { + camTrigger[b].boundsYaw) == TRUE) { //! This should be checked before calling is_pos_in_bounds. (It doesn't belong //! outside the while loop because some events disable area processing) if (!(sStatusFlags & CAM_FLAG_BLOCK_AREA_PROCESSING)) { - sCameraTriggers[level][b].event(c); + camTrigger[b].event(c); insideBounds = TRUE; } } } - if ((sCameraTriggers[level])[b].area == -1) { + if ((camTrigger)[b].area == -1) { // Default triggers are only active if Mario is not already inside another trigger if (!insideBounds) { if (!(sStatusFlags & CAM_FLAG_BLOCK_AREA_PROCESSING)) { - sCameraTriggers[level][b].event(c); + camTrigger[b].event(c); } } } diff --git a/src/game/level_info.c b/src/game/level_info.c index 8494d8d47..7409ce3e3 100644 --- a/src/game/level_info.c +++ b/src/game/level_info.c @@ -7,6 +7,7 @@ #include "level_info.h" #include "level_table.h" #include "types.h" +#include "pc/lua/utils/smlua_level_utils.h" #ifdef VERSION_EU extern u8 *course_name_table_eu_en[]; @@ -165,7 +166,17 @@ const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 // Valid course: BOB to RR, Bowser stages and Secret courses // There is no course name for Cake Ending, make it defaults to "Peach's Castle" - if (courseNum >= COURSE_MIN && courseNum < COURSE_MAX) { + + bool hasCustomName = false; + if (levelNum >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(levelNum); + if (info) { + hasCustomName = true; + snprintf(output, 256, info->fullName); + } + } + + if (!hasCustomName && courseNum >= COURSE_MIN && courseNum < COURSE_MAX) { void **courseNameTbl = NULL; #ifdef VERSION_EU switch (gInGameLanguage) { @@ -181,7 +192,7 @@ const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 } // Castle level - else if (courseNum == COURSE_NONE) { + else if (!hasCustomName && courseNum == COURSE_NONE) { switch (levelNum) { case LEVEL_CASTLE: { switch (areaIndex) { @@ -198,7 +209,7 @@ const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 } // Default - else { + else if (!hasCustomName) { snprintf(output, 256, "Peach's Castle"); } diff --git a/src/game/level_update.c b/src/game/level_update.c index 71ad18cfa..872d1fce9 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -1525,7 +1525,7 @@ s32 lvl_set_current_level(UNUSED s16 arg0, s32 levelNum) { sWarpCheckpointActive = FALSE; gCurrLevelNum = levelNum; - gCurrCourseNum = gLevelToCourseNumTable[levelNum - 1]; + gCurrCourseNum = get_level_course_num(levelNum - 1); if (gCurrDemoInput != NULL || gCurrCreditsEntry != NULL || gCurrCourseNum == COURSE_NONE) { return 0; diff --git a/src/game/save_file.c b/src/game/save_file.c index b73060364..cf5656574 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -13,6 +13,7 @@ #include "macros.h" #include "pc/ini.h" #include "pc/network/network.h" +#include "pc/lua/utils/smlua_level_utils.h" #ifndef bcopy #define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) @@ -50,6 +51,19 @@ s8 gLevelToCourseNumTable[] = { STATIC_ASSERT(ARRAY_COUNT(gLevelToCourseNumTable) == LEVEL_COUNT - 1, "change this array if you are adding levels"); +s8 get_level_course_num(s16 levelNum) { + if (levelNum >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(levelNum); + return (info ? info->courseNum : COURSE_NONE); + } + + if (levelNum < 0 || levelNum >= LEVEL_COUNT) { + return COURSE_NONE; + } + + return gLevelToCourseNumTable[levelNum]; +} + // This was probably used to set progress to 100% for debugging, but // it was removed from the release ROM. static void stub_save_file_1(void) { @@ -745,7 +759,7 @@ void check_if_should_set_warp_checkpoint(struct WarpNode *warpNode) { */ s32 check_warp_checkpoint(struct WarpNode *warpNode) { s16 warpCheckpointActive = FALSE; - s16 currCourseNum = gLevelToCourseNumTable[(warpNode->destLevel & 0x7F) - 1]; + s16 currCourseNum = get_level_course_num((warpNode->destLevel & 0x7F) - 1); // gSavedCourseNum is only used in this function. if (gWarpCheckpoint.courseNum != COURSE_NONE && gSavedCourseNum == currCourseNum diff --git a/src/game/save_file.h b/src/game/save_file.h index e1a22ff46..97d3f3793 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -128,6 +128,7 @@ extern struct WarpCheckpoint gWarpCheckpoint; extern s8 gMainMenuDataModified; extern s8 gSaveFileModified; +s8 get_level_course_num(s16 levelNum); void save_file_do_save(s32 fileIndex, s8 forceSave); void save_file_erase(s32 fileIndex); BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex); diff --git a/src/menu/level_select_menu.c b/src/menu/level_select_menu.c index b23f06de2..ef9732e3b 100644 --- a/src/menu/level_select_menu.c +++ b/src/menu/level_select_menu.c @@ -14,6 +14,7 @@ #include "level_table.h" #include "seq_ids.h" #include "sm64.h" +#include "pc/lua/utils/smlua_level_utils.h" #define PRESS_START_DEMO_TIMER 800 @@ -32,6 +33,19 @@ static s16 D_U_801A7C34 = 1; static s16 gameOverNotPlayed = 1; #endif +static char* get_level_stage_names(s16 levelNum) { + if (levelNum >= CUSTOM_LEVEL_NUM_START) { + struct CustomLevelInfo* info = smlua_level_util_get_info(levelNum); + return (info ? info->fullName : "Unknown"); + } + + if (levelNum < 0 || levelNum >= LEVEL_COUNT) { + return "Unknown"; + } + + return gLevelSelect_StageNamesText[levelNum]; +} + // run the demo timer on the PRESS START screen. // this function will return a non-0 timer once // the demo starts, signaling to the subsystem that @@ -140,7 +154,7 @@ s16 level_select_input_loop(void) { print_text_centered(160, 80, "SELECT STAGE"); print_text_centered(160, 30, "PRESS START BUTTON"); print_text_fmt_int(40, 60, "%2d", gCurrLevelNum); - print_text(80, 60, gLevelSelect_StageNamesText[gCurrLevelNum - 1]); // print stage name + print_text(80, 60, get_level_stage_names(gCurrLevelNum - 1)); // print stage name #define QUIT_LEVEL_SELECT_COMBO (Z_TRIG | START_BUTTON | L_CBUTTONS | R_CBUTTONS) diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index 92cca64e5..51c5086bb 100644 --- a/src/pc/chat_commands.c +++ b/src/pc/chat_commands.c @@ -6,6 +6,7 @@ #include "pc/network/ban_list.h" #include "pc/network/moderator_list.h" #include "pc/debuglog.h" +#include "pc/lua/utils/smlua_level_utils.h" #include "level_table.h" @@ -265,6 +266,12 @@ bool exec_chat_command(char* command) { break; } } + if (level == -1) { + struct CustomLevelInfo* info = smlua_level_util_get_info_from_short_name(paramLevel); + if (info != NULL) { + level = info->levelNum; + } + } if (level == -1) { char message[256]; snprintf(message, 256, "Invalid [LEVEL] parameter: %s", paramLevel); diff --git a/src/pc/lua/smlua.c b/src/pc/lua/smlua.c index 216dff3d3..7331a97ba 100644 --- a/src/pc/lua/smlua.c +++ b/src/pc/lua/smlua.c @@ -6,6 +6,7 @@ #include "pc/lua/utils/smlua_text_utils.h" #include "pc/lua/utils/smlua_audio_utils.h" #include "pc/lua/utils/smlua_model_utils.h" +#include "pc/lua/utils/smlua_level_utils.h" #include "pc/djui/djui.h" lua_State* gLuaState = NULL; @@ -188,6 +189,7 @@ void smlua_shutdown(void) { smlua_cpointer_allowlist_shutdown(); smlua_clear_hooks(); smlua_model_util_reset(); + smlua_level_util_reset(); lua_State* L = gLuaState; if (L != NULL) { lua_close(L); diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index c7f837f5d..04ab0033b 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -13,6 +13,7 @@ #include "src/pc/lua/utils/smlua_anim_utils.h" #include "src/pc/lua/utils/smlua_misc_utils.h" #include "src/pc/lua/utils/smlua_collision_utils.h" +#include "src/pc/lua/utils/smlua_level_utils.h" #include "src/game/spawn_sound.h" #include "src/pc/network/network.h" #include "src/game/hardcoded.h" @@ -392,6 +393,21 @@ static struct LuaObjectField sControllerFields[LUA_CONTROLLER_FIELD_COUNT] = { { "stickY", LVT_F32, offsetof(struct Controller, stickY), false, LOT_NONE }, }; +#define LUA_CUSTOM_LEVEL_INFO_FIELD_COUNT 11 +static struct LuaObjectField sCustomLevelInfoFields[LUA_CUSTOM_LEVEL_INFO_FIELD_COUNT] = { + { "acousticReach", LVT_U32, offsetof(struct CustomLevelInfo, acousticReach), false, LOT_NONE }, + { "courseNum", LVT_S16, offsetof(struct CustomLevelInfo, courseNum), false, LOT_NONE }, + { "echoLevel1", LVT_U32, offsetof(struct CustomLevelInfo, echoLevel1), false, LOT_NONE }, + { "echoLevel2", LVT_U32, offsetof(struct CustomLevelInfo, echoLevel2), false, LOT_NONE }, + { "echoLevel3", LVT_U32, offsetof(struct CustomLevelInfo, echoLevel3), false, LOT_NONE }, + { "fullName", LVT_STRING_P, offsetof(struct CustomLevelInfo, fullName), true, LOT_NONE }, + { "levelNum", LVT_S16, offsetof(struct CustomLevelInfo, levelNum), false, LOT_NONE }, + { "next", LVT_COBJECT_P, offsetof(struct CustomLevelInfo, next), false, LOT_CUSTOMLEVELINFO }, + { "script", LVT_LEVELSCRIPT_P, offsetof(struct CustomLevelInfo, script), true, LOT_POINTER }, + { "scriptEntryName", LVT_STRING_P, offsetof(struct CustomLevelInfo, scriptEntryName), true, LOT_NONE }, + { "shortName", LVT_STRING_P, offsetof(struct CustomLevelInfo, shortName), true, LOT_NONE }, +}; + #define LUA_CUTSCENE_FIELD_COUNT 1 static struct LuaObjectField sCutsceneFields[LUA_CUTSCENE_FIELD_COUNT] = { { "duration", LVT_S16, offsetof(struct Cutscene, duration), false, LOT_NONE }, @@ -1979,6 +1995,7 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN] { LOT_CHAINSEGMENT, sChainSegmentFields, LUA_CHAIN_SEGMENT_FIELD_COUNT }, { LOT_CHARACTER, sCharacterFields, LUA_CHARACTER_FIELD_COUNT }, { LOT_CONTROLLER, sControllerFields, LUA_CONTROLLER_FIELD_COUNT }, + { LOT_CUSTOMLEVELINFO, sCustomLevelInfoFields, LUA_CUSTOM_LEVEL_INFO_FIELD_COUNT }, { LOT_CUTSCENE, sCutsceneFields, LUA_CUTSCENE_FIELD_COUNT }, { LOT_CUTSCENESPLINEPOINT, sCutsceneSplinePointFields, LUA_CUTSCENE_SPLINE_POINT_FIELD_COUNT }, { LOT_CUTSCENEVARIABLE, sCutsceneVariableFields, LUA_CUTSCENE_VARIABLE_FIELD_COUNT }, diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h index 7581587cd..2c93b94f1 100644 --- a/src/pc/lua/smlua_cobject_autogen.h +++ b/src/pc/lua/smlua_cobject_autogen.h @@ -20,6 +20,7 @@ enum LuaObjectAutogenType { LOT_CHAINSEGMENT, LOT_CHARACTER, LOT_CONTROLLER, + LOT_CUSTOMLEVELINFO, LOT_CUTSCENE, LOT_CUTSCENESPLINEPOINT, LOT_CUTSCENEVARIABLE, diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index a75fffb6d..da352c7c8 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -25,6 +25,7 @@ #include "src/pc/lua/utils/smlua_model_utils.h" #include "src/pc/lua/utils/smlua_text_utils.h" #include "src/pc/lua/utils/smlua_audio_utils.h" +#include "src/pc/lua/utils/smlua_level_utils.h" #include "src/engine/surface_load.h" #include "src/game/object_list_processor.h" #include "src/game/behavior_actions.h" @@ -15108,6 +15109,103 @@ int smlua_func_smlua_collision_util_get(lua_State* L) { return 1; } + ///////////////////////// + // smlua_level_utils.h // +///////////////////////// + +int smlua_func_level_register(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 8)) { return 0; } + + const char* scriptEntryName = smlua_to_string(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 courseNum = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + const char* fullName = smlua_to_string(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + const char* shortName = smlua_to_string(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 4"); return 0; } + u32 acousticReach = smlua_to_integer(L, 5); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 5"); return 0; } + u32 echoLevel1 = smlua_to_integer(L, 6); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 6"); return 0; } + u32 echoLevel2 = smlua_to_integer(L, 7); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 7"); return 0; } + u32 echoLevel3 = smlua_to_integer(L, 8); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 8"); return 0; } + + lua_pushinteger(L, level_register(scriptEntryName, courseNum, fullName, shortName, acousticReach, echoLevel1, echoLevel2, echoLevel3)); + + return 1; +} + +int smlua_func_smlua_level_util_get_info(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + s16 levelNum = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + + smlua_push_object(L, LOT_CUSTOMLEVELINFO, smlua_level_util_get_info(levelNum)); + + return 1; +} + +int smlua_func_smlua_level_util_get_info_from_short_name(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + char* shortName = (char*)smlua_to_cobject(L, 1, LOT_NONE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + + smlua_push_object(L, LOT_CUSTOMLEVELINFO, smlua_level_util_get_info_from_short_name(shortName)); + + return 1; +} + +int smlua_func_warp_exit_level(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + s32 aDelay = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + + lua_pushboolean(L, warp_exit_level(aDelay)); + + return 1; +} + +int smlua_func_warp_restart_level(UNUSED lua_State* L) { + if(!smlua_functions_valid_param_count(L, 0)) { return 0; } + + + lua_pushboolean(L, warp_restart_level()); + + return 1; +} + +int smlua_func_warp_to_castle(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + s32 aLevel = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + + lua_pushboolean(L, warp_to_castle(aLevel)); + + return 1; +} + +int smlua_func_warp_to_level(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 3)) { return 0; } + + s32 aLevel = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s32 aArea = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + s32 aAct = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + + lua_pushboolean(L, warp_to_level(aLevel, aArea, aAct)); + + return 1; +} + //////////////////////// // smlua_misc_utils.h // //////////////////////// @@ -15594,52 +15692,6 @@ int smlua_func_set_override_near(lua_State* L) { return 1; } -int smlua_func_warp_exit_level(lua_State* L) { - if(!smlua_functions_valid_param_count(L, 1)) { return 0; } - - s32 aDelay = smlua_to_integer(L, 1); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } - - lua_pushboolean(L, warp_exit_level(aDelay)); - - return 1; -} - -int smlua_func_warp_restart_level(UNUSED lua_State* L) { - if(!smlua_functions_valid_param_count(L, 0)) { return 0; } - - - lua_pushboolean(L, warp_restart_level()); - - return 1; -} - -int smlua_func_warp_to_castle(lua_State* L) { - if(!smlua_functions_valid_param_count(L, 1)) { return 0; } - - s32 aLevel = smlua_to_integer(L, 1); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } - - lua_pushboolean(L, warp_to_castle(aLevel)); - - return 1; -} - -int smlua_func_warp_to_level(lua_State* L) { - if(!smlua_functions_valid_param_count(L, 3)) { return 0; } - - s32 aLevel = smlua_to_integer(L, 1); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } - s32 aArea = smlua_to_integer(L, 2); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } - s32 aAct = smlua_to_integer(L, 3); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } - - lua_pushboolean(L, warp_to_level(aLevel, aArea, aAct)); - - return 1; -} - ///////////////////////// // smlua_model_utils.h // ///////////////////////// @@ -17847,6 +17899,15 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "get_water_surface_pseudo_floor", smlua_func_get_water_surface_pseudo_floor); smlua_bind_function(L, "smlua_collision_util_get", smlua_func_smlua_collision_util_get); + // smlua_level_utils.h + smlua_bind_function(L, "level_register", smlua_func_level_register); + smlua_bind_function(L, "smlua_level_util_get_info", smlua_func_smlua_level_util_get_info); + smlua_bind_function(L, "smlua_level_util_get_info_from_short_name", smlua_func_smlua_level_util_get_info_from_short_name); + smlua_bind_function(L, "warp_exit_level", smlua_func_warp_exit_level); + smlua_bind_function(L, "warp_restart_level", smlua_func_warp_restart_level); + smlua_bind_function(L, "warp_to_castle", smlua_func_warp_to_castle); + smlua_bind_function(L, "warp_to_level", smlua_func_warp_to_level); + // smlua_misc_utils.h smlua_bind_function(L, "allocate_mario_action", smlua_func_allocate_mario_action); smlua_bind_function(L, "camera_config_enable_analog_cam", smlua_func_camera_config_enable_analog_cam); @@ -17892,10 +17953,6 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "set_override_far", smlua_func_set_override_far); smlua_bind_function(L, "set_override_fov", smlua_func_set_override_fov); smlua_bind_function(L, "set_override_near", smlua_func_set_override_near); - smlua_bind_function(L, "warp_exit_level", smlua_func_warp_exit_level); - smlua_bind_function(L, "warp_restart_level", smlua_func_warp_restart_level); - smlua_bind_function(L, "warp_to_castle", smlua_func_warp_to_castle); - smlua_bind_function(L, "warp_to_level", smlua_func_warp_to_level); // smlua_model_utils.h smlua_bind_function(L, "smlua_model_util_get_id", smlua_func_smlua_model_util_get_id); diff --git a/src/pc/lua/utils/smlua_level_utils.c b/src/pc/lua/utils/smlua_level_utils.c new file mode 100644 index 000000000..5310cd8ed --- /dev/null +++ b/src/pc/lua/utils/smlua_level_utils.c @@ -0,0 +1,143 @@ +#include "sm64.h" +#include "types.h" +#include "smlua_level_utils.h" +#include "pc/lua/smlua.h" + +struct CustomLevelInfo* sCustomLevelHead = NULL; +static s16 sCustomLevelNumNext = CUSTOM_LEVEL_NUM_START; + +void smlua_level_util_reset(void) { + struct CustomLevelInfo* node = sCustomLevelHead; + + while (node != NULL) { + struct CustomLevelInfo* next = node->next; + if (node->scriptEntryName) { + free(node->scriptEntryName); + node->scriptEntryName = NULL; + } + if (node->fullName) { + free(node->fullName); + node->fullName = NULL; + } + if (node->shortName) { + free(node->shortName); + node->shortName = NULL; + } + free(node); + node = next; + } + + sCustomLevelHead = NULL; + sCustomLevelNumNext = CUSTOM_LEVEL_NUM_START; +} + +struct CustomLevelInfo* smlua_level_util_get_info(s16 levelNum) { + struct CustomLevelInfo* node = sCustomLevelHead; + while (node != NULL) { + if (node->levelNum == levelNum) { + return node; + } + node = node->next; + } + return NULL; +} + +struct CustomLevelInfo* smlua_level_util_get_info_from_short_name(char* shortName) { + struct CustomLevelInfo* node = sCustomLevelHead; + while (node != NULL) { + if (!strcmp(node->shortName, shortName)) { + return node; + } + node = node->next; + } + return NULL; +} + +static struct CustomLevelInfo* smlua_level_util_get_info_from_script(char* scriptEntryName) { + struct CustomLevelInfo* node = sCustomLevelHead; + while (node != NULL) { + if (!strcmp(node->scriptEntryName, scriptEntryName)) { + return node; + } + node = node->next; + } + return NULL; +} + +s16 level_register(const char* scriptEntryName, s16 courseNum, const char* fullName, const char* shortName, u32 acousticReach, u32 echoLevel1, u32 echoLevel2, u32 echoLevel3) { + // validate params + if (scriptEntryName == NULL) { + LOG_LUA("Provided nil scriptEntryName"); + return 0; + } + + if (fullName == NULL) { + LOG_LUA("Provided nil fullName"); + return 0; + } + + if (shortName == NULL) { + LOG_LUA("Provided nil shortName"); + return 0; + } + + // find duplicate + struct CustomLevelInfo* info = smlua_level_util_get_info_from_script((char*)scriptEntryName); + if (info != NULL) { + return info->levelNum; + } + + // find script + LevelScript* script = dynos_get_level_script((char*)scriptEntryName); + if (script == NULL) { + LOG_LUA("Failed to find script: %s", scriptEntryName); + return 0; + } + + // allocate and fill + info = calloc(1, sizeof(struct CustomLevelInfo)); + info->script = script; + info->scriptEntryName = strdup(scriptEntryName); + info->courseNum = courseNum; + info->levelNum = sCustomLevelNumNext++; + info->fullName = strdup(fullName); + info->shortName = strdup(shortName); + info->acousticReach = acousticReach; + info->echoLevel1 = echoLevel1; + info->echoLevel2 = echoLevel2; + info->echoLevel3 = echoLevel3; + + // add to list + if (!sCustomLevelHead) { + sCustomLevelHead = info; + return info->levelNum; + } + + struct CustomLevelInfo* node = sCustomLevelHead; + while (node) { + if (!node->next) { + node->next = info; + return info->levelNum; + } + node = node->next; + } + + // just in case, should never trigger + return 0; +} + +bool warp_to_level(s32 aLevel, s32 aArea, s32 aAct) { + return dynos_warp_to_level(aLevel, aArea, aAct); +} + +bool warp_restart_level(void) { + return dynos_warp_restart_level(); +} + +bool warp_exit_level(s32 aDelay) { + return dynos_warp_exit_level(aDelay); +} + +bool warp_to_castle(s32 aLevel) { + return dynos_warp_to_castle(aLevel); +} \ No newline at end of file diff --git a/src/pc/lua/utils/smlua_level_utils.h b/src/pc/lua/utils/smlua_level_utils.h new file mode 100644 index 000000000..c151b3c25 --- /dev/null +++ b/src/pc/lua/utils/smlua_level_utils.h @@ -0,0 +1,29 @@ +#ifndef SMLUA_LEVEL_UTILS_H +#define SMLUA_LEVEL_UTILS_H + +struct CustomLevelInfo { + LevelScript* script; + char* scriptEntryName; + s16 courseNum; + s16 levelNum; + char* fullName; + char* shortName; + u32 acousticReach; + u32 echoLevel1; + u32 echoLevel2; + u32 echoLevel3; + struct CustomLevelInfo* next; +}; + +#define CUSTOM_LEVEL_NUM_START 50 + +void smlua_level_util_reset(void); +struct CustomLevelInfo* smlua_level_util_get_info(s16 levelNum); +struct CustomLevelInfo* smlua_level_util_get_info_from_short_name(char* shortName); +s16 level_register(const char* scriptEntryName, s16 courseNum, const char* fullName, const char* shortName, u32 acousticReach, u32 echoLevel1, u32 echoLevel2, u32 echoLevel3); +bool warp_to_level(s32 aLevel, s32 aArea, s32 aAct); +bool warp_restart_level(void); +bool warp_exit_level(s32 aDelay); +bool warp_to_castle(s32 aLevel); + +#endif diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c index 491a055a2..af58f5389 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -272,24 +272,6 @@ bool is_game_paused(void) { /// -bool warp_to_level(s32 aLevel, s32 aArea, s32 aAct) { - return dynos_warp_to_level(aLevel, aArea, aAct); -} - -bool warp_restart_level(void) { - return dynos_warp_restart_level(); -} - -bool warp_exit_level(s32 aDelay) { - return dynos_warp_exit_level(aDelay); -} - -bool warp_to_castle(s32 aLevel) { - return dynos_warp_to_castle(aLevel); -} - -/// - u32 allocate_mario_action(u32 actFlags) { actFlags = actFlags & (~((u32)0xFF)); return actFlags | ACT_FLAG_CUSTOM_ACTION | gLuaMarioActionIndex++; diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index 6f2f8a97d..3ed03f755 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -63,11 +63,6 @@ void camera_config_set_deceleration(u32 value); bool is_game_paused(void); -bool warp_to_level(s32 aLevel, s32 aArea, s32 aAct); -bool warp_restart_level(void); -bool warp_exit_level(s32 aDelay); -bool warp_to_castle(s32 aLevel); - u32 allocate_mario_action(u32 actFlags); f32 get_hand_foot_pos_x(struct MarioState* m, u8 index);