diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index 8e160c5d4..5bbb7065a 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
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 746a8f4c4..c716033b3 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -11647,6 +11647,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/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-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..ecce742d3 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -2071,6 +2071,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/German.ini b/lang/German.ini
index 154ed8718..98c870818 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."
diff --git a/lang/Japanese.ini b/lang/Japanese.ini
index 727c28cf0..13f273684 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,26 +79,25 @@ 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 = "バックグラウンドでのコントローラー認識"
DISABLE_GAMEPADS = "コントローラーを無効化"
GAMEPAD = "コントローラー"
@@ -106,28 +105,28 @@ 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 +134,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 +152,7 @@ AUTO = "自動"
MANUAL = "手動"
UNCAPPED = "無制限"
FRAME_LIMIT = "FPSの制限"
-FAST = "速い"
+FAST = "高速"
ACCURATE = "正確"
INTERPOLATION = "補間"
NEAREST = "ニアレスト"
@@ -196,24 +195,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 +221,7 @@ CONFIRM = "本当に消しますか?"
ERASE = "消す"
EDIT = "編集"
EDIT_TITLE = "EDIT"
-EDIT_NAME = "ファイル名を変更:"
+EDIT_NAME = "マリオ @のおなまえ変更:"
[HOST_SETTINGS]
SETTINGS = "SETTINGS"
@@ -235,7 +234,7 @@ NORMAL = "普通"
TOO_MUCH = "最強"
KNOCKBACK_STRENGTH = "ノックバックの強さ"
CLASSIC_PVP = "クラシック"
-REVAMPED_PVP = "改良"
+REVAMPED_PVP = "改良型"
PVP_MODE = "PvPモード"
LEAVE_LEVEL = "コースを出る"
STAY_IN_LEVEL = "コースに留まる"
@@ -243,14 +242,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 +274,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 +282,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 +306,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 +370,7 @@ PLAYER_TITLE = "PLAYER"
OVERALLS = "オーバーオール"
SHIRT = "シャツ"
GLOVES = "手袋"
-SHOES = "くつ"
+SHOES = "クツ"
HAIR = "髪"
SKIN = "肌"
CAP = "帽子"
@@ -408,8 +407,8 @@ MASTER_VOLUME = "主音量"
MUSIC_VOLUME = "BGM音量"
SFX_VOLUME = "SE音量"
ENV_VOLUME = "環境音量"
-FADEOUT = "遠い音のフェードアウト"
-MUTE_FOCUS_LOSS = "非フォーカス時にミュート"
+FADEOUT = "音の距離減衰"
+MUTE_FOCUS_LOSS = "非フォーカス時に音をミュート"
[LANGUAGE]
LANGUAGE = "LANGUAGE"
@@ -428,12 +427,12 @@ 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 = "ルームが見つかりませんでした。"
SORT_BY = "並べ替え"
NONE = "なし"
NAME = "名前"
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/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 373065415..95dafbed1 100644
--- a/src/pc/controller/controller_sdl2.c
+++ b/src/pc/controller/controller_sdl2.c
@@ -60,11 +60,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;
@@ -190,10 +190,11 @@ 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];
}
// remember buttons that changed from 0 to 1
last_mouse = (mouse_prev ^ mouse) & mouse;
@@ -282,7 +283,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/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index d7ca5a882..60737b707 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"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index e227a54aa..70d040309 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -34301,6 +34301,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; }
@@ -38685,6 +38707,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_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/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