diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 8e160c5d4..1d7dfb2dc 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -8207,7 +8207,13 @@ HOOK_MARIO_OVERRIDE_FLOOR_CLASS = 56 --- @type LuaHookedEventType HOOK_ON_ADD_SURFACE = 57 --- @type LuaHookedEventType HOOK_ON_CLEAR_AREAS = 58 --- @type LuaHookedEventType HOOK_ON_PACKET_BYTESTRING_RECEIVE = 59 --- @type LuaHookedEventType -HOOK_MAX = 60 --- @type LuaHookedEventType +HOOK_ON_FIND_WALL_COLLISION = 60 --- @type LuaHookedEventType +HOOK_ON_FIND_CEIL = 61 --- @type LuaHookedEventType +HOOK_ON_FIND_FLOOR = 62 --- @type LuaHookedEventType +HOOK_ON_FIND_WATER_LEVEL = 63 --- @type LuaHookedEventType +HOOK_ON_FIND_POISON_GAS_LEVEL = 64 --- @type LuaHookedEventType +HOOK_ON_FIND_SURFACE_ON_RAY = 65 --- @type LuaHookedEventType +HOOK_MAX = 66 --- @type LuaHookedEventType --- @alias LuaHookedEventType --- | `HOOK_UPDATE` @@ -8270,6 +8276,12 @@ HOOK_MAX = 60 --- @type LuaHookedEventType --- | `HOOK_ON_ADD_SURFACE` --- | `HOOK_ON_CLEAR_AREAS` --- | `HOOK_ON_PACKET_BYTESTRING_RECEIVE` +--- | `HOOK_ON_FIND_WALL_COLLISION` +--- | `HOOK_ON_FIND_CEIL` +--- | `HOOK_ON_FIND_FLOOR` +--- | `HOOK_ON_FIND_WATER_LEVEL` +--- | `HOOK_ON_FIND_POISON_GAS_LEVEL` +--- | `HOOK_ON_FIND_SURFACE_ON_RAY` --- | `HOOK_MAX` --- @type integer @@ -11254,7 +11266,7 @@ COOP_OBJ_FLAG_NON_SYNC = (1 << 2) COOP_OBJ_FLAG_INITIALIZED = (1 << 3) --- @type string -SM64COOPDX_VERSION = "v1.4.1" +SM64COOPDX_VERSION = "v1.4.2" --- @type string VERSION_TEXT = "v" diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 746a8f4c4..08b0b1587 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -10208,6 +10208,12 @@ function smlua_audio_utils_replace_sequence(sequenceId, bankId, defaultVolume, m -- ... end +--- @return integer +--- Allocates a new sequence ID +function smlua_audio_utils_allocate_sequence() + -- ... +end + --- @param filename string --- @return ModAudio --- Loads an `audio` stream by `filename` (with extension) @@ -11647,6 +11653,14 @@ function get_active_mod() -- ... end +--- @param mod Mod +--- @param subDirectory? string +--- @return table +--- Gets all files a mod contains +function get_mod_files(mod, subDirectory) + -- ... +end + --- @param title string --- Sets the window title to a custom title function set_window_title(title) diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua index 2ba4a4366..2ce161f8a 100644 --- a/autogen/lua_definitions/manual.lua +++ b/autogen/lua_definitions/manual.lua @@ -127,7 +127,7 @@ function update_chat_command_description(command, description) end --- @param hookEventType LuaHookedEventType When a function should run ---- @param func fun(...: any): any The function to run +--- @param func fun(...: any): any?, any? The function to run --- Different hooks can pass in different parameters and have different return values. Be sure to read the hooks guide for more information. function hook_event(hookEventType, func) -- ... diff --git a/data/dynos_bin_col.cpp b/data/dynos_bin_col.cpp index cd5a58e50..331cbb130 100644 --- a/data/dynos_bin_col.cpp +++ b/data/dynos_bin_col.cpp @@ -3,7 +3,9 @@ extern "C" { #include "include/surface_terrains.h" #include "include/level_misc_macros.h" +#include "include/special_presets.h" #include "include/special_preset_names.h" +#include "src/engine/surface_load.h" } // Free data pointers, but keep nodes and tokens intact @@ -34,12 +36,32 @@ struct CollisionValidationData { u32 vtxCount; u32 triAlloc; u32 triCount; + s16 surfaceType; u32 specialAlloc; u32 specialCount; u32 waterBoxAlloc; u32 waterBoxCount; }; +static u8 GetSpecialObjectType(u8 preset) { + for (s32 i = 0; i < ARRAY_COUNT(SpecialObjectPresets); ++i) { + if (SpecialObjectPresets[i].preset_id == preset) { + return SpecialObjectPresets[i].type; + } + } + return SPTYPE_UNKNOWN; +} + +static const char *GetCorrectSpecialObjectCommand(u8 presetType) { + switch (presetType) { + case SPTYPE_NO_YROT_OR_PARAMS: return "SPECIAL_OBJECT"; + case SPTYPE_YROT_NO_PARAMS: return "SPECIAL_OBJECT_WITH_YAW"; + case SPTYPE_PARAMS_AND_YROT: return "SPECIAL_OBJECT_WITH_YAW_AND_PARAM"; + case SPTYPE_DEF_PARAM_AND_YROT: return "SPECIAL_OBJECT_WITH_YAW"; + default: return ""; + } +} + static void ValidateColSectionChange(GfxData* aGfxData, struct CollisionValidationData& aColValData, u8 section) { if (aColValData.section == COL_SECTION_END) { PrintDataError("Found new col section after COL_END"); @@ -67,51 +89,70 @@ static void ValidateColInit(GfxData* aGfxData, struct CollisionValidationData& a ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_VTX); } -static void ValidateColVertexInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0) { +static void ValidateColVertexInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 vertexCount) { if (strcmp(aColValData.lastSymbol, "COL_INIT") != 0) { PrintDataError("COL_VERTEX_INIT found outside of vertex section"); } - if (arg0 < 0) { - PrintDataError("COL_VERTEX_INIT with a negative count: %d", arg0); + if (vertexCount < 0) { + PrintDataError("COL_VERTEX_INIT with a negative count: %d", vertexCount); } - aColValData.vtxAlloc = arg0; + aColValData.vtxAlloc = vertexCount; aColValData.vtxCount = 0; } -static void ValidateColVertex(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2) { +static void ValidateColVertex(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 x, s16 y, s16 z) { if (aColValData.section != COL_SECTION_VTX) { PrintDataError("COL_VERTEX found outside of vertex section"); } aColValData.vtxCount++; } -static void ValidateColTriInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1) { - if (arg1 < 0) { - PrintDataError("COL_TRI_INIT with a negative count: %d", arg1); +static void ValidateColTriInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 surfaceType, s16 triangleCount) { + if (triangleCount < 0) { + PrintDataError("COL_TRI_INIT with a negative count: %d", triangleCount); } ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_TRI); - aColValData.triAlloc = arg1; + aColValData.triAlloc = triangleCount; aColValData.triCount = 0; + aColValData.surfaceType = surfaceType; } -static void ValidateColTri(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2) { +static void ValidateColTri(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 vertex0, s16 vertex1, s16 vertex2) { if (aColValData.section != COL_SECTION_TRI) { PrintDataError("COL_TRI found outside of triangle section"); } - if (arg0 < 0 || arg0 > aColValData.vtxCount) { - PrintDataError("COL_TRI used vertex outside of known range for first param: %d", arg0); + if (surface_has_force(aColValData.surfaceType)) { + PrintDataError("COL_TRI cannot be used by surface types with a force parameter: %d (use COL_TRI_SPECIAL instead)", aColValData.surfaceType); } - if (arg1 < 0 || arg1 > aColValData.vtxCount) { - PrintDataError("COL_TRI used vertex outside of known range for second param: %d", arg1); + if (vertex0 < 0 || vertex0 > aColValData.vtxCount) { + PrintDataError("COL_TRI used vertex outside of known range for first param: %d", vertex0); } - if (arg2 < 0 || arg2 > aColValData.vtxCount) { - PrintDataError("COL_TRI used vertex outside of known range for third param: %d", arg2); + if (vertex1 < 0 || vertex1 > aColValData.vtxCount) { + PrintDataError("COL_TRI used vertex outside of known range for second param: %d", vertex1); + } + if (vertex2 < 0 || vertex2 > aColValData.vtxCount) { + PrintDataError("COL_TRI used vertex outside of known range for third param: %d", vertex2); } aColValData.triCount++; } -static void ValidateColTriSpecial(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3) { - ValidateColTri(aGfxData, aColValData, arg0, arg1, arg2); +static void ValidateColTriSpecial(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 vertex0, s16 vertex1, s16 vertex2, s16 force) { + if (aColValData.section != COL_SECTION_TRI) { + PrintDataError("COL_TRI_SPECIAL found outside of triangle section"); + } + if (!surface_has_force(aColValData.surfaceType)) { + PrintDataError("COL_TRI_SPECIAL cannot be used by surface types with no force parameter: %d (use COL_TRI instead)", aColValData.surfaceType); + } + if (vertex0 < 0 || vertex0 > aColValData.vtxCount) { + PrintDataError("COL_TRI_SPECIAL used vertex outside of known range for first param: %d", vertex0); + } + if (vertex1 < 0 || vertex1 > aColValData.vtxCount) { + PrintDataError("COL_TRI_SPECIAL used vertex outside of known range for second param: %d", vertex1); + } + if (vertex2 < 0 || vertex2 > aColValData.vtxCount) { + PrintDataError("COL_TRI_SPECIAL used vertex outside of known range for third param: %d", vertex2); + } + aColValData.triCount++; } static void ValidateColStop(GfxData* aGfxData, struct CollisionValidationData& aColValData) { @@ -122,49 +163,70 @@ static void ValidateColEnd(GfxData* aGfxData, struct CollisionValidationData& aC ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_END); } -static void ValidateColSpecialInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0) { - if (arg0 < 0) { - PrintDataError("COL_SPECIAL_INIT with a negative count: %d", arg0); +static void ValidateColSpecialInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 specialCount) { + if (specialCount < 0) { + PrintDataError("COL_SPECIAL_INIT with a negative count: %d", specialCount); } ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_SPECIAL); - aColValData.specialAlloc = arg0; + aColValData.specialAlloc = specialCount; aColValData.specialCount = 0; } -static void ValidateColWaterBoxInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0) { - if (arg0 < 0) { - PrintDataError("COL_WATER_BOX_INIT with a negative count: %d", arg0); +static void ValidateColWaterBoxInit(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 waterBoxCount) { + if (waterBoxCount < 0) { + PrintDataError("COL_WATER_BOX_INIT with a negative count: %d", waterBoxCount); } ValidateColSectionChange(aGfxData, aColValData, COL_SECTION_WATER_BOX); - aColValData.waterBoxAlloc = arg0; + aColValData.waterBoxAlloc = waterBoxCount; aColValData.waterBoxCount = 0; } -static void ValidateColWaterBox(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5) { +static void ValidateColWaterBox(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 id, s16 x1, s16 z1, s16 x2, s16 z2, s16 y) { if (aColValData.section != COL_SECTION_WATER_BOX) { PrintDataError("COL_WATER_BOX found outside of water box section"); } aColValData.waterBoxCount++; } -static void ValidateColSpecialObject(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3) { +static void ValidateColSpecialObject(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 preset, s16 posX, s16 posY, s16 posZ) { if (aColValData.section != COL_SECTION_SPECIAL) { PrintDataError("SPECIAL_OBJECT found outside of special section"); } - aColValData.specialCount++; -} - -static void ValidateColSpecialObjectWithYaw(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3, s16 arg4) { - if (aColValData.section != COL_SECTION_SPECIAL) { - PrintDataError("SPECIAL_OBJECT_WITH_YAW found outside of special section"); + u8 presetType = GetSpecialObjectType(preset); + if (presetType == SPTYPE_UNKNOWN) { + PrintDataError("SPECIAL_OBJECT has invalid preset: %d", preset); + } + if (presetType != SPTYPE_NO_YROT_OR_PARAMS) { + PrintDataError("SPECIAL_OBJECT cannot be used with preset: %d (use %s instead)", preset, GetCorrectSpecialObjectCommand(presetType)); } aColValData.specialCount++; } -static void ValidateColSpecialObjectWithYawAndParam(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 arg0, s16 arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5) { +static void ValidateColSpecialObjectWithYaw(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 preset, s16 posX, s16 posY, s16 posZ, s16 yaw) { + if (aColValData.section != COL_SECTION_SPECIAL) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW found outside of special section"); + } + u8 presetType = GetSpecialObjectType(preset); + if (presetType == SPTYPE_UNKNOWN) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW has invalid preset: %d", preset); + } + if (presetType != SPTYPE_YROT_NO_PARAMS && presetType != SPTYPE_DEF_PARAM_AND_YROT) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW cannot be used with preset: %d (use %s instead)", preset, GetCorrectSpecialObjectCommand(presetType)); + } + aColValData.specialCount++; +} + +static void ValidateColSpecialObjectWithYawAndParam(GfxData* aGfxData, struct CollisionValidationData& aColValData, s16 preset, s16 posX, s16 posY, s16 posZ, s16 yaw, s16 param) { if (aColValData.section != COL_SECTION_SPECIAL) { PrintDataError("SPECIAL_OBJECT_WITH_YAW_AND_PARAM found outside of special section"); } + u8 presetType = GetSpecialObjectType(preset); + if (presetType == SPTYPE_UNKNOWN) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW_AND_PARAM has invalid preset: %d", preset); + } + if (presetType != SPTYPE_PARAMS_AND_YROT) { + PrintDataError("SPECIAL_OBJECT_WITH_YAW_AND_PARAM cannot be used with preset: %d (use %s instead)", preset, GetCorrectSpecialObjectCommand(presetType)); + } aColValData.specialCount++; } diff --git a/data/dynos_mgr_anim.cpp b/data/dynos_mgr_anim.cpp index af7f319f4..21db5f690 100644 --- a/data/dynos_mgr_anim.cpp +++ b/data/dynos_mgr_anim.cpp @@ -8,7 +8,7 @@ extern "C" { #include "behavior_data.h" #include "pc/lua/smlua_hooks.h" -s8 geo_get_processing_mario_index(void); +s8 geo_get_processing_mario_index(struct Object *obj); } // @@ -77,7 +77,7 @@ void DynOS_Anim_Swap(void *aPtr) { // Animation index s32 _AnimIndex = -1; - s8 index = geo_get_processing_mario_index(); + s8 index = geo_get_processing_mario_index(_Object); if (index != -1) { _AnimIndex = RetrieveCurrentMarioAnimationIndex(index); diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 0c92edac9..cf00279f7 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -3537,7 +3537,13 @@ | HOOK_ON_ADD_SURFACE | 57 | | HOOK_ON_CLEAR_AREAS | 58 | | HOOK_ON_PACKET_BYTESTRING_RECEIVE | 59 | -| HOOK_MAX | 60 | +| HOOK_ON_FIND_WALL_COLLISION | 60 | +| HOOK_ON_FIND_CEIL | 61 | +| HOOK_ON_FIND_FLOOR | 62 | +| HOOK_ON_FIND_WATER_LEVEL | 63 | +| HOOK_ON_FIND_POISON_GAS_LEVEL | 64 | +| HOOK_ON_FIND_SURFACE_ON_RAY | 65 | +| HOOK_MAX | 66 | - MAX_HOOKED_BEHAVIORS [:arrow_up_small:](#) diff --git a/docs/lua/functions-6.md b/docs/lua/functions-6.md index 41b220737..a639b7db9 100644 --- a/docs/lua/functions-6.md +++ b/docs/lua/functions-6.md @@ -5616,6 +5616,27 @@ Replaces the sequence corresponding to `sequenceId` with one called `m64Name`.m6
+## [smlua_audio_utils_allocate_sequence](#smlua_audio_utils_allocate_sequence) + +### Description +Allocates a new sequence ID + +### Lua Example +`local integerValue = smlua_audio_utils_allocate_sequence()` + +### Parameters +- None + +### Returns +- `integer` + +### C Prototype +`u8 smlua_audio_utils_allocate_sequence(void);` + +[:arrow_up_small:](#) + +
+ ## [audio_stream_load](#audio_stream_load) ### Description diff --git a/docs/lua/functions-7.md b/docs/lua/functions-7.md index 207edbb6f..1dd27c2d7 100644 --- a/docs/lua/functions-7.md +++ b/docs/lua/functions-7.md @@ -2107,6 +2107,30 @@ Gets the mod currently being processed
+## [get_mod_files](#get_mod_files) + +### Description +Gets all files a mod contains + +### Lua Example +`local tableValue = get_mod_files(mod, subDirectory)` + +### Parameters +| Field | Type | +| ----- | ---- | +| mod | [Mod](structs.md#Mod) | +| subDirectory | `string` | + +### Returns +- `table` + +### C Prototype +`LuaTable get_mod_files(struct Mod* mod, OPTIONAL const char* subDirectory);` + +[:arrow_up_small:](#) + +
+ ## [set_window_title](#set_window_title) ### Description diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 4076375df..b463ac63e 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1828,6 +1828,7 @@ - smlua_audio_utils.h - [smlua_audio_utils_reset_all](functions-6.md#smlua_audio_utils_reset_all) - [smlua_audio_utils_replace_sequence](functions-6.md#smlua_audio_utils_replace_sequence) + - [smlua_audio_utils_allocate_sequence](functions-6.md#smlua_audio_utils_allocate_sequence) - [audio_stream_load](functions-6.md#audio_stream_load) - [audio_stream_destroy](functions-6.md#audio_stream_destroy) - [audio_stream_play](functions-6.md#audio_stream_play) @@ -2071,6 +2072,7 @@ - [set_environment_region](functions-7.md#set_environment_region) - [mod_file_exists](functions-7.md#mod_file_exists) - [get_active_mod](functions-7.md#get_active_mod) + - [get_mod_files](functions-7.md#get_mod_files) - [set_window_title](functions-7.md#set_window_title) - [reset_window_title](functions-7.md#reset_window_title) - [get_os_name](functions-7.md#get_os_name) diff --git a/docs/lua/guides/hooks.md b/docs/lua/guides/hooks.md index af1226bae..07d522650 100644 --- a/docs/lua/guides/hooks.md +++ b/docs/lua/guides/hooks.md @@ -151,6 +151,12 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh | HOOK_MARIO_OVERRIDE_FLOOR_CLASS | Called when Mario's floor class logic updates, return a `SURFACE_CLASS_*` constant to override the type. | [MarioState](../structs.md#MarioState) mario, `integer` surfaceClass | | HOOK_ON_ADD_SURFACE | Called when collision surfaces are added. | [Surface](../structs.md#Surface) surface, `boolean` dynamic | | HOOK_ON_CLEAR_AREAS | Called when a level's areas are unloaded. | None | +| HOOK_ON_FIND_WALL_COLLISION | Called after wall collision detection completes. You can modify the `colData` fields directly. Return a number to override `numCollisions` | `number` posX, `number` posY, `number` posZ, [WallCollisionData](../structs.md#WallCollisionData) colData | +| HOOK_ON_FIND_CEIL | Called after ceiling detection completes. Return `height` to override height, or `height, surface` to override both | `number` posX, `number` posY, `number` posZ, [Surface](../structs.md#Surface) ceil, `number` height | +| HOOK_ON_FIND_FLOOR | Called after floor detection completes. Return `height` to override height, or `height, surface` to override both | `number` posX, `number` posY, `number` posZ, [Surface](../structs.md#Surface) floor, `number` height | +| HOOK_ON_FIND_WATER_LEVEL | Called after water level detection completes. Return a number to override the water level | `number` x, `number` z, `number` waterLevel | +| HOOK_ON_FIND_POISON_GAS_LEVEL | Called after poison gas level detection completes. Return a number to override the gas level | `number` x, `number` z, `number` gasLevel | +| HOOK_ON_FIND_SURFACE_ON_RAY | Called after ray-surface intersection completes. Return `surface` to override the hit surface, or `surface, hitPos` to override both | `Vec3f` orig, `Vec3f` dir, [Surface](../structs.md#Surface) hitSurface, `Vec3f` hitPos | ### Parameters diff --git a/lang/Czech.ini b/lang/Czech.ini index ddf7ae547..81a2e89a3 100644 --- a/lang/Czech.ini +++ b/lang/Czech.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Ovládání" EXTRA_BINDS = "Extra Ovládání" BACKGROUND_GAMEPAD = "Ovladač v pozadí" DISABLE_GAMEPADS = "Zakažte gamepady" +EXTENDED_REPORTS = "Rozšířené zprávy" GAMEPAD = "Použít ovladač" DEADZONE = "Deadzone" RUMBLE_STRENGTH = "Síla vibrace" diff --git a/lang/Dutch.ini b/lang/Dutch.ini index a06530f8b..c4e32f77c 100644 --- a/lang/Dutch.ini +++ b/lang/Dutch.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Toetsen" EXTRA_BINDS = "Extra Toetsen" BACKGROUND_GAMEPAD = "Achtergrond Gamepad" DISABLE_GAMEPADS = "Gamepads uitschakelen" +EXTENDED_REPORTS = "Uitgebreide rapporten" GAMEPAD = "Gamepad" DEADZONE = "Doode-zone" RUMBLE_STRENGTH = "Rommel Kracht" diff --git a/lang/English.ini b/lang/English.ini index 14ec1d462..0560d9ac2 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -100,6 +100,7 @@ N64_BINDS = "N64 Binds" EXTRA_BINDS = "Extra Binds" BACKGROUND_GAMEPAD = "Background Gamepad" DISABLE_GAMEPADS = "Disable Gamepads" +EXTENDED_REPORTS = "Extended Reports" GAMEPAD = "Gamepad" DEADZONE = "Deadzone" RUMBLE_STRENGTH = "Rumble Strength" diff --git a/lang/French.ini b/lang/French.ini index b0dfe3be2..645ed0c2a 100644 --- a/lang/French.ini +++ b/lang/French.ini @@ -100,6 +100,7 @@ N64_BINDS = "Touches N64" EXTRA_BINDS = "Touches Supplémentaires" BACKGROUND_GAMEPAD = "Manette en arrière plan" DISABLE_GAMEPADS = "Désactiver les manettes de jeu" +EXTENDED_REPORTS = "Rapports détaillés" GAMEPAD = "Manette" DEADZONE = "Zone Morte" RUMBLE_STRENGTH = "Vibrations" diff --git a/lang/German.ini b/lang/German.ini index 46d0bf440..1fb8031d7 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -46,10 +46,10 @@ NAMETAGS_MISSING_PARAMETERS = "Fehlende Parameter: [OPTION]" SELF_KICK = "Du kannst dich nicht selbst kicken." SELF_BAN = "Du kannst dich nicht selbst bannen." SELF_MOD = "Du kannst dich nicht selbst zum Moderator machen." -KICK_CONFIRM = "Bist du sicher, dass du '@' vom Server kicken möchtest?\nGib '\\#a0ffa0\\/bestätigen\\#fff982\\' ein, um fortzufahren." -BAN_CONFIRM = "Bist du sicher, dass du '@' vom Server bannen möchtest?\nGib '\\#a0ffa0\\/bestätigen\\#fff982\\' ein, um fortzufahren." -PERM_BAN_CONFIRM = "Bist du sicher, dass du '@' dauerhaft vom Server bannen möchtest?\nGib '\\#a0ffa0\\/bestätigen\\#fff982\\' ein, um fortzufahren." -MOD_CONFIRM = "Bist du sicher, dass du '@' zum Moderator ernennen möchtest?\nGib '\\#a0ffa0\\/bestätigen\\#fff982\\' ein." +KICK_CONFIRM = "Bist du sicher, dass du '@' vom Server kicken möchtest?\nGib '\\#a0ffa0\\/confirm\\#fff982\\' ein, um fortzufahren." +BAN_CONFIRM = "Bist du sicher, dass du '@' vom Server bannen möchtest?\nGib '\\#a0ffa0\\/confirm\\#fff982\\' ein, um fortzufahren." +PERM_BAN_CONFIRM = "Bist du sicher, dass du '@' dauerhaft vom Server bannen möchtest?\nGib '\\#a0ffa0\\/confirm\\#fff982\\' ein, um fortzufahren." +MOD_CONFIRM = "Bist du sicher, dass du '@' zum Moderator ernennen möchtest?\nGib '\\#a0ffa0\\/confirm\\#fff982\\' ein." PLAYERS_DESC = "/players - Zeige alle Spieler und ihre IDs." KICK_DESC = "/kick [NAME|ID] - Kicke einen Spieler vom Server." BAN_DESC = "/ban [NAME|ID] - Banne einen Spieler vom Server." @@ -99,6 +99,7 @@ CONTROLS = "STEUERUNG" N64_BINDS = "N64-Einstellungen" EXTRA_BINDS = "Zusätzliche Einstellungen" BACKGROUND_GAMEPAD = "Hintergrund-Gamepad" +EXTENDED_REPORTS = "Erweiterte Berichte" DISABLE_GAMEPADS = "Gamepads deaktivieren" GAMEPAD = "Gamepad" DEADZONE = "Tote Zone" diff --git a/lang/Italian.ini b/lang/Italian.ini index e0827a2f6..dfe13d3bb 100644 --- a/lang/Italian.ini +++ b/lang/Italian.ini @@ -98,6 +98,7 @@ CONTROLS = "CONTROLLI" N64_BINDS = "Comandi N64" EXTRA_BINDS = "Comandi Extra" BACKGROUND_GAMEPAD = "Attivi in Background" +EXTENDED_REPORTS = "Rapporti estesi" DISABLE_GAMEPADS = "Disabilita i Gamepad" GAMEPAD = "Controller" DEADZONE = "Zona Morta" diff --git a/lang/Japanese.ini b/lang/Japanese.ini index 322c449e8..05b20a216 100644 --- a/lang/Japanese.ini +++ b/lang/Japanese.ini @@ -1,61 +1,61 @@ [NOTIF] -CONNECTED = "@が接続しました" -DISCONNECTED = "@が切断しました。" -LEFT_THIS_LEVEL = "@がこのコースから出ました。" -ENTERED_THIS_LEVEL = "@がこのコースに入りました。" -ENTERED = "@が\n#に入りました。" -SERVER_CLOSED = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ 部屋が閉じられました。" -DISCORD_ERROR = "Discordエラーが発生しました。\n解決するには、\n1. ゲームを終了し、\n2. Discordを再起動してから、\n3. もう一度ゲームを開いてください。" +CONNECTED = "@ が参加しました" +DISCONNECTED = "@ が退出しました" +LEFT_THIS_LEVEL = "@ がこのコースから出ました" +ENTERED_THIS_LEVEL = "@ がこのコースに入りました" +ENTERED = "@ が \n# に入りました" +SERVER_CLOSED = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ ルームが閉じられました。" +DISCORD_ERROR = "Discordエラーが発生しました。\n解決するには、\n1. ゲームを終了する\n2. Discordを再起動する\n3. もう一度ゲームを開く\nの手順で進めてください。" DISCORD_DETECT = "\\#ffa0a0\\エラー:\\#dcdcdc\\ Discordを検出できませんでした。\n\\#a0a0a0\\ゲームを終了し、Discordを再起動してから、もう一度お試しください。" DISCONNECT_FULL = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ ルームが満員です。" DISCONNECT_KICK = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ キックされました。" DISCONNECT_BAN = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ BANされました。" DISCONNECT_REJOIN = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ 再参加中です…" DISCONNECT_CLOSED = "\\#ffa0a0\\切断されました:\\#dcdcdc\\ ホストが切断しました。" -DISCONNECT_BIG_MOD = "MODの量が多すぎます!\n切断しました。" -DIED = "@がやられた!" -DEBUG_FLY = "@がデバッグ飛行モードに入りました!" -IMPORT_MOD_SUCCESS = "'@'\n\\#a0ffa0\\MODを読み込みました\\#dcdcdc\\" -IMPORT_DYNOS_SUCCESS = "'@'\n\\#a0ffa0\\DynOSのパックを読み込みました\\#dcdcdc\\" -IMPORT_PALETTE_SUCCESS = "'@'\n\\#a0ffa0\\パレットのプリセットを読み込みました\\#dcdcdc\\" -IMPORT_FAIL = "'@'\n\\#ffa0a0\\読み込みに失敗しました。\\#dcdcdc\\" -IMPORT_FAIL_INGAME = "\\#ffa0a0\\ゲーム中はMODを読み込めません" +DISCONNECT_BIG_MOD = "このルームはMODの量が多すぎます。\n切断しました。" +DIED = "@がやられた" +DEBUG_FLY = "@がデバッグ飛行モードに入った" +IMPORT_MOD_SUCCESS = "\\#a0ffa0\\MODをインポートしました:\n\\#dcdcdc\\@" +IMPORT_DYNOS_SUCCESS = "\\#a0ffa0\\DynOSパックをインポートしました:\n\\#dcdcdc\\@" +IMPORT_PALETTE_SUCCESS = "\\#a0ffa0\\パレットプリセットをインポートしました:\n\\#dcdcdc\\@" +IMPORT_FAIL = "\\#ffa0a0\\インポートに失敗しました:\n\\#dcdcdc\\@" +IMPORT_FAIL_INGAME = "\\#ffa0a0\\ゲーム中はMODをインポートできません" COOPNET_CONNECTION_FAILED = "\\#ffa0a0\\CoopNetに接続できませんでした!" -COOPNET_DISCONNECTED = "\\#ffa0a0\\CoopNetとの接続が途絶えました!" +COOPNET_DISCONNECTED = "\\#ffa0a0\\CoopNetとの接続が失われました!" LOBBY_NOT_FOUND = "\\#ffa0a0\\エラー:\\#dcdcdc\\ ルームがすでに閉じられています!" -LOBBY_JOIN_FAILED = "\\#ffa0a0\\ルームに参加できませんでした。" -LOBBY_PASSWORD_INCORRECT = "\\#ffa0a0\\パスワードが間違っています。" -COOPNET_VERSION = "\\#ffa0a0\\あなたのバージョンはCoopNetに対応していません。アップデートしましょう!" -PEER_FAILED = "\\#ffa0a0\\'@'への接続に失敗しました。" +LOBBY_JOIN_FAILED = "\\#ffa0a0\\ルームへの参加に失敗しました!" +LOBBY_PASSWORD_INCORRECT = "\\#ffa0a0\\パスワードが間違っています!" +COOPNET_VERSION = "\\#ffa0a0\\あなたのゲームバージョンはCoopNetに対応していません。アップデートしましょう!" +PEER_FAILED = "\\#ffa0a0\\プレイヤー @ への接続に失敗しました。" UNKNOWN = "未知" -LOBBY_HOST = "部屋主" +LOBBY_HOST = "ルームのホスト" UPDATE_AVAILABLE = "アップデートが利用可能です!" -LATEST_VERSION = "最新バージョン" +LATEST_VERSION = "最新のバージョン" YOUR_VERSION = "あなたのバージョン" [CHAT] -KICKING = "'@'をキックしました!" -BANNING = "'@'をBANしました!" +KICKING = "@ をキックしました!" +BANNING = "@ をBANしました!" SERVER_ONLY = "このコマンドはホストのみが実行できます。" -PERM_BANNING = "'@'を永久BANしました!" -ADD_MODERATOR = "'@'をモデレーターにしました!" -PLAYERS = "プレイヤー" +PERM_BANNING = "@ を永久BANしました!" +ADD_MODERATOR = "@ をモデレーターにしました!" +PLAYERS = "ルーム内のプレイヤー" NO_PERMS = "このコマンドを実行する権限がありません。" PLAYER_NOT_FOUND = "プレイヤーが見つかりませんでした。" -NAMETAGS_MISSING_PARAMETERS = "引数が不足しています: [OPTION]が必要です。" +NAMETAGS_MISSING_PARAMETERS = "引数が不足しています: [OPTION] が必要です。" SELF_KICK = "自分自身はキックできません。" SELF_BAN = "自分自身はBANできません。" SELF_MOD = "自分自身をモデレーターにすることはできません。" -KICK_CONFIRM = "本当に'@'を強制退出させますか?\n実行するには'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" -BAN_CONFIRM = "本当に'@'をBANしますか?\nBANするには'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" -PERM_BAN_CONFIRM = "本当に'@'を永久BANしますか?\nBANするには'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" -MOD_CONFIRM = "本当に'@'をモデレーターにしますか?\n'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" -PLAYERS_DESC = "/players - プレイヤー名とID一覧を表示します。" -KICK_DESC = "/kick [NAME|ID] - プレイヤーを現在のルームからキックします。" -BAN_DESC = "/ban [NAME|ID] - プレイヤーを現在のルームからBANします。" -PERM_BAN_DESC = "/permban [NAME|ID] - プレイヤーをあなたが今後ホストするすべてのルームからBANします。" -MOD_DESC = "/moderator [NAME|ID] - プレイヤーに/kick、/ban、/permbanのようなコマンドの使用を許可します。" -NAMETAGS_DESC = "/nametags [show-tag|show-health] - あなたの体力やネームタグの表示を変更します。" +KICK_CONFIRM = "本当に @ をキックしますか?\n実行するには'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" +BAN_CONFIRM = "本当に @ をBANしますか?\nBANするには'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" +PERM_BAN_CONFIRM = "本当に @ を永久BANしますか?\nBANするには'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" +MOD_CONFIRM = "本当に @ をモデレーターにしますか?\n'\\#a0ffa0\\/confirm\\#fff982\\' と入力して確定します。" +PLAYERS_DESC = "/players - ルーム内のプレイヤー名とIDの一覧を表示します。" +KICK_DESC = "/kick [プレイヤー名|ID] - 指定したプレイヤーを現在のルームからキックします。" +BAN_DESC = "/ban [プレイヤー名|ID] - 指定したプレイヤーを現在のルームからBANします。" +PERM_BAN_DESC = "/permban [プレイヤー名|ID] - 指定したプレイヤーをあなたが今後ホストするすべてのルームからBANします。" +MOD_DESC = "/moderator [プレイヤー名|ID] - 指定したプレイヤーに/kick、/ban、/permbanのようなコマンドの使用を許可します。" +NAMETAGS_DESC = "/nametags [show-tag|show-health] - あなたのネームタグ/体力の表示を変更します。" UNRECOGNIZED = "未知のコマンドです。" MOD_GRANTED = "\\#fff982\\あなたはモデレーターになりました。" @@ -70,8 +70,8 @@ CAMERA = "CAMERA" FREE_CAMERA = "フリーカメラ" ANALOG_CAMERA = "アナログカメラ" FREE_CAMERA_TITLE = "FREE CAMERA" -FREE_CAMERA_L_CENTERING = "Lセンタリング" -FREE_CAMERA_USE_DPAD = "DPad の動作" +FREE_CAMERA_L_CENTERING = "Lボタンで前を向く" +FREE_CAMERA_USE_DPAD = "十字キー操作" FREE_CAMERA_COLLISION = "カメラの衝突" ROMHACK_CAMERA_TITLE = "ROMHACK\nCAMERA" ROMHACK_CAMERA = "ロムハックカメラ" @@ -79,55 +79,55 @@ ROMHACK_CAMERA_AUTOMATIC = "自動" ROMHACK_CAMERA_ON = "オン" ROMHACK_CAMERA_OFF = "オフ" ROMHACK_CAMERA_IN_BOWSER = "クッパ戦で使用" -ROMHACK_CAMERA_COLLISION = "カメラの衝突" -ROMHACK_CAMERA_L_CENTERING = "Lセンタリング" -ROMHACK_CAMERA_USE_DPAD = "DPad の動作" -ROMHACK_CAMERA_SLOW_FALL = "スローフォール" -CAMERA_TOXIC_GAS = "有毒ガスの調整" +ROMHACK_CAMERA_COLLISION = "カメラの当たり判定" +ROMHACK_CAMERA_L_CENTERING = "Lボタンで前を向く" +ROMHACK_CAMERA_USE_DPAD = "十字キー操作" +ROMHACK_CAMERA_SLOW_FALL = "低速落下" +CAMERA_TOXIC_GAS = "有毒ガス内での調整" MOUSE_LOOK = "マウスでの操作" INVERT_X = "X方向のカメラ反転" INVERT_Y = "Y方向のカメラ反転" X_SENSITIVITY = "X方向の感度" Y_SENSITIVITY = "Y方向の感度" -AGGRESSION = "かたさ" -PAN_LEVEL = "カメラのずれ" -DECELERATION = "カメラ減速" -ROMHACK_CAMERA_OFF = "オフ" +AGGRESSION = "カメラの追従性" +PAN_LEVEL = "カメラの水平速度" +DECELERATION = "カメラ減速の強さ" [CONTROLS] CONTROLS = "CONTROLS" -N64_BINDS = "ニンテンドウ64の入力" -EXTRA_BINDS = "追加の入力" +N64_BINDS = "ニンテンドウ64のボタン割り当て" +EXTRA_BINDS = "追加のボタン割り当て" BACKGROUND_GAMEPAD = "バックグラウンドでのコントローラー認識" +EXTENDED_REPORTS = "拡張レポート" DISABLE_GAMEPADS = "コントローラーを無効化" GAMEPAD = "コントローラー" DEADZONE = "デッドゾーン" RUMBLE_STRENGTH = "振動の強さ" CHAT = "チャット" -PLAYERS = "プレイヤーリストの表示" +PLAYERS = "プレイヤーリスト" D_UP = "十字キー 上" D_DOWN = "十字キー 下" D_LEFT = "十字キー 左" D_RIGHT = "十字キー 右" -X = "X" -Y = "Y" +X = "Xボタン" +Y = "Yボタン" CONSOLE = "コンソール" PREV = "前のページ" NEXT = "次のページ" -DISCONNECT = "切断" +DISCONNECT = "ゲームから切断" -UP = "上" -DOWN = "下" -LEFT = "左" -RIGHT = "右" -A = "A" -B = "B" -START = "スタート" -L = "L" -R = "R" -Z = "Z" +UP = "3Dスティック 上" +DOWN = "3Dスティック 下" +LEFT = "3Dスティック 左" +RIGHT = "3Dスティック 右" +A = "Aボタン" +B = "Bボタン" +START = "STARTボタン" +L = "Lトリガー" +R = "Rトリガー" +Z = "Zトリガー" C_UP = "Cボタン 上" C_DOWN = "Cボタン 下" C_LEFT = "Cボタン 左" @@ -135,7 +135,7 @@ C_RIGHT = "Cボタン 右" ANALOG_STICK_OPTIONS = "アナログスティックのオプション" -ROTATE_LEFT = "左スティックを90度回転させる" +ROTATE_LEFT = "左スティックを90度回転" INVERT_LEFT_X = "左スティックX軸の反転" INVERT_LEFT_Y = "左スティックY軸の反転" ROTATE_RIGHT = "右スティックを90度回転" @@ -153,7 +153,7 @@ AUTO = "自動" MANUAL = "手動" UNCAPPED = "無制限" FRAME_LIMIT = "FPSの制限" -FAST = "速い" +FAST = "高速" ACCURATE = "正確" INTERPOLATION = "補間" NEAREST = "ニアレスト" @@ -196,24 +196,24 @@ LOCAL_PLAYER_MODEL_ONLY = "ローカルのキャラモデルに限定" [HOST_MESSAGE] INFO_TITLE = "INFO" -WARN_DISCORD = "招待したいフレンドを右クリックしてn'\\#d0d0ff\\ゲームに招待\\#dcdcdc\\'.\n\nを押すと招待できます。サーバー内のチャンネルにも、チャット横の\\#d0d0ff\\プラス\\#dcdcdc\\マークから招待メッセージを送信できます。\n\nゲーム アクティビティを\\#ffa0a0\\必ず\\#dcdcdc\\有効にしてください。\n\n\nオフラインに設定していると、招待の送信を\\#ffa0a0\\妨げる\\#dcdcdc\\可能性があります。" -WARN_DISCORD2 = "\\#ffa0a0\\エラー:\\#dcdcdc\\Discordを検出できませんでした。\n\\#a0a0a0\\ゲームを終了し、Discordを再起動してから、もう一度お試しください。" -WARN_SOCKET = "ファイアウォール設定が正しく設定されている事をご確認ください。\n直接接続には、ルータのポート転送でIPv4インバウンド接続を受信するように設定する\\#ffa0a0\\必要\\#dcdcdc\\があります。\n\nUDPポート'%d'番を開放してください。IPv6にも対応しています。" +WARN_DISCORD = "招待したいフレンドを右クリックしてn'\\#d0d0ff\\ゲームに招待\\#dcdcdc\\'.\n\nを押すと招待できます。サーバー内のチャンネルにも、チャット入力欄の横にある\\#d0d0ff\\+\\#dcdcdc\\マークから招待メッセージを送信できます。\n\nDiscordのユーザー設定からゲーム アクティビティを\\#ffa0a0\\必ず\\#dcdcdc\\有効にしてください。\n\n\nステータスをオフラインに設定していると、招待の送信が\\#ffa0a0\\妨げられる\\#dcdcdc\\可能性があります。" +WARN_DISCORD2 = "\\#ffa0a0\\エラー:\\#dcdcdc\\Discordを検出できませんでした。\n\\#a0a0a0\\ゲームを終了してDiscordを再起動してから、もう一度お試しください。" +WARN_SOCKET = "ファイアウォールの設定が正しく完了していることを確認してください。\nダイレクト接続には\\#ffa0a0\\あなた自身が\\#dcdcdc\\ルーターでIPv4の接続を受け入れるようにポートフォワーディング設定を行う必要があります。\n\nUDPポート'\\#d0d0ff\\%d\\#dcdcdc\\'を解放してください。IPv6も使用可能です。" HOST = "ルームを作る" [HOST_MODS] MODS = "MODS" CATEGORIES = "カテゴリ一覧" -WARNING = "\\#ffffa0\\<注意>\\#dcdcdc\\ MOD数が10個以上になっています。ラグや不安定を防ぐため、いくつか無効にしてください" -NO_MODS_FOUND = "MODが見つかりませんでした。" +WARNING = "\\#ffffa0\\<注意>\\#dcdcdc\\ MODの数が10個以上になっています。ラグや不安定を防ぐため、いくつか無効にしてください。" +NO_MODS_FOUND = "MODは見つかりませんでした。" [HOST_MOD_CATEGORIES] ALL = "すべて" MISC = "その他" -ROMHACKS = "ハックロム" -GAMEMODES = "ゲームモード" -MOVESETS = "ムーブセット" -CHARACTER_SELECT = "キャラクター選択" +ROMHACKS = "ロムハック系" +GAMEMODES = "ゲームモード系" +MOVESETS = "ムーブセット系" +CHARACTER_SELECT = "追加キャラクター系" [HOST_SAVE] SAVE_TITLE = "SAVE" @@ -222,7 +222,7 @@ CONFIRM = "本当に消しますか?" ERASE = "消す" EDIT = "編集" EDIT_TITLE = "EDIT" -EDIT_NAME = "ファイル名を変更:" +EDIT_NAME = "マリオ @のおなまえ変更:" [HOST_SETTINGS] SETTINGS = "SETTINGS" @@ -235,7 +235,7 @@ NORMAL = "普通" TOO_MUCH = "最強" KNOCKBACK_STRENGTH = "ノックバックの強さ" CLASSIC_PVP = "クラシック" -REVAMPED_PVP = "改良" +REVAMPED_PVP = "改良型" PVP_MODE = "PvPモード" LEAVE_LEVEL = "コースを出る" STAY_IN_LEVEL = "コースに留まる" @@ -243,14 +243,14 @@ NONSTOP = "ノンストップ" ON_STAR_COLLECTION = "スター取得時の動作" SKIP_INTRO_CUTSCENE = "イントロをスキップ" ENABLE_CHEATS = "チートを有効にする" -BUBBLE_ON_DEATH = "やられた時にシャボンで復活" +BUBBLE_ON_DEATH = "ミス時にシャボンで復活" NAMETAGS = "ネームタグを有効にする" MOD_DEV_MODE = "MOD開発モード" -BOUNCY_BOUNDS_ON_CAP = "オン(制限付き)" +BOUNCY_BOUNDS_ON_CAP = "オン(速度制限)" BOUNCY_BOUNDS_ON = "オン" BOUNCY_BOUNDS_OFF = "オフ" BOUNCY_LEVEL_BOUNDS = "コース境界での跳ね返り" -AMOUNT_OF_PLAYERS = "最大人数" +AMOUNT_OF_PLAYERS = "最大ルーム人数" PAUSE_ANYWHERE = "どこでもポーズ" [HOST] @@ -275,7 +275,7 @@ JOINING = "JOINING" [JOIN] JOIN_TITLE = "JOIN" JOIN_DISCORD = "\\#d0d0ff\\Discord\\#dcdcdc\\ロビーへの参加:\n\nゲームを開いたまま、招待メッセージの参加ボタンを押してください。\n\n「ゲームは終了しました」と表示されている場合は、招待を送信した人の名前をクリックして更新してください。" -JOIN_SOCKET = "\\#d0d0ff\\ダイレクト接続\\#dcdcdc\\のIPとポートを入力してください:" +JOIN_SOCKET = "\\#d0d0ff\\ダイレクト接続先\\#dcdcdc\\のIPアドレスとポート番号を入力してください:" JOIN = "参加する" PUBLIC_LOBBIES = "公開ルーム" PRIVATE_LOBBIES = "非公開ルーム" @@ -283,14 +283,14 @@ DIRECT = "ダイレクト接続" [RULES] RULES_TITLE = "RULES" -RULE_1 = "1. 13歳以上であること。" -RULE_2 = "2. 不快な言葉、中傷、攻撃的な言葉を使わないこと。" -RULE_3 = "3. 非公式ビルドを使わないこと。" -RULE_4 = "4. ゲームをエクスプロイトする外部ツールを使用しないこと。" -RULE_5 = "5. 作者の許可なく、非公開MODを公開しないこと。" -RULE_6 = "6. 全てのNSFWコンテンツは禁止です。" -SUBJECT_TO_CHANGE = "ルールはアップデートで変更される可能性があります。" -NOTICE = "公開ルームではルールをお守りください。" +RULE_1 = "1. CoopNetの利用は13歳以上に限ります。" +RULE_2 = "2. ハラスメント(嫌がらせ)、ヘイトスピーチ、差別用語、その他攻撃的な言動は禁止です。" +RULE_3 = "3. CoopNetでは改造(改ざん)されていない正規のsm64coopdxのみが使用可能です。" +RULE_4 = "4. ゲームの脆弱性を悪用するための外部ツールの使用は禁止です。" +RULE_5 = "5. 作者の許可なく、未公開MODでルームをホストしないでください。" +RULE_6 = "6. ポルノやフェティッシュなコンテンツは一切禁止されています。これにはMOD、キャラクター、成人向けロールプレイなどが含まれますが、これらに限定されません。" +SUBJECT_TO_CHANGE = "これらのルールは今後のアップデートで変更される可能性があります。" +NOTICE = "公開ルームでプレイするためにCoopNetへ接続した時点で、あなたはこれらのルールを遵守することに同意したものとみなされます。" RULES = "ルールを見る" [MAIN] @@ -307,27 +307,27 @@ LEVEL = "コース" STAFF_ROLL = "スタッフロール" MUSIC = "BGM" RANDOM_STAGE = "ランダムなステージ" -PLAY_VANILLA_DEMOS = "バニラゲームのデモを再生" +PLAY_VANILLA_DEMOS = "オリジナルゲームのデモを再生" [MISC] DEBUG_TITLE = "DEBUG" FIXED_COLLISIONS = "修正された当たり判定" LUA_PROFILER = "Luaのプロファイラー" CTX_PROFILER = "Ctxのプロファイラー" -DEBUG_PRINT = "デバッグ情報の表示" -DEBUG_INFO = "デバッグの情報" -DEBUG_ERRORS = "デバッグのエラー" +DEBUG_PRINT = "デバッグログの表示" +DEBUG_INFO = "デバッグ情報の表示" +DEBUG_ERRORS = "デバッグエラーの表示" MISC_TITLE = "MISC" -PAUSE_IN_SINGLEPLAYER = "ソロプレイでの一時停止" +PAUSE_IN_SINGLEPLAYER = "1人プレイ中にポーズで一時停止を有効化" DISABLE_POPUPS = "ポップアップを無効にする" -USE_STANDARD_KEY_BINDINGS_CHAT = "初期のチャット操作" +USE_STANDARD_KEY_BINDINGS_CHAT = "旧式チャット操作" MENU_OPTIONS = "メニューの設定" INFORMATION = "情報" DEBUG = "デバッグ" LANGUAGE = "言語" COOP_COMPATIBILITY = "sm64ex-coopとの互換性を有効にする" R_BUTTON = "Rボタン - 設定" -L_BUTTON = "Lボタン - アクティブなMODを再読み込み" +L_BUTTON = "Lボタン - 有効化されたMODを再読み込み" [INFORMATION] INFORMATION_TITLE = "INFO" @@ -371,7 +371,7 @@ PLAYER_TITLE = "PLAYER" OVERALLS = "オーバーオール" SHIRT = "シャツ" GLOVES = "手袋" -SHOES = "くつ" +SHOES = "クツ" HAIR = "髪" SKIN = "肌" CAP = "帽子" @@ -408,8 +408,8 @@ MASTER_VOLUME = "主音量" MUSIC_VOLUME = "BGM音量" SFX_VOLUME = "SE音量" ENV_VOLUME = "環境音量" -FADEOUT = "遠い音のフェードアウト" -MUTE_FOCUS_LOSS = "非フォーカス時にミュート" +FADEOUT = "音の距離減衰" +MUTE_FOCUS_LOSS = "非フォーカス時に音をミュート" [LANGUAGE] LANGUAGE = "LANGUAGE" @@ -428,12 +428,11 @@ Spanish = "スペイン語 (Español)" [LOBBIES] PUBLIC_LOBBIES = "PUBLIC ROOMS" PRIVATE_LOBBIES = "PRIVATE ROOMS" -REFRESH = "更新" +REFRESH = "更新する" REFRESHING = "更新中…" -ENTER_PASSWORD = "部屋のパスワードを入力してください:" +ENTER_PASSWORD = "ルームのパスワードを入力してください:" SEARCH = "検索" -NONE_FOUND = "部屋が見つかりませんでした" -NO_LOBBIES_FOUND = "ロビーは見つからなかった。" +NO_LOBBIES_FOUND = "ルームが見つかりませんでした。" [CHANGELOG] CHANGELOG_TITLE = "CHANGELOG" diff --git a/lang/Polish.ini b/lang/Polish.ini index 9781c549e..cb983027d 100644 --- a/lang/Polish.ini +++ b/lang/Polish.ini @@ -99,6 +99,7 @@ CONTROLS = "STEROWANIE" N64_BINDS = "Przypisania N64" EXTRA_BINDS = "Dodatkowe Przypisania" BACKGROUND_GAMEPAD = "Gamepad w Tle" +EXTENDED_REPORTS = "Rozszerzone raporty" DISABLE_GAMEPADS = "Wyłącz Gamepady" GAMEPAD = "Gamepad" DEADZONE = "Martwa Strefa" diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini index e935499a6..d3b9e0c6b 100644 --- a/lang/Portuguese.ini +++ b/lang/Portuguese.ini @@ -99,6 +99,7 @@ CONTROLS = "CONTROLES" N64_BINDS = "N64" EXTRA_BINDS = "Outros" BACKGROUND_GAMEPAD = "Controle de fundo" +EXTENDED_REPORTS = "Relatórios detalhados" DISABLE_GAMEPADS = "Desativar controles" GAMEPAD = "Controle" DEADZONE = "Zona morta" diff --git a/lang/Russian.ini b/lang/Russian.ini index 52861a4cf..85d39ae37 100644 --- a/lang/Russian.ini +++ b/lang/Russian.ini @@ -98,6 +98,7 @@ CONTROLS = "CONTROLS" N64_BINDS = "Кнопки N64" EXTRA_BINDS = "Дополнительные кнопки" BACKGROUND_GAMEPAD = "Фоновый ввод" +EXTENDED_REPORTS = "Расширенные отчеты" DISABLE_GAMEPADS = "Отключить геймпады" GAMEPAD = "Геймпад" DEADZONE = "Mёртвая зона" diff --git a/lang/Spanish.ini b/lang/Spanish.ini index f083c8ba2..50660a637 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -100,6 +100,7 @@ N64_BINDS = "Botones de N64" EXTRA_BINDS = "Botones Adicionales" BACKGROUND_GAMEPAD = "Mando en segundo plano" DISABLE_GAMEPADS = "Desactivar mandos" +EXTENDED_REPORTS = "Informes ampliados" GAMEPAD = "Mando" DEADZONE = "Zona muerta" RUMBLE_STRENGTH = "Intensidad de vibración" diff --git a/src/audio/seqplayer.c b/src/audio/seqplayer.c index 433ca0b1e..ac9884e07 100644 --- a/src/audio/seqplayer.c +++ b/src/audio/seqplayer.c @@ -212,7 +212,7 @@ struct SequenceChannel *allocate_sequence_channel(void) { #endif } } - + LOG_ERROR("RAN OUT OF SEQUENCE CHANNELS FOR ALLOCATION!"); return &gSequenceChannelNone; } @@ -281,7 +281,7 @@ void sequence_player_init_channels_extended(struct SequencePlayer* seqPlayer, u6 if (!seqPlayer) { return; } u64 channelBits = channelBitsLower; LOG_DEBUG("Enabling channels (extended) with corresponding bits %llX", channelBits); - + for (u32 i = 0; i < CHANNELS_MAX; i++) { if (i == sizeof(u64) * 8) { channelBits = channelBitsUpper; @@ -350,9 +350,9 @@ void sequence_player_disable_channels_extended(struct SequencePlayer* seqPlayer, void sequence_player_disable_all_channels(struct SequencePlayer *seqPlayer) { if (!seqPlayer) { return; } - + MUTEX_LOCK(gAudioThread); - + eu_stubbed_printf_0("SUBTRACK DIM\n"); for (u32 i = 0; i < CHANNELS_MAX; i++) { struct SequenceChannel *seqChannel = seqPlayer->channels[i]; @@ -371,16 +371,16 @@ void sequence_player_disable_all_channels(struct SequencePlayer *seqPlayer) { seqPlayer->channels[i] = &gSequenceChannelNone; } } - + MUTEX_UNLOCK(gAudioThread); } void sequence_channel_enable(struct SequencePlayer *seqPlayer, u8 channelIndex, void *script) { if (!seqPlayer) { return; } if (channelIndex >= CHANNELS_MAX) { return; } - + MUTEX_LOCK(gAudioThread); - + struct SequenceChannel *seqChannel = seqPlayer->channels[channelIndex]; s32 i; if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == FALSE) { @@ -409,19 +409,19 @@ void sequence_channel_enable(struct SequencePlayer *seqPlayer, u8 channelIndex, seq_channel_layer_free(seqChannel, i); } } - + LOG_DEBUG("Enabled sequence channel %d with script entry of %p", channelIndex, script); } - + MUTEX_UNLOCK(gAudioThread); } void sequence_player_disable(struct SequencePlayer *seqPlayer) { if (!seqPlayer) { return; } MUTEX_LOCK(gAudioThread); - + LOG_DEBUG("Disabling sequence player %p", seqPlayer); - + sequence_player_disable_all_channels(seqPlayer); note_pool_clear(&seqPlayer->notePool); seqPlayer->finished = TRUE; @@ -1620,7 +1620,7 @@ u8 get_instrument(struct SequenceChannel *seqChannel, u8 instId, struct Instrume } void set_instrument(struct SequenceChannel *seqChannel, u8 instId) { - if (instId >= 0x80) { + if (instId >= 0x80 && instId <= 0x83) { seqChannel->instOrWave = instId; seqChannel->instrument = NULL; } else if (instId == 0x7f) { diff --git a/src/engine/graph_node.c b/src/engine/graph_node.c index c9c0023f6..89d9126cb 100644 --- a/src/engine/graph_node.c +++ b/src/engine/graph_node.c @@ -508,7 +508,7 @@ struct GraphNodeBackground *init_graph_node_background(struct DynamicPool *pool, : (backgroundFunc && background >= BACKGROUND_CUSTOM); if (invalidBackground) { - LOG_ERROR("invalid background id"); + LOG_ERROR("invalid background id %d", background); background = BACKGROUND_HAUNTED; } diff --git a/src/engine/math_util.c b/src/engine/math_util.c index f13dc051f..592968651 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -827,7 +827,7 @@ OPTIMIZE_O3 bool mtxf_inverse_non_affine(VEC_OUT Mat4 dest, Mat4 src) { if (fabsf(aug[i][k]) > fabsf(aug[piv][k])) { piv = i; } } - if (fabsf(aug[piv][k]) < FLT_EPSILON) { return false; } + if (fabsf(aug[piv][k]) < __FLT_EPSILON__) { return false; } // swap pivot row into place if (piv != k) { diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 15f6f9a9f..97414235e 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -12,6 +12,7 @@ #include "game/hardcoded.h" #include "pc/utils/misc.h" #include "pc/network/network.h" +#include "pc/lua/smlua_hooks.h" Vec3f gFindWallDirection = { 0 }; u8 gFindWallDirectionActive = false; @@ -343,6 +344,9 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { s32 numCollisions = 0; s16 x = colData->x; s16 z = colData->z; + f32 posX = colData->x; + f32 posY = colData->y; + f32 posZ = colData->z; colData->numWalls = 0; @@ -371,6 +375,8 @@ s32 find_wall_collisions(struct WallCollisionData *colData) { // Increment the debug tracker. gNumCalls.wall += 1; + smlua_call_event_hooks(HOOK_ON_FIND_WALL_COLLISION, posX, posY, posZ, colData, &numCollisions); + return numCollisions; } @@ -544,6 +550,8 @@ f32 find_ceil(f32 posX, f32 posY, f32 posZ, RET struct Surface **pceil) { // Increment the debug tracker. gNumCalls.ceil += 1; + smlua_call_event_hooks(HOOK_ON_FIND_CEIL, posX, posY, posZ, pceil, &height); + return height; } @@ -882,6 +890,8 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, RET struct Surface **pfloor) { // Increment the debug tracker. gNumCalls.floor += 1; + smlua_call_event_hooks(HOOK_ON_FIND_FLOOR, xPos, yPos, zPos, pfloor, &height); + return height; } @@ -922,6 +932,8 @@ f32 find_water_level(f32 x, f32 z) { } } + smlua_call_event_hooks(HOOK_ON_FIND_WATER_LEVEL, x, z, &waterLevel); + return waterLevel; } @@ -963,6 +975,8 @@ f32 find_poison_gas_level(f32 x, f32 z) { } } + smlua_call_event_hooks(HOOK_ON_FIND_POISON_GAS_LEVEL, x, z, &gasLevel); + return gasLevel; } @@ -1227,6 +1241,7 @@ void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Ve if (normalized_dir[1] >= 1.0f || normalized_dir[1] <= -1.0f) { find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length); + smlua_call_event_hooks(HOOK_ON_FIND_SURFACE_ON_RAY, orig, dir, hit_surface, hit_pos); return; } @@ -1249,4 +1264,6 @@ void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Ve cellX = (s16)fCellX; cellZ = (s16)fCellZ; } + + smlua_call_event_hooks(HOOK_ON_FIND_SURFACE_ON_RAY, orig, dir, hit_surface, hit_pos); } diff --git a/src/game/behaviors/texscroll.inc.c b/src/game/behaviors/texscroll.inc.c index 5ac1b4b69..6a1256fdc 100644 --- a/src/game/behaviors/texscroll.inc.c +++ b/src/game/behaviors/texscroll.inc.c @@ -70,13 +70,9 @@ static inline void shift_UV_NORMAL(struct ScrollTarget *scroll, u16 vertcount, s verts[0]->n.flag++; } else { if (bhv < SCROLL_UV_X) { - for (i = 0; i < vertcount; i++) { - scroll->prevF32[i] = scroll->interpF32[i]; - } + memcpy(scroll->prevF32, scroll->interpF32, vertcount * sizeof(f32)); } else { - for (i = 0; i < vertcount; i++) { - scroll->prevS16[i] = scroll->interpS16[i]; - } + memcpy(scroll->prevS16, scroll->interpS16, vertcount * sizeof(s16)); } } } @@ -133,19 +129,31 @@ void uv_update_scroll(void) { scroll->hasInterpInit = true; scroll->bhv = bhv; if (bhv < SCROLL_UV_X) { - scroll->interpF32 = calloc(scroll->size, sizeof(f32)); - scroll->prevF32 = calloc(scroll->size, sizeof(f32)); + scroll->interpF32 = malloc(scroll->size * sizeof(f32)); + scroll->prevF32 = malloc(scroll->size * sizeof(f32)); + if (!scroll->interpF32 || !scroll->prevF32) { + free(scroll->interpF32); + free(scroll->prevF32); + scroll->interpF32 = scroll->prevF32 = NULL; + return; + } u8 bhvIndex = MIN(bhv, 2); - for (u16 k = 0; k < scroll->size; k++) { + for (u32 k = 0; k < scroll->size; k++) { scroll->interpF32[k] = verts[k]->n.ob[bhvIndex]; } } else { - scroll->interpS16 = calloc(scroll->size, sizeof(s16)); - scroll->prevS16 = calloc(scroll->size, sizeof(s16)); + scroll->interpS16 = malloc(scroll->size * sizeof(s16)); + scroll->prevS16 = malloc(scroll->size * sizeof(s16)); + if (!scroll->interpS16 || !scroll->prevS16) { + free(scroll->interpS16); + free(scroll->prevS16); + scroll->interpS16 = scroll->prevS16 = NULL; + return; + } u8 bhvIndex = MIN(bhv-SCROLL_UV_X, 1); - for (u16 k = 0; k < scroll->size; k++) { + for (u32 k = 0; k < scroll->size; k++) { scroll->interpS16[k] = verts[k]->n.tc[bhvIndex]; } } @@ -153,15 +161,9 @@ void uv_update_scroll(void) { // Prepare for interpolation if (bhv < SCROLL_UV_X) { - u8 bhvIndex = MIN(bhv, 2); - for (u16 i = 0; i < scroll->size; i++) { - scroll->prevF32[i] = verts[i]->n.ob[bhvIndex]; - } + memcpy(scroll->prevF32, scroll->interpF32, scroll->size * sizeof(f32)); } else { - u8 bhvIndex = MIN(bhv-SCROLL_UV_X, 1); - for (u16 i = 0; i < scroll->size; i++) { - scroll->prevS16[i] = verts[i]->n.tc[bhvIndex]; - } + memcpy(scroll->prevS16, scroll->interpS16, scroll->size * sizeof(s16)); } scroll->needInterp = true; diff --git a/src/game/game_init.c b/src/game/game_init.c index 2e13c7e57..058cc9697 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -594,8 +594,6 @@ void thread5_game_loop(UNUSED void *arg) { play_music(SEQ_PLAYER_SFX, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0); set_sound_mode(save_file_get_sound_mode()); - thread6_rumble_loop(NULL); - gGlobalTimer++; } @@ -609,6 +607,7 @@ void game_loop_one_iteration(void) { osContStartReadData(&gSIEventMesgQueue); } + thread6_rumble_loop(NULL); audio_game_loop_tick(); config_gfx_pool(); read_controller_inputs(); diff --git a/src/game/interaction.c b/src/game/interaction.c index 4304a23ae..599d6c65a 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -2445,7 +2445,7 @@ void check_death_barrier(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { switch (gCurrCourseNum) { case COURSE_COTMC: // (20) Cavern of the Metal Cap case COURSE_TOTWC: // (21) Tower of the Wing Cap diff --git a/src/game/level_update.c b/src/game/level_update.c index 1ad390601..ff6ae25b1 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -878,8 +878,7 @@ void verify_warp(struct MarioState *m, bool killMario) { return; } - m->numLives--; - if (m->numLives < 0) { + if (m->numLives <= 0) { sDelayedWarpOp = WARP_OP_GAME_OVER; } else { sSourceWarpNodeId = WARP_NODE_DEATH; @@ -934,8 +933,7 @@ s16 level_trigger_warp(struct MarioState *m, s32 warpOp) { break; case WARP_OP_DEATH: - m->numLives--; - if (m->numLives <= -1) { + if (m->numLives <= 0) { sDelayedWarpOp = WARP_OP_GAME_OVER; } sDelayedWarpTimer = 48; diff --git a/src/game/mario.c b/src/game/mario.c index 897bbc3fe..d929544f6 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -447,7 +447,7 @@ void mario_set_bubbled(struct MarioState* m) { gLocalBubbleCounter = 20; drop_and_set_mario_action(m, ACT_BUBBLED, 0); - if (m->numLives > -1) { + if (m->numLives > 0) { m->numLives--; } m->healCounter = 0; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index 00b338447..6112358eb 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -1734,7 +1734,7 @@ s32 act_lava_boost(struct MarioState *m) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { m->health = 0xFF; mario_set_bubbled(m); } else { diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 9162cd84a..293079dc4 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -849,7 +849,7 @@ s32 common_death_handler(struct MarioState *m, s32 animation, s32 frameToDeathWa smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return animFrame; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -922,8 +922,7 @@ s32 act_quicksand_death(struct MarioState *m) { bool allowDeath = true; smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -947,7 +946,7 @@ s32 act_eaten_by_bubba(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { m->health = 0xFF; mario_set_bubbled(m); } else { @@ -1437,6 +1436,12 @@ s32 act_exit_land_save_dialog(struct MarioState *m) { return FALSE; } +static void lose_life_after_death_exit(struct MarioState *m) { + if (sDelayedWarpArg != WARP_ARG_EXIT_COURSE) { + m->numLives--; + } +} + s32 act_death_exit(struct MarioState *m) { if (!m) { return 0; } if (15 < m->actionTimer++ @@ -1447,6 +1452,7 @@ s32 act_death_exit(struct MarioState *m) { play_character_sound(m, CHAR_SOUND_OOOF2); #endif queue_rumble_data_mario(m, 5, 80); + lose_life_after_death_exit(m); // restore 7.75 units of health m->healCounter = 31; } @@ -1463,6 +1469,7 @@ s32 act_unused_death_exit(struct MarioState *m) { #else play_character_sound(m, CHAR_SOUND_OOOF2); #endif + lose_life_after_death_exit(m); // restore 7.75 units of health m->healCounter = 31; } @@ -1479,6 +1486,7 @@ s32 act_falling_death_exit(struct MarioState *m) { #else play_character_sound(m, CHAR_SOUND_OOOF2); #endif + lose_life_after_death_exit(m); queue_rumble_data_mario(m, 5, 80); // restore 7.75 units of health m->healCounter = 31; @@ -1526,6 +1534,7 @@ s32 act_special_death_exit(struct MarioState *m) { if (launch_mario_until_land(m, ACT_HARD_BACKWARD_GROUND_KB, CHAR_ANIM_BACKWARD_AIR_KB, -24.0f)) { queue_rumble_data_mario(m, 5, 80); + lose_life_after_death_exit(m); m->healCounter = 31; } // show Mario @@ -1829,7 +1838,7 @@ s32 act_squished(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -1880,7 +1889,7 @@ s32 act_squished(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { // 0 units of health diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index 61f3a0e15..e4195f64c 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -1003,7 +1003,7 @@ static s32 act_drowning(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -1038,7 +1038,7 @@ static s32 act_water_death(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); @@ -1164,7 +1164,7 @@ static s32 act_caught_in_whirlpool(struct MarioState *m) { smlua_call_event_hooks(HOOK_ON_DEATH, m, &allowDeath); if (!allowDeath) { reset_rumble_timers(m); return FALSE; } - if (mario_can_bubble(m)) { + if ((mario_can_bubble(m) && m->numLives > 0)) { mario_set_bubbled(m); } else { level_trigger_warp(m, WARP_OP_DEATH); diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index 7cb509211..435863e2f 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -334,13 +334,13 @@ static Gfx *make_gfx_mario_alpha(struct GraphNodeGenerated *node, s16 alpha) { } // Calculates if the processing geo is a mirror mario -static s8 geo_get_processing_mirror_mario_index() { - ptrdiff_t ptrDiff = (struct GraphNodeObject *) gCurGraphNodeProcessingObject - gMirrorMario; +static s8 geo_get_processing_mirror_mario_index(struct Object *obj) { + ptrdiff_t ptrDiff = (struct GraphNodeObject *) obj - gMirrorMario; return (ptrDiff >= 0 && ptrDiff < MAX_PLAYERS) ? ptrDiff : -1; } static u8 geo_get_processing_object_index(void) { - s8 index = geo_get_processing_mirror_mario_index(); + s8 index = geo_get_processing_mirror_mario_index(gCurGraphNodeProcessingObject); if (index != -1) { return index; } @@ -351,19 +351,19 @@ static u8 geo_get_processing_object_index(void) { return (index >= MAX_PLAYERS) ? 0 : index; } -s8 geo_get_processing_mario_index(void) { - if (gCurGraphNodeProcessingObject == NULL) { return -1; } +s8 geo_get_processing_mario_index(struct Object *obj) { + if (obj == NULL) { return -1; } - s8 index = geo_get_processing_mirror_mario_index(); + s8 index = geo_get_processing_mirror_mario_index(obj); if (index != -1) { return index; } - if (gCurGraphNodeProcessingObject->behavior != bhvMario) { + if (obj->behavior != bhvMario) { return -1; } - index = gCurGraphNodeProcessingObject->oBehParams - 1; + index = obj->oBehParams - 1; return (index >= MAX_PLAYERS) ? -1 : index; } diff --git a/src/game/scroll_targets.c b/src/game/scroll_targets.c index ff00cf244..a2af8bc19 100644 --- a/src/game/scroll_targets.c +++ b/src/game/scroll_targets.c @@ -13,19 +13,16 @@ struct ScrollTarget *get_scroll_targets(u32 id, u16 size, u16 offset) { if (scroll) { // If we need to, realloc the block of vertices - if ((!scroll->hasOffset && offset > 0) || size < scroll->size) { - if (scroll->hasOffset) { return NULL; } + if (!scroll->hasOffset && (offset > 0 || size < scroll->size)) { if (size > scroll->size) { size = scroll->size; } // Don't use an invalid size - if (size + offset >= scroll->size) { return NULL; } // If the offset is invalid, Abort. - scroll->hasOffset = true; - Vtx* *newVtx = calloc(size, sizeof(Vtx*)); + if (offset > 0 && size + offset >= scroll->size) { return NULL; } // If the offset is invalid, Abort. + Vtx* *newVtx = malloc(size * sizeof(Vtx*)); if (!newVtx) { return NULL; } - for (u32 i = 0; i < size; i++) { - newVtx[i] = scroll->vertices[i + offset]; - } + memcpy(newVtx, scroll->vertices + offset, size * sizeof(Vtx*)); free(scroll->vertices); scroll->vertices = newVtx; scroll->size = size; + scroll->hasOffset = true; } return scroll; @@ -51,11 +48,18 @@ struct ScrollTarget* find_or_create_scroll_targets(u32 id, bool hasOffset) { } if (scroll == NULL) { - scroll = calloc(1, sizeof(struct ScrollTarget)); + scroll = malloc(sizeof(struct ScrollTarget)); scroll->id = id; scroll->size = 0; scroll->vertices = NULL; scroll->hasOffset = hasOffset; + scroll->hasInterpInit = false; + scroll->needInterp = false; + scroll->interpF32 = NULL; + scroll->prevF32 = NULL; + scroll->interpS16 = NULL; + scroll->prevS16 = NULL; + scroll->bhv = 0; hmap_put(sScrollTargets, id, scroll); } @@ -77,8 +81,11 @@ void add_vtx_scroll_target(u32 id, Vtx *vtx, u32 size, bool hasOffset) { Vtx* *newArray = realloc(scroll->vertices, newSize); if (!newArray) { - newArray = calloc(1, newSize); - memcpy(newArray, scroll->vertices, oldSize); + newArray = malloc(newSize); + if (!newArray) { return; } + if (scroll->vertices && oldSize > 0) { + memcpy(newArray, scroll->vertices, oldSize); + } free(scroll->vertices); } @@ -133,13 +140,13 @@ void patch_scroll_targets_interpolated(f32 delta) { Vtx* *verts = scroll->vertices; if (scroll->bhv < SCROLL_UV_X) { u8 bhvIndex = MIN(scroll->bhv, 2); - for (u16 k = 0; k < scroll->size; k++) { + for (u32 k = 0; k < scroll->size; k++) { f32 diff = wrap_f32(scroll->interpF32[k] - scroll->prevF32[k]); verts[k]->n.ob[bhvIndex] = wrap_f32(scroll->prevF32[k] + diff * delta); } } else { u8 bhvIndex = MIN(scroll->bhv-SCROLL_UV_X, 1); - for (u16 k = 0; k < scroll->size; k++) { + for (u32 k = 0; k < scroll->size; k++) { s32 diff = wrap_s32(scroll->interpS16[k] - scroll->prevS16[k]); verts[k]->n.tc[bhvIndex] = wrap_s32(scroll->prevS16[k] + diff * delta); } diff --git a/src/pc/configfile.c b/src/pc/configfile.c index f2267a7db..4e52b50b6 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -127,6 +127,7 @@ unsigned int configStickDeadzone = 16; unsigned int configRumbleStrength = 50; unsigned int configGamepadNumber = 0; bool configBackgroundGamepad = true; +bool configExtendedReports = false; bool configDisableGamepads = false; bool configUseStandardKeyBindingsChat = false; bool configSmoothScrolling = false; @@ -270,6 +271,7 @@ static const struct ConfigOption options[] = { {.name = "rumble_strength", .type = CONFIG_TYPE_UINT, .uintValue = &configRumbleStrength}, {.name = "gamepad_number", .type = CONFIG_TYPE_UINT, .uintValue = &configGamepadNumber}, {.name = "background_gamepad", .type = CONFIG_TYPE_UINT, .boolValue = &configBackgroundGamepad}, + {.name = "extended_reports", .type = CONFIG_TYPE_BOOL, .boolValue = &configExtendedReports}, #ifndef HANDHELD {.name = "disable_gamepads", .type = CONFIG_TYPE_BOOL, .boolValue = &configDisableGamepads}, #endif diff --git a/src/pc/configfile.h b/src/pc/configfile.h index d537970e4..11cdbd5bc 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -93,6 +93,7 @@ extern unsigned int configStickDeadzone; extern unsigned int configRumbleStrength; extern unsigned int configGamepadNumber; extern bool configBackgroundGamepad; +extern bool configExtendedReports; extern bool configDisableGamepads; extern bool configUseStandardKeyBindingsChat; extern bool configSmoothScrolling; diff --git a/src/pc/controller/controller_bind_mapping.c b/src/pc/controller/controller_bind_mapping.c index 8c56299f2..24f81481b 100644 --- a/src/pc/controller/controller_bind_mapping.c +++ b/src/pc/controller/controller_bind_mapping.c @@ -106,11 +106,17 @@ const char* translate_bind_to_name(int bind) { // mouse if (bind >= VK_BASE_SDL_MOUSE) { int mouse_button = (bind - VK_BASE_SDL_MOUSE); - if (mouse_button == 1) { return "L Mouse"; } - if (mouse_button == 2) { return "M Mouse"; } - if (mouse_button == 3) { return "R Mouse"; } - snprintf(name, 8, "Mouse %d", bind - VK_BASE_SDL_MOUSE); - return name; + switch (mouse_button) { + case 1: return "L Mouse"; + case 2: return "M Mouse"; + case 3: return "R Mouse"; + case 6: return "Scroll Up"; + case 7: return "Scroll Down"; + default: { + snprintf(name, 11, "Mouse %d", mouse_button); + return name; + } + } } // gamepad diff --git a/src/pc/controller/controller_mouse.c b/src/pc/controller/controller_mouse.c index 84c10a635..bb047ead5 100644 --- a/src/pc/controller/controller_mouse.c +++ b/src/pc/controller/controller_mouse.c @@ -116,6 +116,11 @@ void controller_mouse_read_relative(void) { #elif defined(CAPI_SDL1) || defined(CAPI_SDL2) mouse_buttons = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); #endif + if (mouse_scroll_y > 0) { + mouse_buttons |= MWHEELUP; + } else if (mouse_scroll_y < 0) { + mouse_buttons |= MWHEELDOWN; + } } void controller_mouse_enter_relative(void) { diff --git a/src/pc/controller/controller_sdl.h b/src/pc/controller/controller_sdl.h index bab54eddc..b6235365d 100644 --- a/src/pc/controller/controller_sdl.h +++ b/src/pc/controller/controller_sdl.h @@ -8,6 +8,8 @@ // mouse buttons are also in the controller namespace, just offset 0x100 #define VK_OFS_SDL_MOUSE 0x0100 #define VK_BASE_SDL_MOUSE (VK_BASE_SDL_GAMEPAD + VK_OFS_SDL_MOUSE) +#define MWHEELUP 0x20 +#define MWHEELDOWN 0x40 extern struct ControllerAPI controller_sdl; diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 9ed2e02fd..6ce6136a8 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -40,6 +40,7 @@ static SDL_GameController *sdl_cntrl = NULL; static SDL_Joystick *sdl_joystick = NULL; static SDL_Haptic *sdl_haptic = NULL; +static bool sExtendedReports = false; static bool sBackgroundGamepad = false; static u32 num_joy_binds = 0; @@ -60,11 +61,11 @@ static s16 invert_s16(s16 val) { static inline void controller_add_binds(const u32 mask, const u32 *btns) { for (u32 i = 0; i < MAX_BINDS; ++i) { if (btns[i] >= VK_BASE_SDL_GAMEPAD && btns[i] <= VK_BASE_SDL_GAMEPAD + VK_SIZE) { - if (btns[i] >= VK_BASE_SDL_MOUSE && num_joy_binds < MAX_JOYBINDS) { + if (btns[i] >= VK_BASE_SDL_MOUSE && num_mouse_binds < MAX_JOYBINDS) { mouse_binds[num_mouse_binds][0] = btns[i] - VK_BASE_SDL_MOUSE; mouse_binds[num_mouse_binds][1] = mask; ++num_mouse_binds; - } else if (num_mouse_binds < MAX_JOYBINDS) { + } else if (num_joy_binds < MAX_JOYBINDS) { joy_binds[num_joy_binds][0] = btns[i] - VK_BASE_SDL_GAMEPAD; joy_binds[num_joy_binds][1] = mask; ++num_joy_binds; @@ -102,6 +103,13 @@ static void controller_sdl_bind(void) { } static void controller_sdl_init(void) { + // Allows extended reports on PS4 and PS5 controllers + if (configExtendedReports) { + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); + } + sExtendedReports = configExtendedReports; + // Allows game to be controlled by gamepad when not in focus if (configBackgroundGamepad) { SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); @@ -212,14 +220,23 @@ static void controller_sdl_read(OSContPad *pad) { controller_mouse_read_relative(); u32 mouse = mouse_buttons; + u32 buttons_down = 0; if (!gInteractableOverridePad) { for (u32 i = 0; i < num_mouse_binds; ++i) if (mouse & SDL_BUTTON(mouse_binds[i][0])) - pad->button |= mouse_binds[i][1]; + buttons_down |= mouse_binds[i][1]; } + pad->button |= buttons_down; // remember buttons that changed from 0 to 1 last_mouse = (mouse_prev ^ mouse) & mouse; + if (configExtendedReports != sExtendedReports) { + sExtendedReports = configExtendedReports; + char* hint = sExtendedReports ? "1" : "0"; + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, hint); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, hint); + } + if (configBackgroundGamepad != sBackgroundGamepad) { sBackgroundGamepad = configBackgroundGamepad; SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, sBackgroundGamepad ? "1" : "0"); @@ -304,7 +321,6 @@ static void controller_sdl_read(OSContPad *pad) { update_button(VK_LTRIGGER - VK_BASE_SDL_GAMEPAD, ltrig > AXIS_THRESHOLD); update_button(VK_RTRIGGER - VK_BASE_SDL_GAMEPAD, rtrig > AXIS_THRESHOLD); - u32 buttons_down = 0; for (u32 i = 0; i < num_joy_binds; ++i) if (joy_buttons[joy_binds[i][0]]) buttons_down |= joy_binds[i][1]; diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index 8ad0a5b15..d36938e79 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -533,8 +533,6 @@ static void djui_hud_print_text_internal(const char* message, f32 x, f32 y, f32 if (message == NULL) { return; } gDjuiHudUtilsZ += 0.001f; - if (djui_hud_text_font_is_legacy()) { scale *= 0.5f; } - const struct DjuiFont* font = djui_hud_get_text_font(); f32 fontScale = font->defaultFontScale * scale; @@ -654,13 +652,22 @@ static void djui_hud_print_text_internal(const char* message, f32 x, f32 y, f32 } void djui_hud_print_text(const char* message, f32 x, f32 y, f32 scale) { + if (message == NULL) { return; } + + if (djui_hud_text_font_is_legacy()) { + scale *= 0.5f; + } + djui_hud_print_text_internal(message, x, y, scale, NULL); } void djui_hud_print_text_interpolated(const char* message, f32 prevX, f32 prevY, f32 prevScale, f32 x, f32 y, f32 scale) { if (message == NULL) { return; } - if (djui_hud_text_font_is_legacy()) { prevScale *= 0.5f; } + if (djui_hud_text_font_is_legacy()) { + scale *= 0.5f; + prevScale *= 0.5f; + } struct InterpHud *interp = djui_hud_create_interp(); if (interp) { diff --git a/src/pc/djui/djui_panel_controls.c b/src/pc/djui/djui_panel_controls.c index 8088580a9..e8b39396d 100644 --- a/src/pc/djui/djui_panel_controls.c +++ b/src/pc/djui/djui_panel_controls.c @@ -43,6 +43,8 @@ void djui_panel_controls_create(struct DjuiBase* caller) { djui_checkbox_create(body, DLANG(MISC, USE_STANDARD_KEY_BINDINGS_CHAT), &configUseStandardKeyBindingsChat, NULL); #ifdef HAVE_SDL2 + djui_checkbox_create(body, DLANG(CONTROLS, EXTENDED_REPORTS), &configExtendedReports, NULL); + int numJoys = SDL_NumJoysticks(); if (numJoys == 0) { numJoys = 1; } diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index d7ca5a882..de7da771c 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -3547,7 +3547,13 @@ char gSmluaConstants[] = "" "HOOK_ON_ADD_SURFACE=57\n" "HOOK_ON_CLEAR_AREAS=58\n" "HOOK_ON_PACKET_BYTESTRING_RECEIVE=59\n" -"HOOK_MAX=60\n" +"HOOK_ON_FIND_WALL_COLLISION=60\n" +"HOOK_ON_FIND_CEIL=61\n" +"HOOK_ON_FIND_FLOOR=62\n" +"HOOK_ON_FIND_WATER_LEVEL=63\n" +"HOOK_ON_FIND_POISON_GAS_LEVEL=64\n" +"HOOK_ON_FIND_SURFACE_ON_RAY=65\n" +"HOOK_MAX=66\n" "MAX_HOOKED_BEHAVIORS=1024\n" "HUD_DISPLAY_LIVES=0\n" "HUD_DISPLAY_COINS=1\n" @@ -4681,7 +4687,7 @@ char gSmluaConstants[] = "" "COOP_OBJ_FLAG_LUA=(1 << 1)\n" "COOP_OBJ_FLAG_NON_SYNC=(1 << 2)\n" "COOP_OBJ_FLAG_INITIALIZED=(1 << 3)\n" -"SM64COOPDX_VERSION='v1.4.1'\n" +"SM64COOPDX_VERSION='v1.4.2'\n" "VERSION_TEXT='v'\n" "VERSION_NUMBER=41\n" "MINOR_VERSION_NUMBER=1\n" diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c index 4054a3ebd..ba79220a8 100644 --- a/src/pc/lua/smlua_functions.c +++ b/src/pc/lua/smlua_functions.c @@ -815,6 +815,11 @@ int smlua_func_log_to_console(lua_State* L) { //////////////////// int smlua_func_add_scroll_target(lua_State* L) { + if (gLuaLoadingMod == NULL) { + LOG_LUA_LINE("add_scroll_target() can only be called on load."); + return 0; + } + // add_scroll_target used to require offset and size of the vertex buffer to be used if (!smlua_functions_valid_param_range(L, 2, 4)) { return 0; } int paramCount = lua_gettop(L); diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index e227a54aa..54bb0672b 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -30424,6 +30424,21 @@ int smlua_func_smlua_audio_utils_replace_sequence(lua_State* L) { return 1; } +int smlua_func_smlua_audio_utils_allocate_sequence(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "smlua_audio_utils_allocate_sequence", 0, top); + return 0; + } + + + lua_pushinteger(L, smlua_audio_utils_allocate_sequence()); + + return 1; +} + int smlua_func_audio_stream_load(lua_State* L) { if (L == NULL) { return 0; } @@ -34301,6 +34316,28 @@ int smlua_func_get_active_mod(UNUSED lua_State* L) { return 1; } +int smlua_func_get_mod_files(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top < 1 || top > 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "get_mod_files", 1, 2, top); + return 0; + } + + struct Mod* mod = (struct Mod*)smlua_to_cobject(L, 1, LOT_MOD); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "get_mod_files"); return 0; } + const char* subDirectory = (const char*) NULL; + if (top >= 2) { + subDirectory = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "get_mod_files"); return 0; } + } + + smlua_push_lua_table(L, get_mod_files(mod, subDirectory)); + + return 1; +} + int smlua_func_set_window_title(lua_State* L) { if (L == NULL) { return 0; } @@ -38448,6 +38485,7 @@ void smlua_bind_functions_autogen(void) { // smlua_audio_utils.h smlua_bind_function(L, "smlua_audio_utils_reset_all", smlua_func_smlua_audio_utils_reset_all); smlua_bind_function(L, "smlua_audio_utils_replace_sequence", smlua_func_smlua_audio_utils_replace_sequence); + smlua_bind_function(L, "smlua_audio_utils_allocate_sequence", smlua_func_smlua_audio_utils_allocate_sequence); smlua_bind_function(L, "audio_stream_load", smlua_func_audio_stream_load); smlua_bind_function(L, "audio_stream_destroy", smlua_func_audio_stream_destroy); smlua_bind_function(L, "audio_stream_play", smlua_func_audio_stream_play); @@ -38685,6 +38723,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "set_environment_region", smlua_func_set_environment_region); smlua_bind_function(L, "mod_file_exists", smlua_func_mod_file_exists); smlua_bind_function(L, "get_active_mod", smlua_func_get_active_mod); + smlua_bind_function(L, "get_mod_files", smlua_func_get_mod_files); smlua_bind_function(L, "set_window_title", smlua_func_set_window_title); smlua_bind_function(L, "reset_window_title", smlua_func_reset_window_title); smlua_bind_function(L, "get_os_name", smlua_func_get_os_name); diff --git a/src/pc/lua/smlua_hook_events.inl b/src/pc/lua/smlua_hook_events.inl index 16bbbaee2..71f4a915c 100644 --- a/src/pc/lua/smlua_hook_events.inl +++ b/src/pc/lua/smlua_hook_events.inl @@ -58,3 +58,9 @@ SMLUA_EVENT_HOOK(HOOK_MARIO_OVERRIDE_FLOOR_CLASS, HOOK_RETURN_ON_OUTPUT_SET, str SMLUA_EVENT_HOOK(HOOK_ON_ADD_SURFACE, HOOK_RETURN_NEVER, struct Surface *surface, bool dynamic) SMLUA_EVENT_HOOK(HOOK_ON_CLEAR_AREAS, HOOK_RETURN_NEVER) SMLUA_EVENT_HOOK(HOOK_ON_PACKET_BYTESTRING_RECEIVE, HOOK_RETURN_NEVER, s32 modIndex, s32 valueIndex) +SMLUA_EVENT_HOOK(HOOK_ON_FIND_WALL_COLLISION, _, f32 posX, f32 posY, f32 posZ, struct WallCollisionData *colData, s32 *numCollisions) // Manually defined hook +SMLUA_EVENT_HOOK(HOOK_ON_FIND_CEIL, _, f32 posX, f32 posY, f32 posZ, struct Surface **pceil, f32 *height) // Manually defined hook +SMLUA_EVENT_HOOK(HOOK_ON_FIND_FLOOR, _, f32 posX, f32 posY, f32 posZ, struct Surface **pfloor, f32 *height) // Manually defined hook +SMLUA_EVENT_HOOK(HOOK_ON_FIND_WATER_LEVEL, _, f32 x, f32 z, f32 *waterLevel) // Manually defined hook +SMLUA_EVENT_HOOK(HOOK_ON_FIND_POISON_GAS_LEVEL, _, f32 x, f32 z, f32 *gasLevel) // Manually defined hook +SMLUA_EVENT_HOOK(HOOK_ON_FIND_SURFACE_ON_RAY, _, Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos) // Manually defined hook diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index fc1dafcae..0984c8223 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -26,6 +26,9 @@ #include "game/print.h" #include "gfx_dimensions.h" +extern void smlua_new_vec3f(Vec3f src); +extern void smlua_get_vec3f(Vec3f dest, int index); + #define MAX_HOOKED_REFERENCES 64 #define LUA_BEHAVIOR_FLAG (1 << 15) @@ -172,7 +175,6 @@ bool smlua_call_event_hooks_HOOK_ON_NAMETAGS_RENDER(s32 playerIndex, Vec3f pos, lua_pushinteger(L, playerIndex); // push pos - extern void smlua_new_vec3f(Vec3f src); smlua_new_vec3f(pos); // call the callback @@ -203,7 +205,6 @@ bool smlua_call_event_hooks_HOOK_ON_NAMETAGS_RENDER(s32 playerIndex, Vec3f pos, // pos lua_getfield(L, -1, "pos"); if (lua_type(L, -1) == LUA_TTABLE) { - extern void smlua_get_vec3f(Vec3f dest, int index); smlua_get_vec3f(pos, -1); override = true; } @@ -220,6 +221,306 @@ bool smlua_call_event_hooks_HOOK_ON_NAMETAGS_RENDER(s32 playerIndex, Vec3f pos, return false; } +bool smlua_call_event_hooks_HOOK_ON_FIND_WALL_COLLISION(f32 posX, f32 posY, f32 posZ, struct WallCollisionData *colData, s32 *numCollisions) { + static bool sInHook = false; + lua_State *L = gLuaState; + if (L == NULL || sInHook) { return false; } + sInHook = true; + + struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_FIND_WALL_COLLISION]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push posX, posY, posZ + lua_pushnumber(L, posX); + lua_pushnumber(L, posY); + lua_pushnumber(L, posZ); + + // push colData + smlua_push_object(L, LOT_WALLCOLLISIONDATA, colData, NULL); + + // call the callback (4 args, 1 result) + if (0 != smlua_call_hook(L, 4, 1, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_FIND_WALL_COLLISION]); + lua_settop(L, prevTop); + continue; + } + + // return number overrides numCollisions + if (lua_type(L, -1) == LUA_TNUMBER) { + *numCollisions = smlua_to_integer(L, -1); + lua_settop(L, prevTop); + sInHook = false; + return true; + } + + lua_settop(L, prevTop); + } + sInHook = false; + return false; +} + +bool smlua_call_event_hooks_HOOK_ON_FIND_CEIL(f32 posX, f32 posY, f32 posZ, struct Surface **pceil, f32 *height) { + static bool sInHook = false; + lua_State *L = gLuaState; + if (L == NULL || sInHook) { return false; } + sInHook = true; + + struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_FIND_CEIL]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push posX, posY, posZ + lua_pushnumber(L, posX); + lua_pushnumber(L, posY); + lua_pushnumber(L, posZ); + + // push current ceil surface (or nil) + if (pceil && *pceil) { + smlua_push_object(L, LOT_SURFACE, *pceil, NULL); + } else { + lua_pushnil(L); + } + + // push current height + lua_pushnumber(L, *height); + + // call the callback (5 args, 2 results) + if (0 != smlua_call_hook(L, 5, 2, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_FIND_CEIL]); + lua_settop(L, prevTop); + continue; + } + + bool override = false; + + // first return value: height (number) + if (lua_type(L, -2) == LUA_TNUMBER) { + *height = smlua_to_number(L, -2); + override = true; + } + + // second return value: surface (userdata) + if (lua_type(L, -1) == LUA_TUSERDATA) { + struct Surface *surface = (struct Surface *)smlua_to_cobject(L, -1, LOT_SURFACE); + if (surface && pceil) { + *pceil = surface; + override = true; + } + } + + lua_settop(L, prevTop); + if (override) { sInHook = false; return true; } + } + sInHook = false; + return false; +} + +bool smlua_call_event_hooks_HOOK_ON_FIND_FLOOR(f32 posX, f32 posY, f32 posZ, struct Surface **pfloor, f32 *height) { + static bool sInHook = false; + lua_State *L = gLuaState; + if (L == NULL || sInHook) { return false; } + sInHook = true; + + struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_FIND_FLOOR]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push posX, posY, posZ + lua_pushnumber(L, posX); + lua_pushnumber(L, posY); + lua_pushnumber(L, posZ); + + // push current floor surface (or nil) + if (pfloor && *pfloor) { + smlua_push_object(L, LOT_SURFACE, *pfloor, NULL); + } else { + lua_pushnil(L); + } + + // push current height + lua_pushnumber(L, *height); + + // call the callback (5 args, 2 results) + if (0 != smlua_call_hook(L, 5, 2, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_FIND_FLOOR]); + lua_settop(L, prevTop); + continue; + } + + bool override = false; + + // first return value: height (number) + if (lua_type(L, -2) == LUA_TNUMBER) { + *height = smlua_to_number(L, -2); + override = true; + } + + // second return value: surface (userdata) + if (lua_type(L, -1) == LUA_TUSERDATA) { + struct Surface *surface = (struct Surface *)smlua_to_cobject(L, -1, LOT_SURFACE); + if (surface && pfloor) { + *pfloor = surface; + override = true; + } + } + + lua_settop(L, prevTop); + if (override) { sInHook = false; return true; } + } + sInHook = false; + return false; +} + +bool smlua_call_event_hooks_HOOK_ON_FIND_SURFACE_ON_RAY(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos) { + static bool sInHook = false; + lua_State *L = gLuaState; + if (L == NULL || sInHook) { return false; } + sInHook = true; + + struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_FIND_SURFACE_ON_RAY]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push orig, dir + smlua_new_vec3f(orig); + smlua_new_vec3f(dir); + + // push hit_surface (or nil) + if (hit_surface && *hit_surface) { + smlua_push_object(L, LOT_SURFACE, *hit_surface, NULL); + } else { + lua_pushnil(L); + } + + // push hit_pos + smlua_new_vec3f(hit_pos); + + // call the callback (4 args, 2 results) + if (0 != smlua_call_hook(L, 4, 2, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_FIND_SURFACE_ON_RAY]); + lua_settop(L, prevTop); + continue; + } + + bool override = false; + + // first return value: surface (userdata) + if (lua_type(L, -2) == LUA_TUSERDATA) { + struct Surface *surface = (struct Surface *)smlua_to_cobject(L, -2, LOT_SURFACE); + if (surface && hit_surface) { + *hit_surface = surface; + override = true; + } + } + + // second return value: hitPos (table {x, y, z}) + if (lua_type(L, -1) == LUA_TTABLE) { + smlua_get_vec3f(hit_pos, -1); + override = true; + } + + lua_settop(L, prevTop); + if (override) { sInHook = false; return true; } + } + sInHook = false; + return false; +} + +bool smlua_call_event_hooks_HOOK_ON_FIND_WATER_LEVEL(f32 x, f32 z, f32 *waterLevel) { + static bool sInHook = false; + lua_State *L = gLuaState; + if (L == NULL || sInHook) { return false; } + sInHook = true; + + struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_FIND_WATER_LEVEL]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push x, z + lua_pushnumber(L, x); + lua_pushnumber(L, z); + + // push current water level + lua_pushnumber(L, *waterLevel); + + // call the callback (3 args, 1 result) + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_FIND_WATER_LEVEL]); + lua_settop(L, prevTop); + continue; + } + + // return number overrides waterLevel + if (lua_type(L, -1) == LUA_TNUMBER) { + *waterLevel = smlua_to_number(L, -1); + lua_settop(L, prevTop); + sInHook = false; + return true; + } + + lua_settop(L, prevTop); + } + sInHook = false; + return false; +} + +bool smlua_call_event_hooks_HOOK_ON_FIND_POISON_GAS_LEVEL(f32 x, f32 z, f32 *gasLevel) { + static bool sInHook = false; + lua_State *L = gLuaState; + if (L == NULL || sInHook) { return false; } + sInHook = true; + + struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_FIND_POISON_GAS_LEVEL]; + for (int i = 0; i < hook->count; i++) { + s32 prevTop = lua_gettop(L); + + // push the callback onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); + + // push x, z + lua_pushnumber(L, x); + lua_pushnumber(L, z); + + // push current gas level + lua_pushnumber(L, *gasLevel); + + // call the callback (3 args, 1 result) + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { + LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_FIND_POISON_GAS_LEVEL]); + lua_settop(L, prevTop); + continue; + } + + // return number overrides gasLevel + if (lua_type(L, -1) == LUA_TNUMBER) { + *gasLevel = smlua_to_number(L, -1); + lua_settop(L, prevTop); + sInHook = false; + return true; + } + + lua_settop(L, prevTop); + } + sInHook = false; + return false; +} + //////////////////// // hooked actions // //////////////////// diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h index a857bbad3..c814c73e9 100644 --- a/src/pc/lua/smlua_hooks.h +++ b/src/pc/lua/smlua_hooks.h @@ -11,6 +11,8 @@ // forward declare struct Camera; struct WarpDest; +struct WallCollisionData; +struct Surface; // ! Hooks must be added at the end enum LuaHookedEventType { @@ -74,6 +76,12 @@ enum LuaHookedEventType { HOOK_ON_ADD_SURFACE, HOOK_ON_CLEAR_AREAS, HOOK_ON_PACKET_BYTESTRING_RECEIVE, + HOOK_ON_FIND_WALL_COLLISION, + HOOK_ON_FIND_CEIL, + HOOK_ON_FIND_FLOOR, + HOOK_ON_FIND_WATER_LEVEL, + HOOK_ON_FIND_POISON_GAS_LEVEL, + HOOK_ON_FIND_SURFACE_ON_RAY, HOOK_MAX, }; diff --git a/src/pc/lua/utils/smlua_audio_utils.c b/src/pc/lua/utils/smlua_audio_utils.c index 80aeca7fc..9b02de52e 100644 --- a/src/pc/lua/utils/smlua_audio_utils.c +++ b/src/pc/lua/utils/smlua_audio_utils.c @@ -173,6 +173,16 @@ void smlua_audio_utils_replace_sequence(u8 sequenceId, u8 bankId, u8 defaultVolu LOG_LUA_LINE("Could not find m64 at path: %s", m64path); } +u8 smlua_audio_utils_allocate_sequence(void) { + for (u8 seqId = SEQ_COUNT + 1; seqId < MAX_AUDIO_OVERRIDE; seqId++) { + if (!sAudioOverrides[seqId].enabled) { + return seqId; + } + } + LOG_ERROR("Cannot allocate more custom sequences."); + return MAX_AUDIO_OVERRIDE; +} + /////////////// // mod audio // /////////////// diff --git a/src/pc/lua/utils/smlua_audio_utils.h b/src/pc/lua/utils/smlua_audio_utils.h index deca8b0d0..93696c80d 100644 --- a/src/pc/lua/utils/smlua_audio_utils.h +++ b/src/pc/lua/utils/smlua_audio_utils.h @@ -8,6 +8,8 @@ void smlua_audio_utils_reset_all(void); bool smlua_audio_utils_override(u8 sequenceId, s32* bankId, void** seqData); /* |description|Replaces the sequence corresponding to `sequenceId` with one called `m64Name`.m64 with `bankId` and `defaultVolume`|descriptionEnd| */ void smlua_audio_utils_replace_sequence(u8 sequenceId, u8 bankId, u8 defaultVolume, const char* m64Name); +/* |description|Allocates a new sequence ID|descriptionEnd| */ +u8 smlua_audio_utils_allocate_sequence(void); //////////////// // mod sounds // diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c index de447b3d1..9e90a45fb 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -607,6 +607,54 @@ struct Mod* get_active_mod(void) { return gLuaActiveMod; } +LuaTable get_mod_files(struct Mod* mod, OPTIONAL const char* subDirectory) { + if (!mod) { + struct lua_State *L = gLuaState; + if (L) { + lua_newtable(L); + return smlua_to_lua_table(L, -1); + } + return 0; + } + + char normalizedSubDir[SYS_MAX_PATH] = { 0 }; + snprintf(normalizedSubDir, SYS_MAX_PATH, "%s", subDirectory ? subDirectory : ""); + normalize_path(normalizedSubDir); + + size_t subDirLen = strlen(normalizedSubDir); + if (subDirLen > 0 && subDirLen + 1 < SYS_MAX_PATH && normalizedSubDir[subDirLen - 1] != '/') { + strcat(normalizedSubDir, "/"); + subDirLen = strlen(normalizedSubDir); + } + + struct lua_State *L = gLuaState; + if (!L) { return 0; } + + LUA_STACK_CHECK_BEGIN_NUM(L, 1); + + lua_newtable(L); + + int luaTableIndex = 1; + for (int i = 0; i < mod->fileCount; i++) { + struct ModFile* file = &mod->files[i]; + char normalizedPath[SYS_MAX_PATH] = { 0 }; + if (snprintf(normalizedPath, SYS_MAX_PATH, "%s", file->relativePath) < 0) { + LOG_ERROR("Failed to copy relativePath for normalization: %s", file->relativePath); + continue; + } + normalize_path(normalizedPath); + + if (strncmp(normalizedPath, normalizedSubDir, subDirLen) == 0) { + lua_pushstring(L, file->relativePath); + lua_rawseti(L, -2, luaTableIndex++); + } + } + + LUA_STACK_CHECK_END(L); + + return smlua_to_lua_table(L, -1); +} + /// void set_window_title(const char* title) { diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index e4bb3c1eb..1b7d929cb 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -37,7 +37,7 @@ enum ActSelectHudPart { ACT_SELECT_HUD_ACT_NAME = 1 << 3, ACT_SELECT_HUD_STAR_NUM = 1 << 4, ACT_SELECT_HUD_PLAYERS_IN_LEVEL = 1 << 5, - + ACT_SELECT_HUD_NONE = 0, ACT_SELECT_HUD_ALL = ACT_SELECT_HUD_SCORE | ACT_SELECT_HUD_LEVEL_NAME | ACT_SELECT_HUD_COURSE_NUM | ACT_SELECT_HUD_ACT_NAME |ACT_SELECT_HUD_STAR_NUM | ACT_SELECT_HUD_PLAYERS_IN_LEVEL }; @@ -246,6 +246,8 @@ void set_environment_region(u8 index, s16 value); bool mod_file_exists(const char* filename); /* |description|Gets the mod currently being processed|descriptionEnd| */ struct Mod* get_active_mod(void); +/* |description|Gets all files a mod contains|descriptionEnd| */ +LuaTable get_mod_files(struct Mod* mod, OPTIONAL const char* subDirectory); /* |description|Sets the window title to a custom title|descriptionEnd| */ void set_window_title(const char* title); diff --git a/src/pc/network/version.h b/src/pc/network/version.h index 473e8fec5..9bda5050c 100644 --- a/src/pc/network/version.h +++ b/src/pc/network/version.h @@ -1,7 +1,7 @@ #ifndef VERSION_H #define VERSION_H -#define SM64COOPDX_VERSION "v1.4.1" +#define SM64COOPDX_VERSION "v1.4.2" // internal version #define VERSION_TEXT "v" diff --git a/src/pc/platform.c b/src/pc/platform.c index 331a7c075..185b85bd9 100644 --- a/src/pc/platform.c +++ b/src/pc/platform.c @@ -411,4 +411,12 @@ static void sys_fatal_impl(const char *msg) { exit(1); } +const char *sys_resource_path(void) { + return "."; +} + +const char *sys_exe_path_dir(void) { + return "."; +} + #endif // platform switch