Changes in level_info; play_transition; dev-only warp command; bug fix for moving sounds played via lua (#69)

Improved level_info.c and added functions to LUA:
        const char *get_level_name_ascii(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase):
            Return a level name as an ascii string. If charCase is 1, capitalize all letters.
            If charCase is -1, decapitalize all letters except the first one of each word.
        const u8 *get_level_name_sm64(s16 courseNum, s16 levelNum, s16 areaIndex, s16 charCase):
            Return a level name as an sm64 u8 string.
            If charCase is 1, capitalize all letters.
            If charCase is -1, decapitalize all letters except the first one of each word.
        const char *get_level_name(s16 courseNum, s16 levelNum, s16 areaIndex):
            Shortcut for get_level_name_ascii(courseNum, levelNum, areaIndex, -1).
        const char *get_star_name_ascii(s16 courseNum, s16 starNum, s16 charCase):
            Return a star name as an ascii string.
            If charCase is 1, capitalize all letters.
            If charCase is -1, decapitalize all letters except the first one of each word.
        const u8 *get_star_name_sm64(s16 courseNum, s16 starNum, s16 charCase):
            Return a star name as an sm64 u8 string.
            If charCase is 1, capitalize all letters.
            If charCase is -1, decapitalize all letters except the first one of each word.
        const char *get_star_name(s16 courseNum, s16 starNum):
            Shortcut for get_star_name_ascii(courseNum, starNum, -1).

    Added play_transition function to LUA.
        I chose to copy the function declaration to smlua_misc_utils.h instead of adding area.h
        to the autogen tool, as most structures, variables and functions in area.h aren't meant
        to be used by LUA scripts.

    Added a dev-only warp chat command.
        This command signature is /warp [LEVEL] [AREA] [ACT]. Level can be either a number
            or a shorthand name (bob, wf, ccm...). Area and Act are numbers.
        This command is available only when building the game with DEBUG and DEVELOPMENT.
        This command cannot be used if hosting through Discord.

    Fixed a bug with moving sounds when they are played via a lua script.
        Bug: Moving sounds (including terrain sounds, flying sound, quicksand sound)
            are not played correctly when a lua script play them via a call of
            play_sound or play_sound_with_freq_scale. This is due to how the moving
            sounds are handled internally. They use the f32 pointer provided to the
            play_sound functions to decide if the sound must be kept playing,
            stopped or restarted. Most of the time, the pointer provided is the
            cameraToObject field of Mario's object graph node. Since smlua uses a
            circular buffer for Vec3f conversion, this pointer is lost, and the
            sound engine can't decide what to do with the sound, resulting in a
            weird and incorrect sound effect.
        Fix: play_sound and play_sound_with_freq_scale now calls
            smlua_get_vec3f_for_play_sound before filling the sound request queue,
            to retrieve the correct pointer from the Vec3f provided by smlua.
This commit is contained in:
PeachyPeach 2022-04-26 22:48:50 +02:00 committed by GitHub
parent 343076dec1
commit 6726a6280a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 687 additions and 139 deletions

View file

@ -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

View file

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

View file

@ -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

View file

@ -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

View file

@ -2924,6 +2924,117 @@
<br />
## [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:](#)
<br />
## [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:](#)
<br />
## [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:](#)
<br />
## [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:](#)
<br />
## [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:](#)
<br />
---
# functions from mario.h

View file

@ -4919,6 +4919,30 @@
<br />
## [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:](#)
<br />
## [save_file_set_using_backup_slot](#save_file_set_using_backup_slot)
### Lua Example

View file

@ -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)
<br />
@ -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)

View file

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

View file

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

View file

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

View file

@ -3,6 +3,11 @@
#include <PR/ultratypes.h>
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

View file

@ -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();
}

View file

@ -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

View file

@ -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"

View file

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

View file

@ -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) {

View file

@ -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