Made adding to behaviors possible in Lua

This commit is contained in:
MysterD 2022-03-05 01:29:24 -08:00
parent 4f9c30fd46
commit 2d8715b330
10 changed files with 69 additions and 22 deletions

View file

@ -0,0 +1,14 @@
-- name: Goomba Leaps
-- description: Makes goombas leap instead of just jump as an example.
function bhv_custom_goomba_loop(obj)
-- make goombas leap instead of just jump
if obj.oGoombaJumpCooldown >= 9 then
obj.oGoombaJumpCooldown = 8
obj.oVelY = obj.oVelY + 20
obj.oForwardVel = 20
end
end
-- hook the behavior
id_bhvCustomGoomba = hook_behavior(id_bhvGoomba, OBJ_LIST_PUSHABLE, false, nil, bhv_custom_goomba_loop)

View file

@ -81,7 +81,7 @@ function bhv_ball_loop(obj)
end
end
id_bhvBall = hook_behavior(0, OBJ_LIST_DEFAULT, bhv_ball_init, bhv_ball_loop)
id_bhvBall = hook_behavior(nil, OBJ_LIST_DEFAULT, true, bhv_ball_init, bhv_ball_loop)
function mario_update_local(m)
if (m.controller.buttonPressed & D_JPAD) ~= 0 then

View file

@ -92,4 +92,4 @@ function bhv_custom_goomba_loop(obj)
end
-- hook the behavior
id_bhvCustomGoomba = hook_behavior(id_bhvGoomba, OBJ_LIST_PUSHABLE, bhv_custom_goomba_init, bhv_custom_goomba_loop)
id_bhvCustomGoomba = hook_behavior(id_bhvGoomba, OBJ_LIST_PUSHABLE, true, bhv_custom_goomba_init, bhv_custom_goomba_loop)

View file

@ -685,7 +685,9 @@
## [define_custom_obj_fields](#define_custom_obj_fields)
Defines a custom set of overlapping object fields. The `fieldTable` table's keys must start with the letter `o` and the values must be either `u32`, `s32`, or `f32`.
Defines a custom set of overlapping object fields.
The `fieldTable` table's keys must start with the letter `o` and the values must be either `u32`, `s32`, or `f32`.
### Lua Example
`define_custom_obj_fields({ oCustomField1 = 'u32', oCustomField2 = 's32', oCustomField3 = 'f32' })`

View file

@ -19,8 +19,9 @@ Hooks are a way for SM64 to trigger Lua code, whereas the functions listed in [f
| Field | Type | Notes |
| ----- | ---- | ----- |
| behaviorId | [enum BehaviorId](constants.md#enum-BehaviorId) | Set to `0` to create a new behavior |
| behaviorId | [enum BehaviorId](constants.md#enum-BehaviorId) | Set to `nil` to create a new behavior |
| objectList | [enum ObjectList](constants.md#enum-ObjectList) | |
| replaceBehavior | `bool` | Prevents the original behavior code from running |
| initFunction | `Lua Function` ([Object](structs.md#Object) obj) | Runs once per object |
| loopFunction | `Lua Function` ([Object](structs.md#Object) obj) | Runs once per frame per object |

View file

@ -49,4 +49,5 @@ All of this is a holdover from when there were only two players. It was a reason
- [HUD Rendering](examples/hud.lua)
- [Object Spawning](examples/spawn-stuff.lua)
- [Custom Ball Behavior](examples/behavior-ball.lua)
- [Replace Goomba Behavior](examples/behavior-goomba.lua)
- [Replace Goomba Behavior](examples/behavior-replace-goomba.lua)
- [Add to Goomba Behavior](examples/behavior-add-to-goomba.lua)

View file

@ -1023,7 +1023,7 @@ cur_obj_update_begin:;
// Execute the behavior script.
gCurBhvCommand = gCurrentObject->curBhvCommand;
u8 skipBehavior = smlua_call_behavior_hook(&gCurBhvCommand, gCurrentObject);
u8 skipBehavior = smlua_call_behavior_hook(&gCurBhvCommand, gCurrentObject, true);
if (!skipBehavior) {
do {
@ -1031,6 +1031,8 @@ cur_obj_update_begin:;
bhvProcResult = bhvCmdProc();
} while (bhvProcResult == BHV_PROC_CONTINUE);
}
smlua_call_behavior_hook(&gCurBhvCommand, gCurrentObject, false);
gCurrentObject->curBhvCommand = gCurBhvCommand;
// Increment the object's timer.

View file

@ -335,13 +335,12 @@ struct Object *create_object(const BehaviorScript *bhvScript) {
s32 objListIndex;
struct Object *obj;
struct ObjectNode *objList;
bhvScript = smlua_override_behavior(bhvScript);
const BehaviorScript *behavior = bhvScript;
const BehaviorScript *behavior = smlua_override_behavior(bhvScript);
// If the first behavior script command is "begin <object list>", then
// extract the object list from it
if ((bhvScript[0] >> 24) == 0) {
objListIndex = (bhvScript[0] >> 16) & 0xFFFF;
if ((behavior[0] >> 24) == 0) {
objListIndex = (behavior[0] >> 16) & 0xFFFF;
} else {
objListIndex = OBJ_LIST_DEFAULT;
}

View file

@ -307,8 +307,10 @@ struct LuaHookedBehavior {
u32 behaviorId;
u32 overrideId;
BehaviorScript behavior[2];
const BehaviorScript* originalBehavior;
int initReference;
int loopReference;
bool replace;
struct ModListEntry* entry;
};
@ -340,7 +342,7 @@ const BehaviorScript* get_lua_behavior_from_id(enum BehaviorId id) {
int smlua_hook_behavior(lua_State* L) {
if (L == NULL) { return 0; }
if (!smlua_functions_valid_param_count(L, 4)) { return 0; }
if (!smlua_functions_valid_param_count(L, 5)) { return 0; }
if (gLuaLoadingEntry == NULL) {
LOG_LUA("hook_behavior() can only be called on load.");
@ -353,7 +355,9 @@ int smlua_hook_behavior(lua_State* L) {
return 0;
}
lua_Integer overrideBehaviorId = smlua_to_integer(L, 1);
bool noOverrideId = (lua_type(L, 1) == LUA_TNIL);
gSmLuaConvertSuccess = true;
lua_Integer overrideBehaviorId = noOverrideId ? 0xFFFFFF : smlua_to_integer(L, 1);
if (!gSmLuaConvertSuccess) {
LOG_LUA("Hook behavior: tried to override invalid behavior: %lld, %u", overrideBehaviorId, gSmLuaConvertSuccess);
smlua_logline();
@ -367,13 +371,24 @@ int smlua_hook_behavior(lua_State* L) {
return 0;
}
bool replaceBehavior = smlua_to_boolean(L, 3);
if (!gSmLuaConvertSuccess) {
LOG_LUA("Hook behavior: could not parse replaceBehavior");
smlua_logline();
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, 3);
int initReferenceType = lua_type(L, 4);
if (initReferenceType == LUA_TNIL) {
// nothing
} else if (initReferenceType == LUA_TFUNCTION) {
// get reference
lua_pushvalue(L, 3);
lua_pushvalue(L, 4);
initReference = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
LOG_LUA("Hook behavior: tried to reference non-function for init");
@ -382,12 +397,12 @@ int smlua_hook_behavior(lua_State* L) {
}
int loopReference = 0;
int loopReferenceType = lua_type(L, 4);
int loopReferenceType = lua_type(L, 5);
if (loopReferenceType == LUA_TNIL) {
// nothing
} else if (loopReferenceType == LUA_TFUNCTION) {
// get reference
lua_pushvalue(L, 4);
lua_pushvalue(L, 5);
loopReference = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
LOG_LUA("Hook behavior: tried to reference non-function for loop");
@ -398,11 +413,13 @@ int smlua_hook_behavior(lua_State* L) {
struct LuaHookedBehavior* hooked = &sHookedBehaviors[sHookedBehaviorsCount];
u16 customBehaviorId = (sHookedBehaviorsCount & 0xFFFF) | LUA_BEHAVIOR_FLAG;
hooked->behaviorId = customBehaviorId;
hooked->overrideId = (overrideBehaviorId == 0) ? customBehaviorId : overrideBehaviorId;
hooked->overrideId = noOverrideId ? customBehaviorId : overrideBehaviorId;
hooked->behavior[0] = (((unsigned int) (((unsigned int)(0x00) & ((0x01 << (8)) - 1)) << (24))) | ((unsigned int) (((unsigned int)(objectList) & ((0x01 << (8)) - 1)) << (16)))); // gross. this is BEGIN(objectList)
hooked->behavior[1] = (((unsigned int) (((unsigned int)(0x39) & ((0x01 << (8)) - 1)) << (24))) | ((unsigned int) (((unsigned int)(customBehaviorId) & ((0x01 << (16)) - 1)) << (0)))); // gross. this is ID(customBehaviorId)
hooked->originalBehavior = originalBehavior ? originalBehavior : hooked->behavior;
hooked->initReference = initReference;
hooked->loopReference = loopReference;
hooked->replace = replaceBehavior;
hooked->entry = gLuaActiveEntry;
sHookedBehaviorsCount++;
@ -413,7 +430,7 @@ int smlua_hook_behavior(lua_State* L) {
return 1;
}
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object) {
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object, bool before) {
lua_State* L = gLuaState;
if (L == NULL) { return false; }
for (int i = 0; i < sHookedBehaviorsCount; i++) {
@ -424,9 +441,17 @@ bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* ob
continue;
}
// figure out whether to run before or after
if (before && !hooked->replace) {
return false;
}
if (!before && hooked->replace) {
return false;
}
// retrieve and remember first run
bool firstRun = (*behavior == hooked->behavior);
if (firstRun) { *behavior = &hooked->behavior[1]; }
bool firstRun = (object->curBhvCommand == hooked->originalBehavior);
if (firstRun && hooked->replace) { *behavior = &hooked->behavior[1]; }
// get function and null check it
int reference = firstRun ? hooked->initReference : hooked->loopReference;
@ -447,12 +472,13 @@ bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* ob
return true;
}
return true;
return hooked->replace;
}
return false;
}
/////////////////////////
// hooked chat command //
/////////////////////////
@ -672,8 +698,10 @@ static void smlua_clear_hooks(void) {
hooked->behaviorId = 0;
hooked->behavior[0] = 0;
hooked->behavior[1] = 0;
hooked->originalBehavior = NULL;
hooked->initReference = 0;
hooked->loopReference = 0;
hooked->replace = false;
hooked->entry = NULL;
}
sHookedBehaviorsCount = 0;

View file

@ -39,7 +39,7 @@ void smlua_call_event_hooks_interact_params(enum LuaHookedEventType hookType, st
const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior);
const BehaviorScript* get_lua_behavior_from_id(enum BehaviorId id);
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object);
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object, bool before);
bool smlua_call_action_hook(struct MarioState* m, s32* returnValue);
u32 smlua_get_action_interaction_type(struct MarioState* m);