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);