diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 94ab707c..099e5b2f 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 73bbd5f7..cf6bc6a3 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) @@ -7211,6 +7262,12 @@ function obj_check_hitbox_overlap(o1, o2) -- ... end +--- @param behaviorId BehaviorId +--- @return integer +function obj_count_objects_with_behavior_id(behaviorId) + -- ... +end + --- @param objList ObjectList --- @return Object function obj_get_first(objList) @@ -7239,6 +7296,13 @@ function obj_get_first_with_behavior_id_and_field_s32(behaviorId, fieldIndex, va -- ... end +--- @param o Object +--- @param behaviorId BehaviorId +--- @return Object +function obj_get_nearest_object_with_behavior_id(o, behaviorId) + -- ... +end + --- @param o Object --- @return Object function obj_get_next(o) diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 98703c22..7227f6a0 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 da4624be..84e86128 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 e1c675ef..5cbae560 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 b2876a44..f3e997f1 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 @@ -5131,6 +5155,26 @@
+## [obj_count_objects_with_behavior_id](#obj_count_objects_with_behavior_id) + +### Lua Example +`local integerValue = obj_count_objects_with_behavior_id(behaviorId)` + +### Parameters +| Field | Type | +| ----- | ---- | +| behaviorId | [enum BehaviorId](constants.md#enum-BehaviorId) | + +### Returns +- `integer` + +### C Prototype +`s32 obj_count_objects_with_behavior_id(enum BehaviorId behaviorId);` + +[:arrow_up_small:](#) + +
+ ## [obj_get_first](#obj_get_first) ### Lua Example @@ -5215,6 +5259,27 @@
+## [obj_get_nearest_object_with_behavior_id](#obj_get_nearest_object_with_behavior_id) + +### Lua Example +`local ObjectValue = obj_get_nearest_object_with_behavior_id(o, behaviorId)` + +### Parameters +| Field | Type | +| ----- | ---- | +| o | [Object](structs.md#Object) | +| behaviorId | [enum BehaviorId](constants.md#enum-BehaviorId) | + +### Returns +[Object](structs.md#Object) + +### C Prototype +`struct Object *obj_get_nearest_object_with_behavior_id(struct Object *o, enum BehaviorId behaviorId);` + +[:arrow_up_small:](#) + +
+ ## [obj_get_next](#obj_get_next) ### Lua Example diff --git a/docs/lua/functions.md b/docs/lua/functions.md index aac2747c..b14e8774 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) @@ -1357,10 +1363,12 @@ - [get_temp_object_hitbox](functions-4.md#get_temp_object_hitbox) - [get_trajectory](functions-4.md#get_trajectory) - [obj_check_hitbox_overlap](functions-4.md#obj_check_hitbox_overlap) + - [obj_count_objects_with_behavior_id](functions-4.md#obj_count_objects_with_behavior_id) - [obj_get_first](functions-4.md#obj_get_first) - [obj_get_first_with_behavior_id](functions-4.md#obj_get_first_with_behavior_id) - [obj_get_first_with_behavior_id_and_field_f32](functions-4.md#obj_get_first_with_behavior_id_and_field_f32) - [obj_get_first_with_behavior_id_and_field_s32](functions-4.md#obj_get_first_with_behavior_id_and_field_s32) + - [obj_get_nearest_object_with_behavior_id](functions-4.md#obj_get_nearest_object_with_behavior_id) - [obj_get_next](functions-4.md#obj_get_next) - [obj_get_next_with_same_behavior_id](functions-4.md#obj_get_next_with_same_behavior_id) - [obj_get_next_with_same_behavior_id_and_field_f32](functions-4.md#obj_get_next_with_same_behavior_id_and_field_f32) diff --git a/docs/lua/structs.md b/docs/lua/structs.md index c085ba10..ef939e69 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 efc4c70c..c79e7ee0 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 532dbf16..a906a043 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 43377038..b0c81722 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/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c index 9617e2ba..418f8e87 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -929,7 +929,7 @@ static void bubbled_offset_visual(struct MarioState* m) { } s32 act_bubbled(struct MarioState* m) { - if (m->playerIndex == 0) { + if (m->playerIndex == 0 && m->area->camera->mode == CAMERA_MODE_WATER_SURFACE) { set_camera_mode(m->area->camera, CAMERA_MODE_FREE_ROAM, 1); } struct MarioState* targetMarioState = nearest_mario_state_to_object(m->marioObj); diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 374d900f..a0d589e8 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -13,6 +13,7 @@ #include "game/level_update.h" #include "pc/lua/smlua_hooks.h" #include "pc/utils/misc.h" +#include "pc/debuglog.h" /** * This file contains the code that processes the scene graph for rendering. @@ -264,7 +265,7 @@ void patch_mtx_interpolated(f32 delta) { static u8 increment_mat_stack() { Mtx *mtx = alloc_display_list(sizeof(*mtx)); Mtx *mtxPrev = alloc_display_list(sizeof(*mtxPrev)); - if (mtx == NULL || mtxPrev == NULL) { return FALSE; } + if (mtx == NULL || mtxPrev == NULL) { LOG_ERROR("Failed to allocate our matrices for the matrix stack."); return FALSE; } gMatStackIndex++; mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); @@ -460,7 +461,7 @@ static void geo_process_camera(struct GraphNodeCamera *node) { Mat4 cameraTransform; // Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); if (rollMtx == NULL) { return; } @@ -511,7 +512,7 @@ static void geo_process_translation_rotation(struct GraphNodeTranslationRotation Vec3f translation; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } vec3s_to_vec3f(translation, node->translation); mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); @@ -540,7 +541,7 @@ static void geo_process_translation(struct GraphNodeTranslation *node) { Vec3f translation; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } vec3s_to_vec3f(translation, node->translation); mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); @@ -568,7 +569,7 @@ static void geo_process_rotation(struct GraphNodeRotation *node) { Mat4 mtxf; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); @@ -604,7 +605,7 @@ static void geo_process_scale(struct GraphNodeScale *node) { Vec3f prevScaleVec; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } vec3f_set(scaleVec, node->scale, node->scale, node->scale); mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); @@ -635,7 +636,7 @@ static void geo_process_billboard(struct GraphNodeBillboard *node) { Vec3f translation; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } s16 nextMatStackIndex = gMatStackIndex + 1; @@ -654,6 +655,8 @@ static void geo_process_billboard(struct GraphNodeBillboard *node) { gCurGraphNodeObject->scale); mtxf_scale_vec3f(gMatStackPrev[nextMatStackIndex], gMatStackPrev[nextMatStackIndex], gCurGraphNodeObject->scale); + } else { + //LOG_ERROR("gCurGraphNodeObject and gCurGraphNodeHeldObject are both NULL!"); } // Increment the matrix stack, If we fail to do so. Just return. @@ -805,7 +808,7 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { Vec3f translationPrev; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } u16 *animAttribute = gCurrAnimAttribute; u8 animType = gCurAnimType; @@ -899,7 +902,7 @@ static void geo_process_shadow(struct GraphNodeShadow *node) { f32 shadowScale; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { if (gCurGraphNodeHeldObject != NULL) { @@ -1284,7 +1287,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { Vec3f scalePrev; // Sanity check our stack index, If we above or equal to our stack size. Return top prevent OOB. - if (gMatStackIndex >= MATRIX_STACK_SIZE) { return; } + if (gMatStackIndex >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; } #ifdef F3DEX_GBI_2 gSPLookAt(gDisplayListHead++, &lookAt); diff --git a/src/pc/chat_commands.c b/src/pc/chat_commands.c index 732a3104..b4ee1351 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 3d362c51..ade3828f 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 0590b301..6783deea 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 e720b52f..c6de9478 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; } @@ -14849,6 +14945,17 @@ int smlua_func_obj_check_hitbox_overlap(lua_State* L) { return 1; } +int smlua_func_obj_count_objects_with_behavior_id(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + int behaviorId = smlua_to_integer(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + + lua_pushinteger(L, obj_count_objects_with_behavior_id(behaviorId)); + + return 1; +} + int smlua_func_obj_get_first(lua_State* L) { if(!smlua_functions_valid_param_count(L, 1)) { return 0; } @@ -14901,6 +15008,19 @@ int smlua_func_obj_get_first_with_behavior_id_and_field_s32(lua_State* L) { return 1; } +int smlua_func_obj_get_nearest_object_with_behavior_id(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 2)) { return 0; } + + struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 1"); return 0; } + int behaviorId = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter 2"); return 0; } + + smlua_push_object(L, LOT_OBJECT, obj_get_nearest_object_with_behavior_id(o, behaviorId)); + + return 1; +} + int smlua_func_obj_get_next(lua_State* L) { if(!smlua_functions_valid_param_count(L, 1)) { return 0; } @@ -16398,6 +16518,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 +17078,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); @@ -16967,10 +17093,12 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "get_temp_object_hitbox", smlua_func_get_temp_object_hitbox); smlua_bind_function(L, "get_trajectory", smlua_func_get_trajectory); smlua_bind_function(L, "obj_check_hitbox_overlap", smlua_func_obj_check_hitbox_overlap); + smlua_bind_function(L, "obj_count_objects_with_behavior_id", smlua_func_obj_count_objects_with_behavior_id); smlua_bind_function(L, "obj_get_first", smlua_func_obj_get_first); smlua_bind_function(L, "obj_get_first_with_behavior_id", smlua_func_obj_get_first_with_behavior_id); smlua_bind_function(L, "obj_get_first_with_behavior_id_and_field_f32", smlua_func_obj_get_first_with_behavior_id_and_field_f32); smlua_bind_function(L, "obj_get_first_with_behavior_id_and_field_s32", smlua_func_obj_get_first_with_behavior_id_and_field_s32); + smlua_bind_function(L, "obj_get_nearest_object_with_behavior_id", smlua_func_obj_get_nearest_object_with_behavior_id); smlua_bind_function(L, "obj_get_next", smlua_func_obj_get_next); smlua_bind_function(L, "obj_get_next_with_same_behavior_id", smlua_func_obj_get_next_with_same_behavior_id); smlua_bind_function(L, "obj_get_next_with_same_behavior_id_and_field_f32", smlua_func_obj_get_next_with_same_behavior_id_and_field_f32); diff --git a/src/pc/lua/smlua_utils.c b/src/pc/lua/smlua_utils.c index 598cfc31..e13632dd 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 d72f349f..ae209995 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 diff --git a/src/pc/lua/utils/smlua_obj_utils.c b/src/pc/lua/utils/smlua_obj_utils.c index 77579dc2..5c2840de 100644 --- a/src/pc/lua/utils/smlua_obj_utils.c +++ b/src/pc/lua/utils/smlua_obj_utils.c @@ -156,6 +156,40 @@ struct Object *obj_get_first_with_behavior_id_and_field_f32(enum BehaviorId beha return NULL; } +struct Object *obj_get_nearest_object_with_behavior_id(struct Object *o, enum BehaviorId behaviorId) { + f32 minDist = 0x20000; + const BehaviorScript *behavior = get_behavior_from_id(behaviorId); + struct Object *closestObj = NULL; + + if (behavior) { + enum ObjectList objList = get_object_list_from_behavior(behavior); + for (struct Object *obj = obj_get_first(objList); obj != NULL; obj = obj_get_next(obj)) { + if (obj->behavior == behavior && obj->activeFlags != ACTIVE_FLAG_DEACTIVATED) { + f32 objDist = dist_between_objects(o, obj); + if (objDist < minDist) { + closestObj = obj; + minDist = objDist; + } + } + } + } + return closestObj; +} + +s32 obj_count_objects_with_behavior_id(enum BehaviorId behaviorId) { + const BehaviorScript *behavior = get_behavior_from_id(behaviorId); + s32 count = 0; + + if (behavior) { + enum ObjectList objList = get_object_list_from_behavior(behavior); + for (struct Object *obj = obj_get_first(objList); obj != NULL; obj = obj_get_next(obj)) { + if (obj->behavior == behavior) { count++; } + } + } + + return count; +} + struct Object *obj_get_next(struct Object *o) { if (gObjectLists && o) { enum ObjectList objList = get_object_list_from_behavior(o->behavior); diff --git a/src/pc/lua/utils/smlua_obj_utils.h b/src/pc/lua/utils/smlua_obj_utils.h index 76996633..e432c085 100644 --- a/src/pc/lua/utils/smlua_obj_utils.h +++ b/src/pc/lua/utils/smlua_obj_utils.h @@ -28,6 +28,10 @@ struct Object *obj_get_next_with_same_behavior_id(struct Object *o); struct Object *obj_get_next_with_same_behavior_id_and_field_s32(struct Object *o, s32 fieldIndex, s32 value); struct Object *obj_get_next_with_same_behavior_id_and_field_f32(struct Object *o, s32 fieldIndex, f32 value); +struct Object *obj_get_nearest_object_with_behavior_id(struct Object *o, enum BehaviorId behaviorId); + +s32 obj_count_objects_with_behavior_id(enum BehaviorId behaviorId); + // misc obj helpers struct SpawnParticlesInfo* obj_get_temp_spawn_particles_info(enum ModelExtendedId modelId);