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