diff --git a/Makefile b/Makefile index 22030e7d..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 @@ -1496,7 +1505,8 @@ ifeq ($(TARGET_N64),1) $(OBJDUMP) -D $< > $@ else $(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(BUILD_DIR)/$(RPC_LIBS) $(BUILD_DIR)/$(DISCORD_SDK_LIBS) $(BUILD_DIR)/$(MOD_DIR) - $(LD) $(PROF_FLAGS) -L $(BUILD_DIR) -o $@ $(O_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) $(EXTRA_INCLUDES) + @$(PRINT) "$(GREEN)Linking executable: $(BLUE)$@ $(NO_COL)\n" + $(V)$(LD) $(PROF_FLAGS) -L $(BUILD_DIR) -o $@ $(O_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) $(EXTRA_INCLUDES) endif 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/include/object_fields.h b/include/object_fields.h index 0aecf1bf..f87b3574 100644 --- a/include/object_fields.h +++ b/include/object_fields.h @@ -316,12 +316,13 @@ #define /*0x0F8*/ oBulletBillInitialMoveYaw OBJECT_FIELD_S32(0x1C) /* Bully (all variants) */ -#define /*0x0F4*/ oBullySubtype OBJECT_FIELD_S32(0x1B) -#define /*0x0F8*/ oBullyPrevX OBJECT_FIELD_F32(0x1C) -#define /*0x0FC*/ oBullyPrevY OBJECT_FIELD_F32(0x1D) -#define /*0x100*/ oBullyPrevZ OBJECT_FIELD_F32(0x1E) -#define /*0x104*/ oBullyKBTimerAndMinionKOCounter OBJECT_FIELD_S32(0x1F) -#define /*0x108*/ oBullyMarioCollisionAngle OBJECT_FIELD_S32(0x20) +#define /*0x0F4*/ oBullySubtype OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBullyPrevX OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oBullyPrevY OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oBullyPrevZ OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oBullyKBTimerAndMinionKOCounter OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oBullyMarioCollisionAngle OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oBullyLastNetworkPlayerIndex OBJECT_FIELD_S16(0x21, 0) /* Butterfly */ #define /*0x0F4*/ oButterflyYPhase OBJECT_FIELD_S32(0x1B) diff --git a/src/game/behaviors/bully.inc.c b/src/game/behaviors/bully.inc.c index 86160032..c13e7b70 100644 --- a/src/game/behaviors/bully.inc.c +++ b/src/game/behaviors/bully.inc.c @@ -47,6 +47,7 @@ static void bhv_bully_network_init(void) { network_init_object_field(o, &o->oBullyPrevY); network_init_object_field(o, &o->oBullyPrevZ); network_init_object_field(o, &o->oBullyMarioCollisionAngle); + network_init_object_field(o, &o->oBullyLastNetworkPlayerIndex); so->syncDeathEvent = FALSE; so->ignore_if_true = bhv_bully_ignore_if_true; so->override_ownership = bhv_bully_override_ownership; @@ -62,6 +63,11 @@ void bhv_small_bully_init(void) { o->oGravity = 4.0; o->oFriction = 0.91; o->oBuoyancy = 1.3; + + // We only set this here so it has a set value just in case. + // A mod may make a small bully spawn a star. + // For whatever reason that may be. + o->oBullyLastNetworkPlayerIndex = UNKNOWN_GLOBAL_INDEX; obj_set_hitbox(o, &sSmallBullyHitbox); bhv_bully_network_init(); @@ -77,6 +83,14 @@ void bhv_big_bully_init(void) { o->oGravity = 5.0; o->oFriction = 0.93; o->oBuoyancy = 1.3; + + // We haven't interacted with a player yet. + // We also don't sync this as not only is it not required + // but it also is only set for an interaction. + // Therefore this object must already be loaded for it to be set + // and if it wasn't. You couldn't of possibly been the one + // who last interacted to begin with. + o->oBullyLastNetworkPlayerIndex = UNKNOWN_GLOBAL_INDEX; obj_set_hitbox(o, &sBigBullyHitbox); bhv_bully_network_init(); @@ -94,16 +108,21 @@ void bully_check_mario_collision(void) { o->oAction != BULLY_ACT_LAVA_DEATH && o->oAction != BULLY_ACT_DEATH_PLANE_DEATH && #endif o->oInteractStatus & INT_STATUS_INTERACTED) { - if (o->oBehParams2ndByte == BULLY_BP_SIZE_SMALL) + if (o->oBehParams2ndByte == BULLY_BP_SIZE_SMALL) { cur_obj_play_sound_2(SOUND_OBJ2_BULLY_ATTACKED); - else + } else { cur_obj_play_sound_2(SOUND_OBJ2_LARGE_BULLY_ATTACKED); + } o->oInteractStatus &= ~INT_STATUS_INTERACTED; o->oAction = BULLY_ACT_KNOCKBACK; o->oFlags &= ~0x8; /* bit 3 */ cur_obj_init_animation(3); o->oBullyMarioCollisionAngle = o->oMoveAngleYaw; + + // Get the player who interacted with us. + struct MarioState *player = nearest_interacting_mario_state_to_object(o); + o->oBullyLastNetworkPlayerIndex = gNetworkPlayers[player->playerIndex].globalIndex; } } @@ -119,12 +138,14 @@ void bully_act_chase_mario(void) { obj_turn_toward_object(o, player, 16, 4096); } else if (o->oBehParams2ndByte == BULLY_BP_SIZE_SMALL) { o->oForwardVel = 20.0; - if (o->oTimer >= 31) + if (o->oTimer >= 31) { o->oTimer = 0; + } } else { o->oForwardVel = 30.0; - if (o->oTimer >= 36) + if (o->oTimer >= 36) { o->oTimer = 0; + } } if (!is_point_within_radius_of_mario(homeX, posY, homeZ, 1000)) { @@ -141,8 +162,9 @@ void bully_act_knockback(void) { o->oFlags |= 0x8; /* bit 3 */ o->oMoveAngleYaw = o->oFaceAngleYaw; obj_turn_toward_object(o, player, 16, 1280); - } else + } else { o->header.gfx.animInfo.animFrame = 0; + } if (o->oBullyKBTimerAndMinionKOCounter == 18) { o->oAction = BULLY_ACT_CHASE_MARIO; @@ -214,8 +236,9 @@ void bully_step(void) { obj_check_floor_death(collisionFlags, sObjFloor); if (o->oBullySubtype & BULLY_STYPE_CHILL) { - if (o->oPosY < 1030.0f) + if (o->oPosY < 1030.0f) { o->oAction = BULLY_ACT_LAVA_DEATH; + } } } @@ -246,10 +269,10 @@ void bully_act_level_death(void) { if (o->oBullySubtype == BULLY_STYPE_CHILL) { f32* starPos = gLevelValues.starPositions.ChillBullyStarPos; - spawn_default_star(starPos[0], starPos[1], starPos[2]); + spawn_networked_default_star(starPos[0], starPos[1], starPos[2], o->oBullyLastNetworkPlayerIndex); } else { f32* starPos = gLevelValues.starPositions.BigBullyTrioStarPos; - spawn_default_star(starPos[0], starPos[1], starPos[2]); + spawn_networked_default_star(starPos[0], starPos[1], starPos[2], o->oBullyLastNetworkPlayerIndex); struct Object* lllTumblingBridge = cur_obj_nearest_object_with_behavior(bhvLllTumblingBridge); if (lllTumblingBridge != NULL) { lllTumblingBridge->oIntangibleTimer = 0; @@ -341,7 +364,7 @@ void big_bully_spawn_star(void) { if (obj_lava_death() == 1) { spawn_mist_particles(); f32* starPos = gLevelValues.starPositions.BigBullyStarPos; - spawn_default_star(starPos[0], starPos[1], starPos[2]); + spawn_networked_default_star(starPos[0], starPos[1], starPos[2], o->oBullyLastNetworkPlayerIndex); } } 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/behaviors/treasure_chest.inc.c b/src/game/behaviors/treasure_chest.inc.c index cf34f474..cc1e6f28 100644 --- a/src/game/behaviors/treasure_chest.inc.c +++ b/src/game/behaviors/treasure_chest.inc.c @@ -1,7 +1,5 @@ // treasure_chest.c.inc -#include "pc/network/network_player.h" - /** * Hitbox for treasure chest bottom. */ @@ -323,7 +321,7 @@ void bhv_treasure_chest_loop(void) { if (o->oTimer == 60) { spawn_mist_particles(); - f32* starPos = gLevelValues.starPositions.TreasureJrbStarPos; + f32* starPos = gLevelValues.starPositions.TreasureChestStarPos; spawn_networked_default_star(starPos[0], starPos[1], starPos[2], o->oTreasureChestLastNetworkPlayerIndex); o->oAction = 2; diff --git a/src/game/characters.h b/src/game/characters.h index 40c109f6..05c97d63 100644 --- a/src/game/characters.h +++ b/src/game/characters.h @@ -4,7 +4,7 @@ #include "types.h" // NOTE: do not include any additional headers -#define PALETTE_MAX 30 +#define PALETTE_MAX 32 enum CharacterType { CT_MARIO, 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/game/mario_misc.c b/src/game/mario_misc.c index 87c018d7..baa549f5 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -114,17 +114,21 @@ struct PlayerColor gPlayerColors[PALETTE_MAX] = { DEFINE_PLAYER_COLOR(0x37, 0x32, 0x42, /**/ 0xe6, 0xe3, 0xff), DEFINE_PLAYER_COLOR(0xff, 0x8a, 0x00, /**/ 0x00, 0x51, 0x10), DEFINE_PLAYER_COLOR(0x65, 0xfa, 0xff, /**/ 0x4c, 0x1e, 0x3f), + DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0xb2, 0x28, 0x18), DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0x00, 0x98, 0x00), DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0x6d, 0x3c, 0x9a), DEFINE_PLAYER_COLOR(0xe6, 0xe6, 0xe6, /**/ 0xf9, 0xeb, 0x30), - + DEFINE_PLAYER_COLOR(0xe7, 0xe7, 0x21, /**/ 0x17, 0x18, 0x15), DEFINE_PLAYER_COLOR(0xaa, 0x27, 0x31, /**/ 0xf7, 0x9a, 0x47), DEFINE_PLAYER_COLOR(0x55, 0x92, 0xb2, /**/ 0xf7, 0xc2, 0x45), DEFINE_PLAYER_COLOR(0x10, 0x1b, 0x2e, /**/ 0xeb, 0x8a, 0x4b), DEFINE_PLAYER_COLOR(0x3b, 0x8f, 0xf7, /**/ 0xd6, 0x35, 0x4d), DEFINE_PLAYER_COLOR(0xff, 0x8e, 0xb2, /**/ 0xd6, 0x35, 0x4d), + + DEFINE_PLAYER_COLOR(0x47, 0xc5, 0xff, /**/ 0xb2, 0x28, 0x18), + DEFINE_PLAYER_COLOR(0x47, 0xc5, 0xff, /**/ 0x00, 0x98, 0x00), }; const size_t gNumPlayerColors = sizeof(gPlayerColors) / sizeof(*gPlayerColors); diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index fb81e5ac..54d16676 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -34,6 +34,7 @@ #include "hardcoded.h" #include "engine/surface_load.h" #include "pc/network/network.h" +#include "pc/network/network_player.h" #include "pc/network/reservation_area.h" #include "pc/lua/utils/smlua_model_utils.h" #include "game/rng_position.h" @@ -949,6 +950,8 @@ s8 UNUSED debug_sequence_tracker(s16 debugInputSequence[]) { return FALSE; } +// We need the spawn_star file first so the spawn_star functions can be accessed by below behaviors. +#include "behaviors/spawn_star.inc.c" #include "behaviors/moving_coin.inc.c" #include "behaviors/seaweed.inc.c" #include "behaviors/bobomb.inc.c" @@ -986,7 +989,6 @@ s8 UNUSED debug_sequence_tracker(s16 debugInputSequence[]) { #include "behaviors/snowman.inc.c" #include "behaviors/boulder.inc.c" #include "behaviors/cap.inc.c" -#include "behaviors/spawn_star.inc.c" #include "behaviors/red_coin.inc.c" #include "behaviors/hidden_star.inc.c" #include "behaviors/rolling_log.inc.c" diff --git a/src/game/save_file.c b/src/game/save_file.c index a51e7e8c..d54a113f 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -14,6 +14,10 @@ #include "pc/ini.h" #include "pc/network/network.h" +#ifndef bcopy +#define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) +#endif + #define MENU_DATA_MAGIC 0x4849 #define SAVE_FILE_MAGIC 0x4441 diff --git a/src/pc/cliopts.c b/src/pc/cliopts.c index 73e44650..6f076659 100644 --- a/src/pc/cliopts.c +++ b/src/pc/cliopts.c @@ -14,7 +14,7 @@ struct PCCLIOptions gCLIOpts; static void print_help(void) { - printf("Super Mario 64 PC Port\n"); + printf("\nsm64ex-coop\n"); printf("%-20s\tEnables the cheat menu.\n", "--cheats"); printf("%-20s\tSaves the configuration file as CONFIGNAME.\n", "--configfile CONFIGNAME"); printf("%-20s\tSets additional data directory name (only 'res' is used by default).\n", "--gamedir DIRNAME"); @@ -22,6 +22,9 @@ static void print_help(void) { printf("%-20s\tStarts the game in full screen mode.\n", "--fullscreen"); printf("%-20s\tSkips the Peach and Castle intro when starting a new game.\n", "--skip-intro"); printf("%-20s\tStarts the game in windowed mode.\n", "--windowed"); + printf("%-20s\tStarts the game and creates a new server.\n", "--server PORT"); + printf("%-20s\tStarts the game and joins an existing server.\n", "--client IP PORT"); + printf("%-20s\tStarts the game using a poolsize of your choice.\n", "--poolsize POOLSIZE"); } static inline int arg_string(const char *name, const char *value, char *target, int maxLength) { diff --git a/src/pc/djui/djui_panel_player.c b/src/pc/djui/djui_panel_player.c index aa09ac48..2814dc6f 100644 --- a/src/pc/djui/djui_panel_player.c +++ b/src/pc/djui/djui_panel_player.c @@ -124,6 +124,8 @@ void djui_panel_player_create(struct DjuiBase* caller) { "Blueberry Pie", "Raspberry", "Bubblegum", + "Ice Mario", + "Ice Luigi", }; struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "Palette", paletteChoices, PALETTE_MAX, &configPlayerPalette); djui_base_set_size_type(&selectionbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 797dec22..93407df0 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1022,7 +1022,7 @@ char gSmluaConstants[] = "" "CAM_EVENT_START_ENDING = 11\n" "CAM_EVENT_START_END_WAVING = 12\n" "CAM_EVENT_START_CREDITS = 13\n" -"PALETTE_MAX = 30\n" +"PALETTE_MAX = 32\n" "CT_MARIO = 0\n" "CT_LUIGI = 1\n" "CT_TOAD = 2\n" 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