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);