Made adding to behaviors possible in Lua
This commit is contained in:
parent
4f9c30fd46
commit
2d8715b330
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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' })`
|
||||
|
|
|
@ -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 |
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue