From 6726a6280a4a46cd2e3727b1f52da1d8f8a23895 Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Tue, 26 Apr 2022 22:48:50 +0200 Subject: [PATCH] Changes in level_info; play_transition; dev-only warp command; bug fix for moving sounds played via lua (#69) Improved level_info.c and added functions to LUA: const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase): Return a level name as an ascii string. If charCase is 1, capitalize all letters. If charCase is -1, decapitalize all letters except the first one of each word. const u8 *get_level_name_sm64(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase): Return a level name as an sm64 u8 string. If charCase is 1, capitalize all letters. If charCase is -1, decapitalize all letters except the first one of each word. const char *get_level_name(s16 courseNum, s16 levelNum, s16 areaIndex): Shortcut for get_level_name_ascii(courseNum, levelNum, areaIndex, -1). const char *get_star_name_ascii(s16 courseNum, s16 starNum, s16 charCase): Return a star name as an ascii string. If charCase is 1, capitalize all letters. If charCase is -1, decapitalize all letters except the first one of each word. const u8 *get_star_name_sm64(s16 courseNum, s16 starNum, s16 charCase): Return a star name as an sm64 u8 string. If charCase is 1, capitalize all letters. If charCase is -1, decapitalize all letters except the first one of each word. const char *get_star_name(s16 courseNum, s16 starNum): Shortcut for get_star_name_ascii(courseNum, starNum, -1). Added play_transition function to LUA. I chose to copy the function declaration to smlua_misc_utils.h instead of adding area.h to the autogen tool, as most structures, variables and functions in area.h aren't meant to be used by LUA scripts. Added a dev-only warp chat command. This command signature is /warp [LEVEL] [AREA] [ACT]. Level can be either a number or a shorthand name (bob, wf, ccm...). Area and Act are numbers. This command is available only when building the game with DEBUG and DEVELOPMENT. This command cannot be used if hosting through Discord. Fixed a bug with moving sounds when they are played via a lua script. Bug: Moving sounds (including terrain sounds, flying sound, quicksand sound) are not played correctly when a lua script play them via a call of play_sound or play_sound_with_freq_scale. This is due to how the moving sounds are handled internally. They use the f32 pointer provided to the play_sound functions to decide if the sound must be kept playing, stopped or restarted. Most of the time, the pointer provided is the cameraToObject field of Mario's object graph node. Since smlua uses a circular buffer for Vec3f conversion, this pointer is lost, and the sound engine can't decide what to do with the sound, resulting in a weird and incorrect sound effect. Fix: play_sound and play_sound_with_freq_scale now calls smlua_get_vec3f_for_play_sound before filling the sound request queue, to retrieve the correct pointer from the Vec3f provided by smlua. --- autogen/lua_definitions/constants.lua | 3 + autogen/lua_definitions/functions.lua | 51 ++++ autogen/lua_definitions/structs.lua | 1 + docs/lua/constants.md | 1 + docs/lua/functions-3.md | 111 ++++++++ docs/lua/functions-4.md | 24 ++ docs/lua/functions.md | 6 + docs/lua/structs.md | 1 + src/audio/external.c | 4 + src/game/level_info.c | 395 +++++++++++++++++--------- src/game/level_info.h | 5 + src/pc/chat_commands.c | 86 ++++++ src/pc/lua/smlua_cobject_autogen.c | 17 +- src/pc/lua/smlua_constants_autogen.c | 1 + src/pc/lua/smlua_functions_autogen.c | 102 +++++++ src/pc/lua/smlua_utils.c | 16 ++ src/pc/lua/utils/smlua_misc_utils.h | 2 + 17 files changed, 687 insertions(+), 139 deletions(-) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 94ab707c6..099e5b2f0 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -3198,6 +3198,9 @@ GEO_CONTEXT_RENDER = 1 --- @type integer GFX_NUM_MASTER_LISTS = 8 +--- @type integer +GRAPH_EXTRA_FORCE_3D = (1 << 0) + --- @type integer GRAPH_NODE_TYPE_400 = 0x400 diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 73bbd5f71..59c8d8948 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -3860,6 +3860,47 @@ function get_level_name(courseNum, levelNum, areaIndex) -- ... end +--- @param courseNum integer +--- @param levelNum integer +--- @param areaIndex integer +--- @param charCase integer +--- @return string +function get_level_name_ascii(courseNum, levelNum, areaIndex, charCase) + -- ... +end + +--- @param courseNum integer +--- @param levelNum integer +--- @param areaIndex integer +--- @param charCase integer +--- @return Pointer_integer +function get_level_name_sm64(courseNum, levelNum, areaIndex, charCase) + -- ... +end + +--- @param courseNum integer +--- @param starNum integer +--- @return string +function get_star_name(courseNum, starNum) + -- ... +end + +--- @param courseNum integer +--- @param starNum integer +--- @param charCase integer +--- @return string +function get_star_name_ascii(courseNum, starNum, charCase) + -- ... +end + +--- @param courseNum integer +--- @param starNum integer +--- @param charCase integer +--- @return Pointer_integer +function get_star_name_sm64(courseNum, starNum, charCase) + -- ... +end + --- @param m MarioState --- @return nil function adjust_sound_for_speed(m) @@ -7149,6 +7190,16 @@ function movtexqc_register(name, level, area, type) -- ... end +--- @param transType integer +--- @param time integer +--- @param red integer +--- @param green integer +--- @param blue integer +--- @return nil +function play_transition(transType, time, red, green, blue) + -- ... +end + --- @param usingBackupSlot boolean --- @return nil function save_file_set_using_backup_slot(usingBackupSlot) diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 98703c221..7227f6a0e 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -488,6 +488,7 @@ --- @class GraphNode --- @field public children GraphNode +--- @field public extraFlags integer --- @field public flags integer --- @field public next GraphNode --- @field public parent GraphNode diff --git a/docs/lua/constants.md b/docs/lua/constants.md index da4624be1..84e861287 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -1058,6 +1058,7 @@ - GEO_CONTEXT_HELD_OBJ - GEO_CONTEXT_RENDER - GFX_NUM_MASTER_LISTS +- GRAPH_EXTRA_FORCE_3D - GRAPH_NODE_TYPE_400 - GRAPH_NODE_TYPE_ANIMATED_PART - GRAPH_NODE_TYPE_BACKGROUND diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md index e1c675ef1..5cbae5605 100644 --- a/docs/lua/functions-3.md +++ b/docs/lua/functions-3.md @@ -2924,6 +2924,117 @@
+## [get_level_name_ascii](#get_level_name_ascii) + +### Lua Example +`local stringValue = get_level_name_ascii(courseNum, levelNum, areaIndex, charCase)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseNum | `integer` | +| levelNum | `integer` | +| areaIndex | `integer` | +| charCase | `integer` | + +### Returns +- `string` + +### C Prototype +`const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase);` + +[:arrow_up_small:](#) + +
+ +## [get_level_name_sm64](#get_level_name_sm64) + +### Lua Example +`local PointerValue = get_level_name_sm64(courseNum, levelNum, areaIndex, charCase)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseNum | `integer` | +| levelNum | `integer` | +| areaIndex | `integer` | +| charCase | `integer` | + +### Returns +- `Pointer` <`integer`> + +### C Prototype +`const u8 *get_level_name_sm64(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase);` + +[:arrow_up_small:](#) + +
+ +## [get_star_name](#get_star_name) + +### Lua Example +`local stringValue = get_star_name(courseNum, starNum)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseNum | `integer` | +| starNum | `integer` | + +### Returns +- `string` + +### C Prototype +`const char *get_star_name(s16 courseNum, s16 starNum);` + +[:arrow_up_small:](#) + +
+ +## [get_star_name_ascii](#get_star_name_ascii) + +### Lua Example +`local stringValue = get_star_name_ascii(courseNum, starNum, charCase)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseNum | `integer` | +| starNum | `integer` | +| charCase | `integer` | + +### Returns +- `string` + +### C Prototype +`const char *get_star_name_ascii(s16 courseNum, s16 starNum, s16 charCase);` + +[:arrow_up_small:](#) + +
+ +## [get_star_name_sm64](#get_star_name_sm64) + +### Lua Example +`local PointerValue = get_star_name_sm64(courseNum, starNum, charCase)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseNum | `integer` | +| starNum | `integer` | +| charCase | `integer` | + +### Returns +- `Pointer` <`integer`> + +### C Prototype +`const u8 *get_star_name_sm64(s16 courseNum, s16 starNum, s16 charCase);` + +[:arrow_up_small:](#) + +
+ --- # functions from mario.h diff --git a/docs/lua/functions-4.md b/docs/lua/functions-4.md index b2876a441..d18a515d0 100644 --- a/docs/lua/functions-4.md +++ b/docs/lua/functions-4.md @@ -4919,6 +4919,30 @@
+## [play_transition](#play_transition) + +### Lua Example +`play_transition(transType, time, red, green, blue)` + +### Parameters +| Field | Type | +| ----- | ---- | +| transType | `integer` | +| time | `integer` | +| red | `integer` | +| green | `integer` | +| blue | `integer` | + +### Returns +- None + +### C Prototype +`void play_transition(s16 transType, s16 time, u8 red, u8 green, u8 blue);` + +[:arrow_up_small:](#) + +
+ ## [save_file_set_using_backup_slot](#save_file_set_using_backup_slot) ### Lua Example diff --git a/docs/lua/functions.md b/docs/lua/functions.md index aac2747c6..8857f6eaa 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -759,6 +759,11 @@ - level_info.h - [get_level_name](functions-3.md#get_level_name) + - [get_level_name_ascii](functions-3.md#get_level_name_ascii) + - [get_level_name_sm64](functions-3.md#get_level_name_sm64) + - [get_star_name](functions-3.md#get_star_name) + - [get_star_name_ascii](functions-3.md#get_star_name_ascii) + - [get_star_name_sm64](functions-3.md#get_star_name_sm64)
@@ -1339,6 +1344,7 @@ - [hud_hide](functions-4.md#hud_hide) - [hud_show](functions-4.md#hud_show) - [movtexqc_register](functions-4.md#movtexqc_register) + - [play_transition](functions-4.md#play_transition) - [save_file_set_using_backup_slot](functions-4.md#save_file_set_using_backup_slot) - [set_environment_region](functions-4.md#set_environment_region) - [warp_exit_level](functions-4.md#warp_exit_level) diff --git a/docs/lua/structs.md b/docs/lua/structs.md index c085ba100..ef939e696 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -709,6 +709,7 @@ | Field | Type | Access | | ----- | ---- | ------ | | children | [GraphNode](structs.md#GraphNode) | | +| extraFlags | `integer` | | | flags | `integer` | | | next | [GraphNode](structs.md#GraphNode) | | | parent | [GraphNode](structs.md#GraphNode) | | diff --git a/src/audio/external.c b/src/audio/external.c index efc4c70c2..c79e7ee0c 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -803,7 +803,10 @@ void create_next_audio_buffer(s16 *samples, u32 num_samples) { /** * Called from threads: thread5_game_loop */ +extern f32 *smlua_get_vec3f_for_play_sound(f32 *pos); + void play_sound(s32 soundBits, f32 *pos) { + pos = smlua_get_vec3f_for_play_sound(pos); sSoundRequests[sSoundRequestCount].soundBits = soundBits; sSoundRequests[sSoundRequestCount].position = pos; sSoundRequests[sSoundRequestCount].customFreqScale = 0; @@ -811,6 +814,7 @@ void play_sound(s32 soundBits, f32 *pos) { } void play_sound_with_freq_scale(s32 soundBits, f32* pos, f32 freqScale) { + pos = smlua_get_vec3f_for_play_sound(pos); sSoundRequests[sSoundRequestCount].soundBits = soundBits; sSoundRequests[sSoundRequestCount].position = pos; sSoundRequests[sSoundRequestCount].customFreqScale = freqScale; diff --git a/src/game/level_info.c b/src/game/level_info.c index 532dbf16f..a906a043d 100644 --- a/src/game/level_info.c +++ b/src/game/level_info.c @@ -7,150 +7,283 @@ #include "level_table.h" #include "types.h" -extern u8* seg2_course_name_table[]; +#ifdef VERSION_EU +extern u8 *course_name_table_eu_en[]; +extern u8 *course_name_table_eu_fr[]; +extern u8 *course_name_table_eu_de[]; +extern u8 *act_name_table_eu_en[]; +extern u8 *act_name_table_eu_fr[]; +extern u8 *act_name_table_eu_de[]; +#else +extern u8 *seg2_course_name_table[]; +extern u8 *seg2_act_name_table[]; +#endif -static const char charset[0xFF + 1] = { - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 7 - ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', // 15 - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 23 - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 31 - 'w', 'x', 'y', 'z', ' ', ' ', ' ', ' ', // 39 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 49 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 55 - ' ', ' ', ' ', ' ', ' ', ' ', '\'', ' ', // 63 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 71 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 79 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 87 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 95 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 103 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ',', // 111 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 119 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 127 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 135 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 143 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 151 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', '-', // 159 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 167 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 175 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 183 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 192 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 199 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 207 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 215 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 223 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 231 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 239 - ' ', ' ', '!', ' ', ' ', ' ', ' ', ' ', // 247 - ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' // 255 +static const struct { const char *str; u8 c; } sSm64CharMap[] = { + + // Digits + { "0", 0x00 }, { "1", 0x01 }, { "2", 0x02 }, { "3", 0x03 }, { "4", 0x04 }, + { "5", 0x05 }, { "6", 0x06 }, { "7", 0x07 }, { "8", 0x08 }, { "9", 0x09 }, + + // Capital letters + { "A", 0x0A }, { "B", 0x0B }, { "C", 0x0C }, { "D", 0x0D }, { "E", 0x0E }, + { "F", 0x0F }, { "G", 0x10 }, { "H", 0x11 }, { "I", 0x12 }, { "J", 0x13 }, + { "K", 0x14 }, { "L", 0x15 }, { "M", 0x16 }, { "N", 0x17 }, { "O", 0x18 }, + { "P", 0x19 }, { "Q", 0x1A }, { "R", 0x1B }, { "S", 0x1C }, { "T", 0x1D }, + { "U", 0x1E }, { "V", 0x1F }, { "W", 0x20 }, { "X", 0x21 }, { "Y", 0x22 }, + { "Z", 0x23 }, + + // Letters + { "a", 0x24 }, { "b", 0x25 }, { "c", 0x26 }, { "d", 0x27 }, { "e", 0x28 }, + { "f", 0x29 }, { "g", 0x2A }, { "h", 0x2B }, { "i", 0x2C }, { "j", 0x2D }, + { "k", 0x2E }, { "l", 0x2F }, { "m", 0x30 }, { "n", 0x31 }, { "o", 0x32 }, + { "p", 0x33 }, { "q", 0x34 }, { "r", 0x35 }, { "s", 0x36 }, { "t", 0x37 }, + { "u", 0x38 }, { "v", 0x39 }, { "w", 0x3A }, { "x", 0x3B }, { "y", 0x3C }, + { "z", 0x3D }, + + // Punctuation + { "...", 0xE6 }, // ellipsis + { ")(", 0xE2 }, // close-open parentheses + { "<<", 0xF5 }, // double quote open + { ">>", 0xF6 }, // double quote close + { "\'", 0x3E }, // apostrophe + { ".", 0x3F }, // period + { ",", 0x6F }, // comma + { " ", 0x9E }, // space + { "-", 0x9F }, // dash + { "(", 0xE1 }, // open parentheses + { ")", 0xE3 }, // close parentheses + { "&", 0xE5 }, // ampersand + { "!", 0xF2 }, // exclamation mark + { "%", 0xF3 }, // percent + { "?", 0xF4 }, // question mark + { "~", 0xF7 }, // tilde + + // Symbols + { "[A]", 0x54 }, // bold A + { "[B]", 0x55 }, // bold B + { "[C]", 0x56 }, // bold C + { "[Z]", 0x57 }, // bold Z + { "[R]", 0x58 }, // bold R + { "<->", 0xE4 }, // left-right arrow + { "^", 0x50 }, // up arrow + { "|", 0x51 }, // down arrow + { "<", 0x52 }, // left arrow + { ">", 0x53 }, // right arrow + { "+", 0xF9 }, // coin + { "@", 0xFA }, // star filled + { "*", 0xFB }, // multiply + { "$", 0xFD }, // star empty + { "\n", 0xFE }, // New line + { NULL, 0xFF }, // Null terminator }; -static void convert_string(const u8* str, char* output) { - s32 strPos = 0; - bool capitalizeChar = true; +static const char *ascii_to_sm64_char(u8 *str64, const char *strAscii) { + for (s32 i = 0; sSm64CharMap[i].str != NULL; ++i) { + if (strstr(strAscii, sSm64CharMap[i].str) == strAscii) { + *str64 = sSm64CharMap[i].c; + return strAscii + strlen(sSm64CharMap[i].str); + } + } + *str64 = 0x9E; + return strAscii + 1; +} - while (str[strPos] != 0xFF) { - if (str[strPos] < 0xFF) { - output[strPos] = charset[str[strPos]]; +static char *sm64_to_ascii_char(char *strAscii, const u8 *str64) { + for (s32 i = 0; sSm64CharMap[i].str != NULL; ++i) { + if (sSm64CharMap[i].c == *str64) { + s32 l = strlen(sSm64CharMap[i].str); + memcpy(strAscii, sSm64CharMap[i].str, l); + return strAscii + l; + } + } + *strAscii = ' '; + return strAscii + 1; +} - // if the char is a letter we can capatalize it - if (capitalizeChar && 0x0A <= str[strPos] && str[strPos] <= 0x23) { - output[strPos] -= ('a' - 'A'); - capitalizeChar = false; +static void convert_string_ascii_to_sm64(u8 *str64, const char *strAscii) { + for (; *strAscii != 0; str64++) { + strAscii = ascii_to_sm64_char(str64, strAscii); + } + *str64 = 0xFF; +} + +static void convert_string_sm64_to_ascii(char *strAscii, const u8 *str64) { + for (; *str64 != 0xFF; str64++) { + strAscii = sm64_to_ascii_char(strAscii, str64); + } + *strAscii = 0; +} + +static void capitalize_string_ascii(char *strAscii) { + for (; *strAscii != 0; strAscii++) { + if (*strAscii >= 'a' && *strAscii <= 'z') { + *strAscii += ('A' - 'a'); + } + } +} + +static void capitalize_string_sm64(u8 *str64) { + for (; *str64 != 0xFF; str64++) { + if (*str64 >= 0x24 && *str64 <= 0x3D) { + *str64 -= 26; + } + } +} + +static void decapitalize_string_ascii(char *strAscii) { + for (bool decap = false; *strAscii != 0; strAscii++) { + if (*strAscii >= 'A' && *strAscii <= 'Z') { + if (decap) { + *strAscii += ('a' - 'A'); + } else { + decap = true; } - - } - else { - output[strPos] = ' '; + } else if (*strAscii < '0' && *strAscii != '\'') { + decap = false; } + } +} - // decide if the next character should be capitalized - switch (output[strPos]) { - case ' ': - //if (str[strPos] != 158) - //fprintf(stdout, "Unknown Character (%i)\n", str[strPos]); // inform that an unknown char was found - case '-': - capitalizeChar = true; - break; - default: - capitalizeChar = false; - break; +static void decapitalize_string_sm64(u8 *str64) { + for (bool decap = false; *str64 != 0xFF; str64++) { + if (*str64 >= 0x0A && *str64 <= 0x23) { + if (decap) { + *str64 += 26; + } else { + decap = true; + } + } else if (*str64 >= 0x3F) { + decap = false; } + } +} - strPos++; +const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase) { + static char output[256]; + + // 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) { + void **courseNameTbl = NULL; +#ifdef VERSION_EU + switch (gInGameLanguage) { + case LANGUAGE_ENGLISH: courseNameTbl = segmented_to_virtual(course_name_table_eu_en); break; + case LANGUAGE_FRENCH: courseNameTbl = segmented_to_virtual(course_name_table_eu_fr); break; + case LANGUAGE_GERMAN: courseNameTbl = segmented_to_virtual(course_name_table_eu_de); break; + } +#else + courseNameTbl = segmented_to_virtual(seg2_course_name_table); +#endif + const u8 *courseName = segmented_to_virtual(courseNameTbl[courseNum - COURSE_BOB]); + convert_string_sm64_to_ascii(output, courseName + 3); + } + + // Castle level + else if (courseNum == COURSE_NONE) { + switch (levelNum) { + case LEVEL_CASTLE: { + switch (areaIndex) { + case 1: snprintf(output, 256, "Castle Main Floor"); break; + case 2: snprintf(output, 256, "Castle Upper Floor"); break; + case 3: snprintf(output, 256, "Castle Basement"); break; + default: snprintf(output, 256, "Castle Purgatory"); break; + } + } break; + case LEVEL_CASTLE_GROUNDS: snprintf(output, 256, "Castle Grounds"); break; + case LEVEL_CASTLE_COURTYARD: snprintf(output, 256, "Castle Courtyard"); break; + default: snprintf(output, 256, "Peach's Castle"); + } + } + + // Default + else { + snprintf(output, 256, "Peach's Castle"); } - output[strPos] = '\0'; + // Capitalize or decapitalize text + if (charCase == -1) { + decapitalize_string_ascii(output); + } else if (charCase == +1) { + capitalize_string_ascii(output); + } + return output; +} + +const u8 *get_level_name_sm64(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase) { + static u8 output[256]; + const char *levelName = get_level_name_ascii(courseNum, levelNum, areaIndex, charCase); + convert_string_ascii_to_sm64(output, levelName); + return output; } const char *get_level_name(s16 courseNum, s16 levelNum, s16 areaIndex) { - static char stage[188] = { 0 }; - - //printf("get_level_name: %i %i %i, COURSE_MAX: %u COURSE_MIN: %u.\n", courseNum, levelNum, areaIndex, COURSE_MAX, COURSE_MIN); - - // Overrides for non-course based locations. - if (courseNum == COURSE_NONE) { - // A switch case is much more effective here - // then a if statement, It allows for the - // same results for a different level much easier. - // It also auto-covers if none of the cases match - // with a default. - switch (levelNum) { - case LEVEL_CASTLE_GROUNDS: - strcpy(stage, "Castle Grounds"); - break; - case LEVEL_CASTLE: - // Switch case inside a switch case, - // I think it looks ugly but it works. - switch (areaIndex) { - case 1: - strcpy(stage, "Castle Main Floor"); - break; - case 2: - strcpy(stage, "Castle Upper Floor"); - break; - case 3: - strcpy(stage, "Castle Basement"); - break; - default: // If we don't have a proper corresponding area, We return the default. - strcpy(stage, "Castle Purgatory"); - break; - } - break; - case LEVEL_CASTLE_COURTYARD: - strcpy(stage, "Castle Courtyard"); - break; - default: // If we don't have a proper corresponding level, We return the default. - strcpy(stage, "Peach's Castle"); - break; - } - return stage; - } - - // If we are in in Course 0 we are in the castle which doesn't have a string. - if (COURSE_IS_VALID_COURSE(courseNum)) { - void **courseNameTbl = NULL; - -#ifndef VERSION_EU - courseNameTbl = segmented_to_virtual(seg2_course_name_table); -#else - switch (gInGameLanguage) { - case LANGUAGE_ENGLISH: - courseNameTbl = segmented_to_virtual(course_name_table_eu_en); - break; - case LANGUAGE_FRENCH: - courseNameTbl = segmented_to_virtual(course_name_table_eu_fr); - break; - case LANGUAGE_GERMAN: - courseNameTbl = segmented_to_virtual(course_name_table_eu_de); - break; - } -#endif - u8 *courseName = segmented_to_virtual(courseNameTbl[courseNum - 1]); - - convert_string(&courseName[3], stage); - } else { - strcpy(stage, "Peach's Castle"); - } - - return stage; + return get_level_name_ascii(courseNum, levelNum, areaIndex, -1); +} + +const char *get_star_name_ascii(s16 courseNum, s16 starNum, s16 charCase) { + static char output[256]; + + // Main courses: BOB to RR + if (COURSE_IS_MAIN_COURSE(courseNum)) { + if (starNum >= 1 && starNum <= 6) { + void **actNameTable = NULL; +#ifdef VERSION_EU + switch (gInGameLanguage) { + case LANGUAGE_ENGLISH: actNameTable = segmented_to_virtual(act_name_table_eu_en); break; + case LANGUAGE_FRENCH: actNameTable = segmented_to_virtual(act_name_table_eu_fr); break; + case LANGUAGE_GERMAN: actNameTable = segmented_to_virtual(act_name_table_eu_de); break; + } +#else + actNameTable = segmented_to_virtual(seg2_act_name_table); +#endif + const u8 *starName = segmented_to_virtual(actNameTable[(courseNum - COURSE_BOB) * 6 + (starNum - 1)]); + convert_string_sm64_to_ascii(output, starName); + } else if (starNum == 7) { + snprintf(output, 256, "100 Coins Star"); + } else { + snprintf(output, 256, "A Secret Star!"); + } + } + + // Castle stars: Toads' and Mips' + else if (courseNum == COURSE_NONE) { + switch (starNum) { + case 1: snprintf(output, 256, "Toad Star 1"); break; + case 2: snprintf(output, 256, "Toad Star 2"); break; + case 3: snprintf(output, 256, "Toad Star 3"); break; + case 4: snprintf(output, 256, "Mips Star 1"); break; + case 5: snprintf(output, 256, "Mips Star 2"); break; + default: snprintf(output, 256, "A Secret Star!"); + } + } + + // Bonus courses: Bowser stages and Secret courses + else if (courseNum <= COURSE_MAX) { + snprintf(output, 256, "Star %d", starNum); + } + + // Default + else { + snprintf(output, 256, "A Secret Star!"); + } + + // Capitalize or decapitalize text + if (charCase == -1) { + decapitalize_string_ascii(output); + } else if (charCase == +1) { + capitalize_string_ascii(output); + } + return output; +} + +const u8 *get_star_name_sm64(s16 courseNum, s16 starNum, s16 charCase) { + static u8 output[256]; + const char *starName = get_star_name_ascii(courseNum, starNum, charCase); + convert_string_ascii_to_sm64(output, starName); + return output; +} + +const char *get_star_name(s16 courseNum, s16 starNum) { + return get_star_name_ascii(courseNum, starNum, -1); } diff --git a/src/game/level_info.h b/src/game/level_info.h index 43377038d..b0c817220 100644 --- a/src/game/level_info.h +++ b/src/game/level_info.h @@ -3,6 +3,11 @@ #include +const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase); +const u8 *get_level_name_sm64(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase); const char *get_level_name(s16 courseNum, s16 levelNum, s16 areaIndex); +const char *get_star_name_ascii(s16 courseNum, s16 starNum, s16 charCase); +const u8 *get_star_name_sm64(s16 courseNum, s16 starNum, s16 charCase); +const char *get_star_name(s16 courseNum, s16 starNum); #endif // LEVEL_INFO_H diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index 732a31041..b4ee1351d 100644 --- a/src/pc/chat_commands.c +++ b/src/pc/chat_commands.c @@ -5,6 +5,7 @@ #include "chat_commands.h" #include "pc/network/ban_list.h" #include "pc/debuglog.h" +#include "level_table.h" enum ChatConfirmCommand { CCC_NONE, @@ -181,6 +182,88 @@ bool exec_chat_command(char* command) { return true; } +#if defined(DEBUG) && defined(DEVELOPMENT) + if (gNetworkSystem == &gNetworkSystemSocket && str_starts_with("/warp ", command)) { + static const struct { const char *name; s32 num; } sLevelNumByName[] = { +#undef STUB_LEVEL +#undef DEFINE_LEVEL +#define STUB_LEVEL(...) +#define DEFINE_LEVEL(_0, levelnum, _2, levelname, ...) { #levelname, levelnum }, +#include "levels/level_defines.h" +#undef STUB_LEVEL +#undef DEFINE_LEVEL + }; + + // Params + char *paramLevel = command + 6; + if (*paramLevel == 0 || *paramLevel == ' ') { + djui_chat_message_create("Missing parameters: [LEVEL] [AREA] [ACT]"); + return true; + } + char *paramArea = strchr(paramLevel, ' '); + if (paramArea++ == NULL || *paramArea == 0 || *paramArea == ' ') { + djui_chat_message_create("Missing parameters: [AREA] [ACT]"); + return true; + } + char *paramAct = strchr(paramArea, ' '); + if (paramAct++ == NULL || *paramAct == 0 || *paramAct == ' ') { + djui_chat_message_create("Missing parameters: [ACT]"); + return true; + } + *(paramArea - 1) = 0; + *(paramAct - 1) = 0; + + // Level + s32 level = -1; + if (sscanf(paramLevel, "%d", &level) <= 0) { + for (s32 i = 0; i != (s32) (sizeof(sLevelNumByName) / sizeof(sLevelNumByName[0])); ++i) { + if (strstr(paramLevel, sLevelNumByName[i].name) == paramLevel) { + level = sLevelNumByName[i].num; + break; + } + } + if (level == -1) { + char message[256]; + snprintf(message, 256, "Invalid [LEVEL] parameter: %s", paramLevel); + djui_chat_message_create(message); + return true; + } + } + + // Area + s32 area = -1; + if (sscanf(paramArea, "%d", &area) <= 0) { + char message[256]; + snprintf(message, 256, "Invalid [AREA] parameter: %s", paramArea); + djui_chat_message_create(message); + return true; + } + + // Act + s32 act = -1; + if (sscanf(paramAct, "%d", &act) <= 0) { + char message[256]; + snprintf(message, 256, "Invalid [ACT] parameter: %s", paramAct); + djui_chat_message_create(message); + return true; + } + + // Warp + if (!dynos_warp_to_level(level, area, act)) { + char message[256]; + snprintf(message, 256, "Unable to warp to: %s %s %s", paramLevel, paramArea, paramAct); + djui_chat_message_create(message); + return true; + } + + // OK + char message[256]; + snprintf(message, 256, "Warping to: %s %s %s...", paramLevel, paramArea, paramAct); + djui_chat_message_create(message); + return true; + } +#endif + return smlua_call_chat_command_hook(command); } @@ -191,6 +274,9 @@ void display_chat_commands(void) { djui_chat_message_create("/ban [NAME|ID] - Ban this player from the current game"); djui_chat_message_create("/permban [NAME|ID] - Ban this player from any game you host"); } +#if defined(DEBUG) && defined(DEVELOPMENT) + djui_chat_message_create("/warp [LEVEL] [AREA] [ACT] - Level can be either a numeric value or a shorthand name"); +#endif if (sConfirming != CCC_NONE) { djui_chat_message_create("/confirm"); } smlua_display_chat_commands(); } \ No newline at end of file diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index c08573129..c10bc9b0e 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -563,15 +563,16 @@ static struct LuaObjectField sGlobalTexturesFields[LUA_GLOBAL_TEXTURES_FIELD_COU { "star", LVT_COBJECT, offsetof(struct GlobalTextures, star), true, LOT_TEXTUREINFO }, }; -#define LUA_GRAPH_NODE_FIELD_COUNT 6 +#define LUA_GRAPH_NODE_FIELD_COUNT 7 static struct LuaObjectField sGraphNodeFields[LUA_GRAPH_NODE_FIELD_COUNT] = { - { "children", LVT_COBJECT_P, offsetof(struct GraphNode, children), false, LOT_GRAPHNODE }, - { "flags", LVT_S16, offsetof(struct GraphNode, flags), false, LOT_NONE }, -// { "georef", LVT_???, offsetof(struct GraphNode, georef), true, LOT_??? }, <--- UNIMPLEMENTED - { "next", LVT_COBJECT_P, offsetof(struct GraphNode, next), false, LOT_GRAPHNODE }, - { "parent", LVT_COBJECT_P, offsetof(struct GraphNode, parent), false, LOT_GRAPHNODE }, - { "prev", LVT_COBJECT_P, offsetof(struct GraphNode, prev), false, LOT_GRAPHNODE }, - { "type", LVT_S16, offsetof(struct GraphNode, type), false, LOT_NONE }, + { "children", LVT_COBJECT_P, offsetof(struct GraphNode, children), false, LOT_GRAPHNODE }, + { "extraFlags", LVT_U8, offsetof(struct GraphNode, extraFlags), false, LOT_NONE }, + { "flags", LVT_S16, offsetof(struct GraphNode, flags), false, LOT_NONE }, +// { "georef", LVT_???, offsetof(struct GraphNode, georef), true, LOT_??? }, <--- UNIMPLEMENTED + { "next", LVT_COBJECT_P, offsetof(struct GraphNode, next), false, LOT_GRAPHNODE }, + { "parent", LVT_COBJECT_P, offsetof(struct GraphNode, parent), false, LOT_GRAPHNODE }, + { "prev", LVT_COBJECT_P, offsetof(struct GraphNode, prev), false, LOT_GRAPHNODE }, + { "type", LVT_S16, offsetof(struct GraphNode, type), false, LOT_NONE }, }; #define LUA_GRAPH_NODE_OBJECT_FIELD_COUNT 19 diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 0590b3010..6783deeaf 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1258,6 +1258,7 @@ char gSmluaConstants[] = "" "GRAPH_RENDER_HAS_ANIMATION = (1 << 5)\n" "GRAPH_RENDER_CYLBOARD = (1 << 6)\n" "GRAPH_RENDER_PLAYER = (1 << 7)\n" +"GRAPH_EXTRA_FORCE_3D = (1 << 0)\n" "GRAPH_NODE_TYPE_FUNCTIONAL = 0x100\n" "GRAPH_NODE_TYPE_400 = 0x400\n" "GRAPH_NODE_TYPE_ROOT = 0x001\n" diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index e720b52f6..b985c4f66 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -7764,6 +7764,83 @@ int smlua_func_get_level_name(lua_State* L) { return 1; } +int smlua_func_get_level_name_ascii(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 4)) { return 0; } + + s16 courseNum = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 levelNum = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + s16 areaIndex = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + s16 charCase = smlua_to_integer(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 4"); return 0; } + + lua_pushstring(L, get_level_name_ascii(courseNum, levelNum, areaIndex, charCase)); + + return 1; +} + +int smlua_func_get_level_name_sm64(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 4)) { return 0; } + + s16 courseNum = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 levelNum = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + s16 areaIndex = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + s16 charCase = smlua_to_integer(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 4"); return 0; } + + smlua_push_pointer(L, LVT_U8_P, (void*)get_level_name_sm64(courseNum, levelNum, areaIndex, charCase)); + + return 1; +} + +int smlua_func_get_star_name(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 2)) { return 0; } + + s16 courseNum = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 starNum = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + + lua_pushstring(L, get_star_name(courseNum, starNum)); + + return 1; +} + +int smlua_func_get_star_name_ascii(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 3)) { return 0; } + + s16 courseNum = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 starNum = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + s16 charCase = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + + lua_pushstring(L, get_star_name_ascii(courseNum, starNum, charCase)); + + return 1; +} + +int smlua_func_get_star_name_sm64(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 3)) { return 0; } + + s16 courseNum = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 starNum = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + s16 charCase = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + + smlua_push_pointer(L, LVT_U8_P, (void*)get_star_name_sm64(courseNum, starNum, charCase)); + + return 1; +} + ///////////// // mario.h // ///////////// @@ -14727,6 +14804,25 @@ int smlua_func_movtexqc_register(lua_State* L) { return 1; } +int smlua_func_play_transition(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 5)) { return 0; } + + s16 transType = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + s16 time = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + u8 red = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 3"); return 0; } + u8 green = smlua_to_integer(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 4"); return 0; } + u8 blue = smlua_to_integer(L, 5); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 5"); return 0; } + + play_transition(transType, time, red, green, blue); + + return 1; +} + int smlua_func_save_file_set_using_backup_slot(lua_State* L) { if(!smlua_functions_valid_param_count(L, 1)) { return 0; } @@ -16398,6 +16494,11 @@ void smlua_bind_functions_autogen(void) { // level_info.h smlua_bind_function(L, "get_level_name", smlua_func_get_level_name); + smlua_bind_function(L, "get_level_name_ascii", smlua_func_get_level_name_ascii); + smlua_bind_function(L, "get_level_name_sm64", smlua_func_get_level_name_sm64); + smlua_bind_function(L, "get_star_name", smlua_func_get_star_name); + smlua_bind_function(L, "get_star_name_ascii", smlua_func_get_star_name_ascii); + smlua_bind_function(L, "get_star_name_sm64", smlua_func_get_star_name_sm64); // mario.h smlua_bind_function(L, "adjust_sound_for_speed", smlua_func_adjust_sound_for_speed); @@ -16953,6 +17054,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "hud_hide", smlua_func_hud_hide); smlua_bind_function(L, "hud_show", smlua_func_hud_show); smlua_bind_function(L, "movtexqc_register", smlua_func_movtexqc_register); + smlua_bind_function(L, "play_transition", smlua_func_play_transition); smlua_bind_function(L, "save_file_set_using_backup_slot", smlua_func_save_file_set_using_backup_slot); smlua_bind_function(L, "set_environment_region", smlua_func_set_environment_region); smlua_bind_function(L, "warp_exit_level", smlua_func_warp_exit_level); diff --git a/src/pc/lua/smlua_utils.c b/src/pc/lua/smlua_utils.c index 598cfc31c..e13632dda 100644 --- a/src/pc/lua/smlua_utils.c +++ b/src/pc/lua/smlua_utils.c @@ -1,5 +1,6 @@ #include "smlua.h" #include "src/pc/mods/mods.h" +#include "audio/external.h" u8 gSmLuaConvertSuccess = false; @@ -21,6 +22,21 @@ s16* smlua_get_vec3s_from_buffer(void) { return sVec3sBuffer[sVec3sBufferIndex++]; } +f32 *smlua_get_vec3f_for_play_sound(f32 *pos) { + if (pos < (f32 *) sVec3fBuffer || pos >= (f32 *) (sVec3fBuffer + VEC3F_BUFFER_COUNT)) { + return pos; + } + if (memcmp(pos, gGlobalSoundSource, sizeof(Vec3f)) == 0) { + return gGlobalSoundSource; + } + for (s32 i = 0; i != MAX_PLAYERS; ++i) { + if (gMarioStates[i].marioObj && memcmp(pos, gMarioStates[i].marioObj->header.gfx.cameraToObject, sizeof(Vec3f)) == 0) { + return gMarioStates[i].marioObj->header.gfx.cameraToObject; + } + } + return pos; +} + /////////////////////////////////////////////////////////////////////////////////////////// void smlua_bind_function(lua_State* L, const char* name, void* func) { diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index d72f349f5..ae209995c 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -29,4 +29,6 @@ void movtexqc_register(const char* name, s16 level, s16 area, s16 type); f32 get_environment_region(u8 index); void set_environment_region(u8 index, s32 value); +void play_transition(s16 transType, s16 time, u8 red, u8 green, u8 blue); + #endif