mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-05-31 05:01:36 +00:00
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
This commit is contained in:
parent
925325c5eb
commit
834512a5c5
24 changed files with 511 additions and 353 deletions
|
|
@ -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<br />\n\n'
|
||||
return s
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:](#)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|||
|
||||
<br />
|
||||
|
||||
## [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:](#)
|
||||
|
||||
<br />
|
||||
|
||||
## [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)`
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <object list>", then
|
||||
|
|
@ -392,7 +391,8 @@ struct Object *create_object(const BehaviorScript *bhvScript) {
|
|||
struct Object *obj = allocate_object(objList);
|
||||
if (obj == NULL) { return NULL; }
|
||||
|
||||
obj->curBhvCommand = luaBehavior ? bhvScript : behavior;
|
||||
obj->initBhvCommand = smlua_get_behavior_command(bhvScript);
|
||||
obj->curBhvCommand = obj->initBhvCommand;
|
||||
obj->behavior = behavior;
|
||||
|
||||
if (objListIndex == OBJ_LIST_UNIMPORTANT) {
|
||||
|
|
|
|||
|
|
@ -1478,7 +1478,7 @@ static struct LuaObjectField sMarioStateFields[LUA_MARIO_STATE_FIELD_COUNT] = {
|
|||
static struct LuaObjectField sModFields[LUA_MOD_FIELD_COUNT] = {
|
||||
{ "basePath", LVT_STRING, offsetof(struct Mod, basePath), true, LOT_NONE },
|
||||
{ "category", LVT_STRING_P, offsetof(struct Mod, category), true, LOT_NONE },
|
||||
{ "customBehaviorIndex", LVT_U8, offsetof(struct Mod, customBehaviorIndex), true, LOT_NONE },
|
||||
{ "customBehaviorIndex", LVT_U16, offsetof(struct Mod, customBehaviorIndex), true, LOT_NONE },
|
||||
{ "description", LVT_STRING_P, offsetof(struct Mod, description), true, LOT_NONE },
|
||||
{ "enabled", LVT_BOOL, offsetof(struct Mod, enabled), true, LOT_NONE },
|
||||
{ "fileCapacity", LVT_U16, offsetof(struct Mod, fileCapacity), true, LOT_NONE },
|
||||
|
|
@ -1602,7 +1602,7 @@ static struct LuaObjectField sNetworkPlayerFields[LUA_NETWORK_PLAYER_FIELD_COUNT
|
|||
{ "type", LVT_U8, offsetof(struct NetworkPlayer, type), true, LOT_NONE },
|
||||
};
|
||||
|
||||
#define LUA_OBJECT_FIELD_COUNT 763
|
||||
#define LUA_OBJECT_FIELD_COUNT 764
|
||||
static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = {
|
||||
{ "activeFlags", LVT_S16, offsetof(struct Object, activeFlags), false, LOT_NONE },
|
||||
{ "allowRemoteInteractions", LVT_U8, offsetof(struct Object, allowRemoteInteractions), false, LOT_NONE },
|
||||
|
|
@ -1629,6 +1629,7 @@ static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = {
|
|||
{ "hookRender", LVT_U8, offsetof(struct Object, hookRender), false, LOT_NONE },
|
||||
{ "hurtboxHeight", LVT_F32, offsetof(struct Object, hurtboxHeight), false, LOT_NONE },
|
||||
{ "hurtboxRadius", LVT_F32, offsetof(struct Object, hurtboxRadius), false, LOT_NONE },
|
||||
{ "initBhvCommand", LVT_BEHAVIORSCRIPT_P, offsetof(struct Object, initBhvCommand), true, LOT_POINTER },
|
||||
{ "numCollidedObjs", LVT_S16, offsetof(struct Object, numCollidedObjs), false, LOT_NONE },
|
||||
{ "numSurfaces", LVT_U32, offsetof(struct Object, numSurfaces), true, LOT_NONE },
|
||||
{ "o1UpForceSpawn", LVT_S32, offsetof(struct Object, o1UpForceSpawn), false, LOT_NONE },
|
||||
|
|
|
|||
|
|
@ -3581,7 +3581,6 @@ char gSmluaConstants[] = ""
|
|||
"HOOK_BEFORE_PLAY_MODE_UPDATE=67\n"
|
||||
"HOOK_ON_PLAY_MODE_UPDATE=68\n"
|
||||
"HOOK_MAX=69\n"
|
||||
"MAX_HOOKED_BEHAVIORS=1024\n"
|
||||
"HUD_DISPLAY_LIVES=0\n"
|
||||
"HUD_DISPLAY_COINS=1\n"
|
||||
"HUD_DISPLAY_STARS=2\n"
|
||||
|
|
|
|||
|
|
@ -9974,6 +9974,23 @@ int smlua_func_get_behavior_from_id(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int smlua_func_get_vanilla_behavior_from_id(lua_State* L) {
|
||||
if (L == NULL) { return 0; }
|
||||
|
||||
int top = lua_gettop(L);
|
||||
if (top != 1) {
|
||||
LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "get_vanilla_behavior_from_id", 1, top);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int id = smlua_to_integer(L, 1);
|
||||
if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "get_vanilla_behavior_from_id"); return 0; }
|
||||
|
||||
smlua_push_pointer(L, LVT_BEHAVIORSCRIPT_P, (void*)get_vanilla_behavior_from_id(id), NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int smlua_func_get_behavior_name_from_id(lua_State* L) {
|
||||
if (L == NULL) { return 0; }
|
||||
|
||||
|
|
@ -37717,6 +37734,7 @@ void smlua_bind_functions_autogen(void) {
|
|||
smlua_bind_function(L, "get_id_from_behavior", smlua_func_get_id_from_behavior);
|
||||
smlua_bind_function(L, "get_id_from_vanilla_behavior", smlua_func_get_id_from_vanilla_behavior);
|
||||
smlua_bind_function(L, "get_behavior_from_id", smlua_func_get_behavior_from_id);
|
||||
smlua_bind_function(L, "get_vanilla_behavior_from_id", smlua_func_get_vanilla_behavior_from_id);
|
||||
smlua_bind_function(L, "get_behavior_name_from_id", smlua_func_get_behavior_name_from_id);
|
||||
smlua_bind_function(L, "get_id_from_behavior_name", smlua_func_get_id_from_behavior_name);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "behavior_commands.h"
|
||||
#include "pc/mods/mod.h"
|
||||
#include "game/object_list_processor.h"
|
||||
#include "game/object_helpers.h"
|
||||
#include "pc/djui/djui_chat_message.h"
|
||||
#include "pc/crash_handler.h"
|
||||
#include "game/hud.h"
|
||||
|
|
@ -30,7 +31,6 @@ 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)
|
||||
|
||||
u64* gBehaviorOffset = &gPcDebug.bhvOffset;
|
||||
|
||||
|
|
@ -699,112 +699,128 @@ u32 smlua_get_action_interaction_type(struct MarioState* m) {
|
|||
// hooked behaviors //
|
||||
//////////////////////
|
||||
|
||||
struct LuaHookedBehavior gHookedBehaviors[MAX_HOOKED_BEHAVIORS] = { 0 };
|
||||
int gHookedBehaviorsCount = 0;
|
||||
struct GrowingArray *gHookedBehaviors = NULL;
|
||||
|
||||
enum BehaviorId smlua_get_original_behavior_id(const BehaviorScript* behavior) {
|
||||
enum BehaviorId id = get_id_from_behavior(behavior);
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior* hooked = &gHookedBehaviors[i];
|
||||
if (hooked->behavior == behavior) {
|
||||
id = hooked->overrideId;
|
||||
static struct LuaHookedBehavior *smlua_find_hooked_behavior(enum BehaviorId id) {
|
||||
growing_array_for_each_(gHookedBehaviors, struct LuaHookedBehavior, hooked) {
|
||||
if (hooked->behaviorId == id || hooked->customId == id) {
|
||||
return hooked;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
const BehaviorScript* smlua_override_behavior(const BehaviorScript *behavior) {
|
||||
lua_State *L = gLuaState;
|
||||
if (L == NULL) { return behavior; }
|
||||
|
||||
enum BehaviorId id = get_id_from_behavior(behavior);
|
||||
const BehaviorScript *hookedBehavior = smlua_get_hooked_behavior_from_id(id, false);
|
||||
if (hookedBehavior != NULL) { return hookedBehavior; }
|
||||
return behavior + *gBehaviorOffset;
|
||||
}
|
||||
|
||||
const BehaviorScript* smlua_get_hooked_behavior_from_id(enum BehaviorId id, bool returnOriginal) {
|
||||
lua_State *L = gLuaState;
|
||||
if (L == NULL) { return NULL; }
|
||||
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior* hooked = &gHookedBehaviors[i];
|
||||
if (hooked->behaviorId != id && hooked->overrideId != id) { continue; }
|
||||
if (returnOriginal && !hooked->replace) { return hooked->originalBehavior; }
|
||||
return hooked->behavior;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool smlua_is_behavior_hooked(const BehaviorScript *behavior) {
|
||||
lua_State *L = gLuaState;
|
||||
if (L == NULL) { return false; }
|
||||
|
||||
enum BehaviorId id = get_id_from_behavior(behavior);
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior *hooked = &gHookedBehaviors[i];
|
||||
if (hooked->behaviorId != id && hooked->overrideId != id) { continue; }
|
||||
return hooked->luaBehavior;
|
||||
}
|
||||
|
||||
return false;
|
||||
static struct LuaHookedBehavior *smlua_create_hooked_behavior() {
|
||||
struct LuaHookedBehavior *hooked = growing_array_alloc(gHookedBehaviors, sizeof(struct LuaHookedBehavior));
|
||||
hooked->bhvNames = growing_array_init(NULL, 4, malloc, free);
|
||||
hooked->initCallbacks = growing_array_init(NULL, 4, malloc, free);
|
||||
hooked->loopCallbacks = growing_array_init(NULL, 4, malloc, free);
|
||||
return hooked;
|
||||
}
|
||||
|
||||
const char* smlua_get_name_from_hooked_behavior_id(enum BehaviorId id) {
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior *hooked = &gHookedBehaviors[i];
|
||||
if (hooked->behaviorId != id && hooked->overrideId != id) { continue; }
|
||||
return hooked->bhvName;
|
||||
// Replace the original behavior with a custom behavior if it exists.
|
||||
const BehaviorScript *smlua_override_behavior(const BehaviorScript *behavior) {
|
||||
enum BehaviorId id = get_id_from_behavior(behavior);
|
||||
struct LuaHookedBehavior *hooked = smlua_find_hooked_behavior(id);
|
||||
if (hooked) {
|
||||
return hooked->script;
|
||||
}
|
||||
return behavior + *gBehaviorOffset;
|
||||
}
|
||||
|
||||
const BehaviorScript *smlua_get_original_behavior_from_id(enum BehaviorId id) {
|
||||
struct LuaHookedBehavior *hooked = smlua_find_hooked_behavior(id);
|
||||
if (hooked) {
|
||||
if (hooked->type == LUA_BEHAVIOR_TYPE_CALLBACKS) {
|
||||
const BehaviorScript *script = get_vanilla_behavior_from_id(id);
|
||||
if (script) {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
return hooked->script;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return the first behavior command that will be executed by a freshly created object.
|
||||
const BehaviorScript *smlua_get_behavior_command(const BehaviorScript *behavior) {
|
||||
enum BehaviorId id = get_id_from_behavior(behavior);
|
||||
struct LuaHookedBehavior *hooked = smlua_find_hooked_behavior(id);
|
||||
|
||||
// Lua and custom behaviors only
|
||||
if (hooked && hooked->type > LUA_BEHAVIOR_TYPE_CALLBACKS) {
|
||||
return hooked->script;
|
||||
}
|
||||
return behavior;
|
||||
}
|
||||
|
||||
const char* smlua_get_behavior_name_from_id(enum BehaviorId id) {
|
||||
struct LuaHookedBehavior *hooked = smlua_find_hooked_behavior(id);
|
||||
if (hooked) {
|
||||
return (const char *) hooked->bhvNames->buffer[hooked->bhvNames->count - 1]; // return the last name registered
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int smlua_hook_custom_bhv(BehaviorScript *bhvScript, const char *bhvName) {
|
||||
if (gHookedBehaviorsCount >= MAX_HOOKED_BEHAVIORS) {
|
||||
LOG_ERROR("Hooked behaviors exceeded maximum references!");
|
||||
return 0;
|
||||
}
|
||||
enum BehaviorId id = get_id_from_behavior(bhvScript);
|
||||
|
||||
u32 originalBehaviorId = get_id_from_behavior(bhvScript);
|
||||
|
||||
if (originalBehaviorId == id_bhvMario) {
|
||||
// Can't hook Mario
|
||||
if (id == id_bhvMario) {
|
||||
LOG_LUA_LINE("Cannot hook Mario's behavior. Use HOOK_MARIO_UPDATE and HOOK_BEFORE_MARIO_UPDATE.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 newBehavior = originalBehaviorId >= id_bhv_max_count;
|
||||
// Do not allow custom behaviors to hook non-vanilla ids
|
||||
bool isVanillaId = id < id_bhv_max_count;
|
||||
struct LuaHookedBehavior *hooked = isVanillaId ? smlua_find_hooked_behavior(id) : NULL;
|
||||
|
||||
struct LuaHookedBehavior *hooked = &gHookedBehaviors[gHookedBehaviorsCount];
|
||||
u16 customBehaviorId = (gHookedBehaviorsCount & 0xFFFF) | LUA_BEHAVIOR_FLAG;
|
||||
hooked->behavior = bhvScript;
|
||||
hooked->behavior[1] = (BehaviorScript)BC_B0H(0x39, customBehaviorId); // This is ID(customBehaviorId)
|
||||
hooked->behaviorId = customBehaviorId;
|
||||
hooked->overrideId = newBehavior ? customBehaviorId : originalBehaviorId;
|
||||
hooked->originalId = originalBehaviorId;
|
||||
hooked->originalBehavior = newBehavior ? bhvScript : get_behavior_from_id(originalBehaviorId);
|
||||
hooked->bhvName = bhvName;
|
||||
hooked->initReference = 0;
|
||||
hooked->loopReference = 0;
|
||||
hooked->replace = true;
|
||||
hooked->luaBehavior = false;
|
||||
hooked->mod = gLuaActiveMod;
|
||||
hooked->modFile = gLuaActiveModFile;
|
||||
// Create a new hooked behavior
|
||||
if (!hooked) {
|
||||
if (gHookedBehaviors->count >= MAX_HOOKED_BEHAVIORS) {
|
||||
LOG_ERROR("Hooked behaviors exceeded maximum references!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
gHookedBehaviorsCount++;
|
||||
enum BehaviorId customId = LUA_BEHAVIOR_START + gHookedBehaviors->count;
|
||||
|
||||
hooked = smlua_create_hooked_behavior();
|
||||
hooked->behaviorId = isVanillaId ? id : customId;
|
||||
hooked->customId = customId;
|
||||
hooked->type = LUA_BEHAVIOR_TYPE_CUSTOM;
|
||||
hooked->script = bhvScript;
|
||||
hooked->script[1] = (BehaviorScript) ID(customId);
|
||||
|
||||
} else {
|
||||
|
||||
// Behavior script can be replaced as long as it's not a custom behavior
|
||||
if (hooked->type != LUA_BEHAVIOR_TYPE_CUSTOM) {
|
||||
free(hooked->script);
|
||||
hooked->type = LUA_BEHAVIOR_TYPE_CUSTOM;
|
||||
hooked->script = bhvScript;
|
||||
hooked->script[1] = (BehaviorScript) ID(hooked->customId);
|
||||
} else {
|
||||
LOG_LUA_WARNING("Hook behavior: the behavior script for the behavior %s is custom and cannot be changed", (const char *) hooked->bhvNames->buffer[hooked->bhvNames->count - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a name to that behavior
|
||||
char *name = growing_array_alloc(hooked->bhvNames, strlen(bhvName) + 1);
|
||||
strcpy(name, bhvName);
|
||||
|
||||
// We want to push the behavior into the global LUA state. So mods can access it.
|
||||
// It's also used for some things that would normally access a LUA behavior instead.
|
||||
lua_State* L = gLuaState;
|
||||
lua_State *L = gLuaState;
|
||||
if (L != NULL) {
|
||||
lua_pushinteger(L, customBehaviorId);
|
||||
lua_pushinteger(L, hooked->behaviorId);
|
||||
lua_setglobal(L, bhvName);
|
||||
LOG_INFO("Registered custom behavior: 0x%04hX - %s", customBehaviorId, bhvName);
|
||||
LOG_INFO("Registered custom behavior for behavior id 0x%04hX (custom id: 0x%04hX, custom name: %s)", hooked->behaviorId, hooked->customId, bhvName);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int smlua_hook_behavior(lua_State* L) {
|
||||
int smlua_hook_behavior(lua_State *L) {
|
||||
if (L == NULL) { return 0; }
|
||||
if (!smlua_functions_valid_param_range(L, 5, 6)) { return 0; }
|
||||
|
||||
|
|
@ -815,88 +831,154 @@ int smlua_hook_behavior(lua_State* L) {
|
|||
|
||||
int paramCount = lua_gettop(L);
|
||||
|
||||
if (gHookedBehaviorsCount >= MAX_HOOKED_BEHAVIORS) {
|
||||
LOG_LUA_LINE("Hooked behaviors exceeded maximum references!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool noOverrideId = (lua_type(L, 1) == LUA_TNIL);
|
||||
gSmLuaConvertSuccess = true;
|
||||
lua_Integer overrideBehaviorId = noOverrideId ? 0xFFFFFF : smlua_to_integer(L, 1);
|
||||
if (!gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: tried to override invalid behavior: %lld, %u", overrideBehaviorId, gSmLuaConvertSuccess);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (overrideBehaviorId == id_bhvMario) {
|
||||
LOG_LUA_LINE("Hook behavior: cannot hook Mario's behavior. Use HOOK_MARIO_UPDATE and HOOK_BEFORE_MARIO_UPDATE.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_Integer objectList = smlua_to_integer(L, 2);
|
||||
if (objectList <= 0 || objectList >= NUM_OBJ_LISTS || !gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: tried use invalid object list: %lld, %u", objectList, gSmLuaConvertSuccess);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool replaceBehavior = smlua_to_boolean(L, 3);
|
||||
if (!gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: could not parse replaceBehavior");
|
||||
return 0;
|
||||
}
|
||||
const BehaviorScript* originalBehavior = noOverrideId ? NULL : get_behavior_from_id(overrideBehaviorId);
|
||||
if (originalBehavior == NULL) {
|
||||
replaceBehavior = true;
|
||||
}
|
||||
|
||||
int initReference = 0;
|
||||
int initReferenceType = lua_type(L, 4);
|
||||
if (initReferenceType == LUA_TNIL) {
|
||||
// nothing
|
||||
} else if (initReferenceType == LUA_TFUNCTION) {
|
||||
// get reference
|
||||
lua_pushvalue(L, 4);
|
||||
initReference = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
} else {
|
||||
LOG_LUA_LINE("Hook behavior: tried to reference non-function for init");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int loopReference = 0;
|
||||
int loopReferenceType = lua_type(L, 5);
|
||||
if (loopReferenceType == LUA_TNIL) {
|
||||
// nothing
|
||||
} else if (loopReferenceType == LUA_TFUNCTION) {
|
||||
// get reference
|
||||
lua_pushvalue(L, 5);
|
||||
loopReference = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
} else {
|
||||
LOG_LUA_LINE("Hook behavior: tried to reference non-function for loop");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *bhvName = NULL;
|
||||
if (paramCount >= 6) {
|
||||
int bhvNameType = lua_type(L, 6);
|
||||
if (bhvNameType == LUA_TNIL) {
|
||||
// nothing
|
||||
} else if (bhvNameType == LUA_TSTRING) {
|
||||
bhvName = smlua_to_string(L, 6);
|
||||
if (!bhvName || !gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: could not parse bhvName");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
LOG_LUA_LINE("Hook behavior: invalid type passed for argument bhvName: %u", bhvNameType);
|
||||
// Get behavior id
|
||||
enum BehaviorId id = LUA_BEHAVIOR_NEW_ID;
|
||||
if (lua_type(L, 1) != LUA_TNIL) {
|
||||
gSmLuaConvertSuccess = true;
|
||||
id = (enum BehaviorId) (u16) smlua_to_integer(L, 1);
|
||||
if (!gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: tried to override invalid behavior id");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If not provided, generate generic behavior name: bhv<ModName>Custom<Index>
|
||||
// Can't hook Mario
|
||||
if (id == id_bhvMario) {
|
||||
LOG_LUA_LINE("Hook behavior: cannot hook Mario's behavior. Use HOOK_MARIO_UPDATE and HOOK_BEFORE_MARIO_UPDATE.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get object list
|
||||
enum ObjectList objectList = (enum ObjectList) (u8) smlua_to_integer(L, 2);
|
||||
if (!gSmLuaConvertSuccess || objectList >= NUM_OBJ_LISTS) {
|
||||
LOG_LUA_LINE("Hook behavior: tried use invalid object list: %d, %u", objectList, gSmLuaConvertSuccess);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check replace if it's a vanilla behavior hook
|
||||
bool replaceBehavior = true;
|
||||
bool isVanillaId = id < id_bhv_max_count;
|
||||
if (isVanillaId) {
|
||||
replaceBehavior = smlua_to_boolean(L, 3);
|
||||
if (!gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: could not convert replaceBehavior to boolean");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get init function
|
||||
int initReference = 0;
|
||||
int initReferenceType = lua_type(L, 4);
|
||||
switch (initReferenceType) {
|
||||
case LUA_TNIL: break;
|
||||
|
||||
case LUA_TFUNCTION: {
|
||||
lua_pushvalue(L, 4);
|
||||
initReference = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
LOG_LUA_LINE("Hook behavior: invalid type passed for argument initFunction: '%s', should be '%s'", lua_typename(L, initReferenceType), lua_typename(L, LUA_TFUNCTION));
|
||||
} return 0;
|
||||
}
|
||||
|
||||
// Get loop function
|
||||
int loopReference = 0;
|
||||
int loopReferenceType = lua_type(L, 5);
|
||||
switch (loopReferenceType) {
|
||||
case LUA_TNIL: break;
|
||||
|
||||
case LUA_TFUNCTION: {
|
||||
lua_pushvalue(L, 5);
|
||||
loopReference = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
LOG_LUA_LINE("Hook behavior: invalid type passed for argument loopFunction: '%s', should be '%s'", lua_typename(L, loopReferenceType), lua_typename(L, LUA_TFUNCTION));
|
||||
} return 0;
|
||||
}
|
||||
|
||||
// Get name
|
||||
const char *bhvName = NULL;
|
||||
if (paramCount >= 6) {
|
||||
int bhvNameType = lua_type(L, 6);
|
||||
switch (bhvNameType) {
|
||||
case LUA_TNIL: break;
|
||||
|
||||
case LUA_TSTRING: {
|
||||
bhvName = smlua_to_string(L, 6);
|
||||
if (!bhvName || !gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: could not parse bhvName");
|
||||
return 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
LOG_LUA_LINE("Hook behavior: invalid type passed for argument bhvName: '%s', should be '%s'", lua_typename(L, bhvNameType), lua_typename(L, LUA_TSTRING));
|
||||
} return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get an existing hooked behavior or create a new one
|
||||
// Do not allow arbitrary non-vanilla ids to be hooked
|
||||
struct LuaHookedBehavior *hooked = NULL;
|
||||
if (id != LUA_BEHAVIOR_NEW_ID) {
|
||||
hooked = smlua_find_hooked_behavior(id);
|
||||
if (!hooked && !isVanillaId) {
|
||||
LOG_LUA_LINE("Hook behavior: behavior id %u is not valid, cannot hook non-existing non-vanilla behaviors", id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!hooked) {
|
||||
if (gHookedBehaviors->count >= MAX_HOOKED_BEHAVIORS) {
|
||||
LOG_ERROR("Hooked behaviors exceeded maximum references!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum BehaviorId customId = LUA_BEHAVIOR_START + gHookedBehaviors->count;
|
||||
|
||||
hooked = smlua_create_hooked_behavior();
|
||||
hooked->behaviorId = id != LUA_BEHAVIOR_NEW_ID ? id : customId;
|
||||
hooked->customId = customId;
|
||||
hooked->type = replaceBehavior ? LUA_BEHAVIOR_TYPE_LUA : LUA_BEHAVIOR_TYPE_CALLBACKS;
|
||||
hooked->script = calloc(4, sizeof(BehaviorScript));
|
||||
hooked->script[0] = (BehaviorScript) BEGIN(objectList);
|
||||
hooked->script[1] = (BehaviorScript) ID(customId);
|
||||
hooked->script[2] = (BehaviorScript) BREAK();
|
||||
hooked->script[3] = (BehaviorScript) BREAK();
|
||||
|
||||
} else {
|
||||
|
||||
// Behavior script can be replaced as long as it's not a custom behavior
|
||||
if (replaceBehavior) {
|
||||
switch (hooked->type) {
|
||||
case LUA_BEHAVIOR_TYPE_CALLBACKS: {
|
||||
hooked->type = LUA_BEHAVIOR_TYPE_LUA;
|
||||
hooked->script[0] = (BehaviorScript) BEGIN(objectList); // Override object list
|
||||
} break;
|
||||
|
||||
case LUA_BEHAVIOR_TYPE_LUA: {
|
||||
// nothing to change
|
||||
} break;
|
||||
|
||||
case LUA_BEHAVIOR_TYPE_CUSTOM: {
|
||||
LOG_LUA_WARNING("Hook behavior: the behavior script for the behavior %s is custom and cannot be changed", (const char *) hooked->bhvNames->buffer[hooked->bhvNames->count - 1]);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Warn user if trying to change the object list
|
||||
enum ObjectList hookedObjectList = get_object_list_from_behavior(hooked->script);
|
||||
if (hookedObjectList != objectList) {
|
||||
LOG_LUA_WARNING("Hook behavior: trying to change the object list of the existing hooked behavior %s: %d (should be %d)", (const char *) hooked->bhvNames->buffer[hooked->bhvNames->count - 1], objectList, hookedObjectList);
|
||||
}
|
||||
}
|
||||
|
||||
// If not provided and the hooked behavior has no name yet, generate generic behavior name: bhv<ModName>Custom<Index>
|
||||
// - <ModName> is the mod name in CamelCase format, alphanumeric chars only
|
||||
// - <Index> is in 3-digit numeric format, ranged from 001 to 256
|
||||
// - <Index> is in 3-digit numeric format (from 001 to 999, no longer applies for index greater than 1000)
|
||||
// For example, the 4th unnamed behavior of the mod "my-great_MOD" will be named "bhvMyGreatMODCustom004"
|
||||
if (!bhvName) {
|
||||
if (!bhvName && hooked->bhvNames->count == 0) {
|
||||
static char sGenericBhvName[MOD_NAME_MAX_LENGTH + 16];
|
||||
s32 i = 3;
|
||||
snprintf(sGenericBhvName, 4, "bhv");
|
||||
|
|
@ -918,90 +1000,76 @@ int smlua_hook_behavior(lua_State* L) {
|
|||
bhvName = sGenericBhvName;
|
||||
}
|
||||
|
||||
struct LuaHookedBehavior* hooked = &gHookedBehaviors[gHookedBehaviorsCount];
|
||||
u16 customBehaviorId = (gHookedBehaviorsCount & 0xFFFF) | LUA_BEHAVIOR_FLAG;
|
||||
hooked->behavior = calloc(4, sizeof(BehaviorScript));
|
||||
hooked->behavior[0] = (BehaviorScript)BC_BB(0x00, objectList); // This is BEGIN(objectList)
|
||||
hooked->behavior[1] = (BehaviorScript)BC_B0H(0x39, customBehaviorId); // This is ID(customBehaviorId)
|
||||
hooked->behavior[2] = (BehaviorScript)BC_B(0x0A); // This is BREAK()
|
||||
hooked->behavior[3] = (BehaviorScript)BC_B(0x0A); // This is BREAK()
|
||||
hooked->behaviorId = customBehaviorId;
|
||||
hooked->overrideId = noOverrideId ? customBehaviorId : overrideBehaviorId;
|
||||
hooked->originalId = customBehaviorId; // For LUA behaviors. The only behavior id they have IS their custom one.
|
||||
hooked->originalBehavior = originalBehavior ? originalBehavior : hooked->behavior;
|
||||
hooked->bhvName = bhvName;
|
||||
hooked->initReference = initReference;
|
||||
hooked->loopReference = loopReference;
|
||||
hooked->replace = replaceBehavior;
|
||||
hooked->luaBehavior = true;
|
||||
hooked->mod = gLuaActiveMod;
|
||||
hooked->modFile = gLuaActiveModFile;
|
||||
// Add name
|
||||
if (bhvName) {
|
||||
char *name = growing_array_alloc(hooked->bhvNames, strlen(bhvName) + 1);
|
||||
strcpy(name, bhvName);
|
||||
}
|
||||
|
||||
gHookedBehaviorsCount++;
|
||||
// Add init function
|
||||
if (initReference) {
|
||||
struct LuaHookedBehaviorCallback *callback = growing_array_alloc(hooked->initCallbacks, sizeof(struct LuaHookedBehaviorCallback));
|
||||
callback->ref = initReference;
|
||||
callback->mod = gLuaActiveMod;
|
||||
callback->modFile = gLuaActiveModFile;
|
||||
}
|
||||
|
||||
// Add loop function
|
||||
if (loopReference) {
|
||||
struct LuaHookedBehaviorCallback *callback = growing_array_alloc(hooked->loopCallbacks, sizeof(struct LuaHookedBehaviorCallback));
|
||||
callback->ref = loopReference;
|
||||
callback->mod = gLuaActiveMod;
|
||||
callback->modFile = gLuaActiveModFile;
|
||||
}
|
||||
|
||||
// We want to push the behavior into the global LUA state. So mods can access it.
|
||||
// It's also used for some things that would normally access a LUA behavior instead.
|
||||
lua_pushinteger(L, customBehaviorId);
|
||||
lua_setglobal(L, bhvName);
|
||||
LOG_INFO("Registered custom behavior: 0x%04hX - %s", customBehaviorId, bhvName);
|
||||
if (bhvName) {
|
||||
lua_pushinteger(L, hooked->behaviorId);
|
||||
lua_setglobal(L, bhvName);
|
||||
} else {
|
||||
bhvName = hooked->bhvNames->buffer[hooked->bhvNames->count - 1]; // log with last registered name
|
||||
}
|
||||
LOG_INFO("Registered Lua behavior for behavior id 0x%04hX (custom id: 0x%04hX, custom name: %s)", hooked->behaviorId, hooked->customId, bhvName);
|
||||
|
||||
// return behavior ID
|
||||
lua_pushinteger(L, customBehaviorId);
|
||||
lua_pushinteger(L, hooked->behaviorId);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object, bool before) {
|
||||
void smlua_call_behavior_hook(struct Object* object) {
|
||||
lua_State* L = gLuaState;
|
||||
if (L == NULL) { return false; }
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior* hooked = &gHookedBehaviors[i];
|
||||
if (L == NULL) { return; }
|
||||
|
||||
// find behavior
|
||||
if (object->behavior != hooked->behavior) {
|
||||
continue;
|
||||
enum BehaviorId id = get_id_from_behavior(object->behavior);
|
||||
struct LuaHookedBehavior *hooked = smlua_find_hooked_behavior(id);
|
||||
if (hooked) {
|
||||
|
||||
// This works for two reasons:
|
||||
// - A behavior first command is always BEGIN(objList), so, after the first update, curBhvCommand will no longer be initBhvCommand
|
||||
// - object->curBhvCommand is not updated until the end of this function
|
||||
bool init = object->curBhvCommand == object->initBhvCommand;
|
||||
|
||||
// Run callbacks one after the other
|
||||
struct GrowingArray *callbacks = init ? hooked->initCallbacks : hooked->loopCallbacks;
|
||||
growing_array_for_each_(callbacks, struct LuaHookedBehaviorCallback, callback) {
|
||||
|
||||
// push the callback onto the stack
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callback->ref);
|
||||
|
||||
// push object
|
||||
smlua_push_object(L, LOT_OBJECT, object, NULL);
|
||||
|
||||
// call the callback
|
||||
if (0 != smlua_call_hook(L, 1, 0, 0, callback->mod, callback->modFile)) {
|
||||
LOG_LUA("Failed to call behavior %s callback for behavior id: %hu",
|
||||
(init ? "init" : "loop"), hooked->behaviorId
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out whether to run before or after
|
||||
if (before && !hooked->replace) {
|
||||
return false;
|
||||
}
|
||||
if (!before && hooked->replace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This behavior doesn't call it's LUA functions in this manner. It actually uses the normal behavior
|
||||
// system.
|
||||
if (!hooked->luaBehavior) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// retrieve and remember first run
|
||||
bool firstRun = (object->curBhvCommand == hooked->originalBehavior) || (object->curBhvCommand == hooked->behavior);
|
||||
if (firstRun && hooked->replace) { *behavior = &hooked->behavior[1]; }
|
||||
|
||||
// get function and null check it
|
||||
int reference = firstRun ? hooked->initReference : hooked->loopReference;
|
||||
if (reference == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// push the callback onto the stack
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, reference);
|
||||
|
||||
// push object
|
||||
smlua_push_object(L, LOT_OBJECT, object, NULL);
|
||||
|
||||
// call the callback
|
||||
if (0 != smlua_call_hook(L, 1, 0, 0, hooked->mod, hooked->modFile)) {
|
||||
LOG_LUA("Failed to call the behavior callback: %u", hooked->behaviorId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return hooked->replace;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1802,10 +1870,13 @@ void smlua_hook_replace_function_references(lua_State* L, int oldReference, int
|
|||
smlua_hook_replace_function_reference(L, &hooked->reference, oldReference, newReference);
|
||||
}
|
||||
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior* hooked = &gHookedBehaviors[i];
|
||||
smlua_hook_replace_function_reference(L, &hooked->initReference, oldReference, newReference);
|
||||
smlua_hook_replace_function_reference(L, &hooked->loopReference, oldReference, newReference);
|
||||
growing_array_for_each_(gHookedBehaviors, struct LuaHookedBehavior, hooked) {
|
||||
growing_array_for_each_(hooked->initCallbacks, struct LuaHookedBehaviorCallback, callback) {
|
||||
smlua_hook_replace_function_reference(L, &callback->ref, oldReference, newReference);
|
||||
}
|
||||
growing_array_for_each_(hooked->loopCallbacks, struct LuaHookedBehaviorCallback, callback) {
|
||||
smlua_hook_replace_function_reference(L, &callback->ref, oldReference, newReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1827,6 +1898,7 @@ void smlua_clear_hooks(void) {
|
|||
memset(hooked->actionHookRefs, 0, sizeof(hooked->actionHookRefs));
|
||||
}
|
||||
sHookedMarioActionsCount = 0;
|
||||
memset(gLuaMarioActionIndex, 0, sizeof(gLuaMarioActionIndex));
|
||||
|
||||
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
||||
struct LuaHookedChatCommand* hooked = &sHookedChatCommands[i];
|
||||
|
|
@ -1858,35 +1930,21 @@ void smlua_clear_hooks(void) {
|
|||
}
|
||||
gHookedModMenuElementsCount = 0;
|
||||
|
||||
for (int i = 0; i < gHookedBehaviorsCount; i++) {
|
||||
struct LuaHookedBehavior* hooked = &gHookedBehaviors[i];
|
||||
growing_array_for_each_(gHookedBehaviors, struct LuaHookedBehavior, hooked) {
|
||||
|
||||
// If this is NULL. We can't do anything with it.
|
||||
if (hooked->behavior != NULL) {
|
||||
// If it's a LUA made behavior, The behavior is allocated so reset and free it.
|
||||
// Otherwise it's a DynOS behavior and it needs to have it's original id put back where it belongs.
|
||||
if (hooked->luaBehavior) {
|
||||
// Just free the allocated behavior.
|
||||
free(hooked->behavior);
|
||||
} else {
|
||||
hooked->behavior[1] = (BehaviorScript)BC_B0H(0x39, hooked->originalId); // This is ID(hooked->originalId)
|
||||
}
|
||||
// Free behavior script if Lua or change back id if it's custom
|
||||
if (hooked->type != LUA_BEHAVIOR_TYPE_CUSTOM) {
|
||||
free(hooked->script);
|
||||
} else {
|
||||
hooked->script[1] = (BehaviorScript) ID(hooked->behaviorId);
|
||||
}
|
||||
// Reset the variables.
|
||||
hooked->behaviorId = 0;
|
||||
hooked->overrideId = 0;
|
||||
hooked->originalId = 0;
|
||||
hooked->behavior = NULL;
|
||||
hooked->originalBehavior = NULL;
|
||||
hooked->initReference = 0;
|
||||
hooked->loopReference = 0;
|
||||
hooked->replace = false;
|
||||
hooked->luaBehavior = false;
|
||||
hooked->mod = NULL;
|
||||
hooked->modFile = NULL;
|
||||
|
||||
// Clear arrays.
|
||||
growing_array_free(&hooked->bhvNames);
|
||||
growing_array_free(&hooked->initCallbacks);
|
||||
growing_array_free(&hooked->loopCallbacks);
|
||||
}
|
||||
gHookedBehaviorsCount = 0;
|
||||
memset(gLuaMarioActionIndex, 0, sizeof(gLuaMarioActionIndex));
|
||||
gHookedBehaviors = growing_array_init(gHookedBehaviors, 16, malloc, free);
|
||||
}
|
||||
|
||||
void smlua_bind_hooks(void) {
|
||||
|
|
|
|||
|
|
@ -138,25 +138,35 @@ extern u32 gLuaMarioActionIndex[];
|
|||
extern struct LuaHookedModMenuElement gHookedModMenuElements[];
|
||||
extern int gHookedModMenuElementsCount;
|
||||
|
||||
#define MAX_HOOKED_BEHAVIORS 1024
|
||||
#define LUA_BEHAVIOR_START (1 << 15)
|
||||
#define LUA_BEHAVIOR_NEW_ID (UINT16_MAX) // behavior id is 2-bytes long
|
||||
#define MAX_HOOKED_BEHAVIORS (LUA_BEHAVIOR_NEW_ID - LUA_BEHAVIOR_START)
|
||||
|
||||
struct LuaHookedBehavior {
|
||||
u32 behaviorId;
|
||||
u32 overrideId;
|
||||
u32 originalId;
|
||||
BehaviorScript *behavior;
|
||||
const BehaviorScript* originalBehavior;
|
||||
const char* bhvName;
|
||||
int initReference;
|
||||
int loopReference;
|
||||
bool replace;
|
||||
bool luaBehavior;
|
||||
struct Mod* mod;
|
||||
struct ModFile* modFile;
|
||||
enum LuaHookedBehaviorType {
|
||||
LUA_BEHAVIOR_TYPE_CALLBACKS, // Lua callbacks on top of an existing behavior
|
||||
LUA_BEHAVIOR_TYPE_LUA, // Full Lua custom behavior
|
||||
LUA_BEHAVIOR_TYPE_CUSTOM, // DynOS custom behavior (with or without callbacks)
|
||||
};
|
||||
|
||||
extern int gHookedBehaviorsCount;
|
||||
extern struct LuaHookedBehavior gHookedBehaviors[MAX_HOOKED_BEHAVIORS];
|
||||
struct LuaHookedBehaviorCallback {
|
||||
int ref;
|
||||
struct Mod *mod;
|
||||
struct ModFile *modFile;
|
||||
};
|
||||
|
||||
struct LuaHookedBehavior {
|
||||
enum BehaviorId behaviorId; // the original behavior id
|
||||
enum BehaviorId customId; // unique Lua/custom behavior id
|
||||
|
||||
enum LuaHookedBehaviorType type;
|
||||
BehaviorScript *script; // Lua/custom behavior script
|
||||
|
||||
struct GrowingArray *bhvNames; // const char *
|
||||
struct GrowingArray *initCallbacks; // struct LuaHookedBehaviorCallback *
|
||||
struct GrowingArray *loopCallbacks; // struct LuaHookedBehaviorCallback *
|
||||
};
|
||||
|
||||
extern struct GrowingArray *gHookedBehaviors;
|
||||
|
||||
#define OUTPUT
|
||||
#define SMLUA_EVENT_HOOK(hookEventType, hookReturn, ...) bool smlua_call_event_hooks_##hookEventType(__VA_ARGS__);
|
||||
|
|
@ -168,12 +178,11 @@ extern struct LuaHookedBehavior gHookedBehaviors[MAX_HOOKED_BEHAVIORS];
|
|||
smlua_call_event_hooks_##hookEventType(__VA_ARGS__)
|
||||
|
||||
int smlua_hook_custom_bhv(BehaviorScript *bhvScript, const char *bhvName);
|
||||
enum BehaviorId smlua_get_original_behavior_id(const BehaviorScript* behavior);
|
||||
const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior);
|
||||
const BehaviorScript* smlua_get_hooked_behavior_from_id(enum BehaviorId id, bool returnOriginal);
|
||||
bool smlua_is_behavior_hooked(const BehaviorScript *behavior);
|
||||
const char* smlua_get_name_from_hooked_behavior_id(enum BehaviorId id);
|
||||
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object, bool before);
|
||||
const BehaviorScript *smlua_override_behavior(const BehaviorScript *behavior);
|
||||
const BehaviorScript *smlua_get_original_behavior_from_id(enum BehaviorId id);
|
||||
const BehaviorScript *smlua_get_behavior_command(const BehaviorScript *behavior);
|
||||
const char *smlua_get_behavior_name_from_id(enum BehaviorId id);
|
||||
void smlua_call_behavior_hook(struct Object *object);
|
||||
|
||||
int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct Mod* activeMod, struct ModFile* activeModFile);
|
||||
bool smlua_call_action_hook(enum LuaActionHookType hookType, struct MarioState* m, s32* cancel);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ struct Mod {
|
|||
bool ignoreScriptWarnings;
|
||||
bool showedScriptWarning;
|
||||
size_t size;
|
||||
u8 customBehaviorIndex;
|
||||
u16 customBehaviorIndex;
|
||||
};
|
||||
|
||||
size_t mod_get_lua_size(struct Mod* mod);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue