diff --git a/Makefile b/Makefile
index 51b9cbe4..ef19c9ee 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,9 @@ DEBUG ?= 0
# Enable development/testing flags
DEVELOPMENT ?= 0
+# Enable lua profiler
+LUA_PROFILER ?= 0
+
# Build for the N64 (turn this off for ports)
TARGET_N64 = 0
@@ -972,6 +975,12 @@ ifeq ($(DEVELOPMENT),1)
CFLAGS += -DDEVELOPMENT
endif
+# Check for lua profiler option
+ifeq ($(LUA_PROFILER),1)
+ CC_CHECK_CFLAGS += -DLUA_PROFILER
+ CFLAGS += -DLUA_PROFILER
+endif
+
# Check for texture fix option
ifeq ($(TEXTURE_FIX),1)
CC_CHECK_CFLAGS += -DTEXTURE_FIX
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 21c793ed..44b33687 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -7176,6 +7176,13 @@ function get_trajectory(name)
-- ...
end
+--- @param o1 Object
+--- @param o2 Object
+--- @return integer
+function obj_check_hitbox_overlap(o1, o2)
+ -- ...
+end
+
--- @param objList ObjectList
--- @return Object
function obj_get_first(objList)
@@ -7252,6 +7259,21 @@ function obj_has_model_extended(o, modelId)
-- ...
end
+--- @param o Object
+--- @return integer
+function obj_is_valid_for_interaction(o)
+ -- ...
+end
+
+--- @param o Object
+--- @param dx number
+--- @param dy number
+--- @param dz number
+--- @return nil
+function obj_move_xyz(o, dx, dy, dz)
+ -- ...
+end
+
--- @param o Object
--- @param modelId ModelExtendedId
--- @return nil
@@ -7259,6 +7281,15 @@ function obj_set_model_extended(o, modelId)
-- ...
end
+--- @param o Object
+--- @param vx number
+--- @param vy number
+--- @param vz number
+--- @return nil
+function obj_set_vel(o, vx, vy, vz)
+ -- ...
+end
+
--- @param behaviorId BehaviorId
--- @param modelId ModelExtendedId
--- @param x number
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 4b3e461d..4df2b360 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -1344,6 +1344,7 @@
- smlua_obj_utils.h
- [get_temp_object_hitbox](#get_temp_object_hitbox)
- [get_trajectory](#get_trajectory)
+ - [obj_check_hitbox_overlap](#obj_check_hitbox_overlap)
- [obj_get_first](#obj_get_first)
- [obj_get_first_with_behavior_id](#obj_get_first_with_behavior_id)
- [obj_get_first_with_behavior_id_and_field_f32](#obj_get_first_with_behavior_id_and_field_f32)
@@ -1355,7 +1356,10 @@
- [obj_get_temp_spawn_particles_info](#obj_get_temp_spawn_particles_info)
- [obj_has_behavior_id](#obj_has_behavior_id)
- [obj_has_model_extended](#obj_has_model_extended)
+ - [obj_is_valid_for_interaction](#obj_is_valid_for_interaction)
+ - [obj_move_xyz](#obj_move_xyz)
- [obj_set_model_extended](#obj_set_model_extended)
+ - [obj_set_vel](#obj_set_vel)
- [spawn_non_sync_object](#spawn_non_sync_object)
- [spawn_sync_object](#spawn_sync_object)
@@ -25086,6 +25090,27 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [obj_check_hitbox_overlap](#obj_check_hitbox_overlap)
+
+### Lua Example
+`local integerValue = obj_check_hitbox_overlap(o1, o2)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| o1 | [Object](structs.md#Object) |
+| o2 | [Object](structs.md#Object) |
+
+### Returns
+- `integer`
+
+### C Prototype
+`s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2);`
+
+[:arrow_up_small:](#)
+
+
+
## [obj_get_first](#obj_get_first)
### Lua Example
@@ -25316,6 +25341,49 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [obj_is_valid_for_interaction](#obj_is_valid_for_interaction)
+
+### Lua Example
+`local integerValue = obj_is_valid_for_interaction(o)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| o | [Object](structs.md#Object) |
+
+### Returns
+- `integer`
+
+### C Prototype
+`s32 obj_is_valid_for_interaction(struct Object *o);`
+
+[:arrow_up_small:](#)
+
+
+
+## [obj_move_xyz](#obj_move_xyz)
+
+### Lua Example
+`obj_move_xyz(o, dx, dy, dz)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| o | [Object](structs.md#Object) |
+| dx | `number` |
+| dy | `number` |
+| dz | `number` |
+
+### Returns
+- None
+
+### C Prototype
+`void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz);`
+
+[:arrow_up_small:](#)
+
+
+
## [obj_set_model_extended](#obj_set_model_extended)
### Lua Example
@@ -25337,6 +25405,29 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [obj_set_vel](#obj_set_vel)
+
+### Lua Example
+`obj_set_vel(o, vx, vy, vz)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| o | [Object](structs.md#Object) |
+| vx | `number` |
+| vy | `number` |
+| vz | `number` |
+
+### Returns
+- None
+
+### C Prototype
+`void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz);`
+
+[:arrow_up_small:](#)
+
+
+
## [spawn_non_sync_object](#spawn_non_sync_object)
### Lua Example
diff --git a/src/game/behaviors/exclamation_box.inc.c b/src/game/behaviors/exclamation_box.inc.c
index 25d97b44..30e46db6 100644
--- a/src/game/behaviors/exclamation_box.inc.c
+++ b/src/game/behaviors/exclamation_box.inc.c
@@ -167,7 +167,7 @@ void exclamation_box_act_4(void) {
spawn_mist_particles_variable(0, 0, 46.0f);
spawn_triangle_break_particles(20, 139, 0.3f, o->oAnimState);
create_sound_spawner(SOUND_GENERAL_BREAK_BOX);
- if (o->oBehParams2ndByte < 3) {
+ if (o->oBehParams2ndByte <= 3) {
o->oAction = 5;
cur_obj_hide();
} else {
diff --git a/src/game/hud.c b/src/game/hud.c
index 90e0d289..1f4263e8 100644
--- a/src/game/hud.c
+++ b/src/game/hud.c
@@ -535,5 +535,10 @@ void render_hud(void) {
{
print_text(10, 60, "SURFACE NODE POOL FULL");
}
+
+#if defined(LUA_PROFILER)
+ extern void lua_profiler_update_counters();
+ lua_profiler_update_counters();
+#endif
}
}
diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c
index dcdb0c0f..eafe1ddc 100644
--- a/src/game/mario_actions_cutscene.c
+++ b/src/game/mario_actions_cutscene.c
@@ -1103,6 +1103,27 @@ s32 act_warp_door_spawn(struct MarioState *m) {
return FALSE;
}
+static s32 launch_mario_until_land_no_collision(struct MarioState *m, s32 endAction, s32 animation, f32 forwardVel) {
+ mario_set_forward_vel(m, forwardVel);
+ set_mario_animation(m, animation);
+ m->pos[0] += m->vel[0];
+ m->pos[1] += m->vel[1];
+ m->pos[2] += m->vel[2];
+ m->vel[1] -= 4.f;
+ if (m->vel[1] < -75.f) {
+ m->vel[1] = -75.f;
+ }
+ f32 floorHeight = find_floor_height(m->pos[0], m->pos[1], m->pos[2]);
+ s32 landed = floorHeight >= m->pos[1];
+ if (landed) {
+ m->pos[1] = floorHeight;
+ set_mario_action(m, endAction, 0);
+ }
+ vec3f_copy(m->marioObj->header.gfx.pos, m->pos);
+ vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0);
+ return landed;
+}
+
s32 act_emerge_from_pipe(struct MarioState *m) {
struct Object *marioObj = m->marioObj;
@@ -1123,6 +1144,16 @@ s32 act_emerge_from_pipe(struct MarioState *m) {
}
}
+ // After Mario has exited the pipe, disable wall and ceiling collision until Mario lands
+ // Fix softlocks in narrow places
+ if (m->actionTimer > 15) {
+ if (launch_mario_until_land_no_collision(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) {
+ mario_set_forward_vel(m, 0.0f);
+ play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
+ }
+ return FALSE;
+ }
+
if (launch_mario_until_land(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) {
mario_set_forward_vel(m, 0.0f);
play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING);
@@ -2894,7 +2925,7 @@ static s32 check_for_instant_quicksand(struct MarioState *m) {
if (m->action == ACT_BUBBLED) { return FALSE; }
if (m->floor->type == SURFACE_INSTANT_QUICKSAND && m->action & ACT_FLAG_INVULNERABLE
- && m->action != ACT_QUICKSAND_DEATH) {
+ && m->action != ACT_QUICKSAND_DEATH && m->action != ACT_SHOCKED) {
update_mario_sound_and_camera(m);
return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0);
}
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index 95913fdc..aae414dd 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -14928,6 +14928,19 @@ int smlua_func_get_trajectory(lua_State* L) {
return 1;
}
+int smlua_func_obj_check_hitbox_overlap(lua_State* L) {
+ if(!smlua_functions_valid_param_count(L, 2)) { return 0; }
+
+ struct Object* o1 = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ struct Object* o2 = (struct Object*)smlua_to_cobject(L, 2, LOT_OBJECT);
+ if (!gSmLuaConvertSuccess) { return 0; }
+
+ lua_pushinteger(L, obj_check_hitbox_overlap(o1, o2));
+
+ return 1;
+}
+
int smlua_func_obj_get_first(lua_State* L) {
if(!smlua_functions_valid_param_count(L, 1)) { return 0; }
@@ -15069,6 +15082,34 @@ int smlua_func_obj_has_model_extended(lua_State* L) {
return 1;
}
+int smlua_func_obj_is_valid_for_interaction(lua_State* L) {
+ if(!smlua_functions_valid_param_count(L, 1)) { return 0; }
+
+ struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT);
+ if (!gSmLuaConvertSuccess) { return 0; }
+
+ lua_pushinteger(L, obj_is_valid_for_interaction(o));
+
+ return 1;
+}
+
+int smlua_func_obj_move_xyz(lua_State* L) {
+ if(!smlua_functions_valid_param_count(L, 4)) { return 0; }
+
+ struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ f32 dx = smlua_to_number(L, 2);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ f32 dy = smlua_to_number(L, 3);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ f32 dz = smlua_to_number(L, 4);
+ if (!gSmLuaConvertSuccess) { return 0; }
+
+ obj_move_xyz(o, dx, dy, dz);
+
+ return 1;
+}
+
int smlua_func_obj_set_model_extended(lua_State* L) {
if(!smlua_functions_valid_param_count(L, 2)) { return 0; }
@@ -15082,6 +15123,23 @@ int smlua_func_obj_set_model_extended(lua_State* L) {
return 1;
}
+int smlua_func_obj_set_vel(lua_State* L) {
+ if(!smlua_functions_valid_param_count(L, 4)) { return 0; }
+
+ struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ f32 vx = smlua_to_number(L, 2);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ f32 vy = smlua_to_number(L, 3);
+ if (!gSmLuaConvertSuccess) { return 0; }
+ f32 vz = smlua_to_number(L, 4);
+ if (!gSmLuaConvertSuccess) { return 0; }
+
+ obj_set_vel(o, vx, vy, vz);
+
+ return 1;
+}
+
int smlua_func_spawn_non_sync_object(lua_State* L) {
if(!smlua_functions_valid_param_count(L, 6)) { return 0; }
@@ -17002,6 +17060,7 @@ void smlua_bind_functions_autogen(void) {
// smlua_obj_utils.h
smlua_bind_function(L, "get_temp_object_hitbox", smlua_func_get_temp_object_hitbox);
smlua_bind_function(L, "get_trajectory", smlua_func_get_trajectory);
+ smlua_bind_function(L, "obj_check_hitbox_overlap", smlua_func_obj_check_hitbox_overlap);
smlua_bind_function(L, "obj_get_first", smlua_func_obj_get_first);
smlua_bind_function(L, "obj_get_first_with_behavior_id", smlua_func_obj_get_first_with_behavior_id);
smlua_bind_function(L, "obj_get_first_with_behavior_id_and_field_f32", smlua_func_obj_get_first_with_behavior_id_and_field_f32);
@@ -17013,7 +17072,10 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "obj_get_temp_spawn_particles_info", smlua_func_obj_get_temp_spawn_particles_info);
smlua_bind_function(L, "obj_has_behavior_id", smlua_func_obj_has_behavior_id);
smlua_bind_function(L, "obj_has_model_extended", smlua_func_obj_has_model_extended);
+ smlua_bind_function(L, "obj_is_valid_for_interaction", smlua_func_obj_is_valid_for_interaction);
+ smlua_bind_function(L, "obj_move_xyz", smlua_func_obj_move_xyz);
smlua_bind_function(L, "obj_set_model_extended", smlua_func_obj_set_model_extended);
+ smlua_bind_function(L, "obj_set_vel", smlua_func_obj_set_vel);
smlua_bind_function(L, "spawn_non_sync_object", smlua_func_spawn_non_sync_object);
smlua_bind_function(L, "spawn_sync_object", smlua_func_spawn_sync_object);
diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c
index b69f6060..bea50c2b 100644
--- a/src/pc/lua/smlua_hooks.c
+++ b/src/pc/lua/smlua_hooks.c
@@ -3,6 +3,76 @@
#include "pc/djui/djui_chat_message.h"
#include "pc/crash_handler.h"
+#if defined(LUA_PROFILER)
+#include "../mods/mods.h"
+#include "game/print.h"
+#include "gfx_dimensions.h"
+
+extern u64 SDL_GetPerformanceCounter(void);
+extern u64 SDL_GetPerformanceFrequency(void);
+
+#define MAX_PROFILED_MODS 16
+#define REFRESH_RATE 15
+
+static struct {
+ f64 start;
+ f64 end;
+ f64 sum;
+ f64 disp;
+} sLuaProfilerCounters[MAX_PROFILED_MODS];
+
+static void lua_profiler_start_counter(struct Mod *mod) {
+ for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
+ if (gActiveMods.entries[i] == mod) {
+ f64 freq = SDL_GetPerformanceFrequency();
+ f64 curr = SDL_GetPerformanceCounter();
+ sLuaProfilerCounters[i].start = curr / freq;
+ return;
+ }
+ }
+}
+
+static void lua_profiler_stop_counter(struct Mod *mod) {
+ for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
+ if (gActiveMods.entries[i] == mod) {
+ f64 freq = SDL_GetPerformanceFrequency();
+ f64 curr = SDL_GetPerformanceCounter();
+ sLuaProfilerCounters[i].end = curr / freq;
+ sLuaProfilerCounters[i].sum += sLuaProfilerCounters[i].end - sLuaProfilerCounters[i].start;
+ return;
+ }
+ }
+}
+
+void lua_profiler_update_counters() {
+ if (gGlobalTimer % REFRESH_RATE == 0) {
+ for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) {
+ sLuaProfilerCounters[i].disp = sLuaProfilerCounters[i].sum / (f64) REFRESH_RATE;
+ sLuaProfilerCounters[i].sum = 0;
+ }
+ }
+ for (s32 i = 0, y = SCREEN_HEIGHT - 60; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i, y -= 18) {
+ const char *modName = gActiveMods.entries[i]->relativePath;
+ s32 modCounterUs = (s32) (sLuaProfilerCounters[i].disp * 1000000.0);
+ char text[256];
+ snprintf(text, 256, " %05d", modCounterUs);
+ memcpy(text, modName, MIN(12, strlen(modName) - (gActiveMods.entries[i]->isDirectory ? 0 : 4)));
+ for (s32 j = 0; j != 12; ++j) {
+ char c = text[j];
+ if (c >= 'a' && c <= 'z') c -= ('a' - 'A');
+ if (c == 'J') c = 'I';
+ if (c == 'Q') c = 'O';
+ if (c == 'V') c = 'U';
+ if (c == 'X') c = '*';
+ if (c == 'Z') c = '2';
+ if ((c < '0' || c > '9') && (c < 'A' || c > 'Z')) c = ' ';
+ text[j] = c;
+ }
+ print_text(GFX_DIMENSIONS_FROM_LEFT_EDGE(4), y, text);
+ }
+}
+#endif
+
#define MAX_HOOKED_REFERENCES 64
#define LUA_BEHAVIOR_FLAG (1 << 15)
@@ -20,7 +90,13 @@ static int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, s
struct Mod* prev = gLuaActiveMod;
gLuaActiveMod = activeMod;
gLuaLastHookMod = activeMod;
+#if defined(LUA_PROFILER)
+ lua_profiler_start_counter(activeMod);
+#endif
int rc = lua_pcall(L, nargs, nresults, errfunc);
+#if defined(LUA_PROFILER)
+ lua_profiler_stop_counter(activeMod);
+#endif
gLuaActiveMod = prev;
return rc;
}
diff --git a/src/pc/lua/utils/smlua_obj_utils.c b/src/pc/lua/utils/smlua_obj_utils.c
index e8de9d24..712e1480 100644
--- a/src/pc/lua/utils/smlua_obj_utils.c
+++ b/src/pc/lua/utils/smlua_obj_utils.c
@@ -2,6 +2,8 @@
#include "object_constants.h"
#include "object_fields.h"
#include "src/game/object_helpers.h"
+#include "src/game/interaction.h"
+#include "engine/math_util.h"
#include "pc/lua/smlua.h"
#include "smlua_obj_utils.h"
@@ -219,3 +221,32 @@ struct ObjectHitbox* get_temp_object_hitbox(void) {
memset(&sTmpHitbox, 0, sizeof(struct ObjectHitbox));
return &sTmpHitbox;
}
+
+s32 obj_is_valid_for_interaction(struct Object *o) {
+ return o->activeFlags != ACTIVE_FLAG_DEACTIVATED && o->oIntangibleTimer == 0 && (o->oInteractStatus & INT_STATUS_INTERACTED) == 0;
+}
+
+s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2) {
+ f32 r2 = sqr(max(o1->hitboxRadius, o1->hurtboxRadius) + max(o2->hitboxRadius, o2->hurtboxRadius));
+ f32 d2 = sqr(o1->oPosX - o2->oPosX) + sqr(o1->oPosZ - o2->oPosZ);
+ if (d2 > r2) return FALSE;
+ f32 hb1lb = o1->oPosY - o1->hitboxDownOffset;
+ f32 hb1ub = hb1lb + max(o1->hitboxHeight, o1->hurtboxHeight);
+ f32 hb2lb = o2->oPosY - o2->hitboxDownOffset;
+ f32 hb2ub = hb2lb + max(o2->hitboxHeight, o2->hurtboxHeight);
+ f32 hbsoh = max(o1->hitboxHeight, o1->hurtboxHeight) + max(o2->hitboxHeight, o2->hurtboxHeight);
+ if (hb2ub - hb1lb > hbsoh || hb1ub - hb2lb > hbsoh) return FALSE;
+ return TRUE;
+}
+
+void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz) {
+ o->oVelX = vx;
+ o->oVelY = vy;
+ o->oVelZ = vz;
+}
+
+void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz) {
+ o->oPosX += dx;
+ o->oPosY += dy;
+ o->oPosZ += dz;
+}
diff --git a/src/pc/lua/utils/smlua_obj_utils.h b/src/pc/lua/utils/smlua_obj_utils.h
index d9c268d6..76996633 100644
--- a/src/pc/lua/utils/smlua_obj_utils.h
+++ b/src/pc/lua/utils/smlua_obj_utils.h
@@ -33,4 +33,9 @@ struct Object *obj_get_next_with_same_behavior_id_and_field_f32(struct Object *o
struct SpawnParticlesInfo* obj_get_temp_spawn_particles_info(enum ModelExtendedId modelId);
struct ObjectHitbox* get_temp_object_hitbox(void);
+s32 obj_is_valid_for_interaction(struct Object *o);
+s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2);
+void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz);
+void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz);
+
#endif