From 834512a5c5dee10c6c8e53910b718fe59a549ee2 Mon Sep 17 00:00:00 2001
From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com>
Date: Tue, 19 May 2026 02:56:08 +0200
Subject: [PATCH] Fix Lua/custom behaviors bugs and flaws (#1229)
* improve custom behaviors logic
* clarify description of get behavior functions; get name returns last name instead of first; create generic name only when needed
* review
* fix potential buffer overflow
---
autogen/convert_constants.py | 5 +-
autogen/lua_definitions/constants.lua | 3 -
autogen/lua_definitions/functions.lua | 17 +-
autogen/lua_definitions/manual.lua | 4 +-
autogen/lua_definitions/structs.lua | 1 +
data/behavior_data.c | 64 ++-
data/behavior_table.c | 38 +-
data/dynos_bin_behavior.cpp | 3 +-
docs/lua/constants.md | 4 -
docs/lua/functions-3.md | 33 +-
docs/lua/functions.md | 1 +
docs/lua/structs.md | 1 +
include/behavior_table.h | 12 +-
include/types.h | 1 +
src/engine/behavior_script.c | 20 +-
src/game/memory.h | 7 +
src/game/object_helpers.c | 3 +-
src/game/spawn_object.c | 4 +-
src/pc/lua/smlua_cobject_autogen.c | 5 +-
src/pc/lua/smlua_constants_autogen.c | 1 -
src/pc/lua/smlua_functions_autogen.c | 18 +
src/pc/lua/smlua_hooks.c | 564 ++++++++++++++------------
src/pc/lua/smlua_hooks.h | 53 ++-
src/pc/mods/mod.h | 2 +-
24 files changed, 511 insertions(+), 353 deletions(-)
diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py
index 7cb49e24c..b8ca66cde 100644
--- a/autogen/convert_constants.py
+++ b/autogen/convert_constants.py
@@ -68,7 +68,7 @@ exclude_constants = {
"src/game/save_file.h": [ "EEPROM_SIZE" ],
"src/game/obj_behaviors.c": [ "^o$" ],
"src/pc/djui/djui_console.h": [ "CONSOLE_MAX_TMP_BUFFER" ],
- "src/pc/lua/smlua_hooks.h": [ "MAX_HOOKED_MOD_MENU_ELEMENTS", "^HOOK_RETURN_.*", "^ACTION_HOOK_.*", "^MOD_MENU_ELEMENT_.*" ],
+ "src/pc/lua/smlua_hooks.h": [ "^LUA_BEHAVIOR_.*", "MAX_HOOKED_.*", "^HOOK_RETURN_.*", "^ACTION_HOOK_.*", "^MOD_MENU_ELEMENT_.*" ],
"src/pc/djui/djui_panel_menu.h": [ "RAINBOW_TEXT_LEN" ],
"src/pc/mods/mod_fs.h": [ "INT_TYPE_MAX", "FLOAT_TYPE_MAX", "FILE_SEEK_MAX" ],
"src/engine/surface_load.h": [ "NUM_CELLS" ],
@@ -451,7 +451,8 @@ def doc_constant_index(processed_files):
s += '- [%s](#%s)\n' % (processed_file['filename'], processed_file['filename'].replace('.', ''))
constants = [x for x in processed_file['constants'] if 'identifier' in x]
for c in constants:
- s += ' - [enum %s](#enum-%s)\n' % (c['identifier'], c['identifier'])
+ if len(c['constants']) > 0:
+ s += ' - [enum %s](#enum-%s)\n' % (c['identifier'], c['identifier'])
s += '\n
\n\n'
return s
diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index 245bc0c4b..79fbddd1f 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -8350,9 +8350,6 @@ HOOK_MAX = 69 --- @type LuaHookedEventType
--- | `HOOK_ON_PLAY_MODE_UPDATE`
--- | `HOOK_MAX`
---- @type integer
-MAX_HOOKED_BEHAVIORS = 1024
-
HUD_DISPLAY_LIVES = 0 --- @type HudDisplayValue
HUD_DISPLAY_COINS = 1 --- @type HudDisplayValue
HUD_DISPLAY_STARS = 2 --- @type HudDisplayValue
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index c5c4db40d..a2457b6db 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -3011,35 +3011,42 @@ end
--- @param behavior Pointer_BehaviorScript
--- @return BehaviorId
---- Gets a behavior ID from a behavior script
+--- Gets the behavior ID of the provided `behavior`
function get_id_from_behavior(behavior)
-- ...
end
--- @param behavior Pointer_BehaviorScript
--- @return BehaviorId
---- Gets a behavior ID from only vanilla behavior scripts
+--- Gets the behavior ID of the provided `behavior` if it's a vanilla behavior, `id_bhv_max_count` otherwise
function get_id_from_vanilla_behavior(behavior)
-- ...
end
--- @param id BehaviorId
--- @return Pointer_BehaviorScript
---- Gets a behavior script from a behavior ID
+--- Gets the behavior script corresponding to the provided `id`
function get_behavior_from_id(id)
-- ...
end
+--- @param id BehaviorId
+--- @return Pointer_BehaviorScript
+--- Gets the behavior script corresponding to the provided `id` if it's a vanilla behavior, `nil` otherwise
+function get_vanilla_behavior_from_id(id)
+ -- ...
+end
+
--- @param id BehaviorId
--- @return string
---- Gets a behavior name from a behavior ID (bhvMyGreatMODCustom004)
+--- Gets the behavior name from the provided `id` (bhvMyGreatMODCustom004)
function get_behavior_name_from_id(id)
-- ...
end
--- @param name string
--- @return BehaviorId
---- Gets a behavior ID from a behavior name
+--- Gets the behavior ID corresponding to the provided `name`
function get_id_from_behavior_name(name)
-- ...
end
diff --git a/autogen/lua_definitions/manual.lua b/autogen/lua_definitions/manual.lua
index 142650be3..b58d31613 100644
--- a/autogen/lua_definitions/manual.lua
+++ b/autogen/lua_definitions/manual.lua
@@ -107,10 +107,10 @@ gHudDisplay = {}
--- @param behaviorId BehaviorId | integer? The behavior id of the object to modify. Pass in as `nil` to create a custom object
--- @param objectList ObjectList | integer Object list
---- @param replaceBehavior boolean Whether or not to completely replace the behavior
+--- @param replaceBehavior boolean Whether or not to completely replace the behavior (ignored for non-vanilla behaviors, which are always replaced)
--- @param initFunction? fun(obj:Object) Run on object creation
--- @param loopFunction? fun(obj:Object) Run every frame
---- @param behaviorName? string Optional
+--- @param behaviorName? string Optional, name to give to the behavior to be able to retrieve it with `get_id_from_behavior_name`
--- @return BehaviorId BehaviorId Use if creating a custom object, otherwise can be ignored
--- Modify an object's behavior or create a new custom object
function hook_behavior(behaviorId, objectList, replaceBehavior, initFunction, loopFunction, behaviorName)
diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua
index 2da475eec..0cbd15600 100644
--- a/autogen/lua_definitions/structs.lua
+++ b/autogen/lua_definitions/structs.lua
@@ -1305,6 +1305,7 @@
--- @field public collidedObjs Object[]
--- @field public collisionData Pointer_Collision
--- @field public behavior Pointer_BehaviorScript
+--- @field public initBhvCommand Pointer_BehaviorScript
--- @field public curBhvCommand Pointer_BehaviorScript
--- @field public bhvStack integer[]
--- @field public bhvStackIndex integer
diff --git a/data/behavior_data.c b/data/behavior_data.c
index 6988c4b50..d3b28073a 100644
--- a/data/behavior_data.c
+++ b/data/behavior_data.c
@@ -303,15 +303,17 @@ const BehaviorScript bhvSmallWaterWave[] = {
SET_RANDOM_FLOAT(oWaterObjUnkFC, /*Minimum*/ 0, /*Range*/ 50),
SUM_FLOAT(/*Dest*/ oPosY, /*Value 1*/ oPosY, /*Value 2*/ oWaterObjUnkFC),
SET_INT(oAnimState, -1),
- CALL(bhvSmallWaterWave398),
+ CALL(bhvSmallWaterWave398 + 2),
BEGIN_REPEAT(60),
- CALL(bhvSmallWaterWave398),
+ CALL(bhvSmallWaterWave398 + 2),
CALL_NATIVE(bhv_small_water_wave_loop),
END_REPEAT(),
DEACTIVATE(),
};
const BehaviorScript bhvSmallWaterWave398[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvSmallWaterWave398),
ADD_INT(oAnimState, 1),
ADD_FLOAT(oPosY, 7),
SET_RANDOM_FLOAT(oWaterObjUnkF4, /*Minimum*/ -2, /*Range*/ 5),
@@ -1283,6 +1285,8 @@ const BehaviorScript bhvCutOutObject[] = {
};
const BehaviorScript bhvBetaMovingFlamesSpawn[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvBetaMovingFlamesSpawn),
BEGIN_LOOP(),
CALL_NATIVE(bhv_beta_moving_flames_spawn_loop),
END_LOOP(),
@@ -2224,6 +2228,8 @@ const BehaviorScript bhvLllMovingOctagonalMeshPlatform[] = {
};
const BehaviorScript bhvSnowBall[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvSnowBall),
BREAK(),
};
@@ -2560,10 +2566,12 @@ const BehaviorScript bhvChirpChirp[] = {
BEGIN(OBJ_LIST_DEFAULT),
ID(id_bhvChirpChirp),
SET_INT(oBirdChirpChirpUnkF4, 1),
- GOTO(bhvChirpChirpUnused),
+ GOTO(bhvChirpChirpUnused + 2),
};
const BehaviorScript bhvChirpChirpUnused[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvChirpChirpUnused),
DISABLE_RENDERING(),
OR_INT(oFlags, (OBJ_FLAG_COMPUTE_DIST_TO_MARIO | OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE)),
BEGIN_LOOP(),
@@ -2732,6 +2740,8 @@ const BehaviorScript bhvSunkenShipPart[] = {
};
const BehaviorScript bhvSunkenShipSetRotation[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvSunkenShipSetRotation),
SET_INT(oFaceAnglePitch, 0xE958),
SET_INT(oFaceAngleYaw, 0xEE6C),
SET_INT(oFaceAngleRoll, 0x0C80),
@@ -2745,7 +2755,7 @@ const BehaviorScript bhvSunkenShipPart2[] = {
SCALE(/*Unused*/ 0, /*Field*/ 100),
SET_FLOAT(oDrawingDistance, 6000),
SET_HOME(),
- CALL(bhvSunkenShipSetRotation),
+ CALL(bhvSunkenShipSetRotation + 2),
BREAK(),
};
@@ -2763,7 +2773,7 @@ const BehaviorScript bhvInSunkenShip2[] = {
// Sunken ship - common:
OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE),
SET_FLOAT(oCollisionDistance, 4000),
- CALL(bhvSunkenShipSetRotation),
+ CALL(bhvSunkenShipSetRotation + 2),
BEGIN_LOOP(),
CALL_NATIVE(load_object_collision_model),
END_LOOP(),
@@ -3141,6 +3151,8 @@ const BehaviorScript bhvPlaysMusicTrackWhenTouched[] = {
#endif
const BehaviorScript bhvInsideCannon[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvInsideCannon),
BREAK(),
};
@@ -3188,14 +3200,6 @@ const BehaviorScript bhvUnusedFakeStar[] = {
END_LOOP(),
};
-// What is this?
-UNUSED static const BehaviorScript unused_1[] = {
- BREAK(),
- BREAK(),
- BREAK(),
- BREAK(),
-};
-
const BehaviorScript bhvStaticObject[] = {
BEGIN(OBJ_LIST_DEFAULT),
ID(id_bhvStaticObject),
@@ -3619,58 +3623,86 @@ const BehaviorScript bhvUnlockDoorStar[] = {
};
const BehaviorScript bhvInstantActiveWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvInstantActiveWarp),
BREAK(),
};
const BehaviorScript bhvAirborneWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvAirborneWarp),
BREAK(),
};
const BehaviorScript bhvHardAirKnockBackWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvHardAirKnockBackWarp),
BREAK(),
};
const BehaviorScript bhvSpinAirborneCircleWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvSpinAirborneCircleWarp),
BREAK(),
};
const BehaviorScript bhvDeathWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvDeathWarp),
BREAK(),
};
const BehaviorScript bhvSpinAirborneWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvSpinAirborneWarp),
BREAK(),
};
const BehaviorScript bhvFlyingWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvFlyingWarp),
BREAK(),
};
const BehaviorScript bhvPaintingStarCollectWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvPaintingStarCollectWarp),
BREAK(),
};
const BehaviorScript bhvPaintingDeathWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvPaintingDeathWarp),
BREAK(),
};
const BehaviorScript bhvAirborneDeathWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvAirborneDeathWarp),
BREAK(),
};
const BehaviorScript bhvAirborneStarCollectWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvAirborneStarCollectWarp),
BREAK(),
};
const BehaviorScript bhvLaunchStarCollectWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvLaunchStarCollectWarp),
BREAK(),
};
const BehaviorScript bhvLaunchDeathWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvLaunchDeathWarp),
BREAK(),
};
const BehaviorScript bhvSwimmingWarp[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvSwimmingWarp),
BREAK(),
};
@@ -4141,15 +4173,17 @@ const BehaviorScript bhvBobombExplosionBubble[] = {
ADD_RANDOM_FLOAT(oPosX, /*Minimum*/ -50, /*Range*/ 100),
ADD_RANDOM_FLOAT(oPosY, /*Minimum*/ -50, /*Range*/ 100),
ADD_RANDOM_FLOAT(oPosZ, /*Minimum*/ -50, /*Range*/ 100),
- CALL(bhvBobombExplosionBubble3600),
+ CALL(bhvBobombExplosionBubble3600 + 2),
DELAY(1),
BEGIN_LOOP(),
- CALL(bhvBobombExplosionBubble3600),
+ CALL(bhvBobombExplosionBubble3600 + 2),
CALL_NATIVE(bhv_bobomb_explosion_bubble_loop),
END_LOOP(),
};
const BehaviorScript bhvBobombExplosionBubble3600[] = {
+ BEGIN(OBJ_LIST_DEFAULT),
+ ID(id_bhvBobombExplosionBubble3600),
ADD_RANDOM_FLOAT(oPosX, /*Minimum*/ -2, /*Range*/ 4),
ADD_RANDOM_FLOAT(oPosZ, /*Minimum*/ -2, /*Range*/ 4),
RETURN(),
diff --git a/data/behavior_table.c b/data/behavior_table.c
index 1312c363f..d086f23d3 100644
--- a/data/behavior_table.c
+++ b/data/behavior_table.c
@@ -573,34 +573,38 @@ enum BehaviorId get_id_from_vanilla_behavior(const BehaviorScript* behavior) {
}
const BehaviorScript* get_behavior_from_id(enum BehaviorId id) {
- const BehaviorScript* behavior = smlua_get_hooked_behavior_from_id(id, true);
- if (behavior != NULL) { return behavior; }
-
- if (id < 0 || id >= id_bhv_max_count) {
- return NULL;
+ const BehaviorScript* behavior = smlua_get_original_behavior_from_id(id);
+ if (behavior != NULL) {
+ return behavior;
}
+ return get_vanilla_behavior_from_id(id);
+}
- return gBehaviorTable[id].script;
+const BehaviorScript* get_vanilla_behavior_from_id(enum BehaviorId id) {
+ if (id >= 0 && id < id_bhv_max_count) {
+ return gBehaviorTable[id].script;
+ }
+ return NULL;
}
const char* get_behavior_name_from_id(enum BehaviorId id) {
- if (id < 0 || id >= id_bhv_max_count) {
- return smlua_get_name_from_hooked_behavior_id(id);
+ if (id >= 0 && id < id_bhv_max_count) {
+ return gBehaviorTable[id].name;
}
-
- return gBehaviorTable[id].name;
+ return smlua_get_behavior_name_from_id(id);
}
enum BehaviorId get_id_from_behavior_name(const char* name) {
- for (enum BehaviorId i = 0; i < id_bhv_max_count; i++) {
- if (gBehaviorTable[i].name && !strcmp(name, gBehaviorTable[i].name)) {
- return i;
+ growing_array_for_each_(gHookedBehaviors, struct LuaHookedBehavior, hooked) {
+ growing_array_for_each_(hooked->bhvNames, const char, bhvName) {
+ if (strcmp(name, bhvName) == 0) {
+ return hooked->customId;
+ }
}
}
- for (int i = 0; i < gHookedBehaviorsCount; i++) {
- struct LuaHookedBehavior *hooked = &gHookedBehaviors[i];
- if (hooked->bhvName && !strcmp(name, hooked->bhvName)) {
- return hooked->overrideId;
+ for (enum BehaviorId id = 0; id < id_bhv_max_count; id++) {
+ if (gBehaviorTable[id].name && strcmp(name, gBehaviorTable[id].name) == 0) {
+ return id;
}
}
return id_bhv_max_count;
diff --git a/data/dynos_bin_behavior.cpp b/data/dynos_bin_behavior.cpp
index 7f0da7ed6..c6d1c6471 100644
--- a/data/dynos_bin_behavior.cpp
+++ b/data/dynos_bin_behavior.cpp
@@ -12,6 +12,7 @@ extern "C" {
#include "game/area.h"
#include "game/object_list_processor.h"
#include "game/interaction.h"
+#include "pc/lua/smlua_hooks.h"
#include "pc/lua/utils/smlua_anim_utils.h"
#include "pc/lua/utils/smlua_collision_utils.h"
@@ -780,7 +781,7 @@ s64 DynOS_Bhv_ParseBehaviorScriptConstants(const String &_Arg, bool *found) {
bhv_constant(id_bhvPointLight);
// Define a special type for new ids that don't override.
- if (_Arg == "id_bhvNewId") { return (BehaviorScript) (0xFFFF); }
+ if (_Arg == "id_bhvNewId") { return (BehaviorScript) LUA_BEHAVIOR_NEW_ID; }
// Legacy behavior ids
bhv_legacy_constant(id_bhvFish2, id_bhvManyBlueFishSpawner);
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index 8b197bcb7..09101617a 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -84,9 +84,6 @@
- [smlua_audio_utils.h](#smlua_audio_utilsh)
- [smlua_hooks.h](#smlua_hooksh)
- [enum LuaHookedEventType](#enum-LuaHookedEventType)
- - [enum LuaHookedEventReturn](#enum-LuaHookedEventReturn)
- - [enum LuaActionHookType](#enum-LuaActionHookType)
- - [enum LuaModMenuElementType](#enum-LuaModMenuElementType)
- [smlua_misc_utils.h](#smlua_misc_utilsh)
- [enum HudDisplayValue](#enum-HudDisplayValue)
- [enum HudDisplayFlags](#enum-HudDisplayFlags)
@@ -3594,7 +3591,6 @@
| HOOK_BEFORE_PLAY_MODE_UPDATE | 67 |
| HOOK_ON_PLAY_MODE_UPDATE | 68 |
| HOOK_MAX | 69 |
-- MAX_HOOKED_BEHAVIORS
[:arrow_up_small:](#)
diff --git a/docs/lua/functions-3.md b/docs/lua/functions-3.md
index ec32aed47..5ea0a8626 100644
--- a/docs/lua/functions-3.md
+++ b/docs/lua/functions-3.md
@@ -190,7 +190,7 @@ Gets the draw distance scalar
## [get_id_from_behavior](#get_id_from_behavior)
### Description
-Gets a behavior ID from a behavior script
+Gets the behavior ID of the provided `behavior`
### Lua Example
`local enumValue = get_id_from_behavior(behavior)`
@@ -213,7 +213,7 @@ Gets a behavior ID from a behavior script
## [get_id_from_vanilla_behavior](#get_id_from_vanilla_behavior)
### Description
-Gets a behavior ID from only vanilla behavior scripts
+Gets the behavior ID of the provided `behavior` if it's a vanilla behavior, `id_bhv_max_count` otherwise
### Lua Example
`local enumValue = get_id_from_vanilla_behavior(behavior)`
@@ -236,7 +236,7 @@ Gets a behavior ID from only vanilla behavior scripts
## [get_behavior_from_id](#get_behavior_from_id)
### Description
-Gets a behavior script from a behavior ID
+Gets the behavior script corresponding to the provided `id`
### Lua Example
`local pointerValue = get_behavior_from_id(id)`
@@ -256,10 +256,33 @@ Gets a behavior script from a behavior ID
+## [get_vanilla_behavior_from_id](#get_vanilla_behavior_from_id)
+
+### Description
+Gets the behavior script corresponding to the provided `id` if it's a vanilla behavior, `nil` otherwise
+
+### Lua Example
+`local pointerValue = get_vanilla_behavior_from_id(id)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| id | [enum BehaviorId](constants.md#enum-BehaviorId) |
+
+### Returns
+- `Pointer` <`BehaviorScript`>
+
+### C Prototype
+`const BehaviorScript* get_vanilla_behavior_from_id(enum BehaviorId id);`
+
+[:arrow_up_small:](#)
+
+
+
## [get_behavior_name_from_id](#get_behavior_name_from_id)
### Description
-Gets a behavior name from a behavior ID (bhvMyGreatMODCustom004)
+Gets the behavior name from the provided `id` (bhvMyGreatMODCustom004)
### Lua Example
`local stringValue = get_behavior_name_from_id(id)`
@@ -282,7 +305,7 @@ Gets a behavior name from a behavior ID (bhvMyGreatMODCustom004)
## [get_id_from_behavior_name](#get_id_from_behavior_name)
### Description
-Gets a behavior ID from a behavior name
+Gets the behavior ID corresponding to the provided `name`
### Lua Example
`local enumValue = get_id_from_behavior_name(name)`
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 1e1721dfb..5b28af7bf 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -632,6 +632,7 @@
- [get_id_from_behavior](functions-3.md#get_id_from_behavior)
- [get_id_from_vanilla_behavior](functions-3.md#get_id_from_vanilla_behavior)
- [get_behavior_from_id](functions-3.md#get_behavior_from_id)
+ - [get_vanilla_behavior_from_id](functions-3.md#get_vanilla_behavior_from_id)
- [get_behavior_name_from_id](functions-3.md#get_behavior_name_from_id)
- [get_id_from_behavior_name](functions-3.md#get_id_from_behavior_name)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md
index 2f68f2d7c..ac807cb4f 100644
--- a/docs/lua/structs.md
+++ b/docs/lua/structs.md
@@ -1906,6 +1906,7 @@
| collidedObjs | `Array` <`Object`> | |
| collisionData | `Pointer` <`Collision`> | |
| behavior | `Pointer` <`BehaviorScript`> | read-only |
+| initBhvCommand | `Pointer` <`BehaviorScript`> | read-only |
| curBhvCommand | `Pointer` <`BehaviorScript`> | read-only |
| bhvStack | `Array` <`integer`> | read-only |
| bhvStackIndex | `integer` | read-only |
diff --git a/include/behavior_table.h b/include/behavior_table.h
index b3ab308f2..6b846ef51 100644
--- a/include/behavior_table.h
+++ b/include/behavior_table.h
@@ -549,15 +549,17 @@ enum BehaviorId {
id_bhv_max_count // must be the last in the list
};
-/* |description|Gets a behavior ID from a behavior script|descriptionEnd| */
+/* |description|Gets the behavior ID of the provided `behavior`|descriptionEnd| */
enum BehaviorId get_id_from_behavior(const BehaviorScript* behavior);
-/* |description|Gets a behavior ID from only vanilla behavior scripts|descriptionEnd| */
+/* |description|Gets the behavior ID of the provided `behavior` if it's a vanilla behavior, `id_bhv_max_count` otherwise|descriptionEnd| */
enum BehaviorId get_id_from_vanilla_behavior(const BehaviorScript* behavior);
-/* |description|Gets a behavior script from a behavior ID|descriptionEnd| */
+/* |description|Gets the behavior script corresponding to the provided `id`|descriptionEnd| */
const BehaviorScript* get_behavior_from_id(enum BehaviorId id);
-/* |description|Gets a behavior name from a behavior ID (bhvMyGreatMODCustom004)|descriptionEnd| */
+/* |description|Gets the behavior script corresponding to the provided `id` if it's a vanilla behavior, `nil` otherwise|descriptionEnd| */
+const BehaviorScript* get_vanilla_behavior_from_id(enum BehaviorId id);
+/* |description|Gets the behavior name from the provided `id` (bhvMyGreatMODCustom004)|descriptionEnd| */
const char* get_behavior_name_from_id(enum BehaviorId id);
-/* |description|Gets a behavior ID from a behavior name|descriptionEnd| */
+/* |description|Gets the behavior ID corresponding to the provided `name`|descriptionEnd| */
enum BehaviorId get_id_from_behavior_name(const char* name);
#endif
diff --git a/include/types.h b/include/types.h
index a8bd4c792..618f58726 100644
--- a/include/types.h
+++ b/include/types.h
@@ -258,6 +258,7 @@ struct Object
void *respawnInfo;
void (*areaTimerRunOnceCallback)(void);
const BehaviorScript *behavior;
+ const BehaviorScript *initBhvCommand;
const BehaviorScript *curBhvCommand;
uintptr_t bhvStack[OBJECT_MAX_BHV_STACK];
diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c
index 316b5b82d..00daaecd0 100644
--- a/src/engine/behavior_script.c
+++ b/src/engine/behavior_script.c
@@ -1372,21 +1372,17 @@ cur_obj_update_begin:;
// Execute the behavior script.
gCurBhvCommand = gCurrentObject->curBhvCommand;
- u8 skipBehavior = smlua_call_behavior_hook(&gCurBhvCommand, gCurrentObject, true);
+ do {
+ if (!gCurBhvCommand) { break; }
- if (!skipBehavior) {
- do {
- if (!gCurBhvCommand) { break; }
+ u32 index = *gCurBhvCommand >> 24;
+ if (index >= BEHAVIOR_CMD_TABLE_MAX) { break; }
- u32 index = *gCurBhvCommand >> 24;
- if (index >= BEHAVIOR_CMD_TABLE_MAX) { break; }
+ bhvCmdProc = BehaviorCmdTable[index];
+ bhvProcResult = bhvCmdProc();
+ } while (bhvProcResult == BHV_PROC_CONTINUE);
- bhvCmdProc = BehaviorCmdTable[index];
- bhvProcResult = bhvCmdProc();
- } while (bhvProcResult == BHV_PROC_CONTINUE);
- }
-
- smlua_call_behavior_hook(&gCurBhvCommand, gCurrentObject, false);
+ smlua_call_behavior_hook(gCurrentObject);
gCurrentObject->curBhvCommand = gCurBhvCommand;
// Increment the object's timer.
diff --git a/src/game/memory.h b/src/game/memory.h
index ba7b9186d..0584d1646 100644
--- a/src/game/memory.h
+++ b/src/game/memory.h
@@ -83,6 +83,13 @@ bool growing_array_swap_and_pop(struct GrowingArray *array, void *ptr);
void growing_array_free(struct GrowingArray **array);
void growing_array_debug_print(struct GrowingArray *array, const char *name, s32 x, s32 y);
+#define growing_array_for_each_(array, type, item) \
+ for (type **_head_ = (type **)((array) != NULL ? (array)->buffer : NULL), \
+ **_tail_ = _head_ + ((array) != NULL ? (array)->count : 0), \
+ * item = NULL; \
+ _head_ != NULL && _head_ != _tail_ && (item = *_head_, TRUE); \
+ _head_++)
+
void alloc_display_list_reset(void);
void *alloc_display_list(u32 size);
diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c
index 8705b804a..df086d6ab 100644
--- a/src/game/object_helpers.c
+++ b/src/game/object_helpers.c
@@ -345,7 +345,8 @@ void obj_set_held_state(struct Object *obj, const BehaviorScript *heldBehavior)
obj->heldByPlayerIndex = 0;
}
} else {
- obj->curBhvCommand = segmented_to_virtual(smlua_override_behavior(heldBehavior));
+ obj->initBhvCommand = smlua_get_behavior_command(heldBehavior);
+ obj->curBhvCommand = obj->initBhvCommand;
obj->bhvStackIndex = 0;
}
}
diff --git a/src/game/spawn_object.c b/src/game/spawn_object.c
index f84adefbc..50e8ce8af 100644
--- a/src/game/spawn_object.c
+++ b/src/game/spawn_object.c
@@ -374,7 +374,6 @@ static void snap_object_to_floor(struct Object *obj) {
struct Object *create_object(const BehaviorScript *bhvScript) {
if (!bhvScript) { return NULL; }
s32 objListIndex = OBJ_LIST_DEFAULT;
- bool luaBehavior = smlua_is_behavior_hooked(bhvScript);
const BehaviorScript *behavior = smlua_override_behavior(bhvScript);
// If the first behavior script command is "begin