diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index 593d36259..939563cb3 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -725,6 +725,86 @@ Shoots a raycast from `startX`, `startY`, and `startZ` in the direction of `dirX
+## [set_exclamation_box_contents](#set_exclamation_box_contents) + +Sets the contents that the exclamation box spawns. A single content has 5 keys: `id`, `unused`, `firstByte`, `model`, and `behavior`. +* `id`: Required; what value the box's oBehParams2ndByte needs to be to spawn this object. +* `unused`: Optional; unused by vanilla. +* `firstByte`: Optional; Overrides the 1st byte given to the spawned object. +* `model`: Required; The model that the object will spawn with. Uses `ModelExtendedId`. +* `behavior`: Required; The behavior ID that the object will spawn with. Uses `BehaviorId`. + +### Lua Example +```lua +set_exclamation_box_contents({ + {id = 0, unused = 0, firstByte = 0, model = E_MODEL_GOOMBA, behavior = id_bhvGoomba}, -- Uses both optional fields + {id = 1, unused = 0, model = E_MODEL_KOOPA_WITH_SHELL, behavior = id_bhvKoopa}, -- Only uses `unused` optional field + {id = 2, firsteByte = model = E_MODEL_BLACK_BOBOMB, behavior = id_bhvBobomb}, -- Only uses `firstByte` optional field + {id = 3, model = E_MODEL_BOO, behavior = id_bhvBoo}, -- Uses no optional fields +}) +``` + +### Parameters +There exists only 1 parameter to this function which is the main table. However, each subtable has 5 different keys that could be accessed. +| Field | Type | +| ----- | ---- | +| id | `integer` | +| unused (Optional) | `integer` | +| firstByte (Optional) | `integer` | +| model | [ModelExtendedId](#ModelExtendedId) | +| behavior | [BehaviorId](#BehaviorId) | + +### Returns +- None + +### C Prototype +N/A + +[:arrow_up_small:](#) + +
+ +## [get_exclamation_box_contents](#get_exclamation_box_contents) + +Gets the contents that the exclamation box spawns. A single content has 5 keys: `id`, `unused`, `firstByte`, `model`, and `behavior`. +* `id`: Required; what value the box's oBehParams2ndByte needs to be to spawn this object. +* `unused`: Optional; unused by vanilla. +* `firstByte`: Optional; Overrides the 1st byte given to the spawned object. +* `model`: Required; The model that the object will spawn with. Uses `ModelExtendedId`. +* `behavior`: Required; The behavior ID that the object will spawn with. Uses `BehaviorId`. + +### Lua Example +```lua +local contents = get_exclamation_box_contents() +for index, content in pairs(contents) do -- Enter the main table + djui_chat_message_create("Table index " .. index) -- Print the current table index + for key, value in pairs(content) do + djui_chat_message_create(key .. ": " .. value) -- Print a key-value pair within this subtable + end + djui_chat_message_create("---------------------------------") -- Separator +end +``` + +### Parameters +- N/A + +### Returns +The function itself does not return every key/value pair. Instead it returns the main table which holds all the subtables that hold each key/value pair. +| Field | Type | +| ----- | ---- | +| id | `integer` | +| unused (Optional) | `integer` | +| firstByte (Optional) | `integer` | +| model | [ModelExtendedId](#ModelExtendedId) | +| behavior | [BehaviorId](#BehaviorId) | + +### C Prototype +N/A + +[:arrow_up_small:](#) + +
+ """ ############################################################################ diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua index 5f4478243..4c8063063 100644 --- a/autogen/lua_definitions/manual.lua +++ b/autogen/lua_definitions/manual.lua @@ -388,3 +388,27 @@ end function collision_find_surface_on_ray(startX, startY, startZ, dirX, dirY, dirZ, precision) -- ... end + +--- @param contents ExclamationBoxContent[] +--- Sets the contents that the exclamation box spawns. +--- A single content has 5 keys: `id`, `unused`, `firstByte`, `model`, and `behavior`. +--- * `id`: Required; what value the box's oBehParams2ndByte needs to be to spawn this object. +--- * `unused`: Optional; unused by vanilla. +--- * `firstByte`: Optional; Overrides the 1st byte given to the spawned object. +--- * `model`: Required; The model that the object will spawn with. Uses `ModelExtendedId`. +--- * `behavior`: Required; The behavior ID that the object will spawn with. Uses `BehaviorId`. +function set_exclamation_box_contents(contents) + -- ... +end + +--- @return ExclamationBoxContent[] +--- Gets the contents that the exclamation box spawns. +--- A single content has 5 keys: `id`, `unused`, `firstByte`, `model`, and `behavior`. +--- * `id`: Required; what value the box's oBehParams2ndByte needs to be to spawn this object. +--- * `unused`: Optional; unused by vanilla. +--- * `firstByte`: Optional; Overrides the 1st byte given to the spawned object. +--- * `model`: Required; The model that the object will spawn with. Uses `ModelExtendedId`. +--- * `behavior`: Required; The behavior ID that the object will spawn with. Uses `BehaviorId`. +function get_exclamation_box_contents() + -- ... +end diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index b9f0e26a4..d3fd460fb 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -590,6 +590,13 @@ --- @field public g integer --- @field public r integer +--- @class ExclamationBoxContent +--- @field public behavior BehaviorId +--- @field public firstByte integer +--- @field public id integer +--- @field public model ModelExtendedId +--- @field public unused integer + --- @class FirstPersonCamera --- @field public centerL boolean --- @field public crouch number diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 782bf43c4..9f28fc269 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -2323,6 +2323,86 @@ Shoots a raycast from `startX`, `startY`, and `startZ` in the direction of `dirX
+## [set_exclamation_box_contents](#set_exclamation_box_contents) + +Sets the contents that the exclamation box spawns. A single content has 5 keys: `id`, `unused`, `firstByte`, `model`, and `behavior`. +* `id`: Required; what value the box's oBehParams2ndByte needs to be to spawn this object. +* `unused`: Optional; unused by vanilla. +* `firstByte`: Optional; Overrides the 1st byte given to the spawned object. +* `model`: Required; The model that the object will spawn with. Uses `ModelExtendedId`. +* `behavior`: Required; The behavior ID that the object will spawn with. Uses `BehaviorId`. + +### Lua Example +```lua +set_exclamation_box_contents({ + {id = 0, unused = 0, firstByte = 0, model = E_MODEL_GOOMBA, behavior = id_bhvGoomba}, -- Uses both optional fields + {id = 1, unused = 0, model = E_MODEL_KOOPA_WITH_SHELL, behavior = id_bhvKoopa}, -- Only uses `unused` optional field + {id = 2, firsteByte = model = E_MODEL_BLACK_BOBOMB, behavior = id_bhvBobomb}, -- Only uses `firstByte` optional field + {id = 3, model = E_MODEL_BOO, behavior = id_bhvBoo}, -- Uses no optional fields +}) +``` + +### Parameters +There exists only 1 parameter to this function which is the main table. However, each subtable has 5 different keys that could be accessed. +| Field | Type | +| ----- | ---- | +| id | `integer` | +| unused (Optional) | `integer` | +| firstByte (Optional) | `integer` | +| model | [ModelExtendedId](#ModelExtendedId) | +| behavior | [BehaviorId](#BehaviorId) | + +### Returns +- None + +### C Prototype +N/A + +[:arrow_up_small:](#) + +
+ +## [get_exclamation_box_contents](#get_exclamation_box_contents) + +Gets the contents that the exclamation box spawns. A single content has 5 keys: `id`, `unused`, `firstByte`, `model`, and `behavior`. +* `id`: Required; what value the box's oBehParams2ndByte needs to be to spawn this object. +* `unused`: Optional; unused by vanilla. +* `firstByte`: Optional; Overrides the 1st byte given to the spawned object. +* `model`: Required; The model that the object will spawn with. Uses `ModelExtendedId`. +* `behavior`: Required; The behavior ID that the object will spawn with. Uses `BehaviorId`. + +### Lua Example +```lua +local contents = get_exclamation_box_contents() +for index, content in pairs(contents) do -- Enter the main table + djui_chat_message_create("Table index " .. index) -- Print the current table index + for key, value in pairs(content) do + djui_chat_message_create(key .. ": " .. value) -- Print a key-value pair within this subtable + end + djui_chat_message_create("---------------------------------") -- Separator +end +``` + +### Parameters +- N/A + +### Returns +The function itself does not return every key/value pair. Instead it returns the main table which holds all the subtables that hold each key/value pair. +| Field | Type | +| ----- | ---- | +| id | `integer` | +| unused (Optional) | `integer` | +| firstByte (Optional) | `integer` | +| model | [ModelExtendedId](#ModelExtendedId) | +| behavior | [BehaviorId](#BehaviorId) | + +### C Prototype +N/A + +[:arrow_up_small:](#) + +
+ --- # functions from area.h diff --git a/docs/lua/structs.md b/docs/lua/structs.md index f15b1b0ea..112aec3a0 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -24,6 +24,7 @@ - [CutsceneVariable](#CutsceneVariable) - [DateTime](#DateTime) - [DjuiColor](#DjuiColor) +- [ExclamationBoxContent](#ExclamationBoxContent) - [FirstPersonCamera](#FirstPersonCamera) - [FloorGeometry](#FloorGeometry) - [GlobalObjectAnimations](#GlobalObjectAnimations) @@ -844,6 +845,20 @@
+## [ExclamationBoxContent](#ExclamationBoxContent) + +| Field | Type | Access | +| ----- | ---- | ------ | +| behavior | [enum BehaviorId](constants.md#enum-BehaviorId) | | +| firstByte | `integer` | | +| id | `integer` | | +| model | [enum ModelExtendedId](constants.md#enum-ModelExtendedId) | | +| unused | `integer` | | + +[:arrow_up_small:](#) + +
+ ## [FirstPersonCamera](#FirstPersonCamera) | Field | Type | Access | diff --git a/src/game/behavior_actions.c b/src/game/behavior_actions.c index f2d4f60c8..1a9d34b4b 100644 --- a/src/game/behavior_actions.c +++ b/src/game/behavior_actions.c @@ -45,9 +45,9 @@ #include "spawn_sound.h" #include "game/rng_position.h" #include "rumble_init.h" -#include "hardcoded.h" #include "pc/lua/utils/smlua_model_utils.h" #include "pc/lua/smlua_hooks.h" +#include "hardcoded.h" #define o gCurrentObject @@ -74,14 +74,6 @@ struct Struct8032F698 { s16 unk4; }; -struct Struct802C0DF0 { - u8 unk0; - u8 unk1; - u8 unk2; - u8 model; - const BehaviorScript *behavior; -}; - struct Struct8032F754 { s32 unk0; Vec3f unk1; diff --git a/src/game/behaviors/exclamation_box.inc.c b/src/game/behaviors/exclamation_box.inc.c index 96d939f08..e206eeb0e 100644 --- a/src/game/behaviors/exclamation_box.inc.c +++ b/src/game/behaviors/exclamation_box.inc.c @@ -12,24 +12,6 @@ struct ObjectHitbox sExclamationBoxHitbox = { /* hurtboxHeight: */ 30, }; -// hack: if any other sync objects get added here we have to check for them (search for hack in this file) -struct Struct802C0DF0 sExclamationBoxContents[] = { { 0, 0, 0, MODEL_MARIOS_WING_CAP, bhvWingCap }, - { 1, 0, 0, MODEL_MARIOS_METAL_CAP, bhvMetalCap }, - { 2, 0, 0, MODEL_MARIOS_CAP, bhvVanishCap }, - { 3, 0, 0, MODEL_KOOPA_SHELL, bhvKoopaShell }, - { 4, 0, 0, MODEL_YELLOW_COIN, bhvSingleCoinGetsSpawned }, - { 5, 0, 0, MODEL_NONE, bhvThreeCoinsSpawn }, - { 6, 0, 0, MODEL_NONE, bhvTenCoinsSpawn }, - { 7, 0, 0, MODEL_1UP, bhv1upWalking }, - { 8, 0, 0, MODEL_STAR, bhvSpawnedStar }, - { 9, 0, 0, MODEL_1UP, bhv1upRunningAway }, - { 10, 0, 1, MODEL_STAR, bhvSpawnedStar }, - { 11, 0, 2, MODEL_STAR, bhvSpawnedStar }, - { 12, 0, 3, MODEL_STAR, bhvSpawnedStar }, - { 13, 0, 4, MODEL_STAR, bhvSpawnedStar }, - { 14, 0, 5, MODEL_STAR, bhvSpawnedStar }, - { 99, 0, 0, 0, NULL } }; - void bhv_rotating_exclamation_box_loop(void) { if (!o->parentObj || o->parentObj->oAction != 1) obj_mark_for_deletion(o); @@ -122,7 +104,8 @@ static s32 exclamation_replace_model(struct MarioState* m, s32 model) { } } -void exclamation_box_spawn_contents(struct Struct802C0DF0 *a0, u8 a1) { +void exclamation_box_spawn_contents(struct ExclamationBoxContent *content, u8 itemId) { + if (content == NULL) { return; } struct MarioState* marioState = nearest_mario_state_to_object(o); struct Object* player = marioState ? marioState->marioObj : NULL; struct Object *spawnedObject = NULL; @@ -131,11 +114,11 @@ void exclamation_box_spawn_contents(struct Struct802C0DF0 *a0, u8 a1) { return; } - while (a0->unk0 != 99) { - if (a1 == a0->unk0) { - s32 model = exclamation_replace_model(marioState, a0->model); + for (u8 i = 0; i < gExclamationBoxSize; i++) { + if (itemId == content->id) { + s32 model = exclamation_replace_model(marioState, smlua_model_util_load(content->model)); - spawnedObject = spawn_object(o, model, a0->behavior); + spawnedObject = spawn_object(o, model, get_behavior_from_id(content->behavior)); if (spawnedObject != NULL) { spawnedObject->oVelY = 20.0f; spawnedObject->oForwardVel = 3.0f; @@ -144,15 +127,15 @@ void exclamation_box_spawn_contents(struct Struct802C0DF0 *a0, u8 a1) { spawnedObject->globalPlayerIndex = player->globalPlayerIndex; } } - o->oBehParams |= a0->unk2 << 24; - if (a0->model == 122) - o->oFlags |= 0x4000; + o->oBehParams |= content->firstByte << 24; + if (content->model == E_MODEL_STAR) + o->oFlags |= OBJ_FLAG_PERSISTENT_RESPAWN; // send non-star spawn events // stars cant be sent here to due jankiness in oBehParams - if (a0->behavior != smlua_override_behavior(bhvSpawnedStar) && spawnedObject != NULL) { + if (content->behavior != get_id_from_behavior(smlua_override_behavior(bhvSpawnedStar)) && spawnedObject != NULL) { // hack: if any other sync objects get spawned here we have to check for them - if (a0->behavior == smlua_override_behavior(bhvKoopaShell)) { + if (content->behavior == get_id_from_behavior(smlua_override_behavior(bhvKoopaShell))) { sync_object_set_id(spawnedObject); } struct Object* spawn_objects[] = { spawnedObject }; @@ -161,12 +144,12 @@ void exclamation_box_spawn_contents(struct Struct802C0DF0 *a0, u8 a1) { } break; } - a0++; + content++; } } void exclamation_box_act_4(void) { - exclamation_box_spawn_contents(sExclamationBoxContents, o->oBehParams2ndByte); + exclamation_box_spawn_contents(gExclamationBoxContents, o->oBehParams2ndByte); spawn_mist_particles_variable(0, 0, 46.0f); spawn_triangle_break_particles(20, 139, 0.3f, o->oAnimState); create_sound_spawner(SOUND_GENERAL_BREAK_BOX); diff --git a/src/game/hardcoded.c b/src/game/hardcoded.c index c6e2d707c..b07c56be2 100644 --- a/src/game/hardcoded.c +++ b/src/game/hardcoded.c @@ -287,6 +287,32 @@ struct BehaviorValues gDefaultBehaviorValues = { struct BehaviorValues gBehaviorValues = { 0 }; +struct ExclamationBoxContent sDefaultExclamationBoxContents[] = { + { 0, 0, 0, E_MODEL_MARIOS_WING_CAP, id_bhvWingCap }, + { 1, 0, 0, E_MODEL_MARIOS_METAL_CAP, id_bhvMetalCap }, + { 2, 0, 0, E_MODEL_MARIOS_CAP, id_bhvVanishCap }, + { 3, 0, 0, E_MODEL_KOOPA_SHELL, id_bhvKoopaShell }, + { 4, 0, 0, E_MODEL_YELLOW_COIN, id_bhvSingleCoinGetsSpawned }, + { 5, 0, 0, E_MODEL_NONE, id_bhvThreeCoinsSpawn }, + { 6, 0, 0, E_MODEL_NONE, id_bhvTenCoinsSpawn }, + { 7, 0, 0, E_MODEL_1UP, id_bhv1upWalking }, + { 8, 0, 0, E_MODEL_STAR, id_bhvSpawnedStar }, + { 9, 0, 0, E_MODEL_1UP, id_bhv1upRunningAway }, + { 10, 0, 1, E_MODEL_STAR, id_bhvSpawnedStar }, + { 11, 0, 2, E_MODEL_STAR, id_bhvSpawnedStar }, + { 12, 0, 3, E_MODEL_STAR, id_bhvSpawnedStar }, + { 13, 0, 4, E_MODEL_STAR, id_bhvSpawnedStar }, + { 14, 0, 5, E_MODEL_STAR, id_bhvSpawnedStar } +}; + +// Hack: Create 2 arrays: one that is constantly default and one that can be changed. + +struct ExclamationBoxContent sDummyContents[EXCLAMATION_BOX_MAX_SIZE]; + +struct ExclamationBoxContent* gExclamationBoxContents = sDummyContents; + +u8 gExclamationBoxSize = 15; + ////////////// // Painting // ////////////// @@ -336,6 +362,10 @@ AT_STARTUP void hardcoded_reset_default_values(void) { memcpy(&sl_painting, &default_sl_painting, sizeof(struct Painting)); memcpy(&thi_huge_painting, &default_thi_huge_painting, sizeof(struct Painting)); memcpy(&ttm_slide_painting, &default_ttm_slide_painting, sizeof(struct Painting)); + memcpy(sDummyContents, sDefaultExclamationBoxContents, sizeof(struct ExclamationBoxContent) * 15); + + gExclamationBoxContents = sDummyContents; + gExclamationBoxSize = 15; gPaintingValues = gDefaultPaintingValues; } diff --git a/src/game/hardcoded.h b/src/game/hardcoded.h index 98aec62e2..54de71a01 100644 --- a/src/game/hardcoded.h +++ b/src/game/hardcoded.h @@ -5,6 +5,8 @@ #include "dialog_ids.h" #include "seq_ids.h" #include "paintings.h" +#include "pc/lua/utils/smlua_model_utils.h" +#include "include/behavior_table.h" //////////// // Levels // @@ -266,6 +268,19 @@ struct BehaviorValues { extern struct BehaviorValues gBehaviorValues; +#define EXCLAMATION_BOX_MAX_SIZE 99 + +struct ExclamationBoxContent { + u8 id; + u8 unused; + u8 firstByte; + enum ModelExtendedId model; + enum BehaviorId behavior; +}; + +extern struct ExclamationBoxContent* gExclamationBoxContents; +extern u8 gExclamationBoxSize; + ////////////// // Painting // ////////////// diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index b2ab63b8c..6ad328248 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -677,6 +677,15 @@ static struct LuaObjectField sDjuiColorFields[LUA_DJUI_COLOR_FIELD_COUNT] = { { "r", LVT_U8, offsetof(struct DjuiColor, r), false, LOT_NONE }, }; +#define LUA_EXCLAMATION_BOX_CONTENT_FIELD_COUNT 5 +static struct LuaObjectField sExclamationBoxContentFields[LUA_EXCLAMATION_BOX_CONTENT_FIELD_COUNT] = { + { "behavior", LVT_S32, offsetof(struct ExclamationBoxContent, behavior), false, LOT_NONE }, + { "firstByte", LVT_U8, offsetof(struct ExclamationBoxContent, firstByte), false, LOT_NONE }, + { "id", LVT_U8, offsetof(struct ExclamationBoxContent, id), false, LOT_NONE }, + { "model", LVT_S32, offsetof(struct ExclamationBoxContent, model), false, LOT_NONE }, + { "unused", LVT_U8, offsetof(struct ExclamationBoxContent, unused), false, LOT_NONE }, +}; + #define LUA_FIRST_PERSON_CAMERA_FIELD_COUNT 10 static struct LuaObjectField sFirstPersonCameraFields[LUA_FIRST_PERSON_CAMERA_FIELD_COUNT] = { { "centerL", LVT_BOOL, offsetof(struct FirstPersonCamera, centerL), false, LOT_NONE }, @@ -2460,6 +2469,7 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN] { LOT_CUTSCENEVARIABLE, sCutsceneVariableFields, LUA_CUTSCENE_VARIABLE_FIELD_COUNT }, { LOT_DATETIME, sDateTimeFields, LUA_DATE_TIME_FIELD_COUNT }, { LOT_DJUICOLOR, sDjuiColorFields, LUA_DJUI_COLOR_FIELD_COUNT }, + { LOT_EXCLAMATIONBOXCONTENT, sExclamationBoxContentFields, LUA_EXCLAMATION_BOX_CONTENT_FIELD_COUNT }, { LOT_FIRSTPERSONCAMERA, sFirstPersonCameraFields, LUA_FIRST_PERSON_CAMERA_FIELD_COUNT }, { LOT_FLOORGEOMETRY, sFloorGeometryFields, LUA_FLOOR_GEOMETRY_FIELD_COUNT }, { LOT_GLOBALOBJECTANIMATIONS, sGlobalObjectAnimationsFields, LUA_GLOBAL_OBJECT_ANIMATIONS_FIELD_COUNT }, diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h index 0ca897f14..f08674bfa 100644 --- a/src/pc/lua/smlua_cobject_autogen.h +++ b/src/pc/lua/smlua_cobject_autogen.h @@ -27,6 +27,7 @@ enum LuaObjectAutogenType { LOT_CUTSCENEVARIABLE, LOT_DATETIME, LOT_DJUICOLOR, + LOT_EXCLAMATIONBOXCONTENT, LOT_FIRSTPERSONCAMERA, LOT_FLOORGEOMETRY, LOT_GLOBALOBJECTANIMATIONS, diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c index b722976b1..dc3ea3b9b 100644 --- a/src/pc/lua/smlua_functions.c +++ b/src/pc/lua/smlua_functions.c @@ -15,6 +15,7 @@ #include "include/macro_presets.h" #include "utils/smlua_anim_utils.h" #include "utils/smlua_collision_utils.h" +#include "game/hardcoded.h" bool smlua_functions_valid_param_count(lua_State* L, int expected) { int top = lua_gettop(L); @@ -179,6 +180,110 @@ int smlua_func_network_send_to(lua_State* L) { return 1; } +int smlua_func_set_exclamation_box_contents(lua_State* L) { + if (!smlua_functions_valid_param_count(L, 1)) { return 0; } + + if (lua_type(L, 1) != LUA_TTABLE) { + LOG_LUA_LINE("Invalid type passed to set_exclamation_box(): %u", lua_type(L, -1)); + return 0; + } + + struct ExclamationBoxContent exclamationBoxNewContents[EXCLAMATION_BOX_MAX_SIZE]; + + u8 exclamationBoxIndex = 0; + lua_pushnil(L); // Initial pop + while (lua_next(L, 1)) /* Main table index */ { + if (lua_type(L, 3) != LUA_TTABLE) { + LOG_LUA_LINE("set_exclamation_box: Subtable is not a table (Subtable %u)", exclamationBoxIndex); + return 0; + } + + lua_pushnil(L); // Subtable initial pop + bool confirm[] = { false, false, false, false, false }; /* id, unused, firstByte, model, behavior */ + while (lua_next(L, 3)) /* Subtable index */ { + // key is index -2, value is index -1 + const char* key = smlua_to_string(L, -2); + if (!gSmLuaConvertSuccess) { + LOG_LUA("set_exclamation_box: Failed to convert subtable key"); + return 0; + } + + s32 value = smlua_to_integer(L, -1); + if (!gSmLuaConvertSuccess) { + LOG_LUA("set_exclamation_box: Failed to convert subtable value"); + return 0; + } + + // Fill fields + if (strcmp(key, "id") == 0) { exclamationBoxNewContents[exclamationBoxIndex].id = value; confirm[0] = true; } + else if (strcmp(key, "unused") == 0) { exclamationBoxNewContents[exclamationBoxIndex].unused = value; confirm[1] = true; } + else if (strcmp(key, "firstByte") == 0) { exclamationBoxNewContents[exclamationBoxIndex].firstByte = value; confirm[2] = true; } + else if (strcmp(key, "model") == 0) { exclamationBoxNewContents[exclamationBoxIndex].model = value; confirm[3] = true; } + else if (strcmp(key, "behavior") == 0) { exclamationBoxNewContents[exclamationBoxIndex].behavior = value; confirm[4] = true; } + else { + LOG_LUA_LINE_WARNING("set_exclamation_box: Invalid key passed (Subtable %d)", exclamationBoxIndex); + } + + lua_pop(L, 1); // Pop value + } + // Check if the fields have been filled + if (!(confirm[0]) || !(confirm[3]) || !(confirm[4])) { + LOG_LUA("set_exclamation_box: A critical component of a content (id, model, or behavior) has not been set (Subtable %d)", exclamationBoxIndex); + return 0; + } + if (!(confirm[1])) { exclamationBoxNewContents[exclamationBoxIndex].unused = 0; } + if (!(confirm[2])) { exclamationBoxNewContents[exclamationBoxIndex].firstByte = 0; } + + if (++exclamationBoxIndex == EXCLAMATION_BOX_MAX_SIZE) { // There is an edge case where the 254th element will warn even though it works just fine + // Immediately exit if at risk for out of bounds array access. + lua_pop(L, 1); + LOG_LUA_LINE_WARNING("set_exclamation_box: Too many items have been set for the exclamation box. Some content spawns may be lost."); + break; + } + lua_pop(L, 1); // Pop subtable + } + + memcpy(gExclamationBoxContents, exclamationBoxNewContents, sizeof(struct ExclamationBoxContent) * exclamationBoxIndex); + gExclamationBoxSize = exclamationBoxIndex; + + return 1; +} + +int smlua_func_get_exclamation_box_contents(lua_State* L) { + if (!smlua_functions_valid_param_count(L, 0)) { return 0; } + + lua_newtable(L); // Index 1 + + for (u8 i = 0; i < gExclamationBoxSize; i++) { + lua_pushinteger(L, i); // Index 2 + lua_newtable(L); // Index 3 + + lua_pushstring(L, "id"); + lua_pushinteger(L, gExclamationBoxContents[i].id); + lua_settable(L, -3); + + lua_pushstring(L, "unused"); + lua_pushinteger(L, gExclamationBoxContents[i].unused); + lua_settable(L, -3); + + lua_pushstring(L, "firstByte"); + lua_pushinteger(L, gExclamationBoxContents[i].firstByte); + lua_settable(L, -3); + + lua_pushstring(L, "model"); + lua_pushinteger(L, gExclamationBoxContents[i].model); + lua_settable(L, -3); + + lua_pushstring(L, "behavior"); + lua_pushinteger(L, gExclamationBoxContents[i].behavior); + lua_settable(L, -3); + + lua_settable(L, 1); // Insert the subtable into the main table + } + + return 1; +} + ////////////// // Textures // ////////////// @@ -855,6 +960,8 @@ void smlua_bind_functions(void) { smlua_bind_function(L, "reset_level", smlua_func_reset_level); smlua_bind_function(L, "network_send", smlua_func_network_send); smlua_bind_function(L, "network_send_to", smlua_func_network_send_to); + smlua_bind_function(L, "set_exclamation_box_contents", smlua_func_set_exclamation_box_contents); + smlua_bind_function(L, "get_exclamation_box_contents", smlua_func_get_exclamation_box_contents); smlua_bind_function(L, "get_texture_info", smlua_func_get_texture_info); smlua_bind_function(L, "djui_hud_render_texture", smlua_func_djui_hud_render_texture); smlua_bind_function(L, "djui_hud_render_texture_tile", smlua_func_djui_hud_render_texture_tile);