From 318807d3c276a73b967b30dd038a18947d9d8df3 Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Mon, 10 Apr 2023 12:25:26 +0200 Subject: [PATCH 01/32] level_script_parse description (#340) enhanced level_script_parse description --- autogen/lua_definitions/manual.lua | 6 +++- mods/sm74/dialog.lua | 45 +++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua index 0ecdb6de..bab2a3eb 100644 --- a/autogen/lua_definitions/manual.lua +++ b/autogen/lua_definitions/manual.lua @@ -222,8 +222,12 @@ function djui_hud_render_texture_tile_interpolated(texInfo, prevX, prevY, prevSc end --- @param levelNum number ---- @param func fun(areaNum:number, bhv:table) +--- @param func fun(areaIndex:number, bhvData:table, macroBhvIds:table, macroBhvArgs:table) --- @return nil +--- When `func` is called, arguments are filled depending on the level command: +--- - `AREA` command: only `areaIndex` is filled. It's a number. +--- - `OBJECT` command: only `bhvData` is filled. `bhvData` is a table with two fields: `behavior` and `behaviorArg`. +--- - `MACRO` command: only `macroBhvIds` and `macroBhvArgs` are filled. `macrobhvIds` is a list of behavior ids. `macroBhvArgs` is a list of behavior params. Both lists have the same size and start at index 0. function level_script_parse(levelNum, func) -- ... end diff --git a/mods/sm74/dialog.lua b/mods/sm74/dialog.lua index cabb1ad9..1f1e58d9 100644 --- a/mods/sm74/dialog.lua +++ b/mods/sm74/dialog.lua @@ -2195,27 +2195,46 @@ possibility.\ Good luck and\ frustration resistance.") +-- smlua_text_utils_dialog_replace(DIALOG_063,1,5,30,200, "It's really a shame but\ +-- this level was way too\ +-- big for the level importer\ +-- so I had to split it\ +-- into two parts.\ +-- That means I CAN'T place\ +-- red coins in the whole\ +-- level. Sad, isn't it?\ +-- I know that you would\ +-- enjoy collecting 30\ +-- red coins, right?\ +-- Well then, instead,\ +-- the red coins are all\ +-- beyond the cannon.\ +-- And since they are so\ +-- close together\ +-- I won't tell you how\ +-- many there are.\ +-- \ +-- (Well, honestly, I forgot\ +-- how many I placed.)") + smlua_text_utils_dialog_replace(DIALOG_063,1,5,30,200, "It's really a shame but\ this level was way too\ big for the level importer\ so I had to split it\ into two parts.\ -That means I CAN'T place\ +Nah! Just kidding!\ +You're no longer playing\ +a Nintendo 64 rom-hack.\ +It's the pc port we're\ +talking about!\ +That means I CAN place\ red coins in the whole\ -level. Sad, isn't it?\ +level. Great, isn't it?\ I know that you would\ enjoy collecting 30\ -red coins, right?\ -Well then, instead,\ -the red coins are all\ -beyond the cannon.\ -And since they are so\ -close together\ -I won't tell you how\ -many there are.\ -\ -(Well, honestly, I forgot\ -how many I placed.)") +red coins. So, after 9\ +years, it's finally\ +possible. Have fun!") smlua_text_utils_dialog_replace(DIALOG_064,1,5,30,200, "You should already know\ the concept of this\ From 265a3c6b03d0790220a71aa23559c043a68ccf5e Mon Sep 17 00:00:00 2001 From: Agent X <44549182+Agent-11@users.noreply.github.com> Date: Mon, 10 Apr 2023 10:31:45 -0700 Subject: [PATCH 02/32] Hopefully fix 3 crashes (#341) * Hopefully fix 3 crashes --- src/game/area.c | 4 ++++ src/pc/network/packets/packet_download.c | 5 +++++ src/pc/network/packets/packet_mod_list.c | 5 ++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/game/area.c b/src/game/area.c index f66dc314..3401634b 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -161,6 +161,10 @@ u32 get_mario_spawn_type(struct Object *o) { } struct ObjectWarpNode *area_get_warp_node(u8 id) { + if (gCurrentArea == NULL || gCurrentArea->warpNodes == NULL) { + return NULL; + } + struct ObjectWarpNode *node = NULL; for (node = gCurrentArea->warpNodes; node != NULL; node = node->next) { diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c index e418c877..03cdef9f 100644 --- a/src/pc/network/packets/packet_download.c +++ b/src/pc/network/packets/packet_download.c @@ -332,6 +332,11 @@ static void open_mod_file(struct Mod* mod, struct ModFile* file) { } void network_receive_download(struct Packet* p) { + if (p == NULL) { + LOG_ERROR("Received null packet"); + return; + } + SOFT_ASSERT(gNetworkType == NT_CLIENT); if (p->localIndex != UNKNOWN_LOCAL_INDEX) { if (gNetworkPlayerServer == NULL || gNetworkPlayerServer->localIndex != p->localIndex) { diff --git a/src/pc/network/packets/packet_mod_list.c b/src/pc/network/packets/packet_mod_list.c index 92fe88b2..dfe32c5e 100644 --- a/src/pc/network/packets/packet_mod_list.c +++ b/src/pc/network/packets/packet_mod_list.c @@ -28,7 +28,10 @@ void network_send_mod_list_request(void) { } void network_receive_mod_list_request(UNUSED struct Packet* p) { - SOFT_ASSERT(gNetworkType == NT_SERVER); + if (gNetworkType != NT_SERVER) { + LOG_ERROR("Network type should be server!"); + return; + } LOG_INFO("received mod list request"); network_send_mod_list(); From 892e48d3da67d551f354131cebe2d2faf3bef016 Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:37:25 +0200 Subject: [PATCH 03/32] level_script_parse now calls the callback for objects with custom behaviors (#342) --- src/pc/lua/smlua_functions.c | 110 +++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c index 9f595df5..4d9f30a5 100644 --- a/src/pc/lua/smlua_functions.c +++ b/src/pc/lua/smlua_functions.c @@ -473,66 +473,104 @@ struct LuaLevelScriptParse { struct LuaLevelScriptParse sLevelScriptParse = { 0 }; s32 smlua_func_level_script_parse_callback(u8 type, void *cmd) { - if (type != 0x24 && type != 0x39 && type != 0x1F) { - return 0; + u32 areaIndex, bhvId, bhvArgs; + u32 *pAreaIndex = NULL, *pBhvId = NULL, *pBhvArgs = NULL; + MacroObject *pMacroData = NULL; + + // Gather arguments + switch (type) { + + // AREA + case 0x1F: { + areaIndex = (u8) dynos_level_cmd_get(cmd, 2); + pAreaIndex = &areaIndex; + } break; + + // OBJECT_WITH_ACTS + case 0x24: { + const BehaviorScript *bhv = (const BehaviorScript *) dynos_level_cmd_get(cmd, 20); + if (bhv) { + bhvId = (u32) get_id_from_behavior(bhv); + bhvArgs = (u32) dynos_level_cmd_get(cmd, 16); + pBhvId = &bhvId; + pBhvArgs = &bhvArgs; + } + } break; + + // OBJECT_WITH_ACTS_EXT + case 0x3F: { + const char *bhvStr = dynos_level_get_token(dynos_level_cmd_get(cmd, 20)); + if (bhvStr) { + bhvId = (u32) smlua_get_any_integer_mod_variable(bhvStr); + bhvArgs = (u32) dynos_level_cmd_get(cmd, 16); + pBhvId = &bhvId; + pBhvArgs = &bhvArgs; + } + } break; + + // OBJECT_WITH_ACTS_EXT2 + case 0x40: { + const char *bhvStr = dynos_level_get_token(dynos_level_cmd_get(cmd, 24)); + if (bhvStr) { + bhvId = (u32) smlua_get_any_integer_mod_variable(bhvStr); + bhvArgs = (u32) dynos_level_cmd_get(cmd, 16); + pBhvId = &bhvId; + pBhvArgs = &bhvArgs; + } + } break; + + // MACRO_OBJECTS + case 0x39: { + pMacroData = (MacroObject *) dynos_level_cmd_get(cmd, 4); + } break; + + // None of the above + default: return 0; } + + // Retrieve Lua state lua_State* L = gLuaState; if (L == NULL) { return 0; } struct LuaLevelScriptParse* preprocess = &sLevelScriptParse; - lua_rawgeti(L, LUA_REGISTRYINDEX, preprocess->reference); - if (type == 0x1F) { - u8 area = (u8) dynos_level_cmd_get(cmd, 2); - lua_pushinteger(L, area); + // Push 'areaIndex' + if (pAreaIndex) { + lua_pushinteger(L, *pAreaIndex); } else { lua_pushnil(L); } - if (type == 0x24) { - const BehaviorScript *bhv = (const BehaviorScript *) dynos_level_cmd_get(cmd, 20); - u32 behaviorArg = (u32) dynos_level_cmd_get(cmd, 16); - + // Push 'bhvData' + if (pBhvId && pBhvArgs) { lua_newtable(L); - lua_pushstring(L, "behavior"); - lua_pushinteger(L, get_id_from_behavior(bhv)); + lua_pushinteger(L, *pBhvId); lua_settable(L, -3); - lua_pushstring(L, "behaviorArg"); - lua_pushinteger(L, behaviorArg); + lua_pushinteger(L, *pBhvArgs); lua_settable(L, -3); } else { lua_pushnil(L); } - if (type == 0x39) { - MacroObject *data = (MacroObject *) dynos_level_cmd_get(cmd, 4); - int i = 0; - s32 len = 0; - + // Push 'macroBhvIds' and 'macroBhvArgs' + if (pMacroData) { lua_newtable(L); - int t = lua_gettop(gLuaState); - + s32 macroBhvIdsIdx = lua_gettop(gLuaState); lua_newtable(L); - int args = lua_gettop(gLuaState); - while (data[len++] != MACRO_OBJECT_END()) { - s32 presetId = (s32) ((data[len - 1] & 0x1FF) - 0x1F); - const BehaviorScript *bhv = (const BehaviorScript *) MacroObjectPresets[presetId].behavior; + s32 macroBhvArgsIdx = lua_gettop(gLuaState); + for (s32 i = 0; *pMacroData != MACRO_OBJECT_END(); pMacroData += 5, i++) { + s32 presetId = (s32) ((pMacroData[0] & 0x1FF) - 0x1F); s32 presetParams = MacroObjectPresets[presetId].param; - s32 objParams = (data[4] & 0xFF00) + (presetParams & 0x00FF); - u32 behaviorArg = ((objParams & 0x00FF) << 16) + (objParams & 0xFF00); - + s32 objParams = (pMacroData[4] & 0xFF00) | (presetParams & 0x00FF); + s32 bhvParams = ((objParams & 0x00FF) << 16) | (objParams & 0xFF00); lua_pushinteger(L, i); - lua_pushinteger(L, get_id_from_behavior(bhv)); - lua_settable(L, t); - + lua_pushinteger(L, get_id_from_behavior(MacroObjectPresets[presetId].behavior)); + lua_settable(L, macroBhvIdsIdx); lua_pushinteger(L, i); - lua_pushinteger(L, behaviorArg); - lua_settable(L, args); - - i++; - len += 4; + lua_pushinteger(L, bhvParams); + lua_settable(L, macroBhvArgsIdx); } } else { lua_pushnil(L); From c1c12feb8f809353882de4d102140168749318e5 Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 14:59:29 -0700 Subject: [PATCH 04/32] Cleanup zip file in more failure scenarios --- src/pc/mods/mod_import.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pc/mods/mod_import.c b/src/pc/mods/mod_import.c index ce2ad599..2831d4c2 100644 --- a/src/pc/mods/mod_import.c +++ b/src/pc/mods/mod_import.c @@ -99,6 +99,7 @@ static bool mod_import_zip(char* path, bool* isLua, bool* isDynos) { snprintf(dstDirectory, SYS_MAX_PATH, "%s", (char*)fs_get_write_path(DYNOS_PACKS_FOLDER)); } else { LOG_ERROR("Could not figure out what type of mod this is"); + mz_zip_reader_end(&zip_archive); return false; } if (!fs_sys_dir_exists(dstDirectory)) { @@ -159,6 +160,8 @@ static bool mod_import_zip(char* path, bool* isLua, bool* isDynos) { FILE* fout = fopen(dst, "wb"); if (fout == NULL) { LOG_ERROR("Failed to open dst path for zip mod import"); + mz_free((void*)p); + mz_zip_reader_end(&zip_archive); return false; } From 69703ac29b6d290020a9346193d09f199a3e0bc2 Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 15:28:47 -0700 Subject: [PATCH 05/32] Erase and recreate lua directories on import --- src/pc/mods/mod_import.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/pc/mods/mod_import.c b/src/pc/mods/mod_import.c index 2831d4c2..e57097da 100644 --- a/src/pc/mods/mod_import.c +++ b/src/pc/mods/mod_import.c @@ -56,6 +56,7 @@ static bool mod_import_lua(char* src) { static bool mod_import_zip(char* path, bool* isLua, bool* isDynos) { LOG_INFO("Importing zip mod: %s", path); + char luaPath[SYS_MAX_PATH] = { 0 }; mz_zip_archive zip_archive = { 0 }; mz_bool status = mz_zip_reader_init_file(&zip_archive, path, 0); if (!status) { @@ -75,6 +76,7 @@ static bool mod_import_zip(char* path, bool* isLua, bool* isDynos) { } if (str_ends_with(file_stat.m_filename, ".lua")) { + path_get_folder(file_stat.m_filename, luaPath); *isLua = true; break; } else if (str_ends_with(file_stat.m_filename, ".tex")) { @@ -102,10 +104,29 @@ static bool mod_import_zip(char* path, bool* isLua, bool* isDynos) { mz_zip_reader_end(&zip_archive); return false; } + + // create mod/dynos path if it doesn't exist if (!fs_sys_dir_exists(dstDirectory)) { fs_sys_mkdir(dstDirectory); } + // erase and create lua path + if (*isLua && strlen(luaPath) > 0) { + if (!concat_path(dst, dstDirectory, luaPath)) { + LOG_ERROR("Failed to concat path for base lua directory"); + mz_zip_reader_end(&zip_archive); + return false; + } + if (fs_sys_dir_exists(dst)) { + mods_delete_folder(dst); + } + if (!fs_sys_mkdir(dst)) { + LOG_ERROR("Failed to mkdir for base lua directory"); + mz_zip_reader_end(&zip_archive); + return false; + } + } + // Extract the archive for (int i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++) { mz_zip_archive_file_stat file_stat; From 3fd8d74dc1ac502fa297fe454785470eeb36ccdb Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 17:41:51 -0700 Subject: [PATCH 06/32] Fixed crash in cur_obj_follow_path --- src/game/object_helpers.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 3cc94e64..49084d1d 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -2282,6 +2282,8 @@ s32 cur_obj_follow_path(UNUSED s32 unusedArg) { f32 objToNextXZ; f32 objToNextX, objToNextY, objToNextZ; + if (o == NULL) { return PATH_NONE; } + if (o->oPathedPrevWaypointFlags == 0) { o->oPathedPrevWaypoint = o->oPathedStartWaypoint; o->oPathedPrevWaypointFlags = WAYPOINT_FLAGS_INITIALIZED; @@ -2295,12 +2297,15 @@ s32 cur_obj_follow_path(UNUSED s32 unusedArg) { struct Waypoint* tmpWaypoint = (lastWaypoint + 1); if (tmpWaypoint == NULL) { tmpWaypoint = lastWaypoint; } - if (tmpWaypoint->flags != WAYPOINT_FLAGS_END) { + if (tmpWaypoint && tmpWaypoint->flags != WAYPOINT_FLAGS_END) { targetWaypoint = tmpWaypoint; } else { targetWaypoint = startWaypoint; } + if (lastWaypoint == NULL) { return PATH_NONE; } + if (targetWaypoint == NULL) { return PATH_NONE; } + o->oPathedPrevWaypointFlags = lastWaypoint->flags | WAYPOINT_FLAGS_INITIALIZED; prevToNextX = targetWaypoint->pos[0] - lastWaypoint->pos[0]; @@ -2320,7 +2325,7 @@ s32 cur_obj_follow_path(UNUSED s32 unusedArg) { o->oPathedPrevWaypoint = targetWaypoint; struct Waypoint* tmpWaypoint2 = (targetWaypoint + 1); if (tmpWaypoint2 == NULL) { tmpWaypoint2 = targetWaypoint; } - if (tmpWaypoint2->flags == WAYPOINT_FLAGS_END) { + if (tmpWaypoint2 && tmpWaypoint2->flags == WAYPOINT_FLAGS_END) { return PATH_REACHED_END; } else { return PATH_REACHED_WAYPOINT; From 6f7b7e65caa7e9c91344c5b1099c642e5f1cbaac Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 17:45:43 -0700 Subject: [PATCH 07/32] Possible crash fix for network_receive_download() --- src/pc/network/packets/packet_download.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c index 03cdef9f..81bdffe2 100644 --- a/src/pc/network/packets/packet_download.c +++ b/src/pc/network/packets/packet_download.c @@ -348,9 +348,13 @@ void network_receive_download(struct Packet* p) { // read the chunk u64 receiveOffset = 0; u64 chunkLength = 0; - u8 chunk[CHUNK_SIZE] = { 0 }; + u8 chunk[CHUNK_SIZE+1] = { 0 }; packet_read(p, &receiveOffset, sizeof(u64)); packet_read(p, &chunkLength, sizeof(u64)); + if (chunkLength > CHUNK_SIZE) { + LOG_ERROR("Received improper chunk length"); + return; + } packet_read(p, &chunk, sizeof(u8) * chunkLength); // mark the offset group as received @@ -384,6 +388,10 @@ after_group:; u64 fileStartOffset = 0; for (u64 modIndex = 0; modIndex < gRemoteMods.entryCount; modIndex++) { struct Mod* mod = gRemoteMods.entries[modIndex]; + if (!mod) { + LOG_ERROR("Null mod"); + continue; + } // skip past mods to get to the right offset if ((fileStartOffset + mod->size) < receiveOffset) { @@ -391,6 +399,11 @@ after_group:; continue; } + if (mod->fileCount > 0 && !mod->files) { + LOG_ERROR("Null mod files"); + continue; + } + for (u64 fileIndex = 0; fileIndex < mod->fileCount; fileIndex++) { struct ModFile* modFile = &mod->files[fileIndex]; From 2d01367cf6d55bac4fabdd62545b75202e62412b Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 17:46:45 -0700 Subject: [PATCH 08/32] Possible crash fix for osPiStartDma() --- src/pc/ultra_reimplementation.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pc/ultra_reimplementation.c b/src/pc/ultra_reimplementation.c index 85651ef7..6cd99cc1 100644 --- a/src/pc/ultra_reimplementation.c +++ b/src/pc/ultra_reimplementation.c @@ -14,6 +14,7 @@ u64 osClockRate = 62500000; s32 osPiStartDma(UNUSED OSIoMesg *mb, UNUSED s32 priority, UNUSED s32 direction, uintptr_t devAddr, void *vAddr, size_t nbytes, UNUSED OSMesgQueue *mq) { + if (!vAddr || !devAddr) { return 0; } memcpy(vAddr, (const void *) devAddr, nbytes); return 0; } From 03c172fa9e6bdafefdfc0932f0358e9c1dc3cd6c Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 17:48:56 -0700 Subject: [PATCH 09/32] Crash fix for discord_activity_update() --- src/pc/network/discord/activity.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pc/network/discord/activity.c b/src/pc/network/discord/activity.c index 2806ec94..e787927c 100644 --- a/src/pc/network/discord/activity.c +++ b/src/pc/network/discord/activity.c @@ -132,6 +132,16 @@ void discord_activity_update(bool hosting) { LOGFILE_INFO(LFT_DISCORD, "truncating details"); } + if (!app.activities) { + LOGFILE_INFO(LFT_DISCORD, "no activities"); + return; + } + + if (!app.activities->update_activity) { + LOGFILE_INFO(LFT_DISCORD, "no update_activity"); + return; + } + app.activities->update_activity(app.activities, &gCurActivity, NULL, on_activity_update_callback); LOGFILE_INFO(LFT_DISCORD, "set activity"); } From 9ffd99473d03bff78338c1b0a4548f1a3141aabe Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 17:50:57 -0700 Subject: [PATCH 10/32] Fix crash in is_anim_past_frame() --- src/game/mario.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/game/mario.c b/src/game/mario.c index abe156b0..d1cdb75e 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -176,7 +176,11 @@ void set_anim_to_frame(struct MarioState *m, s16 animFrame) { s32 is_anim_past_frame(struct MarioState *m, s16 animFrame) { s32 isPastFrame; s32 acceleratedFrame = animFrame << 0x10; + + if (!m || !m->marioObj) { return TRUE; } struct AnimInfo *animInfo = &m->marioObj->header.gfx.animInfo; + + if (!animInfo->curAnim) { return TRUE; } struct Animation *curAnim = animInfo->curAnim; if (animInfo->animAccel) { From 41b226f9a77e9dc8130f29eb4c0d535a9a6607e5 Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 17:53:28 -0700 Subject: [PATCH 11/32] Fixed crash in mod_clear() --- src/pc/mods/mod.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c index 0ac0c44a..1ef68260 100644 --- a/src/pc/mods/mod.c +++ b/src/pc/mods/mod.c @@ -152,15 +152,19 @@ void mod_activate(struct Mod* mod) { } void mod_clear(struct Mod* mod) { - for (int j = 0; j < mod->fileCount; j++) { - struct ModFile* file = &mod->files[j]; - if (file->fp != NULL) { - fclose(file->fp); - file->fp = NULL; - } - if (file->cachedPath != NULL) { - free((char*)file->cachedPath); - file->cachedPath = NULL; + if (!mod) { return; } + + if (mod->files) { + for (int j = 0; j < mod->fileCount; j++) { + struct ModFile* file = &mod->files[j]; + if (file->fp != NULL) { + fclose(file->fp); + file->fp = NULL; + } + if (file->cachedPath != NULL) { + free((char*)file->cachedPath); + file->cachedPath = NULL; + } } } From 9a49ff95c7fd26a1441ebcb8e07e21c02d586217 Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 19:03:34 -0700 Subject: [PATCH 12/32] Fixed graphical corruption when text renders offscreen --- src/pc/djui/djui.c | 2 ++ src/pc/djui/djui_gbi.h | 6 +++++ src/pc/djui/djui_gfx.c | 19 ++++++++++++++++ src/pc/djui/djui_gfx.h | 3 +++ src/pc/gfx/gfx_pc.c | 50 +++++++++++++++++++++++------------------- 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c index 6c0aa995..34204393 100644 --- a/src/pc/djui/djui.c +++ b/src/pc/djui/djui.c @@ -98,6 +98,7 @@ void djui_render(void) { gDjuiHudUtilsZ = 0; create_dl_ortho_matrix(); + djui_gfx_displaylist_begin(); smlua_call_event_hooks(HOOK_ON_HUD_RENDER); @@ -119,4 +120,5 @@ void djui_render(void) { djui_cursor_update(); djui_interactable_update(); + djui_gfx_displaylist_end(); } diff --git a/src/pc/djui/djui_gbi.h b/src/pc/djui/djui_gbi.h index ef5c5900..45c6722d 100644 --- a/src/pc/djui/djui_gbi.h +++ b/src/pc/djui/djui_gbi.h @@ -4,8 +4,14 @@ #define G_TEXOVERRIDE_DJUI 0xe0 #define G_DJUI_SIMPLE_VERT 0x11 #define G_DJUI_SIMPLE_TRI2 0x12 +#define G_TEXADDR_DJUI 0x13 #define G_EXECUTE_DJUI 0xdd +#define gsSPTextureAddrDjui(c) \ +{{ \ + (_SHIFTL(G_TEXADDR_DJUI,24,8)|_SHIFTL(~(u32)(c),0,24)),(u32)(0) \ +}} + #define gSetClippingDjui(pkt, cmd, x1, y1, x2, y2) \ { \ Gfx *_g = (Gfx *)(pkt); \ diff --git a/src/pc/djui/djui_gfx.c b/src/pc/djui/djui_gfx.c index c01099b7..26a185f3 100644 --- a/src/pc/djui/djui_gfx.c +++ b/src/pc/djui/djui_gfx.c @@ -6,6 +6,25 @@ #include "src/pc/pc_main.h" #include "src/pc/gfx/gfx_window_manager_api.h" #include "gfx_dimensions.h" +#include "djui_gfx.h" + +const Gfx dl_djui_display_list_begin[] = { + gsSPTextureAddrDjui(1), + gsSPEndDisplayList(), +}; + +const Gfx dl_djui_display_list_end[] = { + gsSPTextureAddrDjui(0), + gsSPEndDisplayList(), +}; + +void djui_gfx_displaylist_begin(void) { + gSPDisplayList(gDisplayListHead++, dl_djui_display_list_begin); +} + +void djui_gfx_displaylist_end(void) { + gSPDisplayList(gDisplayListHead++, dl_djui_display_list_end); +} static const Vtx vertex_djui_simple_rect[] = { {{{ 0, -1, 0}, 0, { 0, 0 }, { 0xff, 0xff, 0xff, 0xff }}}, diff --git a/src/pc/djui/djui_gfx.h b/src/pc/djui/djui_gfx.h index 205508e9..8369bc15 100644 --- a/src/pc/djui/djui_gfx.h +++ b/src/pc/djui/djui_gfx.h @@ -9,6 +9,9 @@ extern const Gfx dl_djui_simple_rect[]; extern const Gfx dl_djui_img_begin[]; extern const Gfx dl_djui_img_end[]; +void djui_gfx_displaylist_begin(void); +void djui_gfx_displaylist_end(void); + f32 djui_gfx_get_scale(void); void djui_gfx_render_texture(const u8* texture, u32 w, u32 h, u32 bitSize); diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index dfd34647..c76544b5 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -189,6 +189,18 @@ static const uint8_t missing_texture[MISSING_W * MISSING_H * 4] = { 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, }; +static bool sOnlyTextureChangeOnAddrChange = false; + +static void gfx_update_loaded_texture(uint8_t tile_number, uint32_t size_bytes, const uint8_t* addr) { + if (!sOnlyTextureChangeOnAddrChange) { + rdp.textures_changed[tile_number] = true; + } else if (!rdp.textures_changed[tile_number]) { + rdp.textures_changed[tile_number] = rdp.loaded_texture[tile_number].addr != addr; + } + rdp.loaded_texture[tile_number].size_bytes = size_bytes; + rdp.loaded_texture[tile_number].addr = addr; +} + ////////////////////////////////// // forward declaration for djui // ////////////////////////////////// @@ -210,15 +222,9 @@ static unsigned long get_time(void) { static void gfx_flush(void) { if (buf_vbo_len > 0) { - /*int num = buf_vbo_num_tris; - unsigned long t0 = get_time();*/ gfx_rapi->draw_triangles(buf_vbo, buf_vbo_len, buf_vbo_num_tris); buf_vbo_len = 0; buf_vbo_num_tris = 0; - /*unsigned long t1 = get_time(); - if (t1 - t0 > 1000) { - printf("f: %d %d\n", num, (int)(t1 - t0)); - }*/ } } @@ -1241,8 +1247,11 @@ static void gfx_dp_set_tile(uint8_t fmt, uint32_t siz, uint32_t line, uint32_t t rdp.texture_tile.cms = cms; rdp.texture_tile.cmt = cmt; rdp.texture_tile.line_size_bytes = line * 8; - rdp.textures_changed[0] = true; - rdp.textures_changed[1] = true; + if (!sOnlyTextureChangeOnAddrChange) { + // I don't know if we ever need to set these... + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } } if (tile == G_TX_LOADTILE) { @@ -1256,8 +1265,11 @@ static void gfx_dp_set_tile_size(uint8_t tile, uint16_t uls, uint16_t ult, uint1 rdp.texture_tile.ult = ult; rdp.texture_tile.lrs = lrs; rdp.texture_tile.lrt = lrt; - rdp.textures_changed[0] = true; - rdp.textures_changed[1] = true; + if (!sOnlyTextureChangeOnAddrChange) { + // I don't know if we ever need to set these... + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } } } @@ -1290,9 +1302,7 @@ static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t break; } uint32_t size_bytes = (lrs + 1) << word_size_shift; - rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = size_bytes; - rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr; - rdp.textures_changed[rdp.texture_to_load.tile_number] = true; + gfx_update_loaded_texture(rdp.texture_to_load.tile_number, size_bytes, rdp.texture_to_load.addr); } static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t lrt) { @@ -1318,15 +1328,11 @@ static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t } uint32_t size_bytes = (((lrs >> G_TEXTURE_IMAGE_FRAC) + 1) * ((lrt >> G_TEXTURE_IMAGE_FRAC) + 1)) << word_size_shift; - rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = size_bytes; - - rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr; + gfx_update_loaded_texture(rdp.texture_to_load.tile_number, size_bytes, rdp.texture_to_load.addr); rdp.texture_tile.uls = uls; rdp.texture_tile.ult = ult; rdp.texture_tile.lrs = lrs; rdp.texture_tile.lrt = lrt; - - rdp.textures_changed[rdp.texture_to_load.tile_number] = true; } static uint8_t color_comb_component(uint32_t v) { @@ -1977,10 +1983,7 @@ static void OPTIMIZE_O3 djui_gfx_dp_execute_override(void) { uint32_t wordSizeShift = (sDjuiOverrideB == 32) ? 2 : 1; uint32_t lrs = (sDjuiOverrideW * sDjuiOverrideH) - 1; uint32_t sizeBytes = (lrs + 1) << wordSizeShift; - rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = sizeBytes; - rdp.textures_changed[rdp.texture_to_load.tile_number] = rdp.loaded_texture[rdp.texture_to_load.tile_number].addr != rdp.texture_to_load.addr; - rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr; - //rdp.textures_changed[rdp.texture_to_load.tile_number] = true; + gfx_update_loaded_texture(rdp.texture_to_load.tile_number, sizeBytes, rdp.texture_to_load.addr); // gsDPSetTile uint32_t line = (((sDjuiOverrideW * 2) + 7) >> 3); @@ -2135,6 +2138,9 @@ void OPTIMIZE_O3 djui_gfx_run_dl(Gfx* cmd) { djui_gfx_sp_simple_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2); djui_gfx_sp_simple_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2); break; + case G_TEXADDR_DJUI: + sOnlyTextureChangeOnAddrChange = !(C0(0, 24) & 0x01); + break; case G_EXECUTE_DJUI: djui_gfx_dp_execute_djui(cmd->words.w1); break; From 2b111e02b2ec1544e990eb6dc4b463bec6979f3a Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 10 Apr 2023 21:34:37 -0700 Subject: [PATCH 13/32] Updated spanish lang file (thanks Yosho, KanHeaven and SonicDark) --- lang/Spanish.ini | 186 +++++++++++++++++++++++------------------------ 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/lang/Spanish.ini b/lang/Spanish.ini index e0f832fa..c0287fd0 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -2,47 +2,47 @@ CONNECTED = "@ se ha conectado" DISCONNECTED = "@ se ha desconectado" LEFT_THIS_LEVEL = "@ ha salido del nivel" -ENTERED_THIS_LEVEL = "@ ha entrado al nivel" -ENTERED = "@ se ha unido\n#" -SERVER_CLOSED = "\\#ffa0a0\\Desconectado:\\#dcdcdc\\ servidor cerrado" +ENTERED_THIS_LEVEL = "@ se ha unido a tu nivel" +ENTERED = "@ ha entrado a \n#" +SERVER_CLOSED = "\\#ffa0a0\\Desconectado:\\#dcdcdc\\ El servidor ha sido cerrado." DISCORD_ERROR = "Discord ha lanzado un error.\nPara solucionarlo, intenta: \n1. Cerrar el juego.\n2. Reiniciar Discord.\n3. Iniciar el juego." DISCORD_DETECT = "\\#ffa0a0\\Error:\\#c8c8c8\\ No se ha podido detectar Discord.\n\\#a0a0a0\\Prueba a cerrar el juego, reiniciar Discord, e iniciar el juego otra vez." -DISCONNECT_FULL = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ La partida está llena." -DISCONNECT_KICK = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ Has sido expulsado del servidor." -DISCONNECT_BAN = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ Has sido vetado del servidor." -DISCONNECT_REJOIN = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ Uniendose de nuevo..." -DISCONNECT_CLOSED = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ El anfitrión ha cerrado la conexion." -DISCONNECT_BIG_MOD = "El servidor tenía un mod demasiado pesado.\nSaliendo." -DIED = "@ ha muerto" -DEBUG_FLY = "@ está en estado de vuelo libre debug" -IMPORT_MOD_SUCCESS = "\\#a0ffa0\\Mod \n\\#c8c8c8\\'@'\n\\#a0ffa0\\Importado" -IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\DynOS pack \n\\#c8c8c8\\'@'\n\\#a0ffa0\\Importado" -IMPORT_FAIL = "\\#ffa0a0\\Fallo al importar \\#c8c8c8\\\n'@'" -IMPORT_FAIL_INGAME = "\\#ffa0a0\\No se puede importar mientras se está en juego" +DISCONNECT_FULL = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ La partida está llena" +DISCONNECT_KICK = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ has sido\nexpulsado del servidor" +DISCONNECT_BAN = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ has sido\nbaneado del servidor" +DISCONNECT_REJOIN = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ Uniéndose de nuevo..." +DISCONNECT_CLOSED = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ El alfitrión ha cerrado el servidor" +DISCONNECT_BIG_MOD = "\\#ffa0a0\\Desconectado:\\#c8c8c8\\ El servidor tenía\nun mod demasiado pesado" +DIED = "@ ha muerto." +DEBUG_FLY = "@ Está en estado de vuelo libre debug." +IMPORT_MOD_SUCCESS = "El mod \\#c8c8c8\\'@'\\#a0ffa0\\\nha sido importado con éxito." +IMPORT_DYNOS_SUCCESS = "El pack de DynOS \\#c8c8c8\\'@'\\#a0ffa0\\\nha sido importado con éxito." +IMPORT_FAIL = "\n\\#ffa0a0\\Error al importar archivo. \\#c8c8c8\\\n'@'" +IMPORT_FAIL_INGAME = "\\#ffa0a0\\No se pueden importar archivos\nen medio de una partida." [CHAT] -KICKING = "¡Expulsando a '@'!" -BANNING = "¡Vetando a '@'!" -SERVER_ONLY = "Solo el servidor puede usar este comando." -PERM_BANNING = "¡Vetando permanentemente a '@'!" -ADD_MODERATOR = "¡Añadiendo a '@' como Moderador!" +KICKING = "¡'@' ha sido expulsado!" +BANNING = "¡'@' ha sido baneado!" +SERVER_ONLY = "Solo el anfitrión puede usar este comando." +PERM_BANNING = "¡'@' ha sido baneado permanentemente!" +ADD_MODERATOR = "¡'@' ahora es un moderador!" PLAYERS = "Jugadores" NO_PERMS = "No tienes permiso para usar este comando." PLAYER_NOT_FOUND = "No se ha podido encontrar al jugador." SELF_KICK = "No te puedes expulsar a tí mismo." -SELF_BAN = "No te puedes vetar a tí mismo." -SELF_MOD = "No te puedes hacerte moderador a tí mismo." -KICK_CONFIRM = "¿Seguro que quieres expulsar a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\' para expusar." -BAN_CONFIRM = "¿Seguro que quieres vetar a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\' para vetar." -PERM_BAN_CONFIRM = "¿Seguro que quieres vetar permanentemente a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\' para vetar." -MOD_CONFIRM = "¿Seguro que quieres convertir a '@' en moderador?\Escribe '\\#a0ffa0\\/confirm\\#fff982\\'." -PLAYERS_DESC = "/players - Mostrar lista de todos los jugadores y sus IDs" -KICK_DESC = "/kick [NAME|ID] - Expulsar al jugador de la partida actual" -BAN_DESC = "/ban [NAME|ID] - Vetar al jugador de la partida actual" -PERM_BAN_DESC = "/permban [NAME|ID] - Vetar al jugador de todas tus partidas" -MOD_DESC = "/moderator [NAME|ID] - Permitir a este jugador usar comandos como /kick, /ban o /permban de cualquier juego que alojes" -UNRECOGNIZED = "Comando de chat desconocido." -MOD_GRANTED = "\\#fff982\\Ahora eres un Moderador." +SELF_BAN = "No te puedes banear a tí mismo." +SELF_MOD = "No puedes hacerte moderador a tí mismo." +KICK_CONFIRM = "¿Seguro que quieres expulsar a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\' para expulsar." +BAN_CONFIRM = "¿Seguro que quieres banear a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\' para banear." +PERM_BAN_CONFIRM = "¿Seguro que quieres banear permanentemente a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\' para banear." +MOD_CONFIRM = "¿Seguro que quieres hacer moderador a '@'?\nEscribe '\\#a0ffa0\\/confirm\\#fff982\\'." +PLAYERS_DESC = "/players - Muestra la lista de todos los jugadores y sus IDs." +KICK_DESC = "/kick [NAME|ID] - Expulsa al jugador de la partida actual." +BAN_DESC = "/ban [NAME|ID] - Banea al jugador de la partida actual." +PERM_BAN_DESC = "/permban [NAME|ID] - Banea al jugador de todas tus partidas." +MOD_DESC = "/moderator [NAME|ID] - Permite a un jugador usar comandos como /kick, /ban o /permban de cualquier partida que crees." +UNRECOGNIZED = "Comando desconocido." +MOD_GRANTED = "\\#fff982\\Ahora eres un moderador." [MENU] BACK = "Volver" @@ -52,37 +52,37 @@ YES = "Sí" [CAMERA] CAMERA = "CÁMARA" -FREE_CAMERA = "Cámara Libre" -ANALOG_CAMERA = "Cámara Analógica" -MOUSE_LOOK = "Moviemiento con Ratón" +FREE_CAMERA = "Cámara libre" +ANALOG_CAMERA = "Cámara analógica" +MOUSE_LOOK = "Movimiento con mouse" INVERT_X = "Invertir eje X" INVERT_Y = "Invertir eje Y" -X_SENSITIVITY = "Sensibilidad Eje X" -Y_SENSITIVITY = "Sensibilidad Eje Y" -AGGRESSION = "Agresíon" -PAN_LEVEL = "Nivel Seguimiento" +X_SENSITIVITY = "Sensibilidad eje X" +Y_SENSITIVITY = "Sensibilidad eje Y" +AGGRESSION = "Agresión" +PAN_LEVEL = "Nivel de seguimiento" DECELERATION = "Deceleración" [CHEATS] CHEATS = "TRUCOS" -MOON_JUMP = "Salto Lunar" +MOON_JUMP = "Salto lunar" GOD_MODE = "Modo Dios" -INFINITE_LIVES = "Vidas Ilimitadas" -SUPER_SPEED = "Super Velocidad" -RESPONSIVE_CONTROLS = "Controles Responsivos" -RAPID_FIRE = "Pulsación Rápida (A)" -BLJ_ANYWHERE = "BLJ en cualquier parte" -ALWAYS_TRIPLE_JUMP = "Siempre hacer Triple Salto" +INFINITE_LIVES = "Vidas infinitas" +SUPER_SPEED = "Super velocidad" +RESPONSIVE_CONTROLS = "Controles responsivos" +RAPID_FIRE = "Pulsación rápida (A)" +BLJ_ANYWHERE = "BLJ donde sea" +ALWAYS_TRIPLE_JUMP = "Siempre hacer triple salto" [CONTROLS] CONTROLS = "CONTROLES" -N64_BINDS = "Asignaciones N64" -EXTRA_BINDS = "Asignaciones Extra" +N64_BINDS = "Botones N64" +EXTRA_BINDS = "Botones Extra" BACKGROUND_GAMEPAD = "Mando en segundo plano" GAMEPAD = "Mando" DEADZONE = "Zona muerta" -RUMBLE_STRENGTH = "Fuerza de Vibración" +RUMBLE_STRENGTH = "Fuerza de vibración" CHAT = "Chat" PLAYERS = "Jugadores" @@ -111,12 +111,12 @@ C_RIGHT = "C Derecha" [DISPLAY] DISPLAY = "PANTALLA" FULLSCREEN = "Pantalla completa" -PRELOAD_TEXTURES = "Precargar Texturas" -VSYNC = "Sincronización Vertical" +PRELOAD_TEXTURES = "Precargar texturas" +VSYNC = "Sincronización vertical" UNCAPPED_FRAMERATE = "FPS sin límite" FRAME_LIMIT = "Límite de FPS" -FAST = "Rápido" -ACCURATE = "Preciso" +FAST = "Rápida" +ACCURATE = "Precisa" INTERPOLATION = "Interpolación" NEAREST = "Más cercano" LINEAR = "Lineal" @@ -128,33 +128,33 @@ D1P5X = "1.5x" D3X = "3x" D10X = "10x" D100X = "100x" -DRAW_DISTANCE = "Distancia de Dibujado" -DYNOS_PACKS = "Packs DynOS" +DRAW_DISTANCE = "Distancia de dibujado" +DYNOS_PACKS = "Packs de DynOS" [DYNOS] DYNOS = "DYNOS" [HOST_MESSAGE] INFO_TITLE = "INFO" -WARN_DISCORD = "Invita a amigos haciendo click derecho en su nombre en Discord y seleccionando\n'\\#d0d0ff\\Invitar a unirse\\#c8c8c8\\'.\n\nPuedes invitar en canales de un servidor también presionando el\nbotón \\#d0d0ff\\+\\#c8c8c8\\ al lado del cuadro de texto del chat.\n\nEl estado de Actividad Actual \\#ffa0a0\\debe estar\\#c8c8c8\\ activado en tus \najustes de Discord.\n\nEstár invisible \\#ffa0a0\\te prevendrá\\#c8c8c8\\ de crear invitaciones." -WARN_DISCORD2 = "\\#ffa0a0\\Error:\\#c8c8c8\\ No se ha detectado a Discord.\n\n\\#a0a0a0\\Prueba a cerrar el juego,\nreiniciar Discord,\ny abrir el juego de nuevo." -WARN_SOCKET = "Las conexiones directas \\#ffa0a0\\requieren\\#c8c8c8\\ que abras los puertos en tu rúter.\n\nAbre el puerto '\\#d0d0ff\\%d\\#c8c8c8\\' con protocolo UDP." -HOST = "Alojar" +WARN_DISCORD = "Invita a amigos haciendo click derecho en su nombre en Discord y seleccionando\n'\\#d0d0ff\\Invitar a unirse\\#c8c8c8\\'.\n\nPuedes invitar en canales de un servidor también presionando el botón \\#d0d0ff\\+\\#c8c8c8\\ al lado del cuadro de texto del chat.\n\nEl estado de Actividad Actual \\#ffa0a0\\debe estar\\#c8c8c8\\ activado en tus ajustes de Discord.\n\nEstar invisible \\#ffa0a0\\te prevendrá\\#c8c8c8\\ de crear invitaciones." +WARN_DISCORD2 = "\\#ffa0a0\\Error:\\#c8c8c8\\ No se ha detectado Discord.\n\n\\#a0a0a0\\Prueba a cerrar el juego,\nreiniciar Discord,\ny abrir el juego de nuevo." +WARN_SOCKET = "Las conexiones directas \\#ffa0a0\\requieren\\#c8c8c8\\ que abras los puertos en tu router.\n\nAbre el puerto '\\#d0d0ff\\%d\\#c8c8c8\\' con protocolo UDP." +HOST = "Crear" [HOST_MODS] ROMHACKS = "ROMHACKS" MODS = "MODS" [HOST_SAVE] -SAVE_TITLE = "GUARDAR" +SAVE_TITLE = "RANURAS DE\nGUARDADO" ERASE_TITLE = "BORRAR" CONFIRM = "¿Seguro que quieres borrar esta partida?" ERASE = "Borrar" [HOST_SETTINGS] SETTINGS = "AJUSTES" -NONSOLID = "Intangible" -SOLID = "Tangible" +NONSOLID = "No Sólida" +SOLID = "Sólida" FRIENDLY_FIRE = "Fuego Amigo" PLAYER_INTERACTION = "Interacción entre jugadores" WEAK = "Poca" @@ -163,7 +163,7 @@ TOO_MUCH = "Demasiada" KNOCKBACK_STRENGTH = "Fuerza de retroceso" LEAVE_LEVEL = "Salir del nivel" STAY_IN_LEVEL = "Seguir en el nivel" -NONSTOP = "Siguiente misión" +NONSTOP = "Sin parar" ON_STAR_COLLECTION = "Al conseguir una estrella" SKIP_INTRO_CUTSCENE = "Saltar cinemática de introducción" SHARE_LIVES = "Compartir vidas" @@ -172,32 +172,32 @@ BUBBLE_ON_DEATH = "Burbuja al morir" AMOUNT_OF_PLAYERS = "Número de jugadores" [HOST] -SERVER_TITLE = "SERVIDOR" -HOST_TITLE = "ALOJAR" +SERVER_TITLE = "PARTIDA" +HOST_TITLE = "CREAR" DISCORD = "Discord" DIRECT_CONNECTION = "Conexión Directa" NETWORK_SYSTEM = "Modo de conexión" PORT = "Puerto" -SAVE_SLOT = "Ranura de Guardado" +SAVE_SLOT = "Ranuras" SETTINGS = "Ajustes" MODS = "Mods" ROMHACKS = "Rom-Hacks" APPLY = "Aplicar" -HOST = "Alojar" +HOST = "Crear" [JOIN_MESSAGE] -JOINING = "UNIENDOSE" +JOINING = "UNIÉNDOSE" [JOIN] JOIN_TITLE = "UNIRSE" -JOIN_DISCORD = "Para unirte a un lobby en \\#d0d0ff\\Discord\\#c8c8c8\\:\n\nManten el juego abierto y acepta la invitación.\n\nSi la invitación dice que la partida ha terminado, haz click en el nombre de la persona que te invitó para refrescarla." -JOIN_SOCKET = "Introduce la IP y el puerto de \\#d0d0ff\\conexión directa\\#c8c8c8\\:" +JOIN_DISCORD = "Para unirte a un lobby en \\#d0d0ff\\Discord\\#c8c8c8\\:\n\nMantén el juego abierto y acepta la invitación.\n\nSi la invitación dice que la partida ha terminado, haz click en el nombre de la persona que te invitó para\nrefrescarla." +JOIN_SOCKET = "Introduce la IP y el puerto de la \\#d0d0ff\\conexión directa\\#c8c8c8\\:" JOIN = "Unirse" [MAIN] QUIT_TITLE = "SALIR" QUIT_CONFIRM = "¿Seguro que quieres salir?" -HOST = "Alojar" +HOST = "Crear" JOIN = "Unirse" OPTIONS = "Opciones" QUIT = "Salir" @@ -205,9 +205,9 @@ QUIT = "Salir" [MENU_OPTIONS] MAIN_MENU = "MENÚ PRINCIPAL" LEVEL = "Nivel" -USE_STAGE_MUSIC = "Usar Música del Nivel" -RANDOM_STAGE = "Nivel Aleatório" -PLAY_VANILLA_DEMOS = "Reproducir Demostraciones Vanilla" +USE_STAGE_MUSIC = "Usar música del nivel" +RANDOM_STAGE = "Nivel aleatorio" +PLAY_VANILLA_DEMOS = "Demos originales" [MISC] DEBUG_TITLE = "DEPURACIÓN" @@ -216,10 +216,10 @@ LUA_PROFILER = "Perfilador de Lua" DEBUG_PRINT = "Mensajes de Depuración" DEBUG_INFO = "Información de Depuración" DEBUG_ERRORS = "Errores de Depuración" -MISC_TITLE = "MISCELÁNEO" -PAUSE_IN_SINGLEPLAYER = "Pausa en Modo de Un jugador" -DISABLE_POPUPS = "Deshabilitar Mensajes Emergentes" -MENU_OPTIONS = "Opciones del Menú" +MISC_TITLE = "OTROS" +PAUSE_IN_SINGLEPLAYER = "Pausa en modo de un jugador" +DISABLE_POPUPS = "Deshabilitar mensajes emergentes" +MENU_OPTIONS = "Opciones del menú" DEBUG = "Depuración" LANGUAGE = "Idioma" @@ -233,26 +233,26 @@ CAMERA = "Cámara" CONTROLS = "Controles" DISPLAY = "Pantalla" SOUND = "Sonido" -MISC = "Misceláneo" +MISC = "Otros" [PAUSE] QUIT_TITLE = "SALIR" -QUIT_HOST = "¿Seguro que quieres dejar de alojar?" +QUIT_HOST = "¿Seguro que quieres finalizar la partida?" QUIT_CLIENT = "¿Seguro que te quieres desconectar?" PAUSE_TITLE = "PAUSA" PLAYER = "Jugador" -DYNOS_PACKS = "Packs DynOS" +DYNOS_PACKS = "Packs de DynOS" OPTIONS = "Opciones" CHEATS = "Trucos" -SERVER_SETTINGS = "Ajustes del Servidor" +SERVER_SETTINGS = "Ajustes de la partida" RESUME = "Continuar" -STOP_HOSTING = "Dejar de Alojar" +STOP_HOSTING = "Finalizar partida" DISCONNECT = "Desconectarse" [PLAYER] PLAYER_TITLE = "JUGADOR" -OVERALLS = "Tirantes" -SHIRT = "Camisa" +OVERALLS = "Overoles" +SHIRT = "Camiseta" GLOVES = "Guantes" SHOES = "Zapatos" HAIR = "Pelo" @@ -267,7 +267,7 @@ BLUE = "Azul" PLAYER = "Jugador" NAME = "Nombre" MODEL = "Modelo" -PALETTE_PRESET = "Perfil de Paleta" +PALETTE_PRESET = "Paletas predeterminadas" EDIT_PALETTE = "Editar Paleta" [PALETTE] @@ -303,20 +303,20 @@ RASPBERRY = "Frambuesa" BUBBLEGUM = "Chicle" ICE_MARIO = "Mario de Hielo" ICE_LUIGI = "Luigi de Hielo" -CUSTOM = "Personalizado" +CUSTOM = "Personalizada" [PLAYER_LIST] PLAYERS = "JUGADORES" NAME = "nombre" LOCATION = "localización" -ACT = "recorrido" +ACT = "acto" [SOUND] SOUND = "SONIDO" -MASTER_VOLUME = "Volumen Maestro" -MUSIC_VOLUME = "Volumen Música" -SFX_VOLUME = "Volumen Efectos de Sonido" -ENV_VOLUME = "Volumen Entorno" +MASTER_VOLUME = "Volumen General" +MUSIC_VOLUME = "Volumen de Música" +SFX_VOLUME = "Volumen de Efectos de Sonido" +ENV_VOLUME = "Volumen de Entorno" [LANGUAGE] LANGUAGE = "IDIOMA" \ No newline at end of file From 3b0e650dda03efd6e6a4fa484ce8e6946d37daae Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 11 Apr 2023 14:26:42 -0700 Subject: [PATCH 14/32] Use the correct resolution for djui hud interpolation --- src/pc/djui/djui_hud_utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index f92f2de2..6d2e1890 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -91,6 +91,7 @@ struct InterpHud { f32 scaleH; f32 width; f32 height; + enum HudUtilsResolution resolution; }; static struct InterpHud sInterpHuds[MAX_INTERP_HUD] = { 0 }; static u16 sInterpHudCount = 0; @@ -102,12 +103,14 @@ void patch_djui_hud_before(void) { void patch_djui_hud(f32 delta) { f32 savedZ = gDjuiHudUtilsZ; Gfx* savedHeadPos = gDisplayListHead; + enum HudUtilsResolution savedResolution = sResolution; for (u16 i = 0; i < sInterpHudCount; i++) { struct InterpHud* interp = &sInterpHuds[i]; f32 x = delta_interpolate_f32(interp->prevX, interp->x, delta); f32 y = delta_interpolate_f32(interp->prevY, interp->y, delta); f32 scaleW = delta_interpolate_f32(interp->prevScaleW, interp->scaleW, delta); f32 scaleH = delta_interpolate_f32(interp->prevScaleH, interp->scaleH, delta); + sResolution = interp->resolution; gDjuiHudUtilsZ = interp->z; gDisplayListHead = interp->headPos; @@ -125,6 +128,7 @@ void patch_djui_hud(f32 delta) { djui_hud_size_translate(&translatedH); create_dl_scale_matrix(DJUI_MTX_NOPUSH, interp->width * translatedW, interp->height * translatedH, 1.0f); } + sResolution = savedResolution; gDisplayListHead = savedHeadPos; gDjuiHudUtilsZ = savedZ; } @@ -326,6 +330,7 @@ void djui_hud_render_texture_interpolated(struct TextureInfo* texInfo, f32 prevX interp->width = texInfo->width; interp->height = texInfo->height; interp->z = savedZ; + interp->resolution = sResolution; } void djui_hud_render_texture_tile_interpolated(struct TextureInfo* texInfo, f32 prevX, f32 prevY, f32 prevScaleW, f32 prevScaleH, f32 x, f32 y, f32 scaleW, f32 scaleH, u32 tileX, u32 tileY, u32 tileW, u32 tileH) { From aba6483e5e68b7737682aef8f70da6b321023ff4 Mon Sep 17 00:00:00 2001 From: Agent X <44549182+Agent-11@users.noreply.github.com> Date: Tue, 11 Apr 2023 17:32:47 -0400 Subject: [PATCH 15/32] Hopefully fix passes_pvp_interaction_checks crash (#343) * Hopefully fix passes_pvp_interaction_checks crash * Hopefully fix cur_obj_call_action_function crash --- src/game/area.c | 4 +--- src/game/interaction.c | 2 ++ src/game/object_helpers.c | 3 +++ src/pc/network/packets/packet_download.c | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/game/area.c b/src/game/area.c index 3401634b..bb1688af 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -161,9 +161,7 @@ u32 get_mario_spawn_type(struct Object *o) { } struct ObjectWarpNode *area_get_warp_node(u8 id) { - if (gCurrentArea == NULL || gCurrentArea->warpNodes == NULL) { - return NULL; - } + if (!gCurrentArea || !gCurrentArea->warpNodes) { return NULL; } struct ObjectWarpNode *node = NULL; diff --git a/src/game/interaction.c b/src/game/interaction.c index f8739cc2..1e90acc3 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -1353,6 +1353,8 @@ u8 player_is_sliding(struct MarioState* m) { } u8 passes_pvp_interaction_checks(struct MarioState* attacker, struct MarioState* victim) { + if (!attacker || !victim) { return false; } + // attacked u8 isInCutscene = ((attacker->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) || ((victim->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE); isInCutscene = isInCutscene || (attacker->action == ACT_IN_CANNON) || (victim->action == ACT_IN_CANNON); diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 49084d1d..42d5326a 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -2664,7 +2664,10 @@ s32 cur_obj_move_up_and_down(s32 a0) { } void cur_obj_call_action_function(void (*actionFunctions[])(void)) { + if (!actionFunctions) { return; } + void (*actionFunction)(void) = actionFunctions[o->oAction]; + if (!actionFunction) { return; } actionFunction(); } diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c index 81bdffe2..de5a4a94 100644 --- a/src/pc/network/packets/packet_download.c +++ b/src/pc/network/packets/packet_download.c @@ -332,7 +332,7 @@ static void open_mod_file(struct Mod* mod, struct ModFile* file) { } void network_receive_download(struct Packet* p) { - if (p == NULL) { + if (!p) { LOG_ERROR("Received null packet"); return; } From 3825026faa216e82a4e1b22497c7ed78eb57eb95 Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 11 Apr 2023 14:40:24 -0700 Subject: [PATCH 16/32] Made cur_obj_call_action_function safer --- src/game/behaviors/beta_boo_key.inc.c | 2 +- src/game/behaviors/boo.inc.c | 6 +++--- src/game/behaviors/bowser.inc.c | 6 +++--- src/game/behaviors/bowser_puzzle_piece.inc.c | 2 +- src/game/behaviors/bub.inc.c | 4 ++-- src/game/behaviors/bullet_bill.inc.c | 2 +- src/game/behaviors/cannon.inc.c | 2 +- src/game/behaviors/capswitch.inc.c | 2 +- src/game/behaviors/chuckya.inc.c | 2 +- src/game/behaviors/coin.inc.c | 2 +- src/game/behaviors/elevator.inc.c | 2 +- src/game/behaviors/exclamation_box.inc.c | 2 +- src/game/behaviors/fish.inc.c | 4 ++-- src/game/behaviors/heave_ho.inc.c | 2 +- src/game/behaviors/jumping_box.inc.c | 2 +- src/game/behaviors/king_bobomb.inc.c | 2 +- src/game/behaviors/lll_rotating_hex_flame.inc.c | 2 +- src/game/behaviors/mr_i.inc.c | 4 ++-- src/game/behaviors/piranha_plant.inc.c | 2 +- src/game/behaviors/thwomp.inc.c | 2 +- src/game/behaviors/tox_box.inc.c | 2 +- src/game/behaviors/tumbling_bridge.inc.c | 2 +- src/game/behaviors/tuxie.inc.c | 4 ++-- src/game/behaviors/tweester.inc.c | 2 +- src/game/behaviors/ukiki.inc.c | 2 +- src/game/behaviors/ukiki_cage.inc.c | 2 +- src/game/behaviors/whomp.inc.c | 2 +- src/game/object_helpers.c | 4 ++-- src/game/object_helpers.h | 4 +++- 29 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/game/behaviors/beta_boo_key.inc.c b/src/game/behaviors/beta_boo_key.inc.c index 27a85dee..2e81c7f0 100644 --- a/src/game/behaviors/beta_boo_key.inc.c +++ b/src/game/behaviors/beta_boo_key.inc.c @@ -170,5 +170,5 @@ static void (*sBetaBooKeyActions[])(void) = { beta_boo_key_inside_boo_loop, beta * Update function for bhvBetaBooKey. */ void bhv_beta_boo_key_loop(void) { - cur_obj_call_action_function(sBetaBooKeyActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBetaBooKeyActions); } diff --git a/src/game/behaviors/boo.inc.c b/src/game/behaviors/boo.inc.c index d799209f..45e4a960 100644 --- a/src/game/behaviors/boo.inc.c +++ b/src/game/behaviors/boo.inc.c @@ -555,7 +555,7 @@ void bhv_boo_loop(void) { //PARTIAL_UPDATE cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sBooActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBooActions); cur_obj_move_standard(78); boo_approach_target_opacity_and_update_scale(); @@ -793,7 +793,7 @@ void bhv_big_boo_loop(void) { o->oGraphYOffset = o->oBooBaseScale * 60.0f; cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sBooGivingStarActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBooGivingStarActions); cur_obj_move_standard(78); boo_approach_target_opacity_and_update_scale(); @@ -870,7 +870,7 @@ void bhv_boo_with_cage_loop(void) { //PARTIAL_UPDATE cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sBooWithCageActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBooWithCageActions); cur_obj_move_standard(78); boo_approach_target_opacity_and_update_scale(); diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c index 3b88896a..8cf90add 100644 --- a/src/game/behaviors/bowser.inc.c +++ b/src/game/behaviors/bowser.inc.c @@ -59,7 +59,7 @@ void bhv_bowser_tail_anchor_init(void) { } void bhv_bowser_tail_anchor_loop(void) { - cur_obj_call_action_function(sBowserTailAnchorActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBowserTailAnchorActions); o->oParentRelativePosX = 90.0f; if (o->parentObj->oAction == 4) o->parentObj->oIntangibleTimer = -1; @@ -1204,7 +1204,7 @@ void bowser_free_update(void) { o->oBowserUnk10E = 0; cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sBowserActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBowserActions); cur_obj_move_standard(-78); if (bowser_check_fallen_off_stage()) o->oAction = 2; // bowser go home? @@ -1657,7 +1657,7 @@ void bhv_falling_bowser_platform_loop(void) { } } - cur_obj_call_action_function(sFallingBowserPlatformActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sFallingBowserPlatformActions); } void bowser_flame_despawn(void) { diff --git a/src/game/behaviors/bowser_puzzle_piece.inc.c b/src/game/behaviors/bowser_puzzle_piece.inc.c index fa8250d9..c6eb6338 100644 --- a/src/game/behaviors/bowser_puzzle_piece.inc.c +++ b/src/game/behaviors/bowser_puzzle_piece.inc.c @@ -275,7 +275,7 @@ void (*sBowserPuzzlePieceActions[])(void) = { void bhv_lll_bowser_puzzle_piece_loop(void) { bhv_lll_bowser_puzzle_piece_update(); - cur_obj_call_action_function(sBowserPuzzlePieceActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBowserPuzzlePieceActions); o->oPosX = o->oBowserPuzzlePieceOffsetX + o->oHomeX; o->oPosY = o->oBowserPuzzlePieceOffsetY + o->oHomeY; diff --git a/src/game/behaviors/bub.inc.c b/src/game/behaviors/bub.inc.c index bb4e83fb..831b2916 100644 --- a/src/game/behaviors/bub.inc.c +++ b/src/game/behaviors/bub.inc.c @@ -28,7 +28,7 @@ void (*sBirdChirpChirpActions[])(void) = { bub_spawner_act_0, bub_spawner_act_1, bub_spawner_act_2, bub_spawner_act_3 }; void bhv_bub_spawner_loop(void) { - cur_obj_call_action_function(sBirdChirpChirpActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBirdChirpChirpActions); } void bub_move_vertically(s32 a0) { @@ -136,7 +136,7 @@ void bhv_bub_loop(void) { } o->oWallHitboxRadius = 30.0f; cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sCheepCheepActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sCheepCheepActions); cur_obj_move_using_fvel_and_gravity(); if (o->parentObj->oAction == 2) obj_mark_for_deletion(o); diff --git a/src/game/behaviors/bullet_bill.inc.c b/src/game/behaviors/bullet_bill.inc.c index b1cf7ef0..8260f1fa 100644 --- a/src/game/behaviors/bullet_bill.inc.c +++ b/src/game/behaviors/bullet_bill.inc.c @@ -86,7 +86,7 @@ void (*sBulletBillActions[])(void) = { bullet_bill_act_0, bullet_bill_act_1, bul bullet_bill_act_3, bullet_bill_act_4 }; void bhv_bullet_bill_loop(void) { - cur_obj_call_action_function(sBulletBillActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sBulletBillActions); if (cur_obj_check_interacted()) o->oAction = 4; } diff --git a/src/game/behaviors/cannon.inc.c b/src/game/behaviors/cannon.inc.c index a942bd09..c5d7bc6d 100644 --- a/src/game/behaviors/cannon.inc.c +++ b/src/game/behaviors/cannon.inc.c @@ -237,7 +237,7 @@ void bhv_cannon_base_loop(void) { cur_obj_push_mario_away_from_cylinder(220, 300); } - cur_obj_call_action_function(sOpenedCannonActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sOpenedCannonActions); if (o->oCannonUnkF8) o->oCannonUnkF8++; o->oInteractStatus = 0; diff --git a/src/game/behaviors/capswitch.inc.c b/src/game/behaviors/capswitch.inc.c index 2e147963..dab3e6ad 100644 --- a/src/game/behaviors/capswitch.inc.c +++ b/src/game/behaviors/capswitch.inc.c @@ -65,5 +65,5 @@ void bhv_cap_switch_loop(void) { sync_object_init_field(o, &capSwitchForcePress); } - cur_obj_call_action_function(sCapSwitchActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sCapSwitchActions); } diff --git a/src/game/behaviors/chuckya.inc.c b/src/game/behaviors/chuckya.inc.c index 443fef3d..798e5dc0 100644 --- a/src/game/behaviors/chuckya.inc.c +++ b/src/game/behaviors/chuckya.inc.c @@ -193,7 +193,7 @@ void (*sChuckyaActions[])(void) = { chuckya_act_0, chuckya_act_1, chuckya_act_2, void chuckya_move(void) { cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sChuckyaActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sChuckyaActions); cur_obj_move_standard(-30); if (o->oInteractStatus & INT_STATUS_GRABBED_MARIO) { o->oAction = 1; diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c index 4733bcd7..b44835da 100644 --- a/src/game/behaviors/coin.inc.c +++ b/src/game/behaviors/coin.inc.c @@ -255,7 +255,7 @@ void coin_inside_boo_act_0(void) { void (*sCoinInsideBooActions[])(void) = { coin_inside_boo_act_0, coin_inside_boo_act_1 }; void bhv_coin_inside_boo_loop(void) { - cur_obj_call_action_function(sCoinInsideBooActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sCoinInsideBooActions); } void bhv_coin_sparkles_loop(void) { diff --git a/src/game/behaviors/elevator.inc.c b/src/game/behaviors/elevator.inc.c index 19c78e49..a0c02815 100644 --- a/src/game/behaviors/elevator.inc.c +++ b/src/game/behaviors/elevator.inc.c @@ -154,7 +154,7 @@ struct SpawnParticlesInfo D_8032F3FC = { 0, 5, MODEL_WHITE_PARTICLE_DL, 0, 2.0f, 2.0f }; void bhv_elevator_loop(void) { - cur_obj_call_action_function(sElevatorActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sElevatorActions); // allow bubbled players to pass through if (gMarioStates[0].action == ACT_BUBBLED) { diff --git a/src/game/behaviors/exclamation_box.inc.c b/src/game/behaviors/exclamation_box.inc.c index 8a1ea789..01a2e758 100644 --- a/src/game/behaviors/exclamation_box.inc.c +++ b/src/game/behaviors/exclamation_box.inc.c @@ -214,5 +214,5 @@ void bhv_exclamation_box_init(void) { void bhv_exclamation_box_loop(void) { cur_obj_scale(2.0f); - cur_obj_call_action_function(sExclamationBoxActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sExclamationBoxActions); } diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c index 0c4e36c5..330758f6 100644 --- a/src/game/behaviors/fish.inc.c +++ b/src/game/behaviors/fish.inc.c @@ -73,7 +73,7 @@ static void (*sFishSpawnerActions[])(void) = { }; void bhv_fish_spawner_loop(void) { - cur_obj_call_action_function(sFishSpawnerActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sFishSpawnerActions); } /** @@ -276,7 +276,7 @@ void bhv_fish_loop(void) } // Call fish action methods and apply physics engine. - cur_obj_call_action_function(sFishActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sFishActions); cur_obj_move_using_fvel_and_gravity(); // If the parent object has action set to two, then delete the fish object. diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c index 7b84f977..8e86f057 100644 --- a/src/game/behaviors/heave_ho.inc.c +++ b/src/game/behaviors/heave_ho.inc.c @@ -99,7 +99,7 @@ void (*sHeaveHoActions[])(void) = { heave_ho_act_0, heave_ho_act_1, heave_ho_act void heave_ho_move(void) { cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sHeaveHoActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sHeaveHoActions); cur_obj_move_standard(-78); if (o->oMoveFlags & OBJ_MOVE_MASK_IN_WATER) o->oGraphYOffset = -15.0f; diff --git a/src/game/behaviors/jumping_box.inc.c b/src/game/behaviors/jumping_box.inc.c index 8dc8ef8f..732348ac 100644 --- a/src/game/behaviors/jumping_box.inc.c +++ b/src/game/behaviors/jumping_box.inc.c @@ -41,7 +41,7 @@ void jumping_box_free_update(void) { obj_set_hitbox(o, &sJumpingBoxHitbox); cur_obj_update_floor_and_walls(); cur_obj_move_standard(78); - cur_obj_call_action_function(sJumpingBoxActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sJumpingBoxActions); } void bhv_jumping_box_loop(void) { diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c index 17710e01..ffc786e1 100644 --- a/src/game/behaviors/king_bobomb.inc.c +++ b/src/game/behaviors/king_bobomb.inc.c @@ -365,7 +365,7 @@ void king_bobomb_move(void) { cur_obj_move_standard(-78); else cur_obj_move_using_fvel_and_gravity(); - cur_obj_call_action_function(sKingBobombActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sKingBobombActions); exec_anim_sound_state(sKingBobombSoundStates); s32 distanceToPlayer = dist_between_objects(o, gMarioStates[0].marioObj); if (distanceToPlayer < 5000.0f * draw_distance_scalar()) diff --git a/src/game/behaviors/lll_rotating_hex_flame.inc.c b/src/game/behaviors/lll_rotating_hex_flame.inc.c index a47d7ec5..02c55cd2 100644 --- a/src/game/behaviors/lll_rotating_hex_flame.inc.c +++ b/src/game/behaviors/lll_rotating_hex_flame.inc.c @@ -60,7 +60,7 @@ void bhv_lll_rotating_block_fire_bars_loop(void) { sync_object_init(o, 4000.0f); sync_object_init_field(o, &o->oAngleVelYaw); } - cur_obj_call_action_function(sRotatingCwFireBarsActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sRotatingCwFireBarsActions); if (o->oBehParams2ndByte == 0) load_object_collision_model(); } diff --git a/src/game/behaviors/mr_i.inc.c b/src/game/behaviors/mr_i.inc.c index 3202eefd..3033d717 100644 --- a/src/game/behaviors/mr_i.inc.c +++ b/src/game/behaviors/mr_i.inc.c @@ -33,7 +33,7 @@ void mr_i_piranha_particle_act_1(void) { void (*sMrIParticleActions[])(void) = { mr_i_piranha_particle_act_0, mr_i_piranha_particle_act_1 }; void bhv_mr_i_particle_loop(void) { - cur_obj_call_action_function(sMrIParticleActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sMrIParticleActions); } void spawn_mr_i_particle(void) { @@ -283,7 +283,7 @@ void bhv_mr_i_loop(void) { s32 distanceToPlayer = player ? dist_between_objects(o, player) : 10000; obj_set_hitbox(o, &sMrIHitbox); - cur_obj_call_action_function(sMrIActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sMrIActions); if (o->oAction != 3) { if (distanceToPlayer > 3000.0f || o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) { o->oAction = 0; diff --git a/src/game/behaviors/piranha_plant.inc.c b/src/game/behaviors/piranha_plant.inc.c index e7db9cb0..97569d24 100644 --- a/src/game/behaviors/piranha_plant.inc.c +++ b/src/game/behaviors/piranha_plant.inc.c @@ -369,7 +369,7 @@ void bhv_piranha_plant_loop(void) { cur_obj_set_hitbox_radius_and_height(150.0f, 100.0f); cur_obj_set_hurtbox_radius_and_height(150.0f, 100.0f); - cur_obj_call_action_function(TablePiranhaPlantActions); + CUR_OBJ_CALL_ACTION_FUNCTION(TablePiranhaPlantActions); // In WF, hide all Piranha Plants once high enough up. if (gCurrLevelNum == LEVEL_WF) { struct Object* player = gMarioStates[0].marioObj; diff --git a/src/game/behaviors/thwomp.inc.c b/src/game/behaviors/thwomp.inc.c index e016b67b..51d71150 100644 --- a/src/game/behaviors/thwomp.inc.c +++ b/src/game/behaviors/thwomp.inc.c @@ -63,5 +63,5 @@ void bhv_grindel_thwomp_loop(void) { sync_object_init_field(o, &o->oTimer); sync_object_init_field(o, &o->oVelY); } - cur_obj_call_action_function(sGrindelThwompActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sGrindelThwompActions); } diff --git a/src/game/behaviors/tox_box.inc.c b/src/game/behaviors/tox_box.inc.c index 4bdb567b..2a5bbd5d 100644 --- a/src/game/behaviors/tox_box.inc.c +++ b/src/game/behaviors/tox_box.inc.c @@ -85,6 +85,6 @@ void bhv_tox_box_loop(void) { sync_object_init_field(o, &o->oToxBoxMovementStep); } } - cur_obj_call_action_function(sToxBoxActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sToxBoxActions); load_object_collision_model(); } diff --git a/src/game/behaviors/tumbling_bridge.inc.c b/src/game/behaviors/tumbling_bridge.inc.c index 7e2b2152..c8ce02b9 100644 --- a/src/game/behaviors/tumbling_bridge.inc.c +++ b/src/game/behaviors/tumbling_bridge.inc.c @@ -164,5 +164,5 @@ void bhv_tumbling_bridge_loop(void) { } } - cur_obj_call_action_function(sTumblingBridgeActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sTumblingBridgeActions); } diff --git a/src/game/behaviors/tuxie.inc.c b/src/game/behaviors/tuxie.inc.c index fe696cde..0b1fe7a4 100644 --- a/src/game/behaviors/tuxie.inc.c +++ b/src/game/behaviors/tuxie.inc.c @@ -160,7 +160,7 @@ void bhv_tuxies_mother_loop(void) { } o->activeFlags |= ACTIVE_FLAG_UNK10; cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sTuxiesMotherActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sTuxiesMotherActions); cur_obj_move_standard(-78); play_penguin_walking_sound(PENGUIN_WALK_BIG); o->oInteractStatus = 0; @@ -289,7 +289,7 @@ void small_penguin_free_actions(void) { cur_obj_become_tangible(); cur_obj_enable_rendering(); cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sSmallPenguinActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sSmallPenguinActions); cur_obj_move_standard(-78); play_penguin_walking_sound(PENGUIN_WALK_BABY); } diff --git a/src/game/behaviors/tweester.inc.c b/src/game/behaviors/tweester.inc.c index 0f97a9fe..8ac1eadc 100644 --- a/src/game/behaviors/tweester.inc.c +++ b/src/game/behaviors/tweester.inc.c @@ -149,7 +149,7 @@ void bhv_tweester_loop(void) { } obj_set_hitbox(o, &sTweesterHitbox); - cur_obj_call_action_function(sTweesterActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sTweesterActions); o->oInteractStatus = 0; } diff --git a/src/game/behaviors/ukiki.inc.c b/src/game/behaviors/ukiki.inc.c index 0e0e987b..1d437351 100644 --- a/src/game/behaviors/ukiki.inc.c +++ b/src/game/behaviors/ukiki.inc.c @@ -504,7 +504,7 @@ void ukiki_free_loop(void) { s32 steepSlopeAngleDegrees; cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sUkikiActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sUkikiActions); if (o->oAction == UKIKI_ACT_GO_TO_CAGE || o->oAction == UKIKI_ACT_RETURN_HOME) { steepSlopeAngleDegrees = -88; diff --git a/src/game/behaviors/ukiki_cage.inc.c b/src/game/behaviors/ukiki_cage.inc.c index 6ef80bd1..31b47d40 100644 --- a/src/game/behaviors/ukiki_cage.inc.c +++ b/src/game/behaviors/ukiki_cage.inc.c @@ -103,5 +103,5 @@ void (*sUkikiCageActions[])(void) = { * Main behavior loop for the cage. Only calls the relevant action. */ void bhv_ukiki_cage_loop(void) { - cur_obj_call_action_function(sUkikiCageActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sUkikiCageActions); } diff --git a/src/game/behaviors/whomp.inc.c b/src/game/behaviors/whomp.inc.c index 67468d55..75abccbe 100644 --- a/src/game/behaviors/whomp.inc.c +++ b/src/game/behaviors/whomp.inc.c @@ -284,7 +284,7 @@ void bhv_whomp_loop(void) { } cur_obj_update_floor_and_walls(); - cur_obj_call_action_function(sWhompActions); + CUR_OBJ_CALL_ACTION_FUNCTION(sWhompActions); cur_obj_move_standard(-20); if (o->oAction != 9) { // o->oBehParams2ndByte here seems to be a flag diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index 42d5326a..350b7e01 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -2663,9 +2663,9 @@ s32 cur_obj_move_up_and_down(s32 a0) { return FALSE; } -void cur_obj_call_action_function(void (*actionFunctions[])(void)) { +void cur_obj_call_action_function(void (*actionFunctions[])(void), uint32_t actionFunctionsLength) { if (!actionFunctions) { return; } - + if ((uint32_t)o->oAction >= actionFunctionsLength) { return; } void (*actionFunction)(void) = actionFunctions[o->oAction]; if (!actionFunction) { return; } actionFunction(); diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 9e8ef5f3..83c8c531 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -256,6 +256,8 @@ struct GraphNode_802A45E4 { /*0x22*/ s16 unk22; }; +#define CUR_OBJ_CALL_ACTION_FUNCTION(_action_func) cur_obj_call_action_function(_action_func, (sizeof(_action_func) / sizeof(&_action_func[0]))) + void obj_set_hitbox(struct Object *obj, struct ObjectHitbox *hitbox); s32 signum_positive(s32 x); f32 absf(f32 x); @@ -273,7 +275,7 @@ void cur_obj_scale_over_time(s32 a0, s32 a1, f32 sp10, f32 sp14); void cur_obj_set_pos_to_home_with_debug(void); s32 cur_obj_is_mario_on_platform(void); s32 cur_obj_move_up_and_down(s32 a0); -void cur_obj_call_action_function(void (*actionFunctions[])(void)); +void cur_obj_call_action_function(void (*actionFunctions[])(void), uint32_t actionFunctionsLength); void spawn_base_star_with_no_lvl_exit(void); s32 bit_shift_left(s32 a0); s32 cur_obj_mario_far_away(void); From f7ef9326297114e30de4fdf00d185394bd57e1a7 Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 11 Apr 2023 14:52:24 -0700 Subject: [PATCH 17/32] Prevent script error in extended-movset --- mods/extended-moveset.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/extended-moveset.lua b/mods/extended-moveset.lua index 2cd8644d..1ff1f6bf 100644 --- a/mods/extended-moveset.lua +++ b/mods/extended-moveset.lua @@ -1163,7 +1163,7 @@ function act_walking(m) end function act_hold_walking(m) - if m.heldObj.behavior == bhvJumpingBox then + if m.heldObj ~= nil and m.heldObj.behavior == bhvJumpingBox then return set_mario_action(m, ACT_CRAZY_BOX_BOUNCE, 0) end From 1406f15995300e3d32d68e36f9cba40680ce6f57 Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 11 Apr 2023 14:53:04 -0700 Subject: [PATCH 18/32] Make sure lag_compensation_get_local_state() always returns a MarioState --- src/pc/network/lag_compensation.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pc/network/lag_compensation.c b/src/pc/network/lag_compensation.c index 8c15c2ae..90052978 100644 --- a/src/pc/network/lag_compensation.c +++ b/src/pc/network/lag_compensation.c @@ -40,16 +40,16 @@ void lag_compensation_store(void) { } struct MarioState* lag_compensation_get_local_state(struct NetworkPlayer* otherNp) { - if (!otherNp) { return NULL; } - if (gNetworkType == NT_NONE) { return NULL; } - if (!sLocalStateHistoryReady) { return NULL; } + if (!otherNp) { return &gMarioStates[0]; } + if (gNetworkType == NT_NONE) { return &gMarioStates[0]; } + if (!sLocalStateHistoryReady) { return &gMarioStates[0]; } s32 pingToTicks = (otherNp->ping / 1000.0f) * 30; if (pingToTicks > (MAX_LOCAL_STATE_HISTORY-1)) { pingToTicks = (MAX_LOCAL_STATE_HISTORY-1); } //LOG_INFO("Ping: %s :: %u :: %d", otherNp->name, otherNp->ping, pingToTicks); - if (pingToTicks == 0) { return NULL; } + if (pingToTicks == 0) { return &gMarioStates[0]; } s32 index = (s32)sLocalStateHistoryIndex - pingToTicks; while (index < 0) { index += MAX_LOCAL_STATE_HISTORY; } From de9e5f8550ff371cbee673d862648d690617eb6d Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 11 Apr 2023 15:02:51 -0700 Subject: [PATCH 19/32] Fixed crash in smlua_set_sync_table_field_from_network() --- src/pc/lua/smlua_sync_table.c | 12 +++++++++++- src/pc/network/packets/packet_lua_sync_table.c | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pc/lua/smlua_sync_table.c b/src/pc/lua/smlua_sync_table.c index cd2ae3d4..570aa448 100644 --- a/src/pc/lua/smlua_sync_table.c +++ b/src/pc/lua/smlua_sync_table.c @@ -271,7 +271,11 @@ static bool smlua_sync_table_send_field(u8 toLocalIndex, int stackIndex, bool al // send over the network if (!gLuaInitializingScript) { - network_send_lua_sync_table(toLocalIndex, seq, modRemoteIndex, sUnwoundLntsCount, sUnwoundLnts, &lntValue); + if (sUnwoundLntsCount < 2) { + LOG_ERROR("Sent sync table field packet with an invalid key count: %u", sUnwoundLntsCount); + } else { + network_send_lua_sync_table(toLocalIndex, seq, modRemoteIndex, sUnwoundLntsCount, sUnwoundLnts, &lntValue); + } } @@ -311,6 +315,12 @@ void smlua_set_sync_table_field_from_network(u64 seq, u16 modRemoteIndex, u16 ln return; } + // sanity check key count + if (lntKeyCount < 2) { + LOG_ERROR("Received sync table field packet with an invalid key count: %u", lntKeyCount); + return; + } + lua_getglobal(L, "_G"); // get global table lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); // get the file's "global" table lua_remove(L, -2); // remove global table diff --git a/src/pc/network/packets/packet_lua_sync_table.c b/src/pc/network/packets/packet_lua_sync_table.c index fd950129..fbe4e5a2 100644 --- a/src/pc/network/packets/packet_lua_sync_table.c +++ b/src/pc/network/packets/packet_lua_sync_table.c @@ -36,6 +36,7 @@ void network_send_lua_sync_table(u8 toLocalIndex, u64 seq, u16 modRemoteIndex, u //LOG_INFO(" %s", smlua_lnt_to_str(&lntKeys[i])); } //LOG_INFO(" -> %s", smlua_lnt_to_str(lntValue)); + //LOG_INFO(" count %u", lntKeyCount); if (!packet_write_lnt(&p, lntValue)) { return; } @@ -66,6 +67,7 @@ void network_receive_lua_sync_table(struct Packet* p) { //LOG_INFO(" %s", smlua_lnt_to_str(&lntKeys[i])); } //LOG_INFO(" -> %s", smlua_lnt_to_str(&lntValue)); + //LOG_INFO(" count %u", lntKeyCount); if (!packet_read_lnt(p, &lntValue)) { goto cleanup; } From 70de5d26af8b7463165ace8d250f75c8567d7050 Mon Sep 17 00:00:00 2001 From: Agent X <44549182+Agent-11@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:41:41 -0400 Subject: [PATCH 20/32] Flying triple jump off players if wing cap (#344) --- src/game/interaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/interaction.c b/src/game/interaction.c index 1e90acc3..f7c9b4c1 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -1309,7 +1309,7 @@ static u8 resolve_player_collision(struct MarioState* m, struct MarioState* m2) velY = fmax(fmin(55.0f, 15.0f + fabs(m->vel[1])), 35.0f); } else if (m->action == ACT_DOUBLE_JUMP) { mario_stop_riding_and_holding(m); - set_mario_action(m, (m->specialTripleJump && m->playerIndex == 0) ? ACT_SPECIAL_TRIPLE_JUMP : ACT_TRIPLE_JUMP, 0); + set_mario_action(m, (m->specialTripleJump && m->playerIndex == 0) ? ACT_SPECIAL_TRIPLE_JUMP : m->flags & MARIO_WING_CAP ? ACT_FLYING_TRIPLE_JUMP : ACT_TRIPLE_JUMP, 0); velY = fmax(fmin(60.0f, 20.0f + fabs(m->vel[1])), 40.0f); } else { mario_stop_riding_and_holding(m); From 0e7bbd2da6901a247285765967fdb8d00c438562 Mon Sep 17 00:00:00 2001 From: MysterD Date: Tue, 11 Apr 2023 20:07:09 -0700 Subject: [PATCH 21/32] Add single-lua-file mods to mod cache --- src/pc/mods/mod.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c index 1ef68260..f672ad85 100644 --- a/src/pc/mods/mod.c +++ b/src/pc/mods/mod.c @@ -517,12 +517,10 @@ bool mod_load(struct Mods* mods, char* basePath, char* modName) { // print LOG_INFO(" %s", mod->name); - if (isDirectory) { - for (int i = 0; i < mod->fileCount; i++) { - struct ModFile* file = &mod->files[i]; - mod_cache_add(mod, file, true); - LOG_INFO(" - %s", file->relativePath); - } + for (int i = 0; i < mod->fileCount; i++) { + struct ModFile* file = &mod->files[i]; + mod_cache_add(mod, file, true); + LOG_INFO(" - %s", file->relativePath); } return true; From b206b75d7e0d18855bc80185c6a0030a6b3ee10b Mon Sep 17 00:00:00 2001 From: Isaac0-dev <62234577+Isaac0-dev@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:33:34 +1000 Subject: [PATCH 22/32] improve djui paginated panels and fix some typos (#347) * improve djui paginated panels and fix some typos * naming --- src/pc/djui/djui_paginated.c | 77 ++++++++++++++------- src/pc/mods/mod.c | 5 -- src/pc/network/packets/packet.c | 4 +- src/pc/network/packets/packet.h | 4 +- src/pc/network/packets/packet_area.c | 2 +- src/pc/network/packets/packet_command_mod.c | 4 +- 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/pc/djui/djui_paginated.c b/src/pc/djui/djui_paginated.c index 101395d3..e99ec21d 100644 --- a/src/pc/djui/djui_paginated.c +++ b/src/pc/djui/djui_paginated.c @@ -8,16 +8,11 @@ // events // //////////// -static void djui_paginated_prev(struct DjuiBase* base) { - struct DjuiPaginated* paginated = (struct DjuiPaginated*)base->parent; - paginated->startIndex -= paginated->showCount; - if (paginated->startIndex < 0) { paginated->startIndex = 0; } -} - -static void djui_paginated_next(struct DjuiBase* base) { - struct DjuiPaginated* paginated = (struct DjuiPaginated*)base->parent; - paginated->startIndex += paginated->showCount; +static struct DjuiButton* sPrevButton = NULL; +static struct DjuiButton* sNextButton = NULL; +static struct DjuiText* sPageNumText = NULL; +static s32 djui_paginated_get_count(struct DjuiPaginated* paginated) { s32 count = 0; struct DjuiBaseChild* dbc = paginated->layout->base.child; while (dbc != NULL) { @@ -25,6 +20,35 @@ static void djui_paginated_next(struct DjuiBase* base) { dbc = dbc->next; } + return count; +} + +static void djui_paginated_prev(struct DjuiBase* base) { + struct DjuiPaginated* paginated = (struct DjuiPaginated*)base->parent; + paginated->startIndex -= paginated->showCount; + + djui_base_set_enabled(&sPrevButton->base, (paginated->startIndex > 0)); + djui_base_set_enabled(&sNextButton->base, true); + + char pageNumString[32] = { 0 }; + snprintf(pageNumString, 32, "%d/%d", paginated->startIndex / paginated->showCount + 1, djui_paginated_get_count(paginated) / paginated->showCount + 1); + djui_text_set_text(sPageNumText, pageNumString); + + if (paginated->startIndex < 0) { paginated->startIndex = 0; } +} + +static void djui_paginated_next(struct DjuiBase* base) { + struct DjuiPaginated* paginated = (struct DjuiPaginated*)base->parent; + paginated->startIndex += paginated->showCount; + s32 count = djui_paginated_get_count(paginated); + + djui_base_set_enabled(&sNextButton->base, (paginated->startIndex < count - 8)); + djui_base_set_enabled(&sPrevButton->base, true); + + char pageNumString[32] = { 0 }; + snprintf(pageNumString, 32, "%d/%d", paginated->startIndex / paginated->showCount + 1, count / paginated->showCount + 1); + djui_text_set_text(sPageNumText, pageNumString); + if (paginated->startIndex >= count) { paginated->startIndex -= paginated->showCount; } } @@ -57,6 +81,10 @@ void djui_paginated_calculate_height(struct DjuiPaginated* paginated) { } djui_base_set_size(&paginated->base, paginated->base.width.value, height); + + char pageNumString[32] = { 0 }; + snprintf(pageNumString, 32, "%d/%d", paginated->startIndex / paginated->showCount + 1, count / paginated->showCount + 1); + djui_text_set_text(sPageNumText, pageNumString); } bool djui_paginated_render(struct DjuiBase* base) { @@ -112,22 +140,23 @@ struct DjuiPaginated* djui_paginated_create(struct DjuiBase* parent, u32 showCou paginated->layout = layout; } - { - struct DjuiButton* button = djui_button_create(&paginated->base, "<", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_prev); - djui_base_set_alignment(&button->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_BOTTOM); - djui_base_set_size_type(&button->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); - djui_base_set_size(&button->base, 128, 32); - paginated->prevButton = button; - } + sPrevButton = djui_button_create(&paginated->base, "<", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_prev); + djui_base_set_alignment(&sPrevButton->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_BOTTOM); + djui_base_set_size_type(&sPrevButton->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sPrevButton->base, 128, 32); + djui_base_set_enabled(&sPrevButton->base, false); + paginated->prevButton = sPrevButton; - { - struct DjuiButton* button = djui_button_create(&paginated->base, ">", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_next); - djui_base_set_alignment(&button->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM); - djui_base_set_size_type(&button->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); - djui_base_set_size(&button->base, 128, 32); - djui_interactable_hook_click(&button->base, djui_paginated_next); - paginated->nextButton = button; - } + sPageNumText = djui_text_create(&paginated->base, ""); + djui_base_set_color(&sPageNumText->base, 200, 200, 200, 255); + djui_base_set_alignment(&sPageNumText->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM); + sPageNumText->base.y.value -= 30; + + sNextButton = djui_button_create(&paginated->base, ">", DJUI_BUTTON_STYLE_NORMAL, djui_paginated_next); + djui_base_set_alignment(&sNextButton->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM); + djui_base_set_size_type(&sNextButton->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&sNextButton->base, 128, 32); + paginated->nextButton = sNextButton; return paginated; } diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c index 1ef68260..8af01d79 100644 --- a/src/pc/mods/mod.c +++ b/src/pc/mods/mod.c @@ -441,15 +441,10 @@ bool mod_load(struct Mods* mods, char* basePath, char* modName) { valid = true; } else if (is_directory(fullPath)) { char tmpPath[SYS_MAX_PATH] = { 0 }; - char path1[SYS_MAX_PATH] = { 0 }; - char path2[SYS_MAX_PATH] = { 0 }; if (!concat_path(tmpPath, fullPath, "main.lua")) { LOG_ERROR("Failed to concat path '%s' + '%s'", fullPath, "main.lua"); return true; } - if ((concat_path(path1, fullPath, "c-update.lua") && path_exists(path1)) || (concat_path(path2, fullPath, "m-update.lua") && path_exists(path2))) { - return true; - } valid = path_exists(tmpPath); } diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 69983834..0596f4f4 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -52,8 +52,8 @@ void packet_process(struct Packet* p) { case PACKET_JOIN: network_receive_join(p); break; case PACKET_CHAT: network_receive_chat(p); break; case PACKET_KICK: network_receive_kick(p); break; - case PACKET_COMMAND: network_recieve_chat_command(p); break; - case PACKET_MODERATOR: network_recieve_moderator(p); break; + case PACKET_COMMAND: network_receive_chat_command(p); break; + case PACKET_MODERATOR: network_receive_moderator(p); break; case PACKET_KEEP_ALIVE: network_receive_keep_alive(p); break; case PACKET_LEAVING: network_receive_leaving(p); break; case PACKET_SAVE_FILE: network_receive_save_file(p); break; diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index c0fdc0f8..c74d13e5 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -234,11 +234,11 @@ void network_receive_kick(struct Packet* p); // packet_command_mod.c void network_send_chat_command(u8 localIndex, enum ChatConfirmCommand CCC); -void network_recieve_chat_command(struct Packet* p); +void network_receive_chat_command(struct Packet* p); // packet_moderator.c void network_send_moderator(u8 localIndex); -void network_recieve_moderator(struct Packet* p); +void network_receive_moderator(struct Packet* p); // packet_keep_alive.c void network_send_keep_alive(u8 localIndex); diff --git a/src/pc/network/packets/packet_area.c b/src/pc/network/packets/packet_area.c index a7d58057..b2d61fe5 100644 --- a/src/pc/network/packets/packet_area.c +++ b/src/pc/network/packets/packet_area.c @@ -138,7 +138,7 @@ void network_receive_area(struct Packet* p) { LOG_INFO("rx area"); if (p == NULL) { - LOG_ERROR("rx area: the packet was NULL, failed to recieve the area."); + LOG_ERROR("rx area: the packet was NULL, failed to receive the area."); return; } diff --git a/src/pc/network/packets/packet_command_mod.c b/src/pc/network/packets/packet_command_mod.c index c612b553..76b735d2 100644 --- a/src/pc/network/packets/packet_command_mod.c +++ b/src/pc/network/packets/packet_command_mod.c @@ -18,7 +18,7 @@ void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc) { } } -void network_recieve_chat_command(struct Packet *p) { +void network_receive_chat_command(struct Packet *p) { if (!moderator_list_contains(gNetworkSystem->get_id_str(p->localIndex))) { return; } @@ -59,7 +59,7 @@ void network_send_moderator(u8 localIndex) { network_send_to(localIndex, &p); } -void network_recieve_moderator(struct Packet *p) { +void network_receive_moderator(struct Packet *p) { if ((gIsModerator) || (network_player_any_connected() && gNetworkPlayers[p->localIndex].type != NPT_SERVER)) { return; } From cd347b80ee7236acde5f0937fd36178ce133aa5a Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:17:01 -0700 Subject: [PATCH 23/32] Clear interaction's invulnerable flag after each player has been processed --- src/game/interaction.c | 16 ++++++++-------- src/game/interaction.h | 1 + src/game/object_list_processor.c | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/game/interaction.c b/src/game/interaction.c index f7c9b4c1..6c1ff96d 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -59,7 +59,7 @@ enum InteractionFlag { (INT_SLIDE_KICK | INT_FAST_ATTACK_OR_SHELL) u8 sDelayInvincTimer; -s16 sInvulnerable; +s16 gInteractionInvulnerable; u32 interact_coin(struct MarioState *, u32, struct Object *); u32 interact_water_ring(struct MarioState *, u32, struct Object *); u32 interact_star_or_key(struct MarioState *, u32, struct Object *); @@ -824,7 +824,7 @@ u32 take_damage_from_interact_object(struct MarioState *m) { u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) { u32 damage; - if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) + if (!gInteractionInvulnerable && !(m->flags & MARIO_VANISH_CAP) && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO; m->interactObj = o; @@ -1589,7 +1589,7 @@ u32 interact_strong_wind(struct MarioState *m, UNUSED u32 interactType, struct O u32 interact_flame(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { u32 burningAction = ACT_BURNING_JUMP; - if (!sInvulnerable && !(m->flags & MARIO_METAL_CAP) && !(m->flags & MARIO_VANISH_CAP) + if (!gInteractionInvulnerable && !(m->flags & MARIO_METAL_CAP) && !(m->flags & MARIO_VANISH_CAP) && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { queue_rumble_data_mario(m, 5, 80); @@ -1616,7 +1616,7 @@ u32 interact_flame(struct MarioState *m, UNUSED u32 interactType, struct Object } u32 interact_snufit_bullet(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { - if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)) { + if (!gInteractionInvulnerable && !(m->flags & MARIO_VANISH_CAP)) { if (m->flags & MARIO_METAL_CAP) { o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED; play_sound(SOUND_ACTION_UNKNOWN458, m->marioObj->header.gfx.cameraToObject); @@ -1681,7 +1681,7 @@ u32 interact_bully(struct MarioState *m, UNUSED u32 interactType, struct Object return TRUE; } - else if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) + else if (!gInteractionInvulnerable && !(m->flags & MARIO_VANISH_CAP) && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { o->oInteractStatus = INT_STATUS_INTERACTED; m->invincTimer = 2; @@ -1701,7 +1701,7 @@ u32 interact_bully(struct MarioState *m, UNUSED u32 interactType, struct Object } u32 interact_shock(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { - if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) + if (!gInteractionInvulnerable && !(m->flags & MARIO_VANISH_CAP) && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { u32 actionArg = (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) == 0; @@ -1918,7 +1918,7 @@ u32 interact_koopa_shell(struct MarioState *m, UNUSED u32 interactType, struct O u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { if (m != &gMarioStates[0]) { return false; } - if ((!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !sInvulnerable) + if ((!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !gInteractionInvulnerable) && (o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { if (object_facing_mario(m, o, 0x2AAA)) { mario_stop_riding_and_holding(m); @@ -2224,7 +2224,7 @@ void check_kick_or_punch_wall(struct MarioState *m) { void mario_process_interactions(struct MarioState *m) { sDelayInvincTimer = FALSE; - sInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0; + gInteractionInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0; if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0 && is_player_active(m)) { s32 i; diff --git a/src/game/interaction.h b/src/game/interaction.h index 6ab63441..5be968c4 100644 --- a/src/game/interaction.h +++ b/src/game/interaction.h @@ -98,6 +98,7 @@ enum InteractionType { #define INT_STATUS_STOP_RIDING (1 << 22) /* 0x00400000 */ #define INT_STATUS_TOUCHED_BOB_OMB (1 << 23) /* 0x00800000 */ +extern s16 gInteractionInvulnerable; extern u8 gPssSlideStarted; s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o); diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c index af4219f9..7d0015bf 100644 --- a/src/game/object_list_processor.c +++ b/src/game/object_list_processor.c @@ -337,6 +337,7 @@ void bhv_mario_update(void) { update_character_anim_offset(gMarioState); // reset mario state to the local player + gInteractionInvulnerable = false; gMarioState = &gMarioStates[0]; } From e914a4e2c48aded14794767fbec2d264ef8664db Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:23:56 -0700 Subject: [PATCH 24/32] Increased max lag compensation to 1s --- src/pc/network/lag_compensation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pc/network/lag_compensation.c b/src/pc/network/lag_compensation.c index 90052978..f0e5246f 100644 --- a/src/pc/network/lag_compensation.c +++ b/src/pc/network/lag_compensation.c @@ -6,7 +6,7 @@ #include "behavior_table.h" #include "model_ids.h" -#define MAX_LOCAL_STATE_HISTORY 20 +#define MAX_LOCAL_STATE_HISTORY 30 struct StateHistory { struct MarioState m; struct Object marioObj; From 3a807bee06c26120ab8fdf84185953d6f0f6fd5f Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:29:10 -0700 Subject: [PATCH 25/32] Fixed crash in network_receive_lua_custom() --- src/pc/network/packets/packet_lua_custom.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pc/network/packets/packet_lua_custom.c b/src/pc/network/packets/packet_lua_custom.c index a61d690c..61852494 100644 --- a/src/pc/network/packets/packet_lua_custom.c +++ b/src/pc/network/packets/packet_lua_custom.c @@ -11,6 +11,11 @@ void network_send_lua_custom(bool broadcast) { u16 zero = 0; s32 paramIndex = 1; + if (!L) { + LOG_ERROR("Sent lua custom packet when lua is dead"); + return; + } + // figure out mod index if (gLuaActiveMod == NULL) { LOG_LUA_LINE("Could not figure out the current active mod!"); @@ -99,6 +104,11 @@ void network_receive_lua_custom(struct Packet* p) { packet_read(p, &modIndex, sizeof(u16)); packet_read(p, &keyCount, sizeof(u8)); + if (!L) { + LOG_ERROR("Received lua custom packet when lua is dead"); + return; + } + lua_newtable(L); s32 tableIndex = lua_gettop(L); for(u16 i = 0; i < keyCount; i++) { From fadb568519605993a1650f1f4d1ae806a67292c2 Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:32:18 -0700 Subject: [PATCH 26/32] Fixed crash in reset_camera() --- src/game/camera.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/game/camera.c b/src/game/camera.c index bc775111..ff68369c 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -3297,11 +3297,6 @@ void soft_reset_camera(struct Camera* c) { * Reset all the camera variables to their arcane defaults */ void reset_camera(struct Camera *c) { - UNUSED s32 unused = 0; - UNUSED u8 unused1[16]; - UNUSED struct LinearTransitionPoint *start = &sModeInfo.transitionStart; - UNUSED struct LinearTransitionPoint *end = &sModeInfo.transitionEnd; - gCamera = c; gCameraMovementFlags = 0; s2ndRotateFlags = 0; @@ -3315,7 +3310,6 @@ void reset_camera(struct Camera *c) { unused8032CFCC = 0; gSecondCameraFocus = NULL; sCButtonsPressed = 0; - vec3f_copy(sModeTransition.marioPos, sMarioCamState->pos); sModeTransition.framesLeft = 0; unused8032CFCC = -1; unused8032CFC8 = -1; @@ -3340,11 +3334,19 @@ void reset_camera(struct Camera *c) { sCSideButtonYaw = 0; s8DirModeBaseYaw = 0; s8DirModeYawOffset = 0; - c->doorStatus = DOOR_DEFAULT; - sMarioCamState->headRotation[0] = 0; - sMarioCamState->headRotation[1] = 0; - sMarioCamState->cameraEvent = 0; - sMarioCamState->usedObj = NULL; + + if (c) { + c->doorStatus = DOOR_DEFAULT; + } + + if (sMarioCamState) { + vec3f_copy(sModeTransition.marioPos, sMarioCamState->pos); + sMarioCamState->headRotation[0] = 0; + sMarioCamState->headRotation[1] = 0; + sMarioCamState->cameraEvent = 0; + sMarioCamState->usedObj = NULL; + } + gLakituState.shakeMagnitude[0] = 0; gLakituState.shakeMagnitude[1] = 0; gLakituState.shakeMagnitude[2] = 0; @@ -3355,12 +3357,14 @@ void reset_camera(struct Camera *c) { gLakituState.unusedVec1[1] = 0.f; gLakituState.unusedVec1[2] = 0.f; gLakituState.lastFrameAction = 0; + set_fov_function(CAM_FOV_DEFAULT); sFOVState.fov = 45.f; sFOVState.fovOffset = 0.f; sFOVState.unusedIsSleeping = 0; sFOVState.shakeAmplitude = 0.f; sFOVState.shakePhase = 0; + sObjectCutscene = 0; gRecentCutscene = 0; unused8033B30C = 0; From 01c379d50db34b8ccad17a5934df273b6d2e76dd Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:36:47 -0700 Subject: [PATCH 27/32] Fixed crash in network_send_spawn_objects_to() --- src/pc/network/packets/packet_spawn_objects.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pc/network/packets/packet_spawn_objects.c b/src/pc/network/packets/packet_spawn_objects.c index a3e03597..6bca1587 100644 --- a/src/pc/network/packets/packet_spawn_objects.c +++ b/src/pc/network/packets/packet_spawn_objects.c @@ -59,6 +59,11 @@ void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[] return; } + if (objectCount == 0) { + LOG_ERROR("Tried to send 0 objects"); + return; + } + SOFT_ASSERT(objectCount < MAX_SPAWN_OBJECTS_PER_PACKET); // prevent sending spawn objects during credits if (gCurrActStarNum == 99) { @@ -74,6 +79,11 @@ void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[] for (u8 i = 0; i < objectCount; i++) { struct Object* o = objects[i]; + if (!o) { + LOG_ERROR("Tried to send null object"); + return; + } + u32 model = models[i]; u32 parentId = generate_parent_id(objects, i, true); u32 behaviorId = get_id_from_behavior(o->behavior); @@ -96,10 +106,14 @@ void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[] if (sendToLocalIndex == PACKET_DESTINATION_BROADCAST) { network_send(&p); - LOG_INFO("tx spawn objects (BROADCAST) | %u", get_id_from_behavior(objects[0]->behavior)); + if (objects[0] && objects[0]->behavior) { + LOG_INFO("tx spawn objects (BROADCAST) | %u", get_id_from_behavior(objects[0]->behavior)); + } } else { network_send_to(sendToLocalIndex, &p); - LOG_INFO("tx spawn objects to %d | %u", gNetworkPlayers[sendToLocalIndex].globalIndex, get_id_from_behavior(objects[0]->behavior)); + if (objects[0] && objects[0]->behavior) { + LOG_INFO("tx spawn objects to %d | %u", gNetworkPlayers[sendToLocalIndex].globalIndex, get_id_from_behavior(objects[0]->behavior)); + } } } From 694ba4b27313744d535dd37b47154721caa7856f Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:38:32 -0700 Subject: [PATCH 28/32] Fixed crash in void geo_process_node_and_siblings() --- src/game/rendering_graph_node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 207e0f23..95ac6e43 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -1554,7 +1554,7 @@ void geo_process_node_and_siblings(struct GraphNode *firstNode) { } } } else { - if (curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) { + if (curGraphNode && curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) { ((struct GraphNodeObject *) curGraphNode)->throwMatrix = NULL; } } From 7d3bfa78e6083113d8c44aa045d3d16d9395ba3b Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:39:25 -0700 Subject: [PATCH 29/32] Bumped version --- src/pc/network/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pc/network/version.h b/src/pc/network/version.h index 4f6b24c3..72842593 100644 --- a/src/pc/network/version.h +++ b/src/pc/network/version.h @@ -3,7 +3,7 @@ #define VERSION_TEXT "beta" #define VERSION_NUMBER 33 -#define MINOR_VERSION_NUMBER 0 +#define MINOR_VERSION_NUMBER 1 #define PATCH_VERSION_NUMBER 0 #define MAX_VERSION_LENGTH 10 From 0e1cda1b918eb467c36bca5102218af61af7384f Mon Sep 17 00:00:00 2001 From: MysterD Date: Thu, 13 Apr 2023 22:39:42 -0700 Subject: [PATCH 30/32] Ran autogen --- autogen/lua_definitions/constants.lua | 2 +- src/pc/lua/smlua_constants_autogen.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 84078962..d1d6d242 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -11522,7 +11522,7 @@ MAX_LOCAL_VERSION_LENGTH = 12 MAX_VERSION_LENGTH = 10 --- @type integer -MINOR_VERSION_NUMBER = 0 +MINOR_VERSION_NUMBER = 1 --- @type integer PATCH_VERSION_NUMBER = 0 diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index bf6469a6..422513fe 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -4016,7 +4016,7 @@ char gSmluaConstants[] = "" "COOP_OBJ_FLAG_INITIALIZED = (1 << 3)\n" "VERSION_TEXT = 'beta'\n" "VERSION_NUMBER = 33\n" -"MINOR_VERSION_NUMBER = 0\n" +"MINOR_VERSION_NUMBER = 1\n" "PATCH_VERSION_NUMBER = 0\n" "MAX_VERSION_LENGTH = 10\n" "MAX_LOCAL_VERSION_LENGTH = 12\n" From 0d2d6eff04c0fd7b3b8a4d24599e96f3ba54c1b0 Mon Sep 17 00:00:00 2001 From: MysterD Date: Fri, 14 Apr 2023 02:00:31 -0700 Subject: [PATCH 31/32] Arena - make knockback animations twice as fast --- mods/arena/arena-player.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/mods/arena/arena-player.lua b/mods/arena/arena-player.lua index 42ff344b..d8afe043 100644 --- a/mods/arena/arena-player.lua +++ b/mods/arena/arena-player.lua @@ -28,6 +28,14 @@ for i = 0, (MAX_PLAYERS - 1) do s.rank = 0 end +local sKnockbackActions = { + ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB, + ACT_FORWARD_AIR_KB, ACT_FORWARD_AIR_KB, ACT_HARD_FORWARD_AIR_KB, + ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, + ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB, + ACT_BACKWARD_AIR_KB, ACT_BACKWARD_AIR_KB, ACT_HARD_BACKWARD_AIR_KB, + ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB +} ------------ -- hammer -- ------------ @@ -361,6 +369,27 @@ function mario_update(m) local s = gPlayerSyncTable[m.playerIndex] local np = gNetworkPlayers[m.playerIndex] + -- increase knockback animations + local animInfo = nil + if m.marioObj ~= nil then + animInfo = m.marioObj.header.gfx.animInfo + end + for i, value in ipairs(sKnockbackActions) do + if m.action == value then + local frame = animInfo.animFrame + local loopEnd = frame + if animInfo.curAnim ~= nil then + loopEnd = animInfo.curAnim.loopEnd + end + + if frame < loopEnd - 2 then + frame = frame + 1 + end + + animInfo.animFrame = frame + end + end + -- clear invincibilities m.invincTimer = 0 if m.knockbackTimer > 5 then From 12b751e0ec271549de838d72867d6477afdc5403 Mon Sep 17 00:00:00 2001 From: MysterD Date: Fri, 14 Apr 2023 10:53:48 -0700 Subject: [PATCH 32/32] Added cyrillic characters to sprite sheet (thanks luigi with thaumcraft glasses) --- .../custom_font/custom_font_normal.rgba32.png | Bin 12120 -> 10129 bytes .../custom_font/custom_font_tiny.rgba32.png | Bin 3868 -> 3196 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/textures/custom_font/custom_font_normal.rgba32.png b/textures/custom_font/custom_font_normal.rgba32.png index c4552460169513a823d80bc6c4a8778f61ecbf43..14dee8942ca9b92ced4ea8a685f6d6272bda2e69 100644 GIT binary patch literal 10129 zcmch-XH-*96fc?}h=7QKAfSLskq*+N2?)|cks=`wARtmgK)Q5-1(YtmgGeu-_ZFl| zGe}ihM2NIME!05D#ry7h>%9+mt^4Vo4`=4=*|W}>v-a%YZ|~WWMuysq^qlkn0Dw_f z=cx$*Ky}dt0Lo0r4uY1g}OR6LQVCxlX0dO(DQ)^by?*dk@V6ADe;=@6B z*M@h`L&-v7yMvy0{+w!H>%CG6HZ<6_H;S%y>28h-hSqlQ#5M4oHSA!(q;GX(e*E3u zw#D<)(3H`I%Yk5Ny*#LxMxrkCw&f?|e-z3)4Dy>kZc?~xT<70^! z7#~mKhQejt$-F=8DtF#i3zx1}0wXG}w#T}tG$@=8)Jo{jBmW`{FQmQZu} zwafsynURZgG9w#lSG>+UH*f=fkpi(;w@cudGXAfR5pxLWwk@>MEVJ>R0! zyq%i!$Jcm2m0Pm5x*g&BkJ2#Vrct<67_*IdXXh}coR}YHv#rvg>0U=;TSUF8e@ph< z`yrB=uBhU4&HI_=6Zv(~=^w`-lt5D60`GUyH8}`sqVT9F7As6*OHE?i9~k!a=|W8c zeH*Hf$fZMM5EIRdGFhksc6C)Iv);3<@O<4z(a2uGUvJ=&8?jYaN;6WUj*rY)h%bV& zBzgq?<$2kVN%Y19pW01l(p!s)0%w+16Ow2JUQU~fod;L%+aJoC z6&ywcZxF=*`>U!^>63Q+?g^L)$q-nW2wnz0FuYbCu%p{36bg3GMJ&z><5RtY%TrMO zZNx@H+s6-(l!Hax<7nKc}enWQWPGN-iemx8-wvY)r`*R^|3YzJdP`joI zXjOj~$gX)wTY8(G3^UIq+Nn4`ZwORMg_~0z)|YA7B&H@@3n-!=i=2i7qdl}zvWj|J zd9*4}yGia0-KCu|CM==4XX=(UFLnb=vj-_oI=A~Kj~_jQy{XdLk$;eU8c& zWx#sZnsDxgPR^XBJqGg;ue=)5(bTR`Zik@g5#TTUn8DQ-BF1q4+Ff->RtRDeyUAyU zaI}CZcs^h)9x!IG*5a?nxuvTp?U`tv;a(-Z@vv!S=^EbB${?IBbQ! ztd*4t47XX}V@6{QU1P?#IULKy^xU zb{OBj9u76vsVUE?7j`-Nb@>ol0PCKYm)C1%o9=8Uae}!VDE)HY`QDyRCAyG*GkV0&cpE(usp%G#9j6!<1Pcckke%#SM+_PqaOkA-qZ3NXKz>L`YZKW# zjcKTIhAi7KjM$R$>7i)o8^rleu)yp!}%_18NDeYHbmjeeC8B z#%D=UOo;L*8~0xoyv``qNFQ=%p$ZkAsq9GKgi%lpm4p5qQQl7#ZI+Q}nqvY5aZUKXGq8rUp|O~q!OiH?Ew^w+q{N}i zYNfU;?)P+ODXD++^WoxV_1ctMGoOlT{M@m$$x@y^8-ns@h@ySB=NQ^nG!Ws^-oP?U|7?{tQWNd?)mZmVfbthyJE^#0siw}BwZyhCclwqM zp!v3u(MT46pxvXpnSO60K0Ha07m-r+{5B!5($}E>TZ*CJE41rL2xa#7X@5!Nd-z$i z8HA>EKxM0`AmhFEkmcunbSp15H$!a9h#?~^G@#17*ji*4zx~U?v#xviV5zMuDtqla zMwZ^Xf;4Xq}+6ja{)&=JxpA@diT3DXlTh=O|J zu~+rA1&T{ns-@R=u+}TE1n_xtXH5(ieyRVjnn(q>9202jsdS)CBttliroDb zc*S*E6s4aR)52sc8wvrux5Nsai^L2M$RA!^gFB|}1nA#dAt1G1dYpXIKay$NuI~E)^&AEr-u}TOeXMSoE*D#Hz#fbC zGTH5-pbg{+(8{-g*P;qW{)@rlpY~nJ1Be8c%2IUqTX961q7`_Pz26K)pn66k32qjg34 zy;g%SF_mi4G-`pIcTaMBJL+vyka-z~=E)wi{A$CcuSGlK@3CpcrT5$UsB*I+KZ_=^ zWmuf)xH5nd#>|EGQFutW0KJ;Qn$ThH&a#eN%47^5g^s|jd_S)uK@j*DU(sn>d@D?RjSbAt239zqd}F_Ktjf zTVpP9k%b}uvXaSp^$_xHVF!=XcDV#Nbgm&S@Xa|!bK=2Y*MrRC6h)^T5Hh|gO$04w zN-kOkZMyrAgY?Nx#M)z45X=CZJUmm3WN6M4T6!7Sgbu-?wMrc1%4NQA9z-=ABpcrC zs;v2H;Q4;|{MGH8Dth&eWQf>PUxneP;t7#czCZj>AN#Re>gg5-s`trOmX@yub!uHz zQ)i+?7U1KV&4awzvr{fSY7E;90K*qaRntu%@oez^nt$$>L{T5qM@s;E_T*t;qfqy7 zOmLA*W$#rejY`ghj11P8;U7*|-Rigx1pGF!%@@1$PteWy^kbPhiZxjNy;72{8N%497-9c@)o%~; z>9R&*5@qHOcNo|`Cs9iHv0wBoz1x{NMs@u&dG_f!y>oOR&pzkok913bc=6-N$WCa7 zQ-{bG1Ii|Wwnd5e!BU06nF4M1mpAysf+WLkVwvaM7;FnxLNpY<)mV{wJJUmMP#L{@ zt<>Zr`dkE1|GHyogVXF$STS?zG&IPB|1~;jxQxl)^M)f_lwoy+VDjf@ItBp7gX;Rl*795 z(2BM!$^VFa{B;Y)mdvzC}T<5W)4 zo7ktXZX=;U<=g!N-Y{c{4T-Wf862^fYA@5Lk83^o*2lKtL8u#hfdEpE3jS&4>rHJS ze@r>;BXKlKn!aGdnkH!V<1s>k5FZ8Y-JDMig)}-Swqf0qgcuSVPVLnMMWe;4l4JNP z5>_{8Z{Urre>F~y~FmNz%YsHNISP3c52+D4OB zvj^8J@Ju-b?jP}d!yjH^I$6QPNqN=D`lUOdmC0Ahw(o4zskra9*S|+?_ymASeJUPg!luIpg zF#0sua`8~`ceJ@db@U9irHFZ(iOZX#(-KC^@GV^ytmhp{?G@T`(O>*zarfGJ1Md>$ z7r{&uH{AHsibtgMc)?R&ONWq?`ebRrLRq#q1jMkN4 zv!<4c-=}q~b4R8GLx}!CBIC8em>N`Z(OUac&?c(C<#dbva}b%lam(ndgtU#r3!Vgw zX5Z+?Mm+BiMIXpOwPr1a@+v1aw{r8kylDVbVG3mU!g=H?$g_%zp@BZxq07ldufoLZ z?Au#E^dF+Gs(Hrut^{X5pu-`Qd>X)`mSX?@LWMmdlH)#*~?nO zPddl21p*ZPdBpeS9QUSS?XnJBx!KksrwNse%?AHUh8YR5<*pUz20V56tK{8uhXvcL zraA4xbB{3W^FH@hKRRkHK$kO+VCy`B$>f;un9#UaBZ{mzR&Bf1K=>2~d;EX`A5f((LUXqR^T`QO7?5}ji5yjXOPB^}+L51oP*I=b*)tVgNy{ez$ zNas#Uw{1e%MiZ|&Gn@d}SDwi~R!YRJ)^eH;wdeh1Jpf43pXqVw(x^ex{gUEQa`2dO z1{77GIROHb;JO4(;UTp@#8%fMJ9W*Jcd!nlahYu-(j5csj(5S%yQ983TSZSRh0!i; zsEa6v?~;ok<6M*9gMf6}jXxu|gZfuO-P&u7e6%2xx)Xp-OMQnI#iX65zKw{|m8X~u z?JOzb^m^Lbl}CF^&+seLil0EZz`v9B|wdLx1DT zJq0&$C|RWiYnwN6OVc>_*!sYtJ4&^6{Xf)I20zw5XJ5-Ypd>z!&$a77qqD-(RzA?! z`e<00EkQ|CJ`U5N($U1Bb?PG2L-J?{e zX~%D#cZ6eIdv0^Sx^ow!B0Ji% zK14BEy8AIWC-_j&{S9KYRt&-F^h2<3|Q*F^jr(3}c?lIqv{Lx0JVFg6lB^X581 zir@!-S^IHUz`cV~-KiSn8*p99a$hslAYgl`d3q_p`3{Oh?^D_k#e>SK@y4_J?v4tO zjyqaV<5a3}I;|CM)g;AQ_-c+vE`{V7mUPIlx`k5(%Roo@s{1Dw$~#rf)8gNID-nKn z2Y4iLS!J8wMv^5qRlkkkK6Gv~hSsijHRcmVH`v7}v;rY5EY(AL3x5;RhLG;SLhxOF z;G(t4Kv|CG)9ZlW4@Az9)=#(@0NsVBvw<^GfgVpn-3Yl)v*{AtMdPV#pO)66rhv2XM`W}@Ye@#g6#>- z7z}20KXze1uAR~q!1cZ8#V+MWMP<*mj>_OK|CQ5rrQi^AX_Ws&1Jptd2&fvarbG-1 zQe=Mx9}pl4xXgD911a@7V-9}cKXtpoif&aOc8t;x8gbeyCBBs1+Yc{3kaFyAHR*L8 zJXn8_Ik@Cm(Ez{52h3Q_P)P>jX?6CW8)=sS0Xt(3Vt~$}?a2$WH`;g6=e5Jr0>Xa% zFP8W(AMyX@8s7eIndARTP^{YfT&VnhP-Gqjg(o0T>KwUu69(seff-v$%Uq-u4B^dv zekmKr80^5p&wuWvgt7SPa3j1SbpDiXRFg58>IYzC_R>SU@>*P z5Ukgea6PJ8fH6Oi(``&JKaxhR{0QP4#w%OZ{&zkemX*pn@eOfR&N@$FGtF-*t5iQJdQ8OZsN@dPamSE716cmuL0kv zrQ))VVGekjii>h85mFU#1nG%gn&8e7brC3d zgkiR=qHcI`xqoE%E#&q!_e<1(^mpEp*9;2B1bSj<^q5aH{?J~p&ulz?cjY|=ag-OO zWCkvR&t|KPzn8`dT_~@}JM#dzvEFw0wWO+WzJ|6EhNX@X6VP ztnj^J1aX@@jDKYju$;a0_0RFU;8vpb{yfYr0FGPq$W}#WNBapRX**jTZIZQHT00NF zk-g1`LZA>};fj;^>1>*Wl%Bt8)^VE}* z92=zhqfj{KCz$tq4=B_JO}7W$`=!Xm?`auMxm2-l`Yr1OO=-;>jyz8$@VCEZ-Q_J_ z)-@Mzq-#m+ct4GMgafFUl2C$7-nPHu9ls0&S$q3T1=5J7nEppP_g>qWQfOqw904UK zGx<_H_yg!JR>hG+p2qEtQiD9oh}CO~`e*UqoJ?i5AITETQ{y$sm1oQGV)qIplXT`1 ze+h*dttLMHWQGH3UF_v7-6abHa!GTrE7KAN=89B0?tDUU%{8@@%;hMn6+b%2V@QEO>6LfTg+KcJ5yxAKiLL?riMv*|RZT z%G=|o?;a-PnROL&R|)J!BqcR|dKQxh-7zFssd%%LZ+VJGqUA{W9+p$E?bYfGv^u=h zO({Q6(BNJF7opiuFSfAp_z6$%^>I3%iWm_g?J^H6RKHY@M;Z z-)DmynTOY+cO-sMg5o2oo9{$TjC+kV_HiC;xrG&+gaHACl{=R%AucQ{>wweEK;B>u zu>l@vf3WAgap6G6qv(hLK*aB+MNZWSRkpAaxq3I~x}gIg49>Alx%Bz#*)>2~qGX;q z5RJAu2NlqU6>)4cQIBUm-oqD7ye5v&{&T`r&+4-P`H$V*MZp&?_E9w|^#Vr* z5AqKEE_}PmPpb4!<9wJtCIBF{Mq84jHIlrt@=oy*03fR`z`|@wl}`K@=mP+1qt&bq z8#eW5PSmf0nW~qt=3AE~R6ODba^Q+9O1sMbD&FMOzzQ?*;76mTa%s(w%tAarrmb+5 z&li@Mn<;~M>5VB4O(TQxR_o^zyP&+rpNbQyjm5e4%X!JEAsYu}lAuAkA@+}~Opbpp zK;cE9qUnFEDVa6iCt<2c*hXqiO3r0-{dT!Ak-x3{n8*or&*b3`J#-{+c^Gh1#{~Gv zC!1{!Dc-eqP)0Y`xh?5;QkNBq^d+AmIr!B;uV^l;)L{`?xwoU{te$8Y?K1vt?WLZm zMOoy}VGwI{L1Crwbz7~AL@3y@J|gkW(jt|Ah+zalW`3a-pS#f$P;Zn!L|6pPTMAa~4)ACZiK2 z9^zojTy$ch^7$#V7L+nwi}nwP&=Q5LU*_@Mk)#>p|@(WdcBP<5>-}ZCe~4! zkXo8y7iije+69@4NQr2ggr=`+u%P=l(X%dmM^?K-YHc5~qRC(__M5@(XO{t#o;Vh(Qq7#v`A`dZ|=2r1)h&dQxA6* zZnzHZzo=jlZIxUo*sk@+4TvWot#+&db8!UMt83^3rAUJ4^GMm)CnBoMWh+==f`~(3 z-<i??+0NZiZd_GifHQ}@0XeC+l zo{UgkpQ!RmnBlAAe^n5jQQ$rItNuO)x#m|!kAi;^Th$X7r1Bo6#Ds01@u{;j+zw-XoAOGE;G=H+lJ^RUspNN?QrNt?Z^AR5zey|KD^=Fxi6 zwJNX*;RGGL*p>#@`Hb?C5Y;VRJe&@iwqXCywJdljx-RqAl@E5uz_yFCS&` zZOu^n{jlYIdm;B5sZ-HPhx1o7!2xN#S!>j%*?{O>zerX_DQm+mB?xvkUJ& zFTS3tB8shu!8J5X0N_0}3E7ZCkDGUJ`m#Y>;d`{5kOHxNy^M`(VRH>GIK<>EXuxFY zekB`AKs4$Lb9g1QdDlj|({jau6)x@cZi@fU*)z|am^z!dL*H`6%knGKE14fm=F<8f zW-$jcWunZJ=N{|BGt~{V#d$jj{FN$^RoP^lql+VoSq1L6pUkEG+Xpky7DI)b3f?D~ zB9_7Ofc2~P#l+O`9-VX=~`dRg~bizdd z=@uS8r&RZ)UqyKku-!7UTHVMN-lHc}Dt42~E#1$e!F7N^-i2tk-G|Z0->&dEPE;VKp$Z1@+Jf7_(5al=!{i(Ls2**;?SjVEZ=( z68r8W7Gh#U)+c-L@BE>)y-|W}Qe}M=U2Dwp*JTC#iWn*cM2kLbC6Rut?Q36P9~3e8 zER0`B!Jvl9e25khv*IOj>0iX~e|V)w7um1_0b7!jS@(gzADR7QxH?}Mq5w0%*gJqP zyKb0H%py{pS_B!U@Gm;SDnGz`_smrO%{T=9{a{EBKz=khZLB~FAj4$+d2%kW@!fcz zwNi#O;ZkL9jab;AhNE?GkjQNq=tO)u(CEZVP&({<&(pAP(3420**%X3{wCV2P$ws< z_F|SxDr9z;4+>|1!pE+90hC9v9Su8P8`S}!hlMCv`?a}G&o;be;hf7Kowkp7=$VSB zKQoRf0cWnMRVA?y`BsUtK^E{7G+SM(kDHCsl;IbCeC=zB6<{@)XoAz91k0;j8Rzl7 zU{ww*_40*aVrz>%3qC;Q+u9 z(HOAsokKWXsY+?hw$~-%gzBQ{uucdCo+QMJ_Xl^lN|17~1 zH@V2x#!=I(skTD&n>g3WPz{^Cx?Zmc09{^56@kP`XpTf)obS4n8U*e1Ma*zCYNhIO z+KO2*s5KmPC1299#rc3ai&HLrrXI4b&bcdtFQT^?qaW|Ho2{0JwEfB@m;_f zJ|S}<=SlvYYg?NF@w?9Gs^#QaOfi9NzgSdGT0e|=lJei+Rp8yxbI&`NNb)RGk@-&U zs8{mU{sOLw-0S1T^W}`q9kxOZtj1aH6(PjT7Hy~@U}qHn2sluK@7`%toU}3FJE9ri zxs7-!iYu~%$5+88?Uto~S9P227vk^lchVJojh8-j1#xSug@syyfnjnD%G{+XnC&Bk zgu_KaPwVP{V%O3w4)`-EkA>@iDINX1$GutM6@M>IZifG-P5&S6rT=?s&V!Uo>d_$H UI$cSYe{a>*G<;g6Zu|Cs0GBc0FaQ7m literal 12120 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 diff --git a/textures/custom_font/custom_font_tiny.rgba32.png b/textures/custom_font/custom_font_tiny.rgba32.png index 43e8a70f7a0d1469f18640c143e92241144295ee..843af03e04591bf74b32c3c6ef643a67ab319ea7 100644 GIT binary patch delta 3192 zcmV-;42Sca9{d=PBYzA-NkliEbmg4n;jd{{M>&yoYI^GKyLxFBdIUz6WS@ zVoDUXQM>bc5kd$dg#5+({r=zR=acx?F3UDej>!7|Pvvy`$XR@~zetUz!U=dfAYz9% z?eFjJecp)X4jLcPVa4~Rj|?Cc_MeO=e^QvT${&T-Iu`p>r`L0jCqH66WHPOdkk-he8s*S|=#GR%98@H&8Dt#*YJa9fW=H>sZQ0Qg zomxk1tBB+6@PF<9-(G{H#ILn+Z;ZCSZo~W-G+1L_I(DqTuQdDr??QFCN&5PB;MFI^ z@8wz=R{KHho8`o{deHKQwEb45)sLF@>c(R1XvDn_eJ*gl>%3tE9^RFBL6 zqN5x#`dZ5a?-E0kS<5_2SA+_E(HX`lPs4o9S^}tv4u8_N8j#n{6%HU9zrt~39YE#M z#+rgg<4d1qmLzDLC@Xa#4xo2*KgWR4X<|FI(x!%}F6&4xV&mytWp`r!vP+1oJ|nZY zCx9%0kpTdy2jr-GxZIvK`m}OwY0&>{jUcl|^_lCxiW!zBM`hmWzZHMCe($Il(T9}( z%vl9G{eO=v?b4foFJNRwH}OW{MjJSuO+hqlcBDHMWCN{rtln81xf4Y9J9nnqF zinROBGCFTZ$U?(8Y=CrznO^3~#? zvQZvJX}^?vM>9) z^qEI)g|o^Q(JRIMY6DeFKce5fQ^o2F4sYpt-#xdM^Gr05o%vLh$IWSc7WQC{32lza*R8 z!B^^%;#(ur%1$cDtgE=E4-~V2-vZ2>x>t-sR~TuH){Y`YX5WS@&lN8|r7+RE?j9Sm z&TI5JVs}NpW|vQtUwZ(^E@#z-N3T00w|~OSF7y@qC$HohF#Dtz4R@l0J+b4R!q-(e zE8e5uu_BNLeQLw^VA;Pjk2~AV9owu2a}%&7GgVqqkUKZDPH+O5{AHf2R-~VeFD(J9 z_fRr*d_o)f3*_80vq9(Cyo;Q%BI#?oD)ML9>&+ixbJRX5%oPr44_f?)4HfoFzkefT zm9Y=C5ed7)IXrf1L&z%HjvM*n8BcsI?Whhy2qA zgjUKkIx4PU&RFO7S8%0ks=Lb9Q#RT1k6eo$#qZ--SA!A#(d8eCw!GESM_$32Hd+;6 zHNu&4beB}?Kxq1C8mR54?yM7-UH&hl9!a6(&-@k-PDpaA^C@){zvy>V==CzfY?Vi#R69 z5tsaR;4GJ6C$E)fb@GZ1IveDhfUIz{(aOo1p;S4k6+|8jY0kp62Y?h*M0Uasd&Y}b zi~_Sq@|ljN;@hIKopeUrK7W42NMB8BQ{RtZd#!aIQn5(oYHP4)+@*Y zjsBP9Ui;BCc(iP!b*I-xDU7FXkoB*WhSfGR?Xz;9z!T3Zk4J!X@CyBREjaH6)LrwL zrD6sbaiWOxBnGn!@y-?GaR7JDXOvIPHz9=lhMtg+4<)nN(1Hi2E%i5Ps|I;@}UCb&ZZq{J1 zjC`y*Cc8zwAm^PsETS^0M#V6;iHQ%(q($E}tC9Dm(%>>h1SmuJ{LLk`Qe zW04bnzdl1Y=(IJ^Ecwo~UrKq6bd2ItUe~#_HgdB37TnfwLP!pFenJQ#gb+dqA%qY@ z;_ytJJci*_SMc2biaWY8i(Uuzr00pB)m=j7th8bmKdQen?OA8Zo^xi%;KlY;*-)W7 zl13W`xmU3({eNwzVHFJ&k$En=tWJA&2d+pzQhr(cR>ilN)d5kxRwqW{S{HdF-N*%> zTVDbqM5ChfA#qU$=dq!WCpjQZcg6v@{x;w)`3Y&CTt1dOJIl$)J=1GVzcosZ!jIxa zJBwAm*8TvJX7rDgB2U%TFgqKFM=r5SzB~V)D(}OgKYt_PtP$9c+eW%2`!y$m z%s8Ylzn%k2JCG;8;F>MB*lpkG@(MBy+)JMqD}U&BFl1&BtzFgS{SAcqF<$$|oSE{S ztZ3j*rqaQ*qoC9w&{@RHXTg#asd7>kHeG!60Y`zXHO6eYX4^{n$s;Wymt-sCRb;=K zEgB!4g?~tEbjv0!eg?=vYU_(rO|O+R=*?N7G})(ZWi3%<8suAjdS4J%1w|{m)3+FvDIsP}H}e|sO8NMT1mTXHKs%VAo!dOGhc=Qx^mN^&}w-%R=2fYJycO_ST% z!$?M5msa@*v-eG2M&qbzSk~xk$tAt$EKeJu zB_~_yc-s-*6DK=^&E(aJfoPWJ) zGpzI;K)=ozc|mE3fR3Ej2(e-Wwa})iC{K4Dp8R>juGp86+O}p>J1`+jEJT+#?#t*O z-+hL>g*nX&xXEJ?EkwRIHEQ@?$u(ZY_>mEvXF zn0BgA(Z10=il+|{AIFM)i<$ZfA%BDrLI@#*5JCtcgd7f!6v!V=KaM4YOd|6Rgxni? z`VT-G-779S5%LF)BKL!J_Su-vf#2O*X)R3ub1NVHY{{%J(z^r6cBv3N*K5)EyB z>+_kyzf_6P|1A=6Q2 zz#VHs7QxO&$o-h|cL4|?gb+dqA%qY@2qAWDr70000AQG^zIOd%s{h>*4N$v!4z&oX_=E+!;05@Q>N z5ZOj%OlSrf+c0?hobx^J_x<7h6W(*4d(U&v{q5d+p6A>oTWdH!uLLgu0N}r3VRi!m zVEaP>aok*gGNrNkPlL_>zNxA0eGd-+Km{F&ZngYkdkP<$CenRe`k`&mQpv*b6O}~F zw^B=B1+Tqp{-)AvZAG;kw>d?hKY6;e;JXeTwtHYF!~qiu3fXD2Nc!27AX1?_Qz%3F zMEXJD^9GLg>eq~B3@*~W^(ct+n%(I+Z`c>U%^v=q{Jdh+`r2Mh2WBPOc5Cm*ZFD@( zLZ2&qD&Z{uz!2YnmYELY<$~)EHw9rEZW+Gge&V*f=F5@Uh|_PYSQV3QsY6fRMDC69 zSL?n**JC>|ou*FMJ7d)Ah_BfMH8l(A41+||EDbr<(kaItMH^cmYmo1%WYsQpQV1m% zm?Gb#_tmpYQm9eZHS@;Jva^et=PGB8sLQXF@l~^}CiBrjv#7h?(@%r@LSwCOH%IZR z5g%k%?dvMOtxzAa4E0~)j+%ZD7wyENaEgci^4zi=Xqe^6aoP7o%|=*zRt$5{%pzaikb1P za5s8UTEL9niD%RDYe!vdkq?4SoJydis@$)cEjlLP|MFz4;f*Z6x={pfvzR+E?*G7& zU1u_x6NY|AF()er)G02?NKYNE?=1xb%ZIar@NJy%?&zV|X=1W+hbm~yyRtiaaDR7q zS6b=l1?;1X_}7rgpi=V|mzdvuJ_H&@^9)jJ!VjZg{cVz`1<4S_FY!44-D{LJuX<^2 zNP_ue8E%-SsFhND?%mB~S!Y?Nr?9h9?|Eb+3R+chz%N#@b%M)vy*9o6)ME=ojjOHT zo0r{k=zJG~6)$w4DLWZnl17rFiM0@3<>^a#nQq@YuuQ8UdcT}AVdRbGNuqR%RDIGe zoQ4}LIJX-1$c!(lXSFNLoFn^EglD0bm^?|0>38hTfy_OPkC2*x1<+5pAk6w@vDS88$NoiO`s?zPvR}BSkov<0 zav52@U}nI29|{g!%vBju5nWQG(#=j-hUtP1@YM$`&{s%(OvDHfN*?<*p%4sq$8@&W zEYlR%iGp~eCZp@Wkq-=hwsmcHcLJo6d_8GP>?Uu^ulij_V~Fs4lfQM!q8g&Gk?Sn+ zS;q_+i=i>;(OmOQjfmcj75`TgNCb*)WQ3bOG!bRf!%gs2ja1a-rHkVwTYUt=@ZOgy zrrt$GaypJPtr)qMciu-#Ex@-vUtS4QZ+=8KJkS)RU!FV1cE;66J5{Gdy1Vho+R{Z) zx)J)!HX}xAPn`bD`>?!?NLpf*n*(ESAuZCa6U#4RB?kO#NE~fa9{R%A%y8YyZIXI~ zG~h9O`aPEE6}{_oYewJCskvu}*zPEuzr|;uK#_T&K=n3!p>R-LSYuijp9X@Z=4lV$ zx3tK%$4RGqX{*FtZEUN?p0={`dLk?NNLYAyA!>mFti6EaP~-=)0fqhpdI?6m$+U9< z{enQfBBRGLvXT8SX8Yg_XxquQ&!LKM1Cr+V)nWFlHG$RdnT#bN&crvsz0l%QPKtiHHNh*?Pe1VW;A4e z*Tjq~k57uE+OQL|l07-jeiK89h-#BhJxWx?k-r*T96O zoT@hsYsgQd#uNc+*~SIFNqDuVCmIoY9lA8!af?mKKN1gw@l8R{{Gfmzgp-fH|OmD~_NjZ9J=na56gWs$%$Bc#xz9Ka^u8#?Cov$$UpHq8di` zN9$@$wjOMkWl!82z$fT;x8!nLK z^#f20ajKo=P9uOS2}faqvM!Xcw*1ZAc@31jmvju?OoMNAtW%~>G?_6ZwpLvL@kdeW^hM@4{7x%~ zpMLMQlBic}peNRFYb!xwFF(g3n5D!#4l2Qy>0cT>q`IZ9Hrb zv5adPDpo*8o02`nrx911I;WSrp*8bIDqfp2+BsJM8~lCZlEll@Y%;uFj+yP?b)p|S zY843?6OC_!i}vP?2Qy09d6~o~DSqI+EpM+-m<36NtuCg5OT$M+0wu!hf$TAHZ|53J zy~~R2(Y&y6m_B3fKGe?gJbNOL-qFlvV=?Qq9&k0=-;a{;^ zB}wk(EBB~!)7Xudfl4@)*j`-NZ#x=-Q5C2Uf*e#f8z~6*6TGTqere~H8*gfJuCZyr z^dR^`SU1QQ>1F6rj?u|*Qd{w23XV`a0oh@Xg)B|nj87t=Pq!e~ykO*Rd+{xZ1LcJF&x`BiGlJYVH`8 z)-@+oVKNzw;bPh~;>v!5^oMgRkNn-x{12t-`jIry#EQy}x*U?X8qX{3%b{nV$pynG;zo$;0hh2KDqSQwK_8jrNH9ln5z#C~BQ{Ay+D5N-v zG1VQIGxvAlh1@JgR@9Q!DE1osP1K;s|1Ex zPkHtvYvvr1tz=)Ym7)^gy66^A1|!$cv~=uE+F+rQkW3dXu9}*n9E9LOf^0UqpA$*F z_iC1LLG%8QFVF~q#+AX=Zocz~41UvGd4xDN`N_g-3%uAVg?=O*gd`W_LBTuX!8l0! zHaE0|K<<=pnJFral8E1F!LSjIu@7njkdTCp$L-Bk=M@P6N0({7y=`$-_m=lRM2 zei}Fo^lN5ief3J-I)y-Vqh`-$&F(=Xk3}nda5+4mP^M=#Z^mvD$`kkTM^c>42G8%u zkC;}q)==Z;hY^q%zJ-7mR}W3y^#JqFII2 zvEBS_iJA3KJHyf4GdiN7lg}^7o65_wqm)Xzj~wS3l-d6j{{`#L95Fb1y<}<;Pq=Q% zNV8E|&fwNz^f9XBOFHYSKm!x|@xmc1_vvaE<|hGbeQQerCytx?`C%m7^Xd{u#eotDdt}814?U^R1ol}j+eG;IB%D!nWUELuBKb(txWT+C; zg+dcg>0xlvqZ8+oQ4uC_I^v$>{Jr1)77@1|ab12q( z;-h|`hyC1yGn7c7e^^?xSAW$uKCpiGV8=(x1zO`z#Cy{d$Y}X7CneGNqHE3%quIS@ zBhzTlm0z2!!%D8+*?#EQxPOt6+n5r(ezE%0+y7!w|Msf?k;D!@pl+kxi) PJik|9)@HRPcb@zU-D_?2