From a499b4f032bd247a874b57ef3f6590f3a2e0c73c Mon Sep 17 00:00:00 2001 From: NepuShiro Date: Mon, 25 May 2026 12:34:52 -0500 Subject: [PATCH] Implement Rumble --- Makefile | 2 +- import-test-collision.py | 2 +- src/decomp/game/interaction.c | 9 +- src/decomp/game/mario.c | 18 ++-- src/decomp/game/mario_actions_airborne.c | 108 ++++++++++++--------- src/decomp/game/mario_actions_automatic.c | 33 ++++--- src/decomp/game/mario_actions_cutscene.c | 43 ++++---- src/decomp/game/mario_actions_moving.c | 30 +++--- src/decomp/game/mario_actions_object.c | 29 +++--- src/decomp/game/mario_actions_stationary.c | 12 +-- src/decomp/game/mario_actions_submerged.c | 14 +-- src/decomp/game/sound_init.c | 5 +- src/decomp/global_state.c | 1 + src/decomp/global_state.h | 1 + src/libsm64.c | 9 ++ src/libsm64.h | 3 + src/rumble.c | 74 ++++++++++++++ src/rumble.h | 11 +++ test/main.cpp | 52 ++++++++++ 19 files changed, 317 insertions(+), 139 deletions(-) create mode 100644 src/rumble.c create mode 100644 src/rumble.h diff --git a/Makefile b/Makefile index 5d182a3..b130ec6 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ else CXX := c++ LDFLAGS := -lm -shared endif -CFLAGS := -fno-strict-aliasing -g -Wall -Wno-unused-function -fPIC -fvisibility=hidden -DSM64_LIB_EXPORT -DGBI_FLOATS -DVERSION_US -DNO_SEGMENTED_MEMORY +CFLAGS := -fno-strict-aliasing -g -Wall -Wno-unused-function -fPIC -fvisibility=hidden -DSM64_LIB_EXPORT -DGBI_FLOATS -DVERSION_US -DENABLE_RUMBLE -DNO_SEGMENTED_MEMORY SRC_DIRS := src src/decomp src/decomp/engine src/decomp/include/PR src/decomp/game src/decomp/pc src/decomp/pc/audio src/decomp/mario src/decomp/tools src/decomp/audio BUILD_DIR := build diff --git a/import-test-collision.py b/import-test-collision.py index 62b87ff..84ad628 100755 --- a/import-test-collision.py +++ b/import-test-collision.py @@ -38,7 +38,7 @@ def main(): out_lines = [] for tri in tris: - out_lines.append("{%s,0,TERRAIN_SNOW,{{%s,%s,%s},{%s,%s,%s},{%s,%s,%s}}}" % (tri[3], \ + out_lines.append("{%s,0,TERRAIN_SNOW,0,{{%s,%s,%s},{%s,%s,%s},{%s,%s,%s}}}" % (tri[3], \ verts[tri[0]][0], verts[tri[0]][1], verts[tri[0]][2], \ verts[tri[1]][0], verts[tri[1]][1], verts[tri[1]][2], \ verts[tri[2]][0], verts[tri[2]][1], verts[tri[2]][2])) diff --git a/src/decomp/game/interaction.c b/src/decomp/game/interaction.c index 10bcdc5..4f28b0f 100644 --- a/src/decomp/game/interaction.c +++ b/src/decomp/game/interaction.c @@ -31,6 +31,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" #define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01 #define INT_PUNCH (1 << 1) // 0x02 @@ -797,9 +798,9 @@ u32 take_damage_from_interact_object(struct MarioState *m) m->hurtCounter += 4 * damage; - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif // set_camera_shake_from_hit(shake); return damage; @@ -857,9 +858,9 @@ u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struc update_mario_sound_and_camera(m); play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif return set_mario_action(m, ACT_GRABBED, 0); } } diff --git a/src/decomp/game/mario.c b/src/decomp/game/mario.c index edfb94c..40e847f 100644 --- a/src/decomp/game/mario.c +++ b/src/decomp/game/mario.c @@ -31,6 +31,7 @@ #include "mario_step.h" // #include "memory.h" #include "../include/object_fields.h" +#include "../../rumble.h" // #include "object_helpers.h" // #include "object_list_processor.h" // #include "print.h" @@ -1759,6 +1760,7 @@ void update_mario_health(struct MarioState *m) if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_SUBMERGED && m->health < 0x300) { play_sound(SOUND_MOVING_ALMOST_DROWNING, gDefaultSoundArgs); + queue_rumble_data(3, 30); #ifdef VERSION_SH if (!gRumblePakTimer) { @@ -1971,7 +1973,7 @@ void mario_update_hitbox_and_cap_model(struct MarioState *m) // } //} -#ifdef VERSION_SH +// #ifdef VERSION_SH void func_sh_8025574C(void) { if (gMarioState->particleFlags & PARTICLE_HORIZONTAL_STAR) @@ -1986,12 +1988,12 @@ void func_sh_8025574C(void) { queue_rumble_data(5, 80); } - if (gMarioState->heldObj && gMarioState->heldObj->behavior == segmented_to_virtual(bhvBobomb)) - { - reset_rumble_timers(); - } + // if (gMarioState->heldObj && gMarioState->heldObj->behavior == segmented_to_virtual(bhvBobomb)) + // { + // reset_rumble_timers(); + // } } -#endif +// #endif /** * Main function for executing Mario's behavior. @@ -2077,9 +2079,9 @@ s32 execute_mario_action(UNUSED struct Object * o) play_infinite_stairs_music(); gMarioState->marioObj->oInteractStatus = 0; - #ifdef VERSION_SH + // #ifdef VERSION_SH func_sh_8025574C(); - #endif + // #endif return gMarioState->particleFlags; } diff --git a/src/decomp/game/mario_actions_airborne.c b/src/decomp/game/mario_actions_airborne.c index 9e7a884..8580d71 100644 --- a/src/decomp/game/mario_actions_airborne.c +++ b/src/decomp/game/mario_actions_airborne.c @@ -19,6 +19,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" void play_flip_sounds(struct MarioState *m, s16 frame1, s16 frame2, s16 frame3) { @@ -104,9 +105,9 @@ s32 check_fall_damage(struct MarioState *m, u32 hardFallAction) if (fallHeight > 3000.0f) { m->hurtCounter += m->flags & MARIO_CAP_ON_HEAD ? 16 : 24; - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif set_camera_shake_from_hit(SHAKE_FALL_DAMAGE); play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); return drop_and_set_mario_action(m, hardFallAction, 4); @@ -115,9 +116,9 @@ s32 check_fall_damage(struct MarioState *m, u32 hardFallAction) { m->hurtCounter += m->flags & MARIO_CAP_ON_HEAD ? 8 : 12; m->squishTimer = 30; - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif set_camera_shake_from_hit(SHAKE_FALL_DAMAGE); play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); } @@ -165,9 +166,9 @@ s32 check_fall_damage_or_get_stuck(struct MarioState *m, u32 hardFallAction) #endif m->particleFlags |= PARTICLE_MIST_CIRCLE; drop_and_set_mario_action(m, ACT_FEET_STUCK_IN_GROUND, 0); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif return TRUE; } @@ -461,9 +462,9 @@ u32 common_air_action_step(struct MarioState *m, u32 landAction, s32 animation, if (m->forwardVel > 16.0f) { - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 40); - #endif + // #endif mario_bonk_reflection(m, FALSE); m->faceAngle[1] += 0x8000; @@ -589,12 +590,12 @@ s32 act_triple_jump(struct MarioState *m) #endif common_air_action_step(m, ACT_TRIPLE_JUMP_LAND, MARIO_ANIM_TRIPLE_JUMP, 0); - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->action == ACT_TRIPLE_JUMP_LAND) { queue_rumble_data(5, 40); } - #endif + // #endif play_flip_sounds(m, 2, 8, 20); return FALSE; } @@ -608,12 +609,12 @@ s32 act_backflip(struct MarioState *m) play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAH_WAH_HOO); common_air_action_step(m, ACT_BACKFLIP_LAND, MARIO_ANIM_BACKFLIP, 0); - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->action == ACT_BACKFLIP_LAND) { queue_rumble_data(5, 40); } - #endif + // #endif play_flip_sounds(m, 2, 3, 17); return FALSE; } @@ -768,12 +769,12 @@ s32 act_long_jump(struct MarioState *m) } common_air_action_step(m, ACT_LONG_JUMP_LAND, animation, AIR_STEP_CHECK_LEDGE_GRAB); - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->action == ACT_LONG_JUMP_LAND) { queue_rumble_data(5, 40); } - #endif + // #endif return FALSE; } @@ -893,9 +894,10 @@ s32 act_dive(struct MarioState *m) case AIR_STEP_LANDED: if (should_get_stuck_in_ground(m) && m->faceAngle[0] == -0x2AAA) { - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); #else @@ -1120,9 +1122,10 @@ s32 act_ground_pound(struct MarioState *m) { if (should_get_stuck_in_ground(m)) { - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); #else @@ -1180,9 +1183,9 @@ s32 act_burning_jump(struct MarioState *m) { m->health = 0xFF; } - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } @@ -1205,9 +1208,9 @@ s32 act_burning_fall(struct MarioState *m) { m->health = 0xFF; } - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } @@ -1264,9 +1267,10 @@ s32 act_crazy_box_bounce(struct MarioState *m) m->heldObj = NULL; set_mario_action(m, ACT_STOMACH_SLIDE, 0); } - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif m->particleFlags |= PARTICLE_MIST_CIRCLE; break; @@ -1297,12 +1301,12 @@ u32 common_air_knockback_step( break; case AIR_STEP_LANDED: - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->action == ACT_SOFT_BONK) { queue_rumble_data(5, 80); } - #endif + // #endif if (!check_fall_damage_or_get_stuck(m, hardFallAction)) { #ifndef VERSION_JP @@ -1778,15 +1782,16 @@ s32 act_hold_butt_slide_air(struct MarioState *m) s32 act_lava_boost(struct MarioState *m) { - #ifdef VERSION_SH + // #ifdef VERSION_SH if (!(m->flags & MARIO_MARIO_SOUND_PLAYED)) { play_sound_if_no_flag(m, SOUND_MARIO_ON_FIRE, MARIO_MARIO_SOUND_PLAYED); queue_rumble_data(5, 80); } - #else - play_sound_if_no_flag(m, SOUND_MARIO_ON_FIRE, MARIO_MARIO_SOUND_PLAYED); - #endif + // #else + // play_sound_if_no_flag(m, SOUND_MARIO_ON_FIRE, MARIO_MARIO_SOUND_PLAYED); + // queue_rumble_data(5, 80); + // #endif if (!(m->input & INPUT_NONZERO_ANALOG)) { @@ -1807,9 +1812,10 @@ s32 act_lava_boost(struct MarioState *m) } m->vel[1] = 84.0f; play_sound(SOUND_MARIO_ON_FIRE, m->marioObj->header.gfx.cameraToObject); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif } else { @@ -1854,9 +1860,9 @@ s32 act_lava_boost(struct MarioState *m) } m->marioBodyState->eyeState = MARIO_EYES_DEAD; - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } @@ -1982,9 +1988,10 @@ s32 act_shot_from_cannon(struct MarioState *m) set_mario_action(m, ACT_DIVE_SLIDE, 0); m->faceAngle[0] = 0; set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif break; case AIR_STEP_HIT_WALL: @@ -2020,9 +2027,9 @@ s32 act_shot_from_cannon(struct MarioState *m) { m->particleFlags |= PARTICLE_DUST; } - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } @@ -2099,9 +2106,10 @@ s32 act_flying(struct MarioState *m) m->faceAngle[0] = 0; set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif break; case AIR_STEP_HIT_WALL: @@ -2164,9 +2172,10 @@ s32 act_flying(struct MarioState *m) play_sound(SOUND_MARIO_YAHOO_WAHA_YIPPEE + ((gAudioRandom % 5) << 16), m->marioObj->header.gfx.cameraToObject); #endif - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(50, 40); - #endif + // #endif } play_sound(SOUND_MOVING_FLYING, m->marioObj->header.gfx.cameraToObject); @@ -2182,9 +2191,10 @@ s32 act_riding_hoot(struct MarioState *m) m->usedObj->oHootMarioReleaseTime = gGlobalTimer; play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(4, 40); - #endif + // #endif return set_mario_action(m, ACT_FREEFALL, 0); } @@ -2253,9 +2263,10 @@ s32 act_flying_triple_jump(struct MarioState *m) if (is_anim_past_end(m)) { set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(8, 80); - #endif + // #endif m->actionState = 1; } } @@ -2327,9 +2338,10 @@ s32 act_vertical_wind(struct MarioState *m) if (m->marioObj->header.gfx.animInfo.animFrame == 1) { play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(8, 80); - #endif + // #endif } if (is_anim_past_end(m)) diff --git a/src/decomp/game/mario_actions_automatic.c b/src/decomp/game/mario_actions_automatic.c index 4897b83..5823730 100644 --- a/src/decomp/game/mario_actions_automatic.c +++ b/src/decomp/game/mario_actions_automatic.c @@ -20,6 +20,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" static Vec3f gVec3fZero = {0.0f, 0.0f, 0.0f}; @@ -198,9 +199,9 @@ s32 act_holding_pole(struct MarioState *m) // } // } play_climbing_sounds(m, 2); - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif func_80320A4C(1, marioObj->oMarioPoleYawVel / 0x100 * 2); } else @@ -446,14 +447,14 @@ void update_hang_stationary(struct MarioState *m) s32 act_start_hanging(struct MarioState *m) { - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->actionTimer++ == 0) { queue_rumble_data(5, 80); } - #else - m->actionTimer++; - #endif + // #else + // m->actionTimer++; + // #endif if (m->input & INPUT_NONZERO_ANALOG && m->actionTimer >= 31) { @@ -552,9 +553,9 @@ s32 act_hang_moving(struct MarioState *m) if (m->marioObj->header.gfx.animInfo.animFrame == 12) { play_sound(SOUND_ACTION_HANGING_STEP, m->marioObj->header.gfx.cameraToObject); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 30); - #endif + // #endif } if (is_anim_past_end(m)) @@ -782,9 +783,9 @@ s32 act_grabbed(struct MarioState *m) m->faceAngle[1] = m->usedObj->oMoveAngleYaw; vec3f_copy(m->pos, m->marioObj->header.gfx.pos); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 60); - #endif + // #endif return set_mario_action(m, m->forwardVel >= 0.0f ? ACT_THROWN_FORWARD : ACT_THROWN_BACKWARD, thrown); @@ -872,18 +873,18 @@ s32 act_in_cannon(struct MarioState *m) m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; set_mario_action(m, ACT_SHOT_FROM_CANNON, 0); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(60, 70); - #endif + // #endif m->usedObj->oAction = 2; return FALSE; } else if (m->faceAngle[0] != startFacePitch || m->faceAngle[1] != startFaceYaw) { play_sound(SOUND_MOVING_AIM_CANNON, m->marioObj->header.gfx.cameraToObject); - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers_2(0); - #endif + // #endif } } @@ -980,9 +981,9 @@ s32 act_tornado_twirling(struct MarioState *m) vec3f_copy(m->marioObj->header.gfx.pos, m->pos); vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1] + m->twirlYaw, 0); - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } diff --git a/src/decomp/game/mario_actions_cutscene.c b/src/decomp/game/mario_actions_cutscene.c index 55dc1b0..7977998 100644 --- a/src/decomp/game/mario_actions_cutscene.c +++ b/src/decomp/game/mario_actions_cutscene.c @@ -35,6 +35,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" #define SCREEN_WIDTH 320 @@ -1313,9 +1314,10 @@ s32 act_death_exit(struct MarioState *m) #else play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); #endif - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif m->numLives--; // restore 7.75 units of health m->healCounter = 31; @@ -1352,9 +1354,10 @@ s32 act_falling_death_exit(struct MarioState *m) #else play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); #endif - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif m->numLives--; // restore 7.75 units of health m->healCounter = 31; @@ -1405,9 +1408,10 @@ s32 act_special_death_exit(struct MarioState *m) if (launch_mario_until_land(m, ACT_HARD_BACKWARD_GROUND_KB, MARIO_ANIM_BACKWARD_AIR_KB, -24.0f)) { - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif m->numLives--; m->healCounter = 31; } @@ -1500,9 +1504,10 @@ s32 act_bbh_enter_spin(struct MarioState *m) if (perform_air_step(m, 0) == AIR_STEP_LANDED) { level_trigger_warp(m, WARP_OP_UNKNOWN_02); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(15, 80); - #endif + // #endif m->actionState = 4; } if (m->actionState == 2) @@ -1574,13 +1579,13 @@ s32 act_teleport_fade_out(struct MarioState *m) ? MARIO_ANIM_CROUCHING : MARIO_ANIM_FIRST_PERSON); - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->actionTimer == 0) { queue_rumble_data(30, 70); - func_sh_8024C89C(2); + // func_sh_8024C89C(2); } - #endif + // #endif m->flags |= MARIO_TELEPORTING; @@ -1604,13 +1609,13 @@ s32 act_teleport_fade_in(struct MarioState *m) play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED); set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->actionTimer == 0) { queue_rumble_data(30, 70); - func_sh_8024C89C(2); + // func_sh_8024C89C(2); } - #endif + // #endif if (m->actionTimer < 32) { @@ -1720,9 +1725,10 @@ s32 act_squished(struct MarioState *m) // Both of the 1.8's are really floats, but one of them has to // be written as a double for this to match on -O2. vec3f_set(m->marioObj->header.gfx.scale, 1.8, 0.05f, 1.8f); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(10, 80); - #endif + // #endif m->actionState = 1; } break; @@ -1845,9 +1851,10 @@ void stuck_in_ground_handler( } else if (animFrame == unstuckFrame) { - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif play_sound_and_spawn_particles(m, SOUND_ACTION_UNSTUCK_FROM_GROUND, 1); } else if (animFrame == target2 || animFrame == target3) diff --git a/src/decomp/game/mario_actions_moving.c b/src/decomp/game/mario_actions_moving.c index 77e41a1..60cccab 100644 --- a/src/decomp/game/mario_actions_moving.c +++ b/src/decomp/game/mario_actions_moving.c @@ -18,6 +18,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" struct LandingAction { @@ -1490,9 +1491,9 @@ s32 act_riding_shell_ground(struct MarioState *m) } adjust_sound_for_speed(m); - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } @@ -1612,9 +1613,9 @@ s32 act_burning_ground(struct MarioState *m) } m->marioBodyState->eyeState = MARIO_EYES_DEAD; - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } @@ -1632,9 +1633,9 @@ void common_slide_action(struct MarioState *m, u32 endAction, u32 airAction, s32 vec3f_copy(pos, m->pos); play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif adjust_sound_for_speed(m); @@ -1784,9 +1785,10 @@ s32 act_slide_kick_slide(struct MarioState *m) { if (m->input & INPUT_A_PRESSED) { - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif return set_jumping_action(m, ACT_FORWARD_ROLLOUT, 0); } @@ -1821,9 +1823,10 @@ s32 stomach_slide_action(struct MarioState *m, u32 stopAction, u32 airAction, s3 { if (!(m->input & INPUT_ABOVE_SLIDE) && m->input & (INPUT_A_PRESSED | INPUT_B_PRESSED)) { - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif return drop_and_set_mario_action( m, m->forwardVel >= 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, 0); } @@ -1863,11 +1866,10 @@ s32 act_dive_slide(struct MarioState *m) { if (!(m->input & INPUT_ABOVE_SLIDE) && m->input & (INPUT_A_PRESSED | INPUT_B_PRESSED)) { - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif - return set_mario_action(m, m->forwardVel > 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, - 0); + // #endif + return set_mario_action(m, m->forwardVel > 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, 0); } play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); diff --git a/src/decomp/game/mario_actions_object.c b/src/decomp/game/mario_actions_object.c index 8cb3d04..1fa978d 100644 --- a/src/decomp/game/mario_actions_object.c +++ b/src/decomp/game/mario_actions_object.c @@ -16,6 +16,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" /** * Used by act_punching() to determine Mario's forward velocity during each @@ -329,9 +330,9 @@ s32 act_throwing(struct MarioState *m) mario_throw_held_object(m); play_sound_if_no_flag(m, SOUND_MARIO_WAH2, MARIO_MARIO_SOUND_PLAYED); play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(3, 50); - #endif + // #endif } animated_stationary_ground_step(m, MARIO_ANIM_GROUND_THROW, ACT_IDLE); @@ -355,9 +356,9 @@ s32 act_heavy_throw(struct MarioState *m) mario_drop_held_object(m); play_sound_if_no_flag(m, SOUND_MARIO_WAH2, MARIO_MARIO_SOUND_PLAYED); play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(3, 50); - #endif + // #endif } animated_stationary_ground_step(m, MARIO_ANIM_HEAVY_THROW, ACT_IDLE); @@ -393,9 +394,9 @@ s32 act_picking_up_bowser(struct MarioState *m) m->angleVel[1] = 0; m->marioBodyState->grabPos = GRAB_POS_BOWSER; mario_grab_used_object(m); - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(5, 80); - #endif + // #endif play_sound(SOUND_MARIO_HRMM, m->marioObj->header.gfx.cameraToObject); } @@ -492,16 +493,16 @@ s32 act_holding_bowser(struct MarioState *m) // play sound on overflow if (m->angleVel[1] <= -0x100 && spin < m->faceAngle[1]) { - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(4, 20); - #endif + // #endif play_sound(SOUND_OBJ_BOWSER_SPINNING, m->marioObj->header.gfx.cameraToObject); } if (m->angleVel[1] >= 0x100 && spin > m->faceAngle[1]) { - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(4, 20); - #endif + // #endif play_sound(SOUND_OBJ_BOWSER_SPINNING, m->marioObj->header.gfx.cameraToObject); } @@ -524,16 +525,16 @@ s32 act_releasing_bowser(struct MarioState *m) { if (m->actionArg == 0) { - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(4, 50); - #endif + // #endif mario_throw_held_object(m); } else { - #ifdef VERSION_SH + // #ifdef VERSION_SH queue_rumble_data(4, 50); - #endif + // #endif mario_drop_held_object(m); } } diff --git a/src/decomp/game/mario_actions_stationary.c b/src/decomp/game/mario_actions_stationary.c index c04e0f6..7b56afc 100644 --- a/src/decomp/game/mario_actions_stationary.c +++ b/src/decomp/game/mario_actions_stationary.c @@ -22,7 +22,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" - +#include "../../rumble.h" s32 check_common_idle_cancels(struct MarioState *m) { @@ -964,17 +964,15 @@ s32 act_shockwave_bounce(struct MarioState *m) { if (m->marioObj->oInteractStatus & INT_STATUS_HIT_BY_SHOCKWAVE) { - #ifdef VERSION_SH - queue_rumble_data(70, 40); - #endif + // #ifdef VERSION_SH +queue_rumble_data(70, 40);// #endif return hurt_and_set_mario_action(m, ACT_SHOCKED, 0, 4); } if (m->actionTimer == 0) { - #ifdef VERSION_SH - queue_rumble_data(70, 40); - #endif + // #ifdef VERSION_SH +queue_rumble_data(70, 40);// #endif if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK1) { return hurt_and_set_mario_action(m, ACT_BACKWARD_GROUND_KB, 0, 0xc); diff --git a/src/decomp/game/mario_actions_submerged.c b/src/decomp/game/mario_actions_submerged.c index 15f0350..79be787 100644 --- a/src/decomp/game/mario_actions_submerged.c +++ b/src/decomp/game/mario_actions_submerged.c @@ -22,6 +22,7 @@ #include "../include/mario_animation_ids.h" #include "../include/object_fields.h" #include "../include/mario_geo_switch_case_ids.h" +#include "../../rumble.h" #define MIN_SWIM_SPEED 16.0f @@ -989,9 +990,10 @@ static s32 act_water_throw(struct MarioState *m) if (m->actionTimer++ == 5) { mario_throw_held_object(m); - #ifdef VERSION_SH + + // #ifdef VERSION_SH queue_rumble_data(3, 50); - #endif + // #endif } if (is_anim_at_end(m)) @@ -1194,12 +1196,12 @@ static s32 act_water_plunge(struct MarioState *m) m->particleFlags |= PARTICLE_WATER_SPLASH; m->actionState = 1; - #ifdef VERSION_SH + // #ifdef VERSION_SH if (m->prevAction & ACT_FLAG_AIR) { queue_rumble_data(5, 80); } - #endif + // #endif } if (stepResult == WATER_STEP_HIT_FLOOR || m->vel[1] >= endVSpeed || m->actionTimer > 20) @@ -1316,9 +1318,9 @@ static s32 act_caught_in_whirlpool(struct MarioState *m) set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); vec3f_copy(m->marioObj->header.gfx.pos, m->pos); vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); - #ifdef VERSION_SH + // #ifdef VERSION_SH reset_rumble_timers(); - #endif + // #endif return FALSE; } diff --git a/src/decomp/game/sound_init.c b/src/decomp/game/sound_init.c index fcd1abd..e19a0db 100644 --- a/src/decomp/game/sound_init.c +++ b/src/decomp/game/sound_init.c @@ -16,6 +16,7 @@ #include "sound_init.h" #include "../../play_sound.h" //#include "rumble_init.h" +#include "../../rumble.h" #define MUSIC_NONE 0xFFFF @@ -202,12 +203,12 @@ void play_menu_sounds(s16 soundMenuFlags) { play_menu_sounds_extra(20, NULL); } - #if ENABLE_RUMBLE + // #if ENABLE_RUMBLE if (soundMenuFlags & SOUND_MENU_FLAG_LETGOMARIOFACE) { queue_rumble_data(10, 60); } - #endif + // #endif } /** diff --git a/src/decomp/global_state.c b/src/decomp/global_state.c index ac09076..20fdeee 100644 --- a/src/decomp/global_state.c +++ b/src/decomp/global_state.c @@ -10,6 +10,7 @@ struct GlobalState *global_state_create(void) struct GlobalState *state = malloc(sizeof(struct GlobalState)); memset(state, 0, sizeof(struct GlobalState)); state->msSwimStrength = MIN_SWIM_STRENGTH; + state->mgMarioId = -1; return state; } diff --git a/src/decomp/global_state.h b/src/decomp/global_state.h index a4e91b0..2ae3667 100644 --- a/src/decomp/global_state.h +++ b/src/decomp/global_state.h @@ -35,6 +35,7 @@ struct GlobalState s8 mgShowDebugText; s8 mgDebugLevelSelect; s16 mgCurrSaveFileNum; + s32 mgMarioId; struct Controller mgController; struct SpawnInfo mgMarioSpawnInfoVal; struct Area *mgCurrentArea; diff --git a/src/libsm64.c b/src/libsm64.c index 2de6b22..006cfa0 100644 --- a/src/libsm64.c +++ b/src/libsm64.c @@ -30,6 +30,7 @@ #include "decomp/game/sound_init.h" #include "debug_print.h" +#include "rumble.h" #include "load_surfaces.h" #include "gfx_adapter.h" #include "load_anim_data.h" @@ -86,6 +87,11 @@ static void free_area(struct Area *area) free(area); } +SM64_LIB_FN void sm64_register_rumble_callback_function(SM64RumbleCallbackFunctionPtr rumbleCallbackFunction) +{ + g_rumble_callback_func = rumbleCallbackFunction; +} + typedef void (*SM64DebugPrintFunctionPtr)(const char *); SM64_LIB_FN void sm64_register_debug_print_function(SM64DebugPrintFunctionPtr debugPrintFunction) @@ -181,6 +187,7 @@ SM64_LIB_FN int32_t sm64_mario_create(float x, float y, float z) struct MarioInstance *newInstance = s_mario_instance_pool.objects[marioIndex]; newInstance->globalState = global_state_create(); + newInstance->globalState->mgMarioId = marioIndex; global_state_bind(newInstance->globalState); if (!s_init_one_mario) @@ -258,6 +265,8 @@ SM64_LIB_FN void sm64_mario_tick(int32_t marioId, const struct SM64MarioInputs * gAreaUpdateCounter++; + rumble_shim_update(); + outState->health = gMarioState->health; vec3f_copy(outState->position, gMarioState->pos); vec3f_copy(outState->velocity, gMarioState->vel); diff --git a/src/libsm64.h b/src/libsm64.h index b9c8e0d..9cf4446 100644 --- a/src/libsm64.h +++ b/src/libsm64.h @@ -149,6 +149,9 @@ enum SM64_TEXTURE_WIDTH = 64 * 11, SM64_TEXTURE_HEIGHT = 64, SM64_GEO_MAX_TRIANGLES = 1024, }; +typedef void (*SM64RumbleCallbackFunctionPtr)(int32_t marioId, int16_t level, int16_t time); + +extern SM64_LIB_FN void sm64_register_rumble_callback_function(SM64RumbleCallbackFunctionPtr rumbleCallbackFunction); typedef void (*SM64DebugPrintFunctionPtr)(const char *); diff --git a/src/rumble.c b/src/rumble.c new file mode 100644 index 0000000..aaff62d --- /dev/null +++ b/src/rumble.c @@ -0,0 +1,74 @@ +#include "rumble.h" + +#include + +#include "decomp/global_state.h" + +SM64RumbleCallbackFunctionPtr g_rumble_callback_func = NULL; + +static int16_t gShimWindow = 0; +static int16_t gShimPulseRate = 7; +static int16_t gShimFrameCounter = 0; +static int16_t gShimLevel = 80; + +extern void queue_rumble_data_for_mario(int32_t marioId, int16_t level, int16_t time) +{ + if (g_rumble_callback_func) + g_rumble_callback_func(marioId, level, time); +} + +extern void queue_rumble_data(int16_t level, int16_t time) +{ + int32_t marioId = -1; + + if (g_state) + marioId = g_state->mgMarioId; + + queue_rumble_data_for_mario(marioId, level, time); +} + +extern void reset_rumble_timers(void) +{ + if (gShimWindow <= 0) + gShimWindow = 7; + + if (gShimWindow < 4) + gShimWindow = 4; + + gShimPulseRate = 7; +} + +extern void reset_rumble_timers_2(int32_t a0) +{ + if (gShimWindow <= 0) + gShimWindow = 7; + + if (gShimWindow < 4) + gShimWindow = 4; + + if (a0 == 4) gShimPulseRate = 1; + if (a0 == 3) gShimPulseRate = 2; + if (a0 == 2) gShimPulseRate = 3; + if (a0 == 1) gShimPulseRate = 4; + if (a0 == 0) gShimPulseRate = 5; +} + +extern void rumble_shim_update(void) +{ + if (gShimWindow <= 0) + { + gShimFrameCounter = 0; + return; + } + + gShimWindow--; + gShimFrameCounter++; + + if (gShimPulseRate <= 0) + return; + + if ((gShimFrameCounter % gShimPulseRate) == 0) + { + queue_rumble_data(gShimLevel, 33 * 3); + } +} \ No newline at end of file diff --git a/src/rumble.h b/src/rumble.h new file mode 100644 index 0000000..9bd7377 --- /dev/null +++ b/src/rumble.h @@ -0,0 +1,11 @@ +#pragma once + +#include "libsm64.h" + +extern SM64RumbleCallbackFunctionPtr g_rumble_callback_func; + +extern void queue_rumble_data_for_mario(int32_t marioId, int16_t level, int16_t time); +extern void queue_rumble_data(int16_t level, int16_t time); +extern void reset_rumble_timers(void); +extern void reset_rumble_timers_2(int32_t a0); +extern void rumble_shim_update(void); \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index 84cc464..5ac0050 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -23,6 +23,36 @@ extern "C" #include "audio.h" +static SDL_Haptic *gHaptic = nullptr; + +static void InitHaptics(SDL_GameController *controller) +{ + if (controller == nullptr || gHaptic != nullptr) + { + return; + } + + SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller); + + if (joy == nullptr) + { + return; + } + + gHaptic = SDL_HapticOpenFromJoystick(joy); + + if (gHaptic == nullptr) + { + return; + } + + if (SDL_HapticRumbleInit(gHaptic) != 0) + { + SDL_HapticClose(gHaptic); + gHaptic = nullptr; + } +} + uint8_t *utils_read_file_alloc(const char *path, size_t *fileLength) { FILE *f = fopen(path, "rb"); @@ -68,6 +98,20 @@ static void DebugPrint(const char *message) fflush(stdout); } +static void Rumble(int32_t marioId, int16_t level, int16_t time) +{ + if (gHaptic == nullptr) + { + return; + } + + float strength = (float)level; + + Uint32 duration = (Uint32)time * 2; + + SDL_HapticRumblePlay(gHaptic, strength, duration); +} + int main(void) { size_t romSize; @@ -87,6 +131,7 @@ int main(void) sm64_audio_init(rom); // sm64_register_debug_print_function(DebugPrint); + sm64_register_rumble_callback_function(Rumble); sm64_static_surfaces_load(surfaces, surfaces_count); int32_t marioId = sm64_mario_create(0, 1000, 0); @@ -142,6 +187,7 @@ int main(void) tick += dt; SDL_GameController *controller = context_get_controller(); + InitHaptics(controller); float x_axis, y_axis, x0_axis; if (!controller) // keyboard @@ -243,5 +289,11 @@ int main(void) sm64_global_terminate(); context_terminate(); + if (gHaptic != nullptr) + { + SDL_HapticClose(gHaptic); + gHaptic = nullptr; + } + return 0; }