diff --git a/enhancements/platform_displacement_2.diff b/enhancements/platform_displacement_2.diff new file mode 100644 index 00000000..ea8bb129 --- /dev/null +++ b/enhancements/platform_displacement_2.diff @@ -0,0 +1,2618 @@ +diff --git a/enhancements/platformdisplacement2.patch b/enhancements/platformdisplacement2.patch +new file mode 100644 +index 0000000..6bf33a8 +--- /dev/null ++++ b/enhancements/platformdisplacement2.patch +@@ -0,0 +1,288 @@ ++diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c ++index ebc6bf4..987f456 100644 ++--- a/src/game/behaviors/bowser.inc.c +++++ b/src/game/behaviors/bowser.inc.c ++@@ -972,6 +972,8 @@ s32 bowser_check_fallen_off_stage(void) // bowser off stage? ++ return 0; ++ } ++ +++struct PlatformDisplacementInfo sBowserDisplacementInfo; +++ ++ void (*sBowserActions[])(void) = { bowser_act_default, bowser_act_thrown_dropped, bowser_act_jump_onto_stage, bowser_act_dance, ++ bowser_act_dead, bowser_act_text_wait, bowser_act_intro_walk, bowser_act_charge_mario, ++ bowser_act_spit_fire_into_sky, bowser_act_spit_fire_onto_floor, bowser_act_hit_edge, bowser_act_turn_from_edge, ++@@ -1033,7 +1035,7 @@ void bowser_free_update(void) { ++ struct Object *platform; ++ UNUSED f32 floorHeight; ++ if ((platform = o->platform) != NULL) ++- apply_platform_displacement(0, platform); +++ apply_platform_displacement(&sBowserDisplacementInfo, &o->oPosX, &o->oFaceAngleYaw, platform); ++ o->oBowserUnk10E = 0; ++ cur_obj_update_floor_and_walls(); ++ cur_obj_call_action_function(sBowserActions); ++diff --git a/src/game/behaviors/tilting_inverted_pyramid.inc.c b/src/game/behaviors/tilting_inverted_pyramid.inc.c ++index ebce64f..29136e9 100644 ++--- a/src/game/behaviors/tilting_inverted_pyramid.inc.c +++++ b/src/game/behaviors/tilting_inverted_pyramid.inc.c ++@@ -131,7 +131,7 @@ void bhv_tilting_inverted_pyramid_loop(void) { ++ mx += posAfterRotation[0] - posBeforeRotation[0]; ++ my += posAfterRotation[1] - posBeforeRotation[1]; ++ mz += posAfterRotation[2] - posBeforeRotation[2]; ++- set_mario_pos(mx, my, mz); +++ //set_mario_pos(mx, my, mz); ++ } ++ ++ o->header.gfx.throwMatrix = transform; ++diff --git a/src/game/platform_displacement.c b/src/game/platform_displacement.c ++index 29a741c..d1bb016 100644 ++--- a/src/game/platform_displacement.c +++++ b/src/game/platform_displacement.c ++@@ -8,6 +8,7 @@ ++ #include "object_list_processor.h" ++ #include "platform_displacement.h" ++ #include "types.h" +++#include "sm64.h" ++ ++ u16 D_8032FEC0 = 0; ++ ++@@ -84,96 +85,139 @@ void set_mario_pos(f32 x, f32 y, f32 z) { ++ gMarioStates[0].pos[2] = z; ++ } ++ ++-/** ++- * Apply one frame of platform rotation to Mario or an object using the given ++- * platform. If isMario is 0, use gCurrentObject. ++- */ ++-void apply_platform_displacement(u32 isMario, struct Object *platform) { ++- f32 x; ++- f32 y; ++- f32 z; ++- f32 platformPosX; ++- f32 platformPosY; ++- f32 platformPosZ; ++- Vec3f currentObjectOffset; ++- Vec3f relativeOffset; ++- Vec3f newObjectOffset; ++- Vec3s rotation; ++- UNUSED s16 unused1; ++- UNUSED s16 unused2; ++- UNUSED s16 unused3; ++- f32 displaceMatrix[4][4]; ++- ++- rotation[0] = platform->oAngleVelPitch; ++- rotation[1] = platform->oAngleVelYaw; ++- rotation[2] = platform->oAngleVelRoll; ++- ++- if (isMario) { ++- D_8032FEC0 = 0; ++- get_mario_pos(&x, &y, &z); ++- } else { ++- x = gCurrentObject->oPosX; ++- y = gCurrentObject->oPosY; ++- z = gCurrentObject->oPosZ; ++- } ++- ++- x += platform->oVelX; ++- z += platform->oVelZ; +++static struct PlatformDisplacementInfo sMarioDisplacementInfo; +++static Vec3f sMarioAmountDisplaced; ++ ++- if (rotation[0] != 0 || rotation[1] != 0 || rotation[2] != 0) { ++- unused1 = rotation[0]; ++- unused2 = rotation[2]; ++- unused3 = platform->oFaceAngleYaw; +++extern s32 gGlobalTimer; ++ ++- if (isMario) { ++- gMarioStates[0].faceAngle[1] += rotation[1]; ++- } ++- ++- platformPosX = platform->oPosX; ++- platformPosY = platform->oPosY; ++- platformPosZ = platform->oPosZ; ++- ++- currentObjectOffset[0] = x - platformPosX; ++- currentObjectOffset[1] = y - platformPosY; ++- currentObjectOffset[2] = z - platformPosZ; ++- ++- rotation[0] = platform->oFaceAnglePitch - platform->oAngleVelPitch; ++- rotation[1] = platform->oFaceAngleYaw - platform->oAngleVelYaw; ++- rotation[2] = platform->oFaceAngleRoll - platform->oAngleVelRoll; ++- ++- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); ++- linear_mtxf_transpose_mul_vec3f(displaceMatrix, relativeOffset, currentObjectOffset); +++/** +++ * Upscale or downscale a vector by another vector. +++ */ +++static void scale_vec3f(Vec3f dst, Vec3f src, Vec3f scale, u32 doInverted) { +++ if (doInverted) { +++ dst[0] = src[0] / scale[0]; +++ dst[1] = src[1] / scale[1]; +++ dst[2] = src[2] / scale[2]; +++ } else { +++ dst[0] = src[0] * scale[0]; +++ dst[1] = src[1] * scale[1]; +++ dst[2] = src[2] * scale[2]; +++ } +++} ++ ++- rotation[0] = platform->oFaceAnglePitch; ++- rotation[1] = platform->oFaceAngleYaw; ++- rotation[2] = platform->oFaceAngleRoll; +++/** +++ * Apply one frame of platform displacement to Mario or an object using the given +++ * platform. +++ */ +++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform) { +++ Vec3f platformPos; +++ Vec3f posDifference; +++ Vec3f yawVec; +++ Vec3f scaledPos; +++ // Determine how much Mario turned on his own since last frame +++ s16 yawDifference = *yaw - displaceInfo->prevYaw; +++ +++ // Avoid a crash if the platform unloaded its collision while stood on +++ if (platform->header.gfx.throwMatrix == NULL) return; +++ +++ vec3f_copy(platformPos, (*platform->header.gfx.throwMatrix)[3]); +++ +++ // Determine how far Mario moved on his own since last frame +++ vec3f_copy(posDifference, pos); +++ vec3f_sub(posDifference, displaceInfo->prevPos); +++ +++ if ((platform == displaceInfo->prevPlatform) && (gGlobalTimer == displaceInfo->prevTimer + 1)) { +++ // Transform from relative positions to world positions +++ scale_vec3f(scaledPos, displaceInfo->prevTransformedPos, platform->header.gfx.scale, FALSE); +++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, pos, scaledPos); +++ +++ // Add on how much Mario moved in the previous frame +++ vec3f_add(pos, posDifference); +++ +++ // Calculate new yaw +++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, yawVec, displaceInfo->prevTransformedYawVec); +++ *yaw = atan2s(yawVec[2], yawVec[0]) + yawDifference; +++ } else { +++ // First frame of standing on the platform, don't calculate a new position +++ vec3f_sub(pos, platformPos); +++ } +++ +++ // Transform from world positions to relative positions for use next frame +++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, scaledPos, pos); +++ scale_vec3f(displaceInfo->prevTransformedPos, scaledPos, platform->header.gfx.scale, TRUE); +++ vec3f_add(pos, platformPos); +++ +++ // If the object is Mario, set inertia +++ if (pos == gMarioState->pos) { +++ vec3f_copy(sMarioAmountDisplaced, pos); +++ vec3f_sub(sMarioAmountDisplaced, displaceInfo->prevPos); +++ vec3f_sub(sMarioAmountDisplaced, posDifference); +++ +++ // Make sure inertia isn't set on the first frame otherwise the previous value isn't cleared +++ if ((platform != displaceInfo->prevPlatform) || (gGlobalTimer != displaceInfo->prevTimer + 1)) { +++ vec3f_set(sMarioAmountDisplaced, 0.f, 0.f, 0.f); +++ } +++ } +++ +++ // Update info for next frame +++ // Update position +++ vec3f_copy(displaceInfo->prevPos, pos); +++ +++ // Set yaw info +++ vec3f_set(yawVec, sins(*yaw), 0, coss(*yaw)); +++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, displaceInfo->prevTransformedYawVec, yawVec); +++ displaceInfo->prevYaw = *yaw; +++ +++ // Update platform and timer +++ displaceInfo->prevPlatform = platform; +++ displaceInfo->prevTimer = gGlobalTimer; +++} ++ ++- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); ++- linear_mtxf_mul_vec3f(displaceMatrix, newObjectOffset, relativeOffset); +++// Doesn't change in the code, set this to FALSE if you don't want inertia +++u8 gDoInertia = TRUE; ++ ++- x = platformPosX + newObjectOffset[0]; ++- y = platformPosY + newObjectOffset[1]; ++- z = platformPosZ + newObjectOffset[2]; ++- } +++static u8 sShouldApplyInertia = FALSE; +++static u8 sInertiaFirstFrame = FALSE; ++ ++- if (isMario) { ++- set_mario_pos(x, y, z); ++- } else { ++- gCurrentObject->oPosX = x; ++- gCurrentObject->oPosY = y; ++- gCurrentObject->oPosZ = z; ++- } +++/** +++ * Apply inertia based on Mario's last platform. +++ */ +++static void apply_mario_inertia(void) { +++ // On the first frame of leaving the ground, boost Mario's y velocity +++ if (sInertiaFirstFrame) { +++ gMarioState->vel[1] += sMarioAmountDisplaced[1]; +++ } +++ +++ // Apply sideways inertia +++ gMarioState->pos[0] += sMarioAmountDisplaced[0]; +++ gMarioState->pos[2] += sMarioAmountDisplaced[2]; +++ +++ // Drag +++ sMarioAmountDisplaced[0] *= 0.97f; +++ sMarioAmountDisplaced[2] *= 0.97f; +++ +++ // Stop applying inertia once Mario has landed, or when ground pounding +++ if (!(gMarioState->action & ACT_FLAG_AIR) || (gMarioState->action == ACT_GROUND_POUND)) { +++ sShouldApplyInertia = FALSE; +++ } ++ } ++ ++ /** ++- * If Mario's platform is not null, apply platform displacement. +++ * Apply platform displacement or inertia if required. ++ */ ++ void apply_mario_platform_displacement(void) { ++ struct Object *platform; ++ ++ platform = gMarioPlatform; ++- if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL && platform != NULL) { ++- apply_platform_displacement(1, platform); +++ if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL) { +++ if (platform != NULL) { +++ apply_platform_displacement(&sMarioDisplacementInfo, gMarioState->pos, &gMarioState->faceAngle[1], platform); +++ sShouldApplyInertia = TRUE; +++ sInertiaFirstFrame = TRUE; +++ } else if (sShouldApplyInertia && gDoInertia) { +++ apply_mario_inertia(); +++ sInertiaFirstFrame = FALSE; +++ } ++ } ++ } ++ ++diff --git a/src/game/platform_displacement.h b/src/game/platform_displacement.h ++index 556192b..3609e2d 100644 ++--- a/src/game/platform_displacement.h +++++ b/src/game/platform_displacement.h ++@@ -5,10 +5,19 @@ ++ ++ #include "types.h" ++ +++struct PlatformDisplacementInfo { +++ Vec3f prevPos; +++ Vec3f prevTransformedPos; +++ Vec3f prevTransformedYawVec; +++ s16 prevYaw; +++ struct Object *prevPlatform; +++ s32 prevTimer; +++}; +++ ++ void update_mario_platform(void); ++ void get_mario_pos(f32 *x, f32 *y, f32 *z); ++ void set_mario_pos(f32 x, f32 y, f32 z); ++-void apply_platform_displacement(u32 isMario, struct Object *platform); +++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform); ++ void apply_mario_platform_displacement(void); ++ #ifndef VERSION_JP ++ void clear_mario_platform(void); +diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c +index 97cba2a..410e612 100644 +--- a/src/game/behaviors/bowser.inc.c ++++ b/src/game/behaviors/bowser.inc.c +@@ -972,6 +972,8 @@ s32 bowser_check_fallen_off_stage(void) // bowser off stage? + return 0; + } + ++struct PlatformDisplacementInfo sBowserDisplacementInfo; ++ + void (*sBowserActions[])(void) = { bowser_act_default, bowser_act_thrown_dropped, bowser_act_jump_onto_stage, bowser_act_dance, + bowser_act_dead, bowser_act_text_wait, bowser_act_intro_walk, bowser_act_charge_mario, + bowser_act_spit_fire_into_sky, bowser_act_spit_fire_onto_floor, bowser_act_hit_edge, bowser_act_turn_from_edge, +@@ -1032,8 +1034,9 @@ void bowser_free_update(void) { + struct Surface *floor; + struct Object *platform; + UNUSED f32 floorHeight; +- if ((platform = o->platform) != NULL) +- apply_platform_displacement(FALSE, platform); ++ if ((platform = o->platform) != NULL) { ++ apply_platform_displacement(&sBowserDisplacementInfo, &o->oPosX, &o->oFaceAngleYaw, platform); ++ } + o->oBowserUnk10E = 0; + cur_obj_update_floor_and_walls(); + cur_obj_call_action_function(sBowserActions); +diff --git a/src/game/behaviors/bowser.inc.c.orig b/src/game/behaviors/bowser.inc.c.orig +new file mode 100644 +index 0000000..97cba2a +--- /dev/null ++++ b/src/game/behaviors/bowser.inc.c.orig +@@ -0,0 +1,1586 @@ ++// bowser.c.inc ++ ++void bowser_tail_anchor_act_0(void) { ++ struct Object *bowser = o->parentObj; ++ cur_obj_become_tangible(); ++ cur_obj_scale(1.0f); ++ if (bowser->oAction == 19) ++ bowser->oIntangibleTimer = -1; ++ else if (obj_check_if_collided_with_object(o, gMarioObject)) { ++ bowser->oIntangibleTimer = 0; ++ o->oAction = 2; ++ } else ++ bowser->oIntangibleTimer = -1; ++} ++ ++void bowser_tail_anchor_act_1(void) { ++ if (o->oTimer > 30) ++ o->oAction = 0; ++} ++ ++void bowser_tail_anchor_act_2(void) { ++ if (o->parentObj->oAction == 19) { ++ o->parentObj->oIntangibleTimer = -1; ++ o->oAction = 0; ++ } ++ cur_obj_become_intangible(); ++} ++ ++void (*sBowserTailAnchorActions[])(void) = { bowser_tail_anchor_act_0, bowser_tail_anchor_act_1, ++ bowser_tail_anchor_act_2 }; ++s8 D_8032F4FC[] = { 7, 8, 9, 12, 13, 14, 15, 4, 3, 16, 17, 19, 3, 3, 3, 3 }; ++s16 D_8032F50C[] = { 60, 0 }; ++s16 D_8032F510[] = { 50, 0 }; ++s8 D_8032F514[] = { 24, 42, 60, -1 }; ++s16 sBowserDefeatedDialogText[3] = { DIALOG_119, DIALOG_120, DIALOG_121 }; ++s16 D_8032F520[][3] = { { 1, 10, 40 }, { 0, 0, 74 }, { -1, -10, 114 }, { 1, -20, 134 }, ++ { -1, 20, 154 }, { 1, 40, 164 }, { -1, -40, 174 }, { 1, -80, 179 }, ++ { -1, 80, 184 }, { 1, 160, 186 }, { -1, -160, 186 }, { 1, 0, 0 }, }; ++ ++void bhv_bowser_tail_anchor_loop(void) { ++ cur_obj_call_action_function(sBowserTailAnchorActions); ++ o->oParentRelativePosX = 90.0f; ++ if (o->parentObj->oAction == 4) ++ o->parentObj->oIntangibleTimer = -1; ++ o->oInteractStatus = 0; ++} ++ ++void bhv_bowser_flame_spawn_loop(void) { ++ struct Object *bowser = o->parentObj; ++ s32 sp30; ++ f32 sp2C; ++ f32 sp28; ++ f32 sp24 = coss(bowser->oMoveAngleYaw); ++ f32 sp20 = sins(bowser->oMoveAngleYaw); ++ s16 *sp1C = segmented_to_virtual(bowser_seg6_unkmoveshorts_060576FC); ++ if (bowser->oSoundStateID == 6) { ++ sp30 = bowser->header.gfx.animInfo.animFrame + 1.0f; ++ if (bowser->header.gfx.animInfo.curAnim->loopEnd == sp30) ++ sp30 = 0; ++ if (sp30 > 45 && sp30 < 85) { ++ cur_obj_play_sound_1(SOUND_AIR_BOWSER_SPIT_FIRE); ++ sp2C = sp1C[5 * sp30]; ++ sp28 = sp1C[5 * sp30 + 2]; ++ o->oPosX = bowser->oPosX + (sp28 * sp20 + sp2C * sp24); ++ o->oPosY = bowser->oPosY + sp1C[5 * sp30 + 1]; ++ o->oPosZ = bowser->oPosZ + (sp28 * sp24 - sp2C * sp20); ++ o->oMoveAnglePitch = sp1C[5 * sp30 + 4] + 0xC00; ++ o->oMoveAngleYaw = sp1C[5 * sp30 + 3] + (s16) bowser->oMoveAngleYaw; ++ if (!(sp30 & 1)) ++ spawn_object(o, MODEL_RED_FLAME, bhvFlameMovingForwardGrowing); ++ } ++ } ++} ++ ++void bhv_bowser_body_anchor_loop(void) { ++ obj_copy_pos_and_angle(o, o->parentObj); ++ if (o->parentObj->oAction == 4) { ++#ifndef VERSION_JP ++ if (o->parentObj->oSubAction == 11) ++ o->oInteractType = 0; ++ else ++ o->oInteractType = 0x800000; ++#else ++ o->oInteractType = 0x800000; ++#endif ++ } else { ++ o->oInteractType = 8; ++ if (o->parentObj->oOpacity < 100) ++ cur_obj_become_intangible(); ++ else ++ cur_obj_become_tangible(); ++ } ++ if (o->parentObj->oHeldState != HELD_FREE) ++ cur_obj_become_intangible(); ++ o->oInteractStatus = 0; ++} ++ ++s32 bowser_spawn_shockwave(void) { ++ struct Object *wave; ++ if (o->oBehParams2ndByte == 2) { ++ wave = spawn_object(o, MODEL_BOWSER_WAVE, bhvBowserShockWave); ++ wave->oPosY = o->oFloorHeight; ++ return 1; ++ } ++ return 0; ++} ++ ++void bowser_bounce(s32 *a) { ++ if (o->oMoveFlags & OBJ_MOVE_LANDED) { ++ a[0]++; ++ if (a[0] < 4) { ++ cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_THROW_BOUNCE); ++ spawn_mist_particles_variable(0, 0, 60.0f); ++ cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK); ++ } ++ } ++} ++ ++#define BITDW (o->oBehParams2ndByte == 0) ++#define BITFS (o->oBehParams2ndByte == 1) ++#define BITS (o->oBehParams2ndByte == 2) ++ ++s32 bowser_set_anim_look_up_and_walk(void) { ++ cur_obj_init_animation_with_sound(15); ++ if (cur_obj_check_anim_frame(21)) ++ o->oForwardVel = 3.0f; ++ if (cur_obj_check_if_near_animation_end()) ++ return 1; ++ else ++ return 0; ++} ++ ++s32 bowser_set_anim_slow_gait(void) { ++ o->oForwardVel = 3.0f; ++ cur_obj_init_animation_with_sound(13); ++ if (cur_obj_check_if_near_animation_end()) ++ return 1; ++ else ++ return 0; ++} ++ ++s32 bowser_set_anim_look_down(void) { ++ cur_obj_init_animation_with_sound(14); ++ if (cur_obj_check_anim_frame(20)) ++ o->oForwardVel = 0.0f; ++ if (cur_obj_check_if_near_animation_end()) ++ return 1; ++ else ++ return 0; ++} ++ ++void bowser_initialize_action(void) { ++ if (o->oBowserUnk88 == 0) ++ o->oAction = 5; ++ else if (o->oBowserUnk88 == 1) ++ o->oAction = 6; ++ else if (o->oBehParams2ndByte == 1) ++ o->oAction = 13; ++ else ++ o->oAction = 0; ++} ++ ++void bowser_act_text_wait(void) // not much ++{ ++ o->oForwardVel = 0.0f; ++ cur_obj_init_animation_with_sound(12); ++ bowser_initialize_action(); ++} ++ ++void bowser_act_intro_walk(void) { ++ if (o->oSubAction == 0) { ++ if (bowser_set_anim_look_up_and_walk()) ++ o->oSubAction++; ++ } else if (o->oSubAction == 1) { ++ if (bowser_set_anim_slow_gait()) ++ o->oSubAction++; ++ } else if (bowser_set_anim_look_down()) { ++ if (o->oBowserUnk88 == 1) ++ o->oBowserUnk88 = 0; ++ bowser_initialize_action(); ++ } ++} ++ ++static void bowser_debug_actions(void) // unused ++{ ++ if (gDebugInfo[5][1] != 0) { ++ o->oAction = D_8032F4FC[gDebugInfo[5][2] & 0xf]; ++ gDebugInfo[5][1] = 0; ++ } ++} ++ ++void bowser_bitdw_act_controller(void) { ++ f32 rand = random_float(); ++ if (o->oBowserUnk110 == 0) { ++ if (o->oBowserUnkF4 & 2) { ++ if (o->oDistanceToMario < 1500.0f) ++ o->oAction = 15; // nearby ++ else ++ o->oAction = 17; // far away ++ } else ++ o->oAction = 14; ++ o->oBowserUnk110++; ++ } else { ++ o->oBowserUnk110 = 0; ++#ifndef VERSION_JP ++ if (!gCurrDemoInput) { ++ if (rand < 0.1) ++ o->oAction = 3; // rare 1/10 chance ++ else ++ o->oAction = 14; // common ++ } else { ++ o->oAction = 14; // ensure demo starts with action 14. ++ } ++#else ++ if (rand < 0.1) ++ o->oAction = 3; // rare 1/10 chance ++ else ++ o->oAction = 14; // common ++#endif ++ } ++} ++ ++void bowser_bitfs_act_controller(void) { ++ f32 rand = random_float(); ++ if (o->oBowserUnk110 == 0) { ++ if (o->oBowserUnkF4 & 2) { ++ if (o->oDistanceToMario < 1300.0f) // nearby ++ { ++ if (rand < 0.5) // 50/50 ++ o->oAction = 16; ++ else ++ o->oAction = 9; ++ } else // far away ++ { ++ o->oAction = 7; ++ if (500.0f < o->oBowserDistToCentre && o->oBowserDistToCentre < 1500.0f ++ && rand < 0.5) // away from centre and good luck ++ o->oAction = 13; ++ } ++ } else ++ o->oAction = 14; ++ o->oBowserUnk110++; ++ } else { ++ o->oBowserUnk110 = 0; ++ o->oAction = 14; ++ } ++} ++ ++void bowser_general_bits_act_controller(void) { ++ f32 rand = random_float(); ++ if (o->oBowserUnkF4 & 2) { ++ if (o->oDistanceToMario < 1000.0f) { ++ if (rand < 0.4) ++ o->oAction = 9; ++ else if (rand < 0.8) ++ o->oAction = 8; ++ else ++ o->oAction = 15; ++ } else if (rand < 0.5) ++ o->oAction = 13; ++ else ++ o->oAction = 7; ++ } else ++ o->oAction = 14; ++} ++ ++void bowser_set_act_jump(void) { ++ o->oAction = 13; ++} ++ ++void bowser_bits_act_controller(void) { ++ switch (o->oBowserUnk110) { ++ case 0: ++ if (o->oBowserUnk106 == 0) ++ bowser_general_bits_act_controller(); ++ else ++ bowser_set_act_jump(); ++ o->oBowserUnk110 = 1; ++ break; ++ case 1: ++ o->oBowserUnk110 = 0; ++ o->oAction = 14; ++ break; ++ } ++} ++ ++#ifndef VERSION_JP ++void bowser_reset_fallen_off_stage(void) { ++ if (o->oVelY < 0 && o->oPosY < (o->oHomeY - 300.0f)) { ++ o->oPosX = o->oPosZ = 0; ++ o->oPosY = o->oHomeY + 2000.0f; ++ o->oVelY = 0; ++ o->oForwardVel = 0; ++ } ++} ++#endif ++ ++void bowser_act_unused_slow_walk(void) // unused? ++{ ++ if (cur_obj_init_animation_and_check_if_near_end(12)) ++ o->oAction = 0; ++} ++ ++void bowser_act_default(void) // only lasts one frame ++{ ++ o->oBowserEyesShut = 0; ++ cur_obj_init_animation_with_sound(12); ++ // stop him still ++ o->oAngleVelYaw = 0; ++ o->oForwardVel = 0.0f; ++ o->oVelY = 0.0f; ++ if (BITDW) ++ bowser_bitdw_act_controller(); ++ else if (BITFS) ++ bowser_bitfs_act_controller(); ++ else ++ bowser_bits_act_controller(); ++ // Action 14 commonly follows ++} ++ ++void bowser_act_breath_fire(void) { ++ o->oForwardVel = 0.0f; ++ if (o->oTimer == 0) ++ cur_obj_play_sound_2(SOUND_OBJ_BOWSER_INHALING); ++ if (cur_obj_init_animation_and_check_if_near_end(6)) ++ o->oAction = 0; ++} ++ ++void bowser_act_walk_to_mario(void) // turn towards Mario ++{ ++ UNUSED s32 facing; // is Bowser facing Mario? ++ s16 turnSpeed; ++ s16 angleFromMario = abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario); ++ if (BITFS) ++ turnSpeed = 0x400; ++ else if (o->oHealth > 2) ++ turnSpeed = 0x400; ++ else if (o->oHealth == 2) ++ turnSpeed = 0x300; ++ else ++ turnSpeed = 0x200; ++ facing = cur_obj_rotate_yaw_toward(o->oAngleToMario, turnSpeed); ++ if (o->oSubAction == 0) { ++ o->oBowserUnkF8 = 0; ++ if (bowser_set_anim_look_up_and_walk()) ++ o->oSubAction++; ++ } else if (o->oSubAction == 1) { ++ if (bowser_set_anim_slow_gait()) { ++ o->oBowserUnkF8++; ++ if (o->oBowserUnkF4 & 0x20000) { ++ if (o->oBowserUnkF8 > 4) ++ o->oBowserUnkF4 &= ~0x20000; ++ } else if (angleFromMario < 0x2000) ++ o->oSubAction++; ++ } ++ } else if (bowser_set_anim_look_down()) ++ o->oAction = 0; ++} ++ ++void bowser_act_teleport(void) { ++ switch (o->oSubAction) { ++ case 0: ++ cur_obj_become_intangible(); ++ o->oBowserUnk1AC = 0; ++ o->oBowserUnkF8 = 30; ++ if (o->oTimer == 0) ++ cur_obj_play_sound_2(SOUND_OBJ2_BOWSER_TELEPORT); ++ if (o->oOpacity == 0) { ++ o->oSubAction++; ++ o->oMoveAngleYaw = o->oAngleToMario; ++ } ++ break; ++ case 1: ++ if (o->oBowserUnkF8--) ++ o->oForwardVel = 100.0f; ++ else { ++ o->oSubAction = 2; ++ o->oMoveAngleYaw = o->oAngleToMario; ++ } ++ if (abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario) > 0x4000) ++ if (o->oDistanceToMario > 500.0f) { ++ o->oSubAction = 2; ++ o->oMoveAngleYaw = o->oAngleToMario; // large change in angle? ++ cur_obj_play_sound_2(SOUND_OBJ2_BOWSER_TELEPORT); ++ } ++ break; ++ case 2: ++ o->oForwardVel = 0.0f; ++ o->oBowserUnk1AC = 0xFF; ++ if (o->oOpacity == 0xFF) ++ o->oAction = 0; ++ cur_obj_become_tangible(); ++ break; ++ } ++} ++ ++void bowser_act_spit_fire_into_sky(void) // only in sky ++{ ++ s32 frame; ++ cur_obj_init_animation_with_sound(11); ++ frame = o->header.gfx.animInfo.animFrame; ++ if (frame > 24 && frame < 36) { ++ cur_obj_play_sound_1(SOUND_AIR_BOWSER_SPIT_FIRE); ++ if (frame == 35) ++ spawn_object_relative(1, 0, 0x190, 0x64, o, MODEL_RED_FLAME, bhvBlueBowserFlame); ++ else ++ spawn_object_relative(0, 0, 0x190, 0x64, o, MODEL_RED_FLAME, bhvBlueBowserFlame); ++ } ++ if (cur_obj_check_if_near_animation_end()) ++ o->oAction = 0; ++ o->oBowserUnkF4 |= 0x20000; ++} ++ ++void bowser_act_hit_mine(void) { ++ if (o->oTimer == 0) { ++ o->oForwardVel = -400.0f; ++ o->oVelY = 100.0f; ++ o->oMoveAngleYaw = o->oBowserAngleToCentre + 0x8000; ++ o->oBowserEyesShut = 1; ++ } ++ if (o->oSubAction == 0) { ++ cur_obj_init_animation_with_sound(25); ++ o->oSubAction++; ++ o->oBowserUnkF8 = 0; ++ } else if (o->oSubAction == 1) { ++ cur_obj_init_animation_with_sound(25); ++ cur_obj_extend_animation_if_at_end(); ++ bowser_bounce(&o->oBowserUnkF8); ++ if ((o->oBowserUnkF8 > 2)) { ++ cur_obj_init_animation_with_sound(26); ++ o->oVelY = 0.0f; ++ o->oForwardVel = 0.0f; ++ o->oSubAction++; ++ } ++ } else if (o->oSubAction == 2) { ++ if (cur_obj_check_if_near_animation_end()) { ++ if (o->oHealth == 1) ++ o->oAction = 3; ++ else ++ o->oAction = 0; ++ o->oBowserEyesShut = 0; ++ } ++ } else { ++ } ++} ++ ++s32 bowser_set_anim_in_air(void) { ++ cur_obj_init_animation_with_sound(9); ++ if (cur_obj_check_anim_frame(11)) ++ return 1; ++ else ++ return 0; ++} ++ ++s32 bowser_land(void) { ++ if (o->oMoveFlags & OBJ_MOVE_LANDED) { ++ o->oForwardVel = 0; ++ o->oVelY = 0; ++ spawn_mist_particles_variable(0, 0, 60.0f); ++ cur_obj_init_animation_with_sound(8); ++ o->header.gfx.animInfo.animFrame = 0; ++ cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_JUMP); ++ if (BITDW) { ++ if (o->oDistanceToMario < 850.0f) ++ gMarioObject->oInteractStatus |= INT_STATUS_MARIO_UNK1; ++ else ++ gMarioObject->oInteractStatus |= INT_STATUS_HOOT_GRABBED_BY_MARIO; // hmm... ++ } ++ return 1; ++ } else ++ return 0; ++} ++ ++void bowser_short_second_hop(void) { ++ if (BITS && o->oBowserUnkF4 & 0x10000) ++ if (o->oBowserDistToCentre > 1000.0f) ++ o->oForwardVel = 60.0f; ++} ++ ++void bowser_act_jump(void) { ++ UNUSED s32 unused; ++ if (o->oSubAction == 0) { ++ if (bowser_set_anim_in_air()) { ++ if (BITS && o->oBowserUnkF4 & 0x10000) ++ o->oVelY = 70.0f; ++ else ++ o->oVelY = 80.0f; ++ o->oBowserUnkF8 = 0; ++ bowser_short_second_hop(); ++ o->oSubAction++; ++ } ++ } else if (o->oSubAction == 1) { ++#ifndef VERSION_JP ++ if (o->oBehParams2ndByte == 2 && o->oBowserUnkF4 & 0x10000) ++ bowser_reset_fallen_off_stage(); ++#endif ++ if (bowser_land()) { ++ o->oBowserUnkF4 &= ~0x10000; ++ o->oForwardVel = 0.0f; ++ o->oSubAction++; ++ bowser_spawn_shockwave(); ++ if (BITFS) ++ o->oAction = 19; ++ } else { ++ } ++ } else if (cur_obj_check_if_near_animation_end()) ++ o->oAction = 0; ++} ++ ++void bowser_act_jump_towards_mario(void) { ++ f32 sp1C = D_8032F50C[0]; ++ f32 sp18 = D_8032F510[0]; ++ if (o->oSubAction == 0) { ++ if (bowser_set_anim_in_air()) { ++ o->oVelY = sp1C; ++ o->oForwardVel = sp18; ++ o->oBowserUnkF8 = 0; ++ o->oSubAction++; ++ } ++ } else if (o->oSubAction == 1) { ++ if (bowser_land()) ++ o->oSubAction++; ++ } else if (cur_obj_check_if_near_animation_end()) ++ o->oAction = 0; ++} ++ ++void bowser_act_hit_edge(void) { ++ o->oForwardVel = 0.0f; ++ if (o->oTimer == 0) ++ o->oBowserUnkF8 = 0; ++ switch (o->oSubAction) { ++ case 0: ++ cur_obj_init_animation_with_sound(23); ++ if (cur_obj_check_if_near_animation_end()) ++ o->oBowserUnkF8++; ++ if (o->oBowserUnkF8 > 0) ++ o->oSubAction++; ++ break; ++ case 1: ++ cur_obj_init_animation_with_sound(24); ++ if (cur_obj_check_if_near_animation_end()) ++ o->oAction = 11; ++ break; ++ } ++} ++ ++void bowser_act_spit_fire_onto_floor(void) { ++ if (gHudDisplay.wedges < 4) ++ o->oBowserUnk108 = 3; ++ else ++ o->oBowserUnk108 = random_float() * 3.0f + 1.0f; ++ cur_obj_init_animation_with_sound(22); ++ if (cur_obj_check_anim_frame(5)) ++ obj_spit_fire(0, 200, 180, 7.0f, MODEL_RED_FLAME, 30.0f, 10.0f, 0x1000); ++ if (cur_obj_check_if_near_animation_end()) ++ o->oSubAction++; ++ if (o->oSubAction >= o->oBowserUnk108) ++ o->oAction = 0; ++} ++ ++s32 bowser_turn_on_timer(s32 a0, s16 a1) { ++ if (o->oSubAction == 0) { ++ if (cur_obj_init_animation_and_check_if_near_end(15)) ++ o->oSubAction++; ++ } else if (o->oSubAction == 1) { ++ if (cur_obj_init_animation_and_check_if_near_end(14)) ++ o->oSubAction++; ++ } else ++ cur_obj_init_animation_with_sound(12); ++ o->oForwardVel = 0.0f; ++ o->oMoveAngleYaw += a1; ++ if (o->oTimer >= a0) ++ return 1; ++ else ++ return 0; ++} ++ ++void bowser_act_turn_from_edge(void) { ++ if (bowser_turn_on_timer(63, 0x200)) ++ o->oAction = 0; ++} ++ ++void bowser_act_charge_mario(void) { ++ s32 sp34; ++ if (o->oTimer == 0) ++ o->oForwardVel = 0.0f; ++ switch (o->oSubAction) { ++ case 0: ++ o->oBowserUnkF8 = 0; ++ if (cur_obj_init_animation_and_check_if_near_end(18)) ++ o->oSubAction = 1; ++ break; ++ case 1: ++ o->oForwardVel = 50.0f; ++ if (cur_obj_init_animation_and_check_if_near_end(0x13) != 0) { ++ o->oBowserUnkF8++; ++ if (o->oBowserUnkF8 >= 6) ++ o->oSubAction = 3; ++ if (o->oBowserUnkF8 >= 2) ++ if (abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw) > 0x2000) ++ o->oSubAction = 3; ++ } ++ cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x200); ++ break; ++ case 3: ++ o->oBowserUnkF8 = 0; ++ cur_obj_init_animation_with_sound(21); ++ spawn_object_relative_with_scale(0, 100, -50, 0, 3.0f, o, MODEL_SMOKE, bhvWhitePuffSmoke2); ++ spawn_object_relative_with_scale(0, -100, -50, 0, 3.0f, o, MODEL_SMOKE, ++ bhvWhitePuffSmoke2); ++ if (approach_f32_signed(&o->oForwardVel, 0, -1.0f)) ++ o->oSubAction = 2; ++ cur_obj_extend_animation_if_at_end(); ++ break; ++ case 2: ++ o->oForwardVel = 0.0f; ++ cur_obj_init_animation_with_sound(20); ++ if (cur_obj_check_if_near_animation_end()) { ++ if (BITS) ++ sp34 = 10; ++ else ++ sp34 = 30; ++ if (o->oBowserUnkF8 > sp34) ++ o->oAction = 0; ++ o->oBowserUnkF8++; ++ } ++ cur_obj_extend_animation_if_at_end(); ++ break; ++ } ++ if (o->oMoveFlags & OBJ_MOVE_HIT_EDGE) ++ o->oAction = 10; ++} ++ ++s32 bowser_check_hit_mine(void) { ++ struct Object *mine; ++ f32 sp18; ++ mine = cur_obj_find_nearest_object_with_behavior(bhvBowserBomb, &sp18); ++ if (mine != NULL && sp18 < 800.0f) { ++ mine->oInteractStatus |= INT_STATUS_HIT_MINE; ++ return 1; ++ } ++ return 0; ++} ++ ++void bowser_act_thrown_dropped(void) ++{ ++ UNUSED s32 unused; ++ if (o->oTimer < 2) ++ o->oBowserUnkF8 = 0; ++ if (o->oSubAction == 0) { ++ cur_obj_init_animation_with_sound(2); ++ bowser_bounce(&o->oBowserUnkF8); ++ if (o->oMoveFlags & OBJ_MOVE_ON_GROUND) { ++ o->oForwardVel = 0.0f; ++ o->oSubAction++; ++ } ++ } else if (cur_obj_init_animation_and_check_if_near_end(0)) ++ o->oAction = 0; ++ if (bowser_check_hit_mine()) { ++ o->oHealth--; ++ if (o->oHealth <= 0) ++ o->oAction = 4; ++ else ++ o->oAction = 12; ++ } ++} ++ ++void bowser_set_goal_invisible(void) { ++ o->oBowserUnk1AC = 0; ++ if (o->oOpacity == 0) { ++ o->oForwardVel = 0.0f; ++ o->oVelY = 0.0f; ++ o->oPosY = o->oHomeY - 1000.0f; ++ } ++} ++ ++void bowser_act_jump_onto_stage(void) { ++ s32 sp2C; ++ UNUSED s32 unused; ++ struct Surface *sp24 = o->oFloor; ++ if (sp24 != NULL && sp24->flags & 1) ++ sp2C = 1; ++ else ++ sp2C = 0; ++ o->oBowserUnkF4 |= 0x10000; ++ switch (o->oSubAction) { ++ case 0: ++ if (o->oTimer == 0) { ++ o->oFaceAnglePitch = 0; ++ o->oFaceAngleRoll = 0; ++ } //? missing else ++ o->oFaceAnglePitch += 0x800; ++ o->oFaceAngleRoll += 0x800; ++ if (!(o->oFaceAnglePitch & 0xFFFF)) ++ o->oSubAction++; ++ bowser_set_goal_invisible(); ++ break; ++ case 1: ++ cur_obj_init_animation_with_sound(9); ++ if (cur_obj_check_anim_frame(11)) { ++ o->oMoveAngleYaw = o->oBowserAngleToCentre; ++ o->oVelY = 150.0f; ++ o->oBowserUnk1AC = 0xFF; ++ o->oBowserUnkF8 = 0; ++ o->oSubAction++; ++ } else ++ bowser_set_goal_invisible(); ++ break; ++ case 2: ++ if (o->oPosY > o->oHomeY) { ++ o->oDragStrength = 0.0f; ++ if (o->oBowserDistToCentre < 2500.0f) { ++ if (absf(o->oFloorHeight - o->oHomeY) < 100.0f) ++ approach_f32_signed(&o->oForwardVel, 0, -5.0f); ++ else ++ cur_obj_forward_vel_approach_upward(150.0f, 2.0f); ++ } else ++ cur_obj_forward_vel_approach_upward(150.0f, 2.0f); ++ } ++ if (bowser_land()) { ++ o->oDragStrength = 10.0f; ++ o->oSubAction++; ++ if (sp2C == 0) ++ bowser_spawn_shockwave(); ++ else if (BITS) ++ o->oAction = 13; ++ if (BITFS) ++ o->oAction = 19; ++ } ++#ifndef VERSION_JP ++ bowser_reset_fallen_off_stage(); ++#else ++ if (o->oVelY < 0.0f && o->oPosY < o->oHomeY - 300.0f) { ++ o->oPosZ = 0.0f, o->oPosX = o->oPosZ; ++ o->oPosY = o->oHomeY + 2000.0f; ++ o->oVelY = 0.0f; ++ } ++#endif ++ break; ++ case 3: ++ if (cur_obj_check_if_near_animation_end()) { ++ o->oAction = 0; ++ o->oBowserUnkF4 &= ~0x10000; ++ cur_obj_extend_animation_if_at_end(); ++ } ++ break; ++ } ++ print_debug_bottom_up("sp %d", o->oForwardVel); ++} ++ ++void bowser_act_dance(void) { ++ if (is_item_in_array(o->oTimer, D_8032F514)) ++ cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK); ++ if (cur_obj_init_animation_and_check_if_near_end(10)) ++ o->oAction = 0; ++} ++ ++void bowser_spawn_grand_star_key(void) { ++ if (BITS) ++ gSecondCameraFocus = spawn_object(o, MODEL_STAR, bhvGrandStar); ++ else { ++ gSecondCameraFocus = spawn_object(o, MODEL_BOWSER_KEY, bhvBowserKey); ++ cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_KEY); ++ } ++ gSecondCameraFocus->oAngleVelYaw = o->oAngleVelYaw; ++} ++ ++void bowser_fly_back_dead(void) { ++ cur_obj_init_animation_with_sound(16); ++ if (BITS) ++ o->oForwardVel = -400.0f; ++ else ++ o->oForwardVel = -200.0f; ++ o->oVelY = 100.0f; ++ o->oMoveAngleYaw = o->oBowserAngleToCentre + 0x8000; ++ o->oBowserUnkF8 = 0; ++ o->oSubAction++; ++} ++ ++void bowser_dead_bounce(void) { ++ o->oBowserEyesShut = 1; ++ bowser_bounce(&o->oBowserUnkF8); ++ if (o->oMoveFlags & OBJ_MOVE_LANDED) ++ cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK); ++ if (o->oMoveFlags & OBJ_MOVE_ON_GROUND) { ++ o->oForwardVel = 0.0f; ++ o->oSubAction++; ++ } ++} ++ ++s32 bowser_dead_wait_for_mario(void) { ++ s32 ret = 0; ++ cur_obj_become_intangible(); ++ if (cur_obj_init_animation_and_check_if_near_end(17) && o->oDistanceToMario < 700.0f ++ && abs_angle_diff(gMarioObject->oMoveAngleYaw, o->oAngleToMario) > 0x6000) ++ ret = 1; ++ cur_obj_extend_animation_if_at_end(); ++ o->oBowserUnkF8 = 0; ++ return ret; ++} ++ ++s32 bowser_dead_twirl_into_trophy(void) { ++ s32 ret = 0; ++ if (o->header.gfx.scale[0] < 0.8) ++ o->oAngleVelYaw += 0x80; ++ if (o->header.gfx.scale[0] > 0.2) { ++ o->header.gfx.scale[0] = o->header.gfx.scale[0] - 0.02; ++ o->header.gfx.scale[2] = o->header.gfx.scale[2] - 0.02; ++ } else { ++ o->header.gfx.scale[1] = o->header.gfx.scale[1] - 0.01; ++ o->oVelY = 20.0f; ++ o->oGravity = 0.0f; ++ } ++ if (o->header.gfx.scale[1] < 0.5) ++ ret = 1; ++ o->oMoveAngleYaw += o->oAngleVelYaw; ++ if (o->oOpacity >= 3) ++ o->oOpacity -= 2; ++ return ret; ++} ++ ++void bowser_dead_hide(void) { ++ cur_obj_scale(0); ++ o->oForwardVel = 0; ++ o->oVelY = 0; ++ o->oGravity = 0; ++} ++ ++s32 bowser_dead_not_bits_end(void) { ++ s32 ret = 0; ++ if (o->oBowserUnkF8 < 2) { ++ if (o->oBowserUnkF8 == 0) { ++ seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40); ++ o->oBowserUnkF8++; ++ } ++ if (cur_obj_update_dialog(2, 18, sBowserDefeatedDialogText[o->oBehParams2ndByte], 0)) { ++ o->oBowserUnkF8++; ++ cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_EXPLODE); ++ seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60); ++ seq_player_fade_out(SEQ_PLAYER_LEVEL, 1); ++ } ++ } else if (bowser_dead_twirl_into_trophy()) { ++ bowser_dead_hide(); ++ spawn_triangle_break_particles(20, 116, 1.0f, 0); ++ bowser_spawn_grand_star_key(); ++ set_mario_npc_dialog(0); ++ ret = 1; ++ } ++ return ret; ++} ++ ++s32 bowser_dead_bits_end(void) { ++ UNUSED s32 unused; ++ s32 ret = 0; ++ s32 dialogID; ++ if (o->oBowserUnkF8 < 2) { ++ if (gHudDisplay.stars < 120) ++ dialogID = DIALOG_121; ++ else ++ dialogID = DIALOG_163; ++ if (o->oBowserUnkF8 == 0) { ++ seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40); ++ o->oBowserUnkF8++; ++ } ++ if (cur_obj_update_dialog(2, 18, dialogID, 0)) { ++ cur_obj_set_model(MODEL_BOWSER2); ++ seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60); ++ seq_player_fade_out(SEQ_PLAYER_LEVEL, 1); ++ bowser_spawn_grand_star_key(); ++ o->oBowserUnkF8++; ++ } ++ } else if (o->oOpacity > 4) ++ o->oOpacity -= 4; ++ else { ++ bowser_dead_hide(); ++ ret = 1; ++ } ++ return ret; ++} ++ ++void bowser_act_dead(void) { ++ switch (o->oSubAction) { ++ case 0: ++ bowser_fly_back_dead(); ++ break; ++ case 1: ++ bowser_dead_bounce(); ++ break; ++ case 2: ++ if (bowser_dead_wait_for_mario()) { ++ o->oBowserUnkF8 = 0; ++ if (BITS) ++ o->oSubAction = 10; ++ else { ++ o->activeFlags |= ACTIVE_FLAG_DITHERED_ALPHA; ++ o->oSubAction++; ++ } ++ } ++ break; ++ case 3: ++ if (bowser_dead_not_bits_end()) ++ o->oSubAction++; ++ break; ++ case 4: ++ break; ++ case 10: ++ if (bowser_dead_bits_end()) ++ o->oSubAction++; ++ break; ++ case 11: ++ break; ++ } ++} ++ ++void bowser_tilt_platform(struct Object *platform, s16 a1) { ++ s16 angle; ++ angle = o->oBowserAngleToCentre + 0x8000; ++ platform->oAngleVelPitch = coss(angle) * a1; ++ platform->oAngleVelRoll = -sins(angle) * a1; ++} ++ ++void bowser_act_ride_tilting_platform(void) { ++ struct Object *platform = cur_obj_nearest_object_with_behavior(bhvTiltingBowserLavaPlatform); ++ UNUSED s16 sp2A = o->oBowserAngleToCentre + 0x8000; ++ s16 sp28; ++ UNUSED s32 unused; ++ s32 i; ++ s32 sp1C; ++ if (platform == NULL) ++ o->oAction = 0; ++ else { ++ i = 0; ++ sp1C = 1; ++ while (D_8032F520[i][2] != 0) { ++ if (o->oTimer < D_8032F520[i][2]) { ++ sp28 = D_8032F520[i][1]; ++ if (D_8032F520[i][0] > 0) ++ sp28 = (D_8032F520[i][2] - o->oTimer - 1) * sp28; ++ else ++ sp28 = (o->oTimer - D_8032F520[i - 1][2]) * sp28; ++ bowser_tilt_platform(platform, sp28); ++ if (sp28 != 0) ++ play_sound(SOUND_ENV_UNKNOWN4, platform->header.gfx.cameraToObject); ++ sp1C = 0; ++ break; ++ } ++ i++; ++ } ++ if (sp1C) { ++ o->oAction = 0; ++ platform->oAngleVelPitch = 0; ++ platform->oAngleVelRoll = 0; ++ platform->oFaceAnglePitch = 0; ++ platform->oFaceAngleRoll = 0; ++ } ++ } ++ cur_obj_extend_animation_if_at_end(); ++} ++ ++s32 bowser_check_fallen_off_stage(void) // bowser off stage? ++{ ++ if (o->oAction != 2 && o->oAction != 19) { ++ if (o->oPosY < o->oHomeY - 1000.0f) ++ return 1; ++ if (o->oMoveFlags & OBJ_MOVE_LANDED) { ++ if (o->oFloorType == SURFACE_BURNING) ++ return 1; ++ if (o->oFloorType == SURFACE_DEATH_PLANE) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++void (*sBowserActions[])(void) = { bowser_act_default, bowser_act_thrown_dropped, bowser_act_jump_onto_stage, bowser_act_dance, ++ bowser_act_dead, bowser_act_text_wait, bowser_act_intro_walk, bowser_act_charge_mario, ++ bowser_act_spit_fire_into_sky, bowser_act_spit_fire_onto_floor, bowser_act_hit_edge, bowser_act_turn_from_edge, ++ bowser_act_hit_mine, bowser_act_jump, bowser_act_walk_to_mario, bowser_act_breath_fire, ++ bowser_act_teleport, bowser_act_jump_towards_mario, bowser_act_unused_slow_walk, bowser_act_ride_tilting_platform }; ++struct SoundState D_8032F5B8[] = { { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 1, 0, -1, SOUND_OBJ_BOWSER_WALK }, ++ { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR }, ++ { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 1, 20, 40, SOUND_OBJ_BOWSER_WALK }, ++ { 1, 20, -1, SOUND_OBJ_BOWSER_WALK }, ++ { 1, 20, 40, SOUND_OBJ_BOWSER_WALK }, ++ { 1, 0, -1, SOUND_OBJ_BOWSER_TAIL_PICKUP }, ++ { 1, 0, -1, SOUND_OBJ_BOWSER_DEFEATED }, ++ { 1, 8, -1, SOUND_OBJ_BOWSER_WALK }, ++ { 1, 8, 17, SOUND_OBJ_BOWSER_WALK }, ++ { 1, 8, -10, SOUND_OBJ_BOWSER_WALK }, ++ { 0, 0, 0, NO_SOUND }, ++ { 1, 5, -1, SOUND_OBJ_FLAME_BLOWN }, ++ { 0, 0, 0, NO_SOUND }, ++ { 0, 0, 0, NO_SOUND }, ++ { 1, 0, -1, SOUND_OBJ_BOWSER_TAIL_PICKUP }, ++ { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR } }; ++s8 D_8032F690[4] = { 0, 0, 1, 0 }; ++s8 D_8032F694[4] = { 1, 1, 3, 0 }; ++extern u8 bowser_3_seg7_collision_07004B94[]; ++extern u8 bowser_3_seg7_collision_07004C18[]; ++extern u8 bowser_3_seg7_collision_07004C9C[]; ++extern u8 bowser_3_seg7_collision_07004D20[]; ++extern u8 bowser_3_seg7_collision_07004DA4[]; ++extern u8 bowser_3_seg7_collision_07004E28[]; ++extern u8 bowser_3_seg7_collision_07004EAC[]; ++extern u8 bowser_3_seg7_collision_07004F30[]; ++extern u8 bowser_3_seg7_collision_07004FB4[]; ++extern u8 bowser_3_seg7_collision_07005038[]; ++struct Struct8032F698 D_8032F698[] = { { NULL, 0, 0, 0, 0 }, ++ { bowser_3_seg7_collision_07004B94, -800, -1000, -20992, 0 }, ++ { bowser_3_seg7_collision_07004C18, -1158, 390, -18432, 0 }, ++ { bowser_3_seg7_collision_07004C9C, -1158, 390, -7680, 0 }, ++ { bowser_3_seg7_collision_07004D20, 0, 1240, -6144, 0 }, ++ { bowser_3_seg7_collision_07004DA4, 0, 1240, 6144, 0 }, ++ { bowser_3_seg7_collision_07004E28, 1158, 390, 7680, 0 }, ++ { bowser_3_seg7_collision_07004EAC, 1158, 390, 18432, 0 }, ++ { bowser_3_seg7_collision_07004F30, 800, -1000, 20992, 0 }, ++ { bowser_3_seg7_collision_07004FB4, 800, -1000, -31744, 0 }, ++ { bowser_3_seg7_collision_07005038, -800, -1000, 31744, 0 } }; ++ ++void bowser_free_update(void) { ++ struct Surface *floor; ++ struct Object *platform; ++ UNUSED f32 floorHeight; ++ if ((platform = o->platform) != NULL) ++ apply_platform_displacement(FALSE, platform); ++ o->oBowserUnk10E = 0; ++ cur_obj_update_floor_and_walls(); ++ cur_obj_call_action_function(sBowserActions); ++ cur_obj_move_standard(-78); ++ if (bowser_check_fallen_off_stage()) ++ o->oAction = 2; // bowser go home? ++ floorHeight = find_floor(o->oPosX, o->oPosY, o->oPosZ, &floor); ++ if ((floor != NULL) && (floor->object != 0)) ++ o->platform = floor->object; ++ else ++ o->platform = NULL; ++ exec_anim_sound_state(D_8032F5B8); ++} ++ ++void bowser_held_update(void) { ++ o->oBowserUnkF4 &= ~0x20000; ++ cur_obj_become_intangible(); ++ switch (o->oBowserUnk10E) { ++ case 0: ++ cur_obj_play_sound_2(SOUND_OBJ_BOWSER_TAIL_PICKUP); ++ cur_obj_unrender_and_reset_state(3, 1); ++ o->oBowserUnk10E++; ++ break; ++ case 1: ++ if (cur_obj_check_if_near_animation_end()) { ++ cur_obj_init_animation_with_sound(2); ++ o->oBowserUnk10E++; ++ } ++ break; ++ case 2: ++ break; ++ } ++ o->oMoveFlags = 0; ++ o->oBowserHeldAnglePitch = gMarioObject->oMoveAnglePitch; ++ o->oBowserHeldAngleVelYaw = gMarioObject->oAngleVelYaw; ++ o->oMoveAngleYaw = gMarioObject->oMoveAngleYaw; ++} ++ ++void bowser_thrown_dropped_update(void) { ++ f32 sp1C; ++ o->oBowserUnk10E = 0; ++ cur_obj_get_thrown_or_placed(1.0f, 1.0f, 1); ++ sp1C = o->oBowserHeldAngleVelYaw / 3000.0 * 70.0f; ++ if (sp1C < 0.0f) ++ sp1C = -sp1C; ++ if (sp1C > 90.0f) ++ sp1C *= 2.5; // > 90 => get bigger? ++ o->oForwardVel = coss(o->oBowserHeldAnglePitch) * sp1C; ++ o->oVelY = -sins(o->oBowserHeldAnglePitch) * sp1C; ++ cur_obj_become_intangible(); ++ o->prevObj->oAction = 1; // not sure what prevObj is ++ o->prevObj->oTimer = 0; ++ o->prevObj->oSubAction = 0; ++ o->oTimer = 0; ++ o->oSubAction = 0; ++} ++ ++void bhv_bowser_loop(void) { ++ s16 angleToMario; // AngleToMario from Bowser's perspective ++ s16 angleToCentre; // AngleToCentre from Bowser's perspective ++ ++ o->oBowserDistToCentre = sqrtf(o->oPosX * o->oPosX + o->oPosZ * o->oPosZ); ++ o->oBowserAngleToCentre = atan2s(0.0f - o->oPosZ, 0.0f - o->oPosX); ++ angleToMario = abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario); ++ angleToCentre = abs_angle_diff(o->oMoveAngleYaw, o->oBowserAngleToCentre); ++ o->oBowserUnkF4 &= ~0xFF; ++ if (angleToMario < 0x2000) ++ o->oBowserUnkF4 |= 2; ++ if (angleToCentre < 0x3800) ++ o->oBowserUnkF4 |= 4; ++ if (o->oBowserDistToCentre < 1000.0f) ++ o->oBowserUnkF4 |= 0x10; ++ if (o->oDistanceToMario < 850.0f) ++ o->oBowserUnkF4 |= 8; ++ switch (o->oHeldState) { ++ case HELD_FREE: ++ bowser_free_update(); ++ break; ++ case HELD_HELD: ++ bowser_held_update(); ++ break; ++ case HELD_THROWN: ++ bowser_thrown_dropped_update(); ++ break; ++ case HELD_DROPPED: ++ bowser_thrown_dropped_update(); ++ break; ++ } ++ cur_obj_align_gfx_with_floor(); ++ if (o->oAction != 4) ++ if (o->oBowserUnk1AC != o->oOpacity) { ++ if (o->oBowserUnk1AC > o->oOpacity) { ++ o->oOpacity += 20; ++ if (o->oOpacity >= 0x100) ++ o->oOpacity = 0xFF; ++ } else { ++ o->oOpacity -= 20; ++ if (o->oOpacity < 0) ++ o->oOpacity = 0; ++ } ++ } ++} ++ ++void bhv_bowser_init(void) { ++ s32 level; // 0 is dw, 1 is fs, 2 is sky ++ o->oBowserUnk110 = 1; ++ o->oOpacity = 0xFF; ++ o->oBowserUnk1AC = 0xFF; ++ if (gCurrLevelNum == LEVEL_BOWSER_2) ++ level = 1; ++ else if (gCurrLevelNum == LEVEL_BOWSER_3) ++ level = 2; ++ else ++ level = 0; ++ o->oBehParams2ndByte = level; ++ o->oBowserUnk1B2 = D_8032F690[level]; ++ o->oHealth = D_8032F694[level]; ++ cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_INIT); ++ o->oAction = 5; ++ o->oBowserUnk1AE = 0; ++ o->oBowserEyesShut = 0; ++} ++ ++#undef BITDW ++#undef BITFS ++#undef BITS ++ ++Gfx *geo_update_body_rot_from_parent(s32 run, UNUSED struct GraphNode *node, Mat4 mtx) { ++ Mat4 sp20; ++ struct Object *sp1C; ++ ++ if (run == TRUE) { ++ sp1C = (struct Object *) gCurGraphNodeObject; ++ if (sp1C->prevObj != NULL) { ++ create_transformation_from_matrices(sp20, mtx, *gCurGraphNodeCamera->matrixPtr); ++ obj_update_pos_from_parent_transformation(sp20, sp1C->prevObj); ++ obj_set_gfx_pos_from_pos(sp1C->prevObj); ++ } ++ } ++ return NULL; ++} ++ ++void bowser_open_eye_switch(struct Object *a0, struct GraphNodeSwitchCase *switchCase) { ++ s32 sp1C; ++ s16 sp1A; ++ sp1A = abs_angle_diff(a0->oMoveAngleYaw, a0->oAngleToMario); ++ sp1C = switchCase->selectedCase; ++ switch (sp1C) { ++ case 0: ++ if (sp1A > 0x2000) { ++ if (a0->oAngleVelYaw > 0) ++ switchCase->selectedCase = 5; ++ if (a0->oAngleVelYaw < 0) ++ switchCase->selectedCase = 3; ++ } ++ if (a0->oBowserUnk1AE > 50) ++ switchCase->selectedCase = 1; ++ break; ++ case 1: ++ if (a0->oBowserUnk1AE > 2) ++ switchCase->selectedCase = 2; ++ break; ++ case 2: ++ if (a0->oBowserUnk1AE > 2) ++ switchCase->selectedCase = 9; ++ break; ++ case 9: ++ if (a0->oBowserUnk1AE > 2) ++ switchCase->selectedCase = 0; ++ break; ++ case 5: ++ if (a0->oBowserUnk1AE > 2) { ++ switchCase->selectedCase = 6; ++ if (a0->oAngleVelYaw <= 0) ++ switchCase->selectedCase = 0; ++ } ++ break; ++ case 6: ++ if (a0->oAngleVelYaw <= 0) ++ switchCase->selectedCase = 5; ++ break; ++ case 3: ++ if (a0->oBowserUnk1AE > 2) { ++ switchCase->selectedCase = 4; ++ if (a0->oAngleVelYaw >= 0) ++ switchCase->selectedCase = 0; ++ } ++ break; ++ case 4: ++ if (a0->oAngleVelYaw >= 0) ++ switchCase->selectedCase = 3; ++ break; ++ default: ++ switchCase->selectedCase = 0; ++ } ++ if (switchCase->selectedCase != sp1C) ++ a0->oBowserUnk1AE = -1; ++} ++ ++/** Geo switch for controlling the state of bowser's eye direction and open/closed ++ * state. Checks whether oBowserEyesShut is TRUE and closes eyes if so and processes ++ * direction otherwise. ++ */ ++Gfx *geo_switch_bowser_eyes(s32 run, struct GraphNode *node, UNUSED Mat4 *mtx) { ++ UNUSED s16 sp36; ++ UNUSED s32 unused; ++ struct Object *obj = (struct Object *) gCurGraphNodeObject; ++ struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; ++ if (run == TRUE) { ++ if (gCurGraphNodeHeldObject != NULL) ++ obj = gCurGraphNodeHeldObject->objNode; ++ switch (sp36 = obj->oBowserEyesShut) { ++ case 0: // eyes open, handle eye looking direction ++ bowser_open_eye_switch(obj, switchCase); ++ break; ++ case 1: // eyes closed, blinking ++ switchCase->selectedCase = 2; ++ break; ++ } ++ obj->oBowserUnk1AE++; ++ } ++ return NULL; ++} ++ ++Gfx *geo_bits_bowser_coloring(s32 run, struct GraphNode *node, UNUSED s32 a2) { ++ Gfx *sp2C = NULL; ++ Gfx *sp28; ++ struct Object *sp24; ++ struct GraphNodeGenerated *sp20; ++ ++ if (run == 1) { ++ sp24 = (struct Object *) gCurGraphNodeObject; ++ sp20 = (struct GraphNodeGenerated *) node; ++ if (gCurGraphNodeHeldObject != 0) ++ sp24 = gCurGraphNodeHeldObject->objNode; ++ if (sp24->oOpacity == 0xFF) ++ sp20->fnNode.node.flags = (sp20->fnNode.node.flags & 0xFF) | GRAPH_NODE_TYPE_FUNCTIONAL; ++ else ++ sp20->fnNode.node.flags = (sp20->fnNode.node.flags & 0xFF) | (GRAPH_NODE_TYPE_FUNCTIONAL | GRAPH_NODE_TYPE_400); ++ sp28 = sp2C = alloc_display_list(2 * sizeof(Gfx)); ++ ++ if (sp24->oBowserUnk1B2 != 0) { ++ gSPClearGeometryMode(sp28++, G_LIGHTING); ++ } ++ gSPEndDisplayList(sp28); ++ } ++ return sp2C; ++} ++ ++void falling_bowser_plat_act_0(void) { ++ o->oPlatformUnkF8 = cur_obj_nearest_object_with_behavior(bhvBowser); ++ obj_set_collision_data(o, D_8032F698[o->oBehParams2ndByte].unk0); ++ if (o->oPlatformUnkF8 != 0) ++ o->oAction = 1; ++} ++ ++void falling_bowser_plat_act_1(void) { ++ UNUSED s32 unused; ++ struct Object *sp0 = o->oPlatformUnkF8; ++ if (sp0->platform == o) ++ if (sp0->oAction == 13 && sp0->oBowserUnkF4 & 0x10000) ++ o->oAction = 2; ++ if (sp0->oHealth == 1 && (sp0->oAction == 3 || sp0->oHeldState != HELD_FREE)) ++ o->oSubAction = 1; ++ if (o->oSubAction == 0) ++ o->oPlatformUnkFC = 0; ++ else { ++ if ((gDebugInfo[4][6] + 20) * (o->oBehParams2ndByte - 1) < o->oPlatformUnkFC) ++ o->oAction = 2; ++ o->oPlatformUnkFC++; ++ } ++} ++ ++void falling_bowser_plat_act_2(void) { ++ Vec3f sp24; ++ s16 sp22; ++ f32 sp1C; ++ UNUSED struct Object *sp18 = o->oPlatformUnkF8; ++ if (o->oTimer == 0 || o->oTimer == 22) ++ cur_obj_play_sound_2(SOUND_GENERAL_BOWSER_PLATFORM_2); ++ if (o->oTimer < 22) { ++ set_environmental_camera_shake(SHAKE_ENV_FALLING_BITS_PLAT); ++ o->oVelY = 8.0f; ++ o->oGravity = 0.0f; ++ } else ++ o->oGravity = -4.0f; ++ if ((o->oTimer & 1) == 0 && o->oTimer < 14) { ++ sp22 = D_8032F698[o->oBehParams2ndByte].unk3 + (gDebugInfo[4][1] << 8); ++ sp1C = -(o->oTimer / 2) * 290 + 1740; ++ vec3f_copy_2(sp24, &o->oPosX); ++ o->oPosX = D_8032F698[o->oBehParams2ndByte].unk1 + sins(sp22 + 5296) * sp1C; ++ o->oPosZ = D_8032F698[o->oBehParams2ndByte].unk2 + coss(sp22 + 5296) * sp1C; ++ o->oPosY = 307.0f; ++ spawn_mist_particles_variable(4, 0, 100.0f); ++ o->oPosX = D_8032F698[o->oBehParams2ndByte].unk1 + sins(sp22 - 5296) * sp1C; ++ o->oPosZ = D_8032F698[o->oBehParams2ndByte].unk2 + coss(sp22 - 5296) * sp1C; ++ spawn_mist_particles_variable(4, 0, 100); ++ vec3f_copy_2(&o->oPosX, sp24); ++ } ++ cur_obj_move_using_fvel_and_gravity(); ++ if (o->oTimer > 300) ++ obj_mark_for_deletion(o); ++} ++ ++void (*sFallingBowserPlatformActions[])(void) = { falling_bowser_plat_act_0, ++ falling_bowser_plat_act_1, ++ falling_bowser_plat_act_2 }; ++ ++struct ObjectHitbox sGrowingBowserFlameHitbox = { ++ /* interactType: */ INTERACT_FLAME, ++ /* downOffset: */ 20, ++ /* damageOrCoinValue: */ 1, ++ /* health: */ 0, ++ /* numLootCoins: */ 0, ++ /* radius: */ 10, ++ /* height: */ 40, ++ /* hurtboxRadius: */ 0, ++ /* hurtboxHeight: */ 0, ++}; ++ ++struct ObjectHitbox sBowserFlameHitbox = { ++ /* interactType: */ INTERACT_FLAME, ++ /* downOffset: */ 0, ++ /* damageOrCoinValue: */ 1, ++ /* health: */ 0, ++ /* numLootCoins: */ 0, ++ /* radius: */ 10, ++ /* height: */ 40, ++ /* hurtboxRadius: */ 0, ++ /* hurtboxHeight: */ 0, ++}; ++ ++f32 D_8032F748[] = { -8.0f, -6.0f, -3.0f }; ++ ++void bhv_falling_bowser_platform_loop(void) { ++ cur_obj_call_action_function(sFallingBowserPlatformActions); ++} ++ ++void bowser_flame_despawn(void) { ++ obj_mark_for_deletion(o); ++ spawn_object_with_scale(o, MODEL_NONE, bhvBlackSmokeUpward, 1.0f); ++ if (random_float() < 0.1) ++ spawn_object(o, MODEL_YELLOW_COIN, bhvTemporaryYellowCoin); ++} ++ ++s32 bowser_flame_should_despawn(s32 maxTime) { ++ if (maxTime < o->oTimer) ++ return 1; ++ if (o->oFloorType == SURFACE_BURNING) ++ return 1; ++ if (o->oFloorType == SURFACE_DEATH_PLANE) ++ return 1; ++ return 0; ++} ++ ++void bhv_flame_bowser_init(void) { ++ o->oAnimState = (s32)(random_float() * 10.0f); ++ o->oMoveAngleYaw = random_u16(); ++ if (random_float() < 0.2) ++ o->oVelY = 80.0f; ++ else ++ o->oVelY = 20.0f; ++ o->oForwardVel = 10.0f; ++ o->oGravity = -1.0f; ++ o->oFlameScale = random_float() + 1.0f; ++} ++ ++void bhv_flame_large_burning_out_init(void) { ++ o->oAnimState = (s32)(random_float() * 10.0f); ++ o->oMoveAngleYaw = random_u16(); ++ o->oVelY = 10.0f; ++ o->oForwardVel = 0.0f; ++ o->oFlameScale = 7.0f; ++} ++ ++void bowser_flame_move(void) { ++ s32 sp4; ++ sp4 = ((o->oFlameSpeedTimerOffset + gGlobalTimer) & 0x3F) << 10; ++ o->oPosX += sins(o->oMoveAngleYaw) * sins(sp4) * 4.0f; ++ o->oPosZ += coss(o->oMoveAngleYaw) * sins(sp4) * 4.0f; ++} ++ ++void bhv_flame_bowser_loop(void) { ++ cur_obj_update_floor_and_walls(); ++ cur_obj_move_standard(78); ++ if (o->oVelY < -4.0f) ++ o->oVelY = -4.0f; ++ if (o->oAction == 0) { ++ cur_obj_become_intangible(); ++ bowser_flame_move(); ++ if (o->oMoveFlags & OBJ_MOVE_LANDED) { ++ o->oAction++; ++ if (cur_obj_has_behavior(bhvFlameLargeBurningOut)) ++ o->oFlameScale = 8.0f; ++ else ++ o->oFlameScale = random_float() * 2 + 6.0f; ++ o->oForwardVel = 0; ++ o->oVelY = 0; ++ o->oGravity = 0; ++ } ++ } else { ++ cur_obj_become_tangible(); ++ if (o->oTimer > o->oFlameScale * 10 + 5.0f) { ++ o->oFlameScale -= 0.15; ++ if (o->oFlameScale <= 0) ++ bowser_flame_despawn(); ++ } ++ } ++ cur_obj_scale(o->oFlameScale); ++ o->oGraphYOffset = o->header.gfx.scale[1] * 14.0f; ++ obj_set_hitbox(o, &sBowserFlameHitbox); ++} ++ ++void bhv_flame_moving_forward_growing_init(void) { ++ o->oForwardVel = 30.0f; ++ obj_translate_xz_random(o, 80.0f); ++ o->oAnimState = (s32)(random_float() * 10.0f); ++ o->oFlameScale = 3.0f; ++} ++ ++void bhv_flame_moving_forward_growing_loop(void) { ++ UNUSED s32 unused; ++ UNUSED struct Object *sp18; ++ obj_set_hitbox(o, &sGrowingBowserFlameHitbox); ++ o->oFlameScale = o->oFlameScale + 0.5; ++ cur_obj_scale(o->oFlameScale); ++ if (o->oMoveAnglePitch > 0x800) ++ o->oMoveAnglePitch -= 0x200; ++ cur_obj_set_pos_via_transform(); ++ cur_obj_update_floor_height(); ++ if (o->oFlameScale > 30.0f) ++ obj_mark_for_deletion(o); ++ if (o->oPosY < o->oFloorHeight) { ++ o->oPosY = o->oFloorHeight; ++ sp18 = spawn_object(o, MODEL_RED_FLAME, bhvFlameBowser); ++ obj_mark_for_deletion(o); ++ } ++} ++ ++void bhv_flame_floating_landing_init(void) { ++ o->oAnimState = (s32)(random_float() * 10.0f); ++ o->oMoveAngleYaw = random_u16(); ++ if (o->oBehParams2ndByte != 0) ++ o->oForwardVel = random_float() * 5.0f; ++ else ++ o->oForwardVel = random_float() * 70.0f; ++ o->oVelY = random_float() * 20.0f; ++ o->oGravity = -1.0f; ++ o->oFlameSpeedTimerOffset = random_float() * 64.0f; ++} ++ ++void bhv_flame_floating_landing_loop(void) { ++ UNUSED s32 unused; ++ cur_obj_update_floor_and_walls(); ++ cur_obj_move_standard(0x4e); ++ bowser_flame_move(); ++ if (bowser_flame_should_despawn(900)) ++ obj_mark_for_deletion(o); ++ if (o->oVelY < D_8032F748[o->oBehParams2ndByte]) ++ o->oVelY = D_8032F748[o->oBehParams2ndByte]; ++ if (o->oMoveFlags & OBJ_MOVE_LANDED) { ++ if (o->oBehParams2ndByte == 0) ++ spawn_object(o, MODEL_RED_FLAME, bhvFlameLargeBurningOut); ++ else ++ spawn_object(o, MODEL_NONE, bhvBlueFlamesGroup); //? wonder if they meant MODEL_BLUE_FLAME? ++ obj_mark_for_deletion(o); ++ } ++ o->oGraphYOffset = o->header.gfx.scale[1] * 14.0f; ++} ++ ++void bhv_blue_bowser_flame_init(void) { ++ obj_translate_xz_random(o, 80.0f); ++ o->oAnimState = (s32)(random_float() * 10.0f); ++ o->oVelY = 7.0f; ++ o->oForwardVel = 35.0f; ++ o->oFlameScale = 3.0f; ++ o->oFlameUnkFC = random_float() * 0.5; ++ o->oGravity = 1.0f; ++ o->oFlameSpeedTimerOffset = (s32)(random_float() * 64.0f); ++} ++ ++void bhv_blue_bowser_flame_loop(void) { ++ s32 i; ++ obj_set_hitbox(o, &sGrowingBowserFlameHitbox); ++ if (o->oFlameScale < 16.0f) ++ o->oFlameScale = o->oFlameScale + 0.5; ++ cur_obj_scale(o->oFlameScale); ++ cur_obj_update_floor_and_walls(); ++ cur_obj_move_standard(0x4e); ++ if (o->oTimer > 0x14) { ++ if (o->oBehParams2ndByte == 0) ++ for (i = 0; i < 3; i++) ++ spawn_object_relative_with_scale(0, 0, 0, 0, 5.0f, o, MODEL_RED_FLAME, ++ bhvFlameFloatingLanding); ++ else { ++ spawn_object_relative_with_scale(1, 0, 0, 0, 8.0f, o, MODEL_BLUE_FLAME, ++ bhvFlameFloatingLanding); ++ spawn_object_relative_with_scale(2, 0, 0, 0, 8.0f, o, MODEL_BLUE_FLAME, ++ bhvFlameFloatingLanding); ++ } ++ obj_mark_for_deletion(o); ++ } ++} ++ ++void bhv_flame_bouncing_init(void) { ++ o->oAnimState = (s32)(random_float() * 10.0f); ++ o->oVelY = 30.0f; ++ o->oForwardVel = 20.0f; ++ o->oFlameScale = o->header.gfx.scale[0]; ++ o->oFlameSpeedTimerOffset = (s32)(random_float() * 64.0f); ++} ++ ++void bhv_flame_bouncing_loop(void) { ++ struct Object *bowser; ++ if (o->oTimer == 0) ++ o->oFlameBowser = cur_obj_nearest_object_with_behavior(bhvBowser); ++ bowser = o->oFlameBowser; ++ o->oForwardVel = 15.0f; ++ o->oBounciness = -1.0f; ++ cur_obj_scale(o->oFlameScale); ++ obj_set_hitbox(o, &sGrowingBowserFlameHitbox); ++ cur_obj_update_floor_and_walls(); ++ cur_obj_move_standard(78); ++ if (bowser_flame_should_despawn(300)) ++ obj_mark_for_deletion(o); ++ if (bowser != NULL) ++ if (bowser->oHeldState == 0) ++ if (lateral_dist_between_objects(o, bowser) < 300.0f) ++ obj_mark_for_deletion(o); ++} ++ ++void bhv_blue_flames_group_loop(void) { ++ struct Object *flame; ++ s32 i; ++ if (o->oTimer == 0) { ++ o->oMoveAngleYaw = obj_angle_to_object(o, gMarioObject); ++ o->oBlueFlameNextScale = 5.0f; ++ } ++ if (o->oTimer < 16) { ++ if ((o->oTimer & 1) == 0) { ++ for (i = 0; i < 3; i++) { ++ flame = spawn_object(o, MODEL_BLUE_FLAME, bhvFlameBouncing); ++ flame->oMoveAngleYaw += i * 0x5555; ++ flame->header.gfx.scale[0] = o->oBlueFlameNextScale; ++ } ++ o->oBlueFlameNextScale -= 0.5; ++ } ++ } else ++ obj_mark_for_deletion(o); ++} +diff --git a/src/game/behaviors/bowser.inc.c.rej b/src/game/behaviors/bowser.inc.c.rej +new file mode 100644 +index 0000000..f47808d +--- /dev/null ++++ b/src/game/behaviors/bowser.inc.c.rej +@@ -0,0 +1,11 @@ ++--- src/game/behaviors/bowser.inc.c +++++ src/game/behaviors/bowser.inc.c ++@@ -1035,7 +1037,7 @@ void bowser_free_update(void) { ++ struct Object *platform; ++ UNUSED f32 floorHeight; ++ if ((platform = o->platform) != NULL) ++- apply_platform_displacement(0, platform); +++ apply_platform_displacement(&sBowserDisplacementInfo, &o->oPosX, &o->oFaceAngleYaw, platform); ++ o->oBowserUnk10E = 0; ++ cur_obj_update_floor_and_walls(); ++ cur_obj_call_action_function(sBowserActions); + +diff --git a/src/game/behaviors/tilting_inverted_pyramid.inc.c b/src/game/behaviors/tilting_inverted_pyramid.inc.c +index ebce64f..29136e9 100644 +--- a/src/game/behaviors/tilting_inverted_pyramid.inc.c ++++ b/src/game/behaviors/tilting_inverted_pyramid.inc.c +@@ -131,7 +131,7 @@ void bhv_tilting_inverted_pyramid_loop(void) { + mx += posAfterRotation[0] - posBeforeRotation[0]; + my += posAfterRotation[1] - posBeforeRotation[1]; + mz += posAfterRotation[2] - posBeforeRotation[2]; +- set_mario_pos(mx, my, mz); ++ //set_mario_pos(mx, my, mz); + } + + o->header.gfx.throwMatrix = transform; +diff --git a/src/game/platform_displacement.c b/src/game/platform_displacement.c +index 9153bc4..1e8cd86 100644 +--- a/src/game/platform_displacement.c ++++ b/src/game/platform_displacement.c +@@ -8,6 +8,7 @@ + #include "object_list_processor.h" + #include "platform_displacement.h" + #include "types.h" ++#include "sm64.h" + + u16 D_8032FEC0 = 0; + +@@ -84,98 +85,144 @@ void set_mario_pos(f32 x, f32 y, f32 z) { + gMarioStates[0].pos[2] = z; + } + +-/** +- * Apply one frame of platform rotation to Mario or an object using the given +- * platform. If isMario is false, use gCurrentObject. +- */ +-void apply_platform_displacement(u32 isMario, struct Object *platform) { +- f32 x; +- f32 y; +- f32 z; +- f32 platformPosX; +- f32 platformPosY; +- f32 platformPosZ; +- Vec3f currentObjectOffset; +- Vec3f relativeOffset; +- Vec3f newObjectOffset; +- Vec3s rotation; +- UNUSED s16 unused1; +- UNUSED s16 unused2; +- UNUSED s16 unused3; +- f32 displaceMatrix[4][4]; +- +- rotation[0] = platform->oAngleVelPitch; +- rotation[1] = platform->oAngleVelYaw; +- rotation[2] = platform->oAngleVelRoll; +- +- if (isMario) { +- D_8032FEC0 = 0; +- get_mario_pos(&x, &y, &z); +- } else { +- x = gCurrentObject->oPosX; +- y = gCurrentObject->oPosY; +- z = gCurrentObject->oPosZ; +- } +- +- x += platform->oVelX; +- z += platform->oVelZ; +- +- if (rotation[0] != 0 || rotation[1] != 0 || rotation[2] != 0) { +- unused1 = rotation[0]; +- unused2 = rotation[2]; +- unused3 = platform->oFaceAngleYaw; ++static struct PlatformDisplacementInfo sMarioDisplacementInfo; ++static Vec3f sMarioAmountDisplaced; + +- if (isMario) { +- gMarioStates[0].faceAngle[1] += rotation[1]; +- } ++extern s32 gGlobalTimer; + +- platformPosX = platform->oPosX; +- platformPosY = platform->oPosY; +- platformPosZ = platform->oPosZ; +- +- currentObjectOffset[0] = x - platformPosX; +- currentObjectOffset[1] = y - platformPosY; +- currentObjectOffset[2] = z - platformPosZ; +- +- rotation[0] = platform->oFaceAnglePitch - platform->oAngleVelPitch; +- rotation[1] = platform->oFaceAngleYaw - platform->oAngleVelYaw; +- rotation[2] = platform->oFaceAngleRoll - platform->oAngleVelRoll; +- +- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); +- linear_mtxf_transpose_mul_vec3f(displaceMatrix, relativeOffset, currentObjectOffset); ++/** ++ * Upscale or downscale a vector by another vector. ++ */ ++static void scale_vec3f(Vec3f dst, Vec3f src, Vec3f scale, u32 doInverted) { ++ if (doInverted) { ++ dst[0] = src[0] / scale[0]; ++ dst[1] = src[1] / scale[1]; ++ dst[2] = src[2] / scale[2]; ++ } else { ++ dst[0] = src[0] * scale[0]; ++ dst[1] = src[1] * scale[1]; ++ dst[2] = src[2] * scale[2]; ++ } ++} + +- rotation[0] = platform->oFaceAnglePitch; +- rotation[1] = platform->oFaceAngleYaw; +- rotation[2] = platform->oFaceAngleRoll; ++/** ++ * Apply one frame of platform displacement to Mario or an object using the given ++ * platform. ++ */ ++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform) { ++ Vec3f platformPos; ++ Vec3f posDifference; ++ Vec3f yawVec; ++ Vec3f scaledPos; ++ // Determine how much Mario turned on his own since last frame ++ s16 yawDifference = *yaw - displaceInfo->prevYaw; ++ ++ // Avoid a crash if the platform unloaded its collision while stood on ++ if (platform->header.gfx.throwMatrix == NULL) return; ++ ++ vec3f_copy(platformPos, (*platform->header.gfx.throwMatrix)[3]); ++ ++ // Determine how far Mario moved on his own since last frame ++ vec3f_copy(posDifference, pos); ++ vec3f_sub(posDifference, displaceInfo->prevPos); ++ ++ if ((platform == displaceInfo->prevPlatform) && (gGlobalTimer == displaceInfo->prevTimer + 1)) { ++ // Transform from relative positions to world positions ++ scale_vec3f(scaledPos, displaceInfo->prevTransformedPos, platform->header.gfx.scale, FALSE); ++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, pos, scaledPos); ++ ++ // Add on how much Mario moved in the previous frame ++ vec3f_add(pos, posDifference); ++ ++ // Calculate new yaw ++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, yawVec, displaceInfo->prevTransformedYawVec); ++ *yaw = atan2s(yawVec[2], yawVec[0]) + yawDifference; ++ } else { ++ // First frame of standing on the platform, don't calculate a new position ++ vec3f_sub(pos, platformPos); ++ } ++ ++ // Transform from world positions to relative positions for use next frame ++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, scaledPos, pos); ++ scale_vec3f(displaceInfo->prevTransformedPos, scaledPos, platform->header.gfx.scale, TRUE); ++ vec3f_add(pos, platformPos); ++ ++ // If the object is Mario, set inertia ++ if (pos == gMarioState->pos) { ++ vec3f_copy(sMarioAmountDisplaced, pos); ++ vec3f_sub(sMarioAmountDisplaced, displaceInfo->prevPos); ++ vec3f_sub(sMarioAmountDisplaced, posDifference); ++ ++ // Make sure inertia isn't set on the first frame otherwise the previous value isn't cleared ++ if ((platform != displaceInfo->prevPlatform) || (gGlobalTimer != displaceInfo->prevTimer + 1)) { ++ vec3f_set(sMarioAmountDisplaced, 0.f, 0.f, 0.f); ++ } ++ } ++ ++ // Update info for next frame ++ // Update position ++ vec3f_copy(displaceInfo->prevPos, pos); ++ ++ // Set yaw info ++ vec3f_set(yawVec, sins(*yaw), 0, coss(*yaw)); ++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, displaceInfo->prevTransformedYawVec, yawVec); ++ displaceInfo->prevYaw = *yaw; ++ ++ // Update platform and timer ++ displaceInfo->prevPlatform = platform; ++ displaceInfo->prevTimer = gGlobalTimer; ++} + +- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); +- linear_mtxf_mul_vec3f(displaceMatrix, newObjectOffset, relativeOffset); ++// Doesn't change in the code, set this to FALSE if you don't want inertia ++u8 gDoInertia = TRUE; + +- x = platformPosX + newObjectOffset[0]; +- y = platformPosY + newObjectOffset[1]; +- z = platformPosZ + newObjectOffset[2]; +- } ++static u8 sShouldApplyInertia = FALSE; ++static u8 sInertiaFirstFrame = FALSE; + +- if (isMario) { +- set_mario_pos(x, y, z); +- } else { +- gCurrentObject->oPosX = x; +- gCurrentObject->oPosY = y; +- gCurrentObject->oPosZ = z; +- } ++/** ++ * Apply inertia based on Mario's last platform. ++ */ ++static void apply_mario_inertia(void) { ++ // On the first frame of leaving the ground, boost Mario's y velocity ++ if (sInertiaFirstFrame) { ++ gMarioState->vel[1] += sMarioAmountDisplaced[1]; ++ } ++ ++ // Apply sideways inertia ++ gMarioState->pos[0] += sMarioAmountDisplaced[0]; ++ gMarioState->pos[2] += sMarioAmountDisplaced[2]; ++ ++ // Drag ++ sMarioAmountDisplaced[0] *= 0.97f; ++ sMarioAmountDisplaced[2] *= 0.97f; ++ ++ // Stop applying inertia once Mario has landed, or when ground pounding ++ if (!(gMarioState->action & ACT_FLAG_AIR) || (gMarioState->action == ACT_GROUND_POUND)) { ++ sShouldApplyInertia = FALSE; ++ } + } + + /** +- * If Mario's platform is not null, apply platform displacement. ++ * Apply platform displacement or inertia if required. + */ + void apply_mario_platform_displacement(void) { +- struct Object *platform = gMarioPlatform; +- +- if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL && platform != NULL) { +- apply_platform_displacement(TRUE, platform); ++ struct Object *platform; ++ ++ platform = gMarioPlatform; ++ if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL) { ++ if (platform != NULL) { ++ apply_platform_displacement(&sMarioDisplacementInfo, gMarioState->pos, &gMarioState->faceAngle[1], platform); ++ sShouldApplyInertia = TRUE; ++ sInertiaFirstFrame = TRUE; ++ } else if (sShouldApplyInertia && gDoInertia) { ++ apply_mario_inertia(); ++ sInertiaFirstFrame = FALSE; ++ } + } + } + ++ ++ + #ifndef VERSION_JP + /** + * Set Mario's platform to NULL. +diff --git a/src/game/platform_displacement.c.orig b/src/game/platform_displacement.c.orig +new file mode 100644 +index 0000000..9153bc4 +--- /dev/null ++++ b/src/game/platform_displacement.c.orig +@@ -0,0 +1,186 @@ ++#include ++ ++#include "engine/math_util.h" ++#include "engine/surface_collision.h" ++#include "level_update.h" ++#include "object_fields.h" ++#include "object_helpers.h" ++#include "object_list_processor.h" ++#include "platform_displacement.h" ++#include "types.h" ++ ++u16 D_8032FEC0 = 0; ++ ++u32 unused_8032FEC4[4] = { 0 }; ++ ++struct Object *gMarioPlatform = NULL; ++ ++/** ++ * Determine if Mario is standing on a platform object, meaning that he is ++ * within 4 units of the floor. Set his referenced platform object accordingly. ++ */ ++void update_mario_platform(void) { ++ struct Surface *floor; ++ UNUSED u32 unused; ++ f32 marioX; ++ f32 marioY; ++ f32 marioZ; ++ f32 floorHeight; ++ u32 awayFromFloor; ++ ++ if (gMarioObject == NULL) { ++ return; ++ } ++ ++ //! If Mario moves onto a rotating platform in a PU, the find_floor call ++ // will detect the platform and he will end up receiving a large amount ++ // of displacement since he is considered to be far from the platform's ++ // axis of rotation. ++ ++ marioX = gMarioObject->oPosX; ++ marioY = gMarioObject->oPosY; ++ marioZ = gMarioObject->oPosZ; ++ floorHeight = find_floor(marioX, marioY, marioZ, &floor); ++ ++ if (absf(marioY - floorHeight) < 4.0f) { ++ awayFromFloor = 0; ++ } else { ++ awayFromFloor = 1; ++ } ++ ++ switch (awayFromFloor) { ++ case 1: ++ gMarioPlatform = NULL; ++ gMarioObject->platform = NULL; ++ break; ++ ++ case 0: ++ if (floor != NULL && floor->object != NULL) { ++ gMarioPlatform = floor->object; ++ gMarioObject->platform = floor->object; ++ } else { ++ gMarioPlatform = NULL; ++ gMarioObject->platform = NULL; ++ } ++ break; ++ } ++} ++ ++/** ++ * Get Mario's position and store it in x, y, and z. ++ */ ++void get_mario_pos(f32 *x, f32 *y, f32 *z) { ++ *x = gMarioStates[0].pos[0]; ++ *y = gMarioStates[0].pos[1]; ++ *z = gMarioStates[0].pos[2]; ++} ++ ++/** ++ * Set Mario's position. ++ */ ++void set_mario_pos(f32 x, f32 y, f32 z) { ++ gMarioStates[0].pos[0] = x; ++ gMarioStates[0].pos[1] = y; ++ gMarioStates[0].pos[2] = z; ++} ++ ++/** ++ * Apply one frame of platform rotation to Mario or an object using the given ++ * platform. If isMario is false, use gCurrentObject. ++ */ ++void apply_platform_displacement(u32 isMario, struct Object *platform) { ++ f32 x; ++ f32 y; ++ f32 z; ++ f32 platformPosX; ++ f32 platformPosY; ++ f32 platformPosZ; ++ Vec3f currentObjectOffset; ++ Vec3f relativeOffset; ++ Vec3f newObjectOffset; ++ Vec3s rotation; ++ UNUSED s16 unused1; ++ UNUSED s16 unused2; ++ UNUSED s16 unused3; ++ f32 displaceMatrix[4][4]; ++ ++ rotation[0] = platform->oAngleVelPitch; ++ rotation[1] = platform->oAngleVelYaw; ++ rotation[2] = platform->oAngleVelRoll; ++ ++ if (isMario) { ++ D_8032FEC0 = 0; ++ get_mario_pos(&x, &y, &z); ++ } else { ++ x = gCurrentObject->oPosX; ++ y = gCurrentObject->oPosY; ++ z = gCurrentObject->oPosZ; ++ } ++ ++ x += platform->oVelX; ++ z += platform->oVelZ; ++ ++ if (rotation[0] != 0 || rotation[1] != 0 || rotation[2] != 0) { ++ unused1 = rotation[0]; ++ unused2 = rotation[2]; ++ unused3 = platform->oFaceAngleYaw; ++ ++ if (isMario) { ++ gMarioStates[0].faceAngle[1] += rotation[1]; ++ } ++ ++ platformPosX = platform->oPosX; ++ platformPosY = platform->oPosY; ++ platformPosZ = platform->oPosZ; ++ ++ currentObjectOffset[0] = x - platformPosX; ++ currentObjectOffset[1] = y - platformPosY; ++ currentObjectOffset[2] = z - platformPosZ; ++ ++ rotation[0] = platform->oFaceAnglePitch - platform->oAngleVelPitch; ++ rotation[1] = platform->oFaceAngleYaw - platform->oAngleVelYaw; ++ rotation[2] = platform->oFaceAngleRoll - platform->oAngleVelRoll; ++ ++ mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); ++ linear_mtxf_transpose_mul_vec3f(displaceMatrix, relativeOffset, currentObjectOffset); ++ ++ rotation[0] = platform->oFaceAnglePitch; ++ rotation[1] = platform->oFaceAngleYaw; ++ rotation[2] = platform->oFaceAngleRoll; ++ ++ mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); ++ linear_mtxf_mul_vec3f(displaceMatrix, newObjectOffset, relativeOffset); ++ ++ x = platformPosX + newObjectOffset[0]; ++ y = platformPosY + newObjectOffset[1]; ++ z = platformPosZ + newObjectOffset[2]; ++ } ++ ++ if (isMario) { ++ set_mario_pos(x, y, z); ++ } else { ++ gCurrentObject->oPosX = x; ++ gCurrentObject->oPosY = y; ++ gCurrentObject->oPosZ = z; ++ } ++} ++ ++/** ++ * If Mario's platform is not null, apply platform displacement. ++ */ ++void apply_mario_platform_displacement(void) { ++ struct Object *platform = gMarioPlatform; ++ ++ if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL && platform != NULL) { ++ apply_platform_displacement(TRUE, platform); ++ } ++} ++ ++#ifndef VERSION_JP ++/** ++ * Set Mario's platform to NULL. ++ */ ++void clear_mario_platform(void) { ++ gMarioPlatform = NULL; ++} ++#endif +diff --git a/src/game/platform_displacement.c.rej b/src/game/platform_displacement.c.rej +new file mode 100644 +index 0000000..ff20eca +--- /dev/null ++++ b/src/game/platform_displacement.c.rej +@@ -0,0 +1,218 @@ ++--- src/game/platform_displacement.c +++++ src/game/platform_displacement.c ++@@ -85,96 +86,139 @@ void set_mario_pos(f32 x, f32 y, f32 z) { ++ gMarioStates[0].pos[2] = z; ++ } ++ ++-/** ++- * Apply one frame of platform rotation to Mario or an object using the given ++- * platform. If isMario is 0, use gCurrentObject. ++- */ ++-void apply_platform_displacement(u32 isMario, struct Object *platform) { ++- f32 x; ++- f32 y; ++- f32 z; ++- f32 platformPosX; ++- f32 platformPosY; ++- f32 platformPosZ; ++- Vec3f currentObjectOffset; ++- Vec3f relativeOffset; ++- Vec3f newObjectOffset; ++- Vec3s rotation; ++- UNUSED s16 unused1; ++- UNUSED s16 unused2; ++- UNUSED s16 unused3; ++- f32 displaceMatrix[4][4]; ++- ++- rotation[0] = platform->oAngleVelPitch; ++- rotation[1] = platform->oAngleVelYaw; ++- rotation[2] = platform->oAngleVelRoll; ++- ++- if (isMario) { ++- D_8032FEC0 = 0; ++- get_mario_pos(&x, &y, &z); ++- } else { ++- x = gCurrentObject->oPosX; ++- y = gCurrentObject->oPosY; ++- z = gCurrentObject->oPosZ; ++- } ++- ++- x += platform->oVelX; ++- z += platform->oVelZ; +++static struct PlatformDisplacementInfo sMarioDisplacementInfo; +++static Vec3f sMarioAmountDisplaced; ++ ++- if (rotation[0] != 0 || rotation[1] != 0 || rotation[2] != 0) { ++- unused1 = rotation[0]; ++- unused2 = rotation[2]; ++- unused3 = platform->oFaceAngleYaw; +++extern s32 gGlobalTimer; ++ ++- if (isMario) { ++- gMarioStates[0].faceAngle[1] += rotation[1]; ++- } ++- ++- platformPosX = platform->oPosX; ++- platformPosY = platform->oPosY; ++- platformPosZ = platform->oPosZ; ++- ++- currentObjectOffset[0] = x - platformPosX; ++- currentObjectOffset[1] = y - platformPosY; ++- currentObjectOffset[2] = z - platformPosZ; ++- ++- rotation[0] = platform->oFaceAnglePitch - platform->oAngleVelPitch; ++- rotation[1] = platform->oFaceAngleYaw - platform->oAngleVelYaw; ++- rotation[2] = platform->oFaceAngleRoll - platform->oAngleVelRoll; ++- ++- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); ++- linear_mtxf_transpose_mul_vec3f(displaceMatrix, relativeOffset, currentObjectOffset); +++/** +++ * Upscale or downscale a vector by another vector. +++ */ +++static void scale_vec3f(Vec3f dst, Vec3f src, Vec3f scale, u32 doInverted) { +++ if (doInverted) { +++ dst[0] = src[0] / scale[0]; +++ dst[1] = src[1] / scale[1]; +++ dst[2] = src[2] / scale[2]; +++ } else { +++ dst[0] = src[0] * scale[0]; +++ dst[1] = src[1] * scale[1]; +++ dst[2] = src[2] * scale[2]; +++ } +++} ++ ++- rotation[0] = platform->oFaceAnglePitch; ++- rotation[1] = platform->oFaceAngleYaw; ++- rotation[2] = platform->oFaceAngleRoll; +++/** +++ * Apply one frame of platform displacement to Mario or an object using the given +++ * platform. +++ */ +++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform) { +++ Vec3f platformPos; +++ Vec3f posDifference; +++ Vec3f yawVec; +++ Vec3f scaledPos; +++ // Determine how much Mario turned on his own since last frame +++ s16 yawDifference = *yaw - displaceInfo->prevYaw; +++ +++ // Avoid a crash if the platform unloaded its collision while stood on +++ if (platform->header.gfx.throwMatrix == NULL) return; +++ +++ vec3f_copy(platformPos, (*platform->header.gfx.throwMatrix)[3]); +++ +++ // Determine how far Mario moved on his own since last frame +++ vec3f_copy(posDifference, pos); +++ vec3f_sub(posDifference, displaceInfo->prevPos); +++ +++ if ((platform == displaceInfo->prevPlatform) && (gGlobalTimer == displaceInfo->prevTimer + 1)) { +++ // Transform from relative positions to world positions +++ scale_vec3f(scaledPos, displaceInfo->prevTransformedPos, platform->header.gfx.scale, FALSE); +++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, pos, scaledPos); +++ +++ // Add on how much Mario moved in the previous frame +++ vec3f_add(pos, posDifference); +++ +++ // Calculate new yaw +++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, yawVec, displaceInfo->prevTransformedYawVec); +++ *yaw = atan2s(yawVec[2], yawVec[0]) + yawDifference; +++ } else { +++ // First frame of standing on the platform, don't calculate a new position +++ vec3f_sub(pos, platformPos); +++ } +++ +++ // Transform from world positions to relative positions for use next frame +++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, scaledPos, pos); +++ scale_vec3f(displaceInfo->prevTransformedPos, scaledPos, platform->header.gfx.scale, TRUE); +++ vec3f_add(pos, platformPos); +++ +++ // If the object is Mario, set inertia +++ if (pos == gMarioState->pos) { +++ vec3f_copy(sMarioAmountDisplaced, pos); +++ vec3f_sub(sMarioAmountDisplaced, displaceInfo->prevPos); +++ vec3f_sub(sMarioAmountDisplaced, posDifference); +++ +++ // Make sure inertia isn't set on the first frame otherwise the previous value isn't cleared +++ if ((platform != displaceInfo->prevPlatform) || (gGlobalTimer != displaceInfo->prevTimer + 1)) { +++ vec3f_set(sMarioAmountDisplaced, 0.f, 0.f, 0.f); +++ } +++ } +++ +++ // Update info for next frame +++ // Update position +++ vec3f_copy(displaceInfo->prevPos, pos); +++ +++ // Set yaw info +++ vec3f_set(yawVec, sins(*yaw), 0, coss(*yaw)); +++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, displaceInfo->prevTransformedYawVec, yawVec); +++ displaceInfo->prevYaw = *yaw; +++ +++ // Update platform and timer +++ displaceInfo->prevPlatform = platform; +++ displaceInfo->prevTimer = gGlobalTimer; +++} ++ ++- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); ++- linear_mtxf_mul_vec3f(displaceMatrix, newObjectOffset, relativeOffset); +++// Doesn't change in the code, set this to FALSE if you don't want inertia +++u8 gDoInertia = TRUE; ++ ++- x = platformPosX + newObjectOffset[0]; ++- y = platformPosY + newObjectOffset[1]; ++- z = platformPosZ + newObjectOffset[2]; ++- } +++static u8 sShouldApplyInertia = FALSE; +++static u8 sInertiaFirstFrame = FALSE; ++ ++- if (isMario) { ++- set_mario_pos(x, y, z); ++- } else { ++- gCurrentObject->oPosX = x; ++- gCurrentObject->oPosY = y; ++- gCurrentObject->oPosZ = z; ++- } +++/** +++ * Apply inertia based on Mario's last platform. +++ */ +++static void apply_mario_inertia(void) { +++ // On the first frame of leaving the ground, boost Mario's y velocity +++ if (sInertiaFirstFrame) { +++ gMarioState->vel[1] += sMarioAmountDisplaced[1]; +++ } +++ +++ // Apply sideways inertia +++ gMarioState->pos[0] += sMarioAmountDisplaced[0]; +++ gMarioState->pos[2] += sMarioAmountDisplaced[2]; +++ +++ // Drag +++ sMarioAmountDisplaced[0] *= 0.97f; +++ sMarioAmountDisplaced[2] *= 0.97f; +++ +++ // Stop applying inertia once Mario has landed, or when ground pounding +++ if (!(gMarioState->action & ACT_FLAG_AIR) || (gMarioState->action == ACT_GROUND_POUND)) { +++ sShouldApplyInertia = FALSE; +++ } ++ } ++ ++ /** ++- * If Mario's platform is not null, apply platform displacement. +++ * Apply platform displacement or inertia if required. ++ */ ++ void apply_mario_platform_displacement(void) { ++ struct Object *platform; ++ ++ platform = gMarioPlatform; ++- if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL && platform != NULL) { ++- apply_platform_displacement(1, platform); +++ if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL) { +++ if (platform != NULL) { +++ apply_platform_displacement(&sMarioDisplacementInfo, gMarioState->pos, &gMarioState->faceAngle[1], platform); +++ sShouldApplyInertia = TRUE; +++ sInertiaFirstFrame = TRUE; +++ } else if (sShouldApplyInertia && gDoInertia) { +++ apply_mario_inertia(); +++ sInertiaFirstFrame = FALSE; +++ } ++ } ++ } ++ +diff --git a/src/game/platform_displacement.h b/src/game/platform_displacement.h +index 556192b..3609e2d 100644 +--- a/src/game/platform_displacement.h ++++ b/src/game/platform_displacement.h +@@ -5,10 +5,19 @@ + + #include "types.h" + ++struct PlatformDisplacementInfo { ++ Vec3f prevPos; ++ Vec3f prevTransformedPos; ++ Vec3f prevTransformedYawVec; ++ s16 prevYaw; ++ struct Object *prevPlatform; ++ s32 prevTimer; ++}; ++ + void update_mario_platform(void); + void get_mario_pos(f32 *x, f32 *y, f32 *z); + void set_mario_pos(f32 x, f32 y, f32 z); +-void apply_platform_displacement(u32 isMario, struct Object *platform); ++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform); + void apply_mario_platform_displacement(void); + #ifndef VERSION_JP + void clear_mario_platform(void); diff --git a/enhancements/platformdisplacement2.patch b/enhancements/platformdisplacement2.patch new file mode 100644 index 00000000..6bf33a8c --- /dev/null +++ b/enhancements/platformdisplacement2.patch @@ -0,0 +1,288 @@ +diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c +index ebc6bf4..987f456 100644 +--- a/src/game/behaviors/bowser.inc.c ++++ b/src/game/behaviors/bowser.inc.c +@@ -972,6 +972,8 @@ s32 bowser_check_fallen_off_stage(void) // bowser off stage? + return 0; + } + ++struct PlatformDisplacementInfo sBowserDisplacementInfo; ++ + void (*sBowserActions[])(void) = { bowser_act_default, bowser_act_thrown_dropped, bowser_act_jump_onto_stage, bowser_act_dance, + bowser_act_dead, bowser_act_text_wait, bowser_act_intro_walk, bowser_act_charge_mario, + bowser_act_spit_fire_into_sky, bowser_act_spit_fire_onto_floor, bowser_act_hit_edge, bowser_act_turn_from_edge, +@@ -1033,7 +1035,7 @@ void bowser_free_update(void) { + struct Object *platform; + UNUSED f32 floorHeight; + if ((platform = o->platform) != NULL) +- apply_platform_displacement(0, platform); ++ apply_platform_displacement(&sBowserDisplacementInfo, &o->oPosX, &o->oFaceAngleYaw, platform); + o->oBowserUnk10E = 0; + cur_obj_update_floor_and_walls(); + cur_obj_call_action_function(sBowserActions); +diff --git a/src/game/behaviors/tilting_inverted_pyramid.inc.c b/src/game/behaviors/tilting_inverted_pyramid.inc.c +index ebce64f..29136e9 100644 +--- a/src/game/behaviors/tilting_inverted_pyramid.inc.c ++++ b/src/game/behaviors/tilting_inverted_pyramid.inc.c +@@ -131,7 +131,7 @@ void bhv_tilting_inverted_pyramid_loop(void) { + mx += posAfterRotation[0] - posBeforeRotation[0]; + my += posAfterRotation[1] - posBeforeRotation[1]; + mz += posAfterRotation[2] - posBeforeRotation[2]; +- set_mario_pos(mx, my, mz); ++ //set_mario_pos(mx, my, mz); + } + + o->header.gfx.throwMatrix = transform; +diff --git a/src/game/platform_displacement.c b/src/game/platform_displacement.c +index 29a741c..d1bb016 100644 +--- a/src/game/platform_displacement.c ++++ b/src/game/platform_displacement.c +@@ -8,6 +8,7 @@ + #include "object_list_processor.h" + #include "platform_displacement.h" + #include "types.h" ++#include "sm64.h" + + u16 D_8032FEC0 = 0; + +@@ -84,96 +85,139 @@ void set_mario_pos(f32 x, f32 y, f32 z) { + gMarioStates[0].pos[2] = z; + } + +-/** +- * Apply one frame of platform rotation to Mario or an object using the given +- * platform. If isMario is 0, use gCurrentObject. +- */ +-void apply_platform_displacement(u32 isMario, struct Object *platform) { +- f32 x; +- f32 y; +- f32 z; +- f32 platformPosX; +- f32 platformPosY; +- f32 platformPosZ; +- Vec3f currentObjectOffset; +- Vec3f relativeOffset; +- Vec3f newObjectOffset; +- Vec3s rotation; +- UNUSED s16 unused1; +- UNUSED s16 unused2; +- UNUSED s16 unused3; +- f32 displaceMatrix[4][4]; +- +- rotation[0] = platform->oAngleVelPitch; +- rotation[1] = platform->oAngleVelYaw; +- rotation[2] = platform->oAngleVelRoll; +- +- if (isMario) { +- D_8032FEC0 = 0; +- get_mario_pos(&x, &y, &z); +- } else { +- x = gCurrentObject->oPosX; +- y = gCurrentObject->oPosY; +- z = gCurrentObject->oPosZ; +- } +- +- x += platform->oVelX; +- z += platform->oVelZ; ++static struct PlatformDisplacementInfo sMarioDisplacementInfo; ++static Vec3f sMarioAmountDisplaced; + +- if (rotation[0] != 0 || rotation[1] != 0 || rotation[2] != 0) { +- unused1 = rotation[0]; +- unused2 = rotation[2]; +- unused3 = platform->oFaceAngleYaw; ++extern s32 gGlobalTimer; + +- if (isMario) { +- gMarioStates[0].faceAngle[1] += rotation[1]; +- } +- +- platformPosX = platform->oPosX; +- platformPosY = platform->oPosY; +- platformPosZ = platform->oPosZ; +- +- currentObjectOffset[0] = x - platformPosX; +- currentObjectOffset[1] = y - platformPosY; +- currentObjectOffset[2] = z - platformPosZ; +- +- rotation[0] = platform->oFaceAnglePitch - platform->oAngleVelPitch; +- rotation[1] = platform->oFaceAngleYaw - platform->oAngleVelYaw; +- rotation[2] = platform->oFaceAngleRoll - platform->oAngleVelRoll; +- +- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); +- linear_mtxf_transpose_mul_vec3f(displaceMatrix, relativeOffset, currentObjectOffset); ++/** ++ * Upscale or downscale a vector by another vector. ++ */ ++static void scale_vec3f(Vec3f dst, Vec3f src, Vec3f scale, u32 doInverted) { ++ if (doInverted) { ++ dst[0] = src[0] / scale[0]; ++ dst[1] = src[1] / scale[1]; ++ dst[2] = src[2] / scale[2]; ++ } else { ++ dst[0] = src[0] * scale[0]; ++ dst[1] = src[1] * scale[1]; ++ dst[2] = src[2] * scale[2]; ++ } ++} + +- rotation[0] = platform->oFaceAnglePitch; +- rotation[1] = platform->oFaceAngleYaw; +- rotation[2] = platform->oFaceAngleRoll; ++/** ++ * Apply one frame of platform displacement to Mario or an object using the given ++ * platform. ++ */ ++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform) { ++ Vec3f platformPos; ++ Vec3f posDifference; ++ Vec3f yawVec; ++ Vec3f scaledPos; ++ // Determine how much Mario turned on his own since last frame ++ s16 yawDifference = *yaw - displaceInfo->prevYaw; ++ ++ // Avoid a crash if the platform unloaded its collision while stood on ++ if (platform->header.gfx.throwMatrix == NULL) return; ++ ++ vec3f_copy(platformPos, (*platform->header.gfx.throwMatrix)[3]); ++ ++ // Determine how far Mario moved on his own since last frame ++ vec3f_copy(posDifference, pos); ++ vec3f_sub(posDifference, displaceInfo->prevPos); ++ ++ if ((platform == displaceInfo->prevPlatform) && (gGlobalTimer == displaceInfo->prevTimer + 1)) { ++ // Transform from relative positions to world positions ++ scale_vec3f(scaledPos, displaceInfo->prevTransformedPos, platform->header.gfx.scale, FALSE); ++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, pos, scaledPos); ++ ++ // Add on how much Mario moved in the previous frame ++ vec3f_add(pos, posDifference); ++ ++ // Calculate new yaw ++ linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, yawVec, displaceInfo->prevTransformedYawVec); ++ *yaw = atan2s(yawVec[2], yawVec[0]) + yawDifference; ++ } else { ++ // First frame of standing on the platform, don't calculate a new position ++ vec3f_sub(pos, platformPos); ++ } ++ ++ // Transform from world positions to relative positions for use next frame ++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, scaledPos, pos); ++ scale_vec3f(displaceInfo->prevTransformedPos, scaledPos, platform->header.gfx.scale, TRUE); ++ vec3f_add(pos, platformPos); ++ ++ // If the object is Mario, set inertia ++ if (pos == gMarioState->pos) { ++ vec3f_copy(sMarioAmountDisplaced, pos); ++ vec3f_sub(sMarioAmountDisplaced, displaceInfo->prevPos); ++ vec3f_sub(sMarioAmountDisplaced, posDifference); ++ ++ // Make sure inertia isn't set on the first frame otherwise the previous value isn't cleared ++ if ((platform != displaceInfo->prevPlatform) || (gGlobalTimer != displaceInfo->prevTimer + 1)) { ++ vec3f_set(sMarioAmountDisplaced, 0.f, 0.f, 0.f); ++ } ++ } ++ ++ // Update info for next frame ++ // Update position ++ vec3f_copy(displaceInfo->prevPos, pos); ++ ++ // Set yaw info ++ vec3f_set(yawVec, sins(*yaw), 0, coss(*yaw)); ++ linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, displaceInfo->prevTransformedYawVec, yawVec); ++ displaceInfo->prevYaw = *yaw; ++ ++ // Update platform and timer ++ displaceInfo->prevPlatform = platform; ++ displaceInfo->prevTimer = gGlobalTimer; ++} + +- mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); +- linear_mtxf_mul_vec3f(displaceMatrix, newObjectOffset, relativeOffset); ++// Doesn't change in the code, set this to FALSE if you don't want inertia ++u8 gDoInertia = TRUE; + +- x = platformPosX + newObjectOffset[0]; +- y = platformPosY + newObjectOffset[1]; +- z = platformPosZ + newObjectOffset[2]; +- } ++static u8 sShouldApplyInertia = FALSE; ++static u8 sInertiaFirstFrame = FALSE; + +- if (isMario) { +- set_mario_pos(x, y, z); +- } else { +- gCurrentObject->oPosX = x; +- gCurrentObject->oPosY = y; +- gCurrentObject->oPosZ = z; +- } ++/** ++ * Apply inertia based on Mario's last platform. ++ */ ++static void apply_mario_inertia(void) { ++ // On the first frame of leaving the ground, boost Mario's y velocity ++ if (sInertiaFirstFrame) { ++ gMarioState->vel[1] += sMarioAmountDisplaced[1]; ++ } ++ ++ // Apply sideways inertia ++ gMarioState->pos[0] += sMarioAmountDisplaced[0]; ++ gMarioState->pos[2] += sMarioAmountDisplaced[2]; ++ ++ // Drag ++ sMarioAmountDisplaced[0] *= 0.97f; ++ sMarioAmountDisplaced[2] *= 0.97f; ++ ++ // Stop applying inertia once Mario has landed, or when ground pounding ++ if (!(gMarioState->action & ACT_FLAG_AIR) || (gMarioState->action == ACT_GROUND_POUND)) { ++ sShouldApplyInertia = FALSE; ++ } + } + + /** +- * If Mario's platform is not null, apply platform displacement. ++ * Apply platform displacement or inertia if required. + */ + void apply_mario_platform_displacement(void) { + struct Object *platform; + + platform = gMarioPlatform; +- if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL && platform != NULL) { +- apply_platform_displacement(1, platform); ++ if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL) { ++ if (platform != NULL) { ++ apply_platform_displacement(&sMarioDisplacementInfo, gMarioState->pos, &gMarioState->faceAngle[1], platform); ++ sShouldApplyInertia = TRUE; ++ sInertiaFirstFrame = TRUE; ++ } else if (sShouldApplyInertia && gDoInertia) { ++ apply_mario_inertia(); ++ sInertiaFirstFrame = FALSE; ++ } + } + } + +diff --git a/src/game/platform_displacement.h b/src/game/platform_displacement.h +index 556192b..3609e2d 100644 +--- a/src/game/platform_displacement.h ++++ b/src/game/platform_displacement.h +@@ -5,10 +5,19 @@ + + #include "types.h" + ++struct PlatformDisplacementInfo { ++ Vec3f prevPos; ++ Vec3f prevTransformedPos; ++ Vec3f prevTransformedYawVec; ++ s16 prevYaw; ++ struct Object *prevPlatform; ++ s32 prevTimer; ++}; ++ + void update_mario_platform(void); + void get_mario_pos(f32 *x, f32 *y, f32 *z); + void set_mario_pos(f32 x, f32 y, f32 z); +-void apply_platform_displacement(u32 isMario, struct Object *platform); ++void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform); + void apply_mario_platform_displacement(void); + #ifndef VERSION_JP + void clear_mario_platform(void); diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c index 97cba2af..410e612f 100644 --- a/src/game/behaviors/bowser.inc.c +++ b/src/game/behaviors/bowser.inc.c @@ -972,6 +972,8 @@ s32 bowser_check_fallen_off_stage(void) // bowser off stage? return 0; } +struct PlatformDisplacementInfo sBowserDisplacementInfo; + void (*sBowserActions[])(void) = { bowser_act_default, bowser_act_thrown_dropped, bowser_act_jump_onto_stage, bowser_act_dance, bowser_act_dead, bowser_act_text_wait, bowser_act_intro_walk, bowser_act_charge_mario, bowser_act_spit_fire_into_sky, bowser_act_spit_fire_onto_floor, bowser_act_hit_edge, bowser_act_turn_from_edge, @@ -1032,8 +1034,9 @@ void bowser_free_update(void) { struct Surface *floor; struct Object *platform; UNUSED f32 floorHeight; - if ((platform = o->platform) != NULL) - apply_platform_displacement(FALSE, platform); + if ((platform = o->platform) != NULL) { + apply_platform_displacement(&sBowserDisplacementInfo, &o->oPosX, &o->oFaceAngleYaw, platform); + } o->oBowserUnk10E = 0; cur_obj_update_floor_and_walls(); cur_obj_call_action_function(sBowserActions); diff --git a/src/game/behaviors/tilting_inverted_pyramid.inc.c b/src/game/behaviors/tilting_inverted_pyramid.inc.c index ebce64fc..29136e97 100644 --- a/src/game/behaviors/tilting_inverted_pyramid.inc.c +++ b/src/game/behaviors/tilting_inverted_pyramid.inc.c @@ -131,7 +131,7 @@ void bhv_tilting_inverted_pyramid_loop(void) { mx += posAfterRotation[0] - posBeforeRotation[0]; my += posAfterRotation[1] - posBeforeRotation[1]; mz += posAfterRotation[2] - posBeforeRotation[2]; - set_mario_pos(mx, my, mz); + //set_mario_pos(mx, my, mz); } o->header.gfx.throwMatrix = transform; diff --git a/src/game/platform_displacement.c b/src/game/platform_displacement.c index 9153bc4d..1e8cd865 100644 --- a/src/game/platform_displacement.c +++ b/src/game/platform_displacement.c @@ -8,6 +8,7 @@ #include "object_list_processor.h" #include "platform_displacement.h" #include "types.h" +#include "sm64.h" u16 D_8032FEC0 = 0; @@ -84,98 +85,144 @@ void set_mario_pos(f32 x, f32 y, f32 z) { gMarioStates[0].pos[2] = z; } +static struct PlatformDisplacementInfo sMarioDisplacementInfo; +static Vec3f sMarioAmountDisplaced; + +extern s32 gGlobalTimer; + /** - * Apply one frame of platform rotation to Mario or an object using the given - * platform. If isMario is false, use gCurrentObject. + * Upscale or downscale a vector by another vector. */ -void apply_platform_displacement(u32 isMario, struct Object *platform) { - f32 x; - f32 y; - f32 z; - f32 platformPosX; - f32 platformPosY; - f32 platformPosZ; - Vec3f currentObjectOffset; - Vec3f relativeOffset; - Vec3f newObjectOffset; - Vec3s rotation; - UNUSED s16 unused1; - UNUSED s16 unused2; - UNUSED s16 unused3; - f32 displaceMatrix[4][4]; - - rotation[0] = platform->oAngleVelPitch; - rotation[1] = platform->oAngleVelYaw; - rotation[2] = platform->oAngleVelRoll; - - if (isMario) { - D_8032FEC0 = 0; - get_mario_pos(&x, &y, &z); - } else { - x = gCurrentObject->oPosX; - y = gCurrentObject->oPosY; - z = gCurrentObject->oPosZ; - } - - x += platform->oVelX; - z += platform->oVelZ; - - if (rotation[0] != 0 || rotation[1] != 0 || rotation[2] != 0) { - unused1 = rotation[0]; - unused2 = rotation[2]; - unused3 = platform->oFaceAngleYaw; - - if (isMario) { - gMarioStates[0].faceAngle[1] += rotation[1]; - } - - platformPosX = platform->oPosX; - platformPosY = platform->oPosY; - platformPosZ = platform->oPosZ; - - currentObjectOffset[0] = x - platformPosX; - currentObjectOffset[1] = y - platformPosY; - currentObjectOffset[2] = z - platformPosZ; - - rotation[0] = platform->oFaceAnglePitch - platform->oAngleVelPitch; - rotation[1] = platform->oFaceAngleYaw - platform->oAngleVelYaw; - rotation[2] = platform->oFaceAngleRoll - platform->oAngleVelRoll; - - mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); - linear_mtxf_transpose_mul_vec3f(displaceMatrix, relativeOffset, currentObjectOffset); - - rotation[0] = platform->oFaceAnglePitch; - rotation[1] = platform->oFaceAngleYaw; - rotation[2] = platform->oFaceAngleRoll; - - mtxf_rotate_zxy_and_translate(displaceMatrix, currentObjectOffset, rotation); - linear_mtxf_mul_vec3f(displaceMatrix, newObjectOffset, relativeOffset); - - x = platformPosX + newObjectOffset[0]; - y = platformPosY + newObjectOffset[1]; - z = platformPosZ + newObjectOffset[2]; - } - - if (isMario) { - set_mario_pos(x, y, z); - } else { - gCurrentObject->oPosX = x; - gCurrentObject->oPosY = y; - gCurrentObject->oPosZ = z; - } +static void scale_vec3f(Vec3f dst, Vec3f src, Vec3f scale, u32 doInverted) { + if (doInverted) { + dst[0] = src[0] / scale[0]; + dst[1] = src[1] / scale[1]; + dst[2] = src[2] / scale[2]; + } else { + dst[0] = src[0] * scale[0]; + dst[1] = src[1] * scale[1]; + dst[2] = src[2] * scale[2]; + } } /** - * If Mario's platform is not null, apply platform displacement. + * Apply one frame of platform displacement to Mario or an object using the given + * platform. + */ +void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform) { + Vec3f platformPos; + Vec3f posDifference; + Vec3f yawVec; + Vec3f scaledPos; + // Determine how much Mario turned on his own since last frame + s16 yawDifference = *yaw - displaceInfo->prevYaw; + + // Avoid a crash if the platform unloaded its collision while stood on + if (platform->header.gfx.throwMatrix == NULL) return; + + vec3f_copy(platformPos, (*platform->header.gfx.throwMatrix)[3]); + + // Determine how far Mario moved on his own since last frame + vec3f_copy(posDifference, pos); + vec3f_sub(posDifference, displaceInfo->prevPos); + + if ((platform == displaceInfo->prevPlatform) && (gGlobalTimer == displaceInfo->prevTimer + 1)) { + // Transform from relative positions to world positions + scale_vec3f(scaledPos, displaceInfo->prevTransformedPos, platform->header.gfx.scale, FALSE); + linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, pos, scaledPos); + + // Add on how much Mario moved in the previous frame + vec3f_add(pos, posDifference); + + // Calculate new yaw + linear_mtxf_mul_vec3f(*platform->header.gfx.throwMatrix, yawVec, displaceInfo->prevTransformedYawVec); + *yaw = atan2s(yawVec[2], yawVec[0]) + yawDifference; + } else { + // First frame of standing on the platform, don't calculate a new position + vec3f_sub(pos, platformPos); + } + + // Transform from world positions to relative positions for use next frame + linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, scaledPos, pos); + scale_vec3f(displaceInfo->prevTransformedPos, scaledPos, platform->header.gfx.scale, TRUE); + vec3f_add(pos, platformPos); + + // If the object is Mario, set inertia + if (pos == gMarioState->pos) { + vec3f_copy(sMarioAmountDisplaced, pos); + vec3f_sub(sMarioAmountDisplaced, displaceInfo->prevPos); + vec3f_sub(sMarioAmountDisplaced, posDifference); + + // Make sure inertia isn't set on the first frame otherwise the previous value isn't cleared + if ((platform != displaceInfo->prevPlatform) || (gGlobalTimer != displaceInfo->prevTimer + 1)) { + vec3f_set(sMarioAmountDisplaced, 0.f, 0.f, 0.f); + } + } + + // Update info for next frame + // Update position + vec3f_copy(displaceInfo->prevPos, pos); + + // Set yaw info + vec3f_set(yawVec, sins(*yaw), 0, coss(*yaw)); + linear_mtxf_transpose_mul_vec3f(*platform->header.gfx.throwMatrix, displaceInfo->prevTransformedYawVec, yawVec); + displaceInfo->prevYaw = *yaw; + + // Update platform and timer + displaceInfo->prevPlatform = platform; + displaceInfo->prevTimer = gGlobalTimer; +} + +// Doesn't change in the code, set this to FALSE if you don't want inertia +u8 gDoInertia = TRUE; + +static u8 sShouldApplyInertia = FALSE; +static u8 sInertiaFirstFrame = FALSE; + +/** + * Apply inertia based on Mario's last platform. + */ +static void apply_mario_inertia(void) { + // On the first frame of leaving the ground, boost Mario's y velocity + if (sInertiaFirstFrame) { + gMarioState->vel[1] += sMarioAmountDisplaced[1]; + } + + // Apply sideways inertia + gMarioState->pos[0] += sMarioAmountDisplaced[0]; + gMarioState->pos[2] += sMarioAmountDisplaced[2]; + + // Drag + sMarioAmountDisplaced[0] *= 0.97f; + sMarioAmountDisplaced[2] *= 0.97f; + + // Stop applying inertia once Mario has landed, or when ground pounding + if (!(gMarioState->action & ACT_FLAG_AIR) || (gMarioState->action == ACT_GROUND_POUND)) { + sShouldApplyInertia = FALSE; + } +} + +/** + * Apply platform displacement or inertia if required. */ void apply_mario_platform_displacement(void) { - struct Object *platform = gMarioPlatform; + struct Object *platform; - if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL && platform != NULL) { - apply_platform_displacement(TRUE, platform); + platform = gMarioPlatform; + if (!(gTimeStopState & TIME_STOP_ACTIVE) && gMarioObject != NULL) { + if (platform != NULL) { + apply_platform_displacement(&sMarioDisplacementInfo, gMarioState->pos, &gMarioState->faceAngle[1], platform); + sShouldApplyInertia = TRUE; + sInertiaFirstFrame = TRUE; + } else if (sShouldApplyInertia && gDoInertia) { + apply_mario_inertia(); + sInertiaFirstFrame = FALSE; + } } } + + #ifndef VERSION_JP /** * Set Mario's platform to NULL. diff --git a/src/game/platform_displacement.h b/src/game/platform_displacement.h index 556192b5..3609e2d4 100644 --- a/src/game/platform_displacement.h +++ b/src/game/platform_displacement.h @@ -5,10 +5,19 @@ #include "types.h" +struct PlatformDisplacementInfo { + Vec3f prevPos; + Vec3f prevTransformedPos; + Vec3f prevTransformedYawVec; + s16 prevYaw; + struct Object *prevPlatform; + s32 prevTimer; +}; + void update_mario_platform(void); void get_mario_pos(f32 *x, f32 *y, f32 *z); void set_mario_pos(f32 x, f32 y, f32 z); -void apply_platform_displacement(u32 isMario, struct Object *platform); +void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform); void apply_mario_platform_displacement(void); #ifndef VERSION_JP void clear_mario_platform(void);