From a1af1c2ffb0c23aab02119bbdcc3a031d6126573 Mon Sep 17 00:00:00 2001 From: Prince Frizzy Date: Wed, 30 Mar 2022 22:03:53 -0400 Subject: [PATCH] Grand star fix and etc (#47) A ton of Bowser fixes, Grand star included. Fixed Grand Star cutscene syncing. Partially fix grand star syncing after joining late. Fix Bowser's intro cutscene always playing even if you joined after the first person. Hopefully fixed Bowser's state getting interrupted mid-fight just a bit. Add nothing state sanity check, and send object reliability when cutscene is finished. Fix ownership override. --- src/game/behaviors/bowser.inc.c | 111 +++++++++++++++++++--------- src/game/behaviors/grand_star.inc.c | 37 +++++++--- src/game/camera.c | 19 ++++- src/game/obj_behaviors.c | 8 ++ src/game/obj_behaviors.h | 1 + src/game/object_helpers.c | 16 ++++ src/game/object_helpers.h | 1 + 7 files changed, 145 insertions(+), 48 deletions(-) diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c index f7fa7740..aae60a52 100644 --- a/src/game/behaviors/bowser.inc.c +++ b/src/game/behaviors/bowser.inc.c @@ -1,19 +1,22 @@ // bowser.c.inc static u32 networkBowserAnimationIndex = 0; static u8 bowserIsDying = FALSE; +static u8 bowserCutscenePlayed = FALSE; +static u8 bowserIsCutscenePlayer = FALSE; void bowser_tail_anchor_act_0(void) { struct Object* bowser = o->parentObj; struct Object* player = nearest_player_to_object(o); cur_obj_become_tangible(); cur_obj_scale(1.0f); - if (bowser->oAction == 19) + if (bowser->oAction == 5 || bowser->oAction == 6 || bowser->oAction == 19 || bowser->oAction == 20) { bowser->oIntangibleTimer = -1; - else if (obj_check_if_collided_with_object(o, player)) { + } else if (obj_check_if_collided_with_object(o, player)) { bowser->oIntangibleTimer = 0; o->oAction = 2; - } else + } else { bowser->oIntangibleTimer = -1; + } } void bowser_tail_anchor_act_1(void) { @@ -177,31 +180,37 @@ s32 bowser_set_anim_look_up_and_walk(void) { 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()) + if (cur_obj_check_if_near_animation_end()) { return 1; - else - return 0; + } + return 0; } s32 bowser_set_anim_look_down(void) { cur_obj_init_animation_with_sound(14); - if (cur_obj_check_anim_frame(20)) + if (cur_obj_check_anim_frame(20)) { o->oForwardVel = 0.0f; - if (cur_obj_check_if_near_animation_end()) + } + if (cur_obj_check_if_near_animation_end()) { return 1; - else - return 0; + } + return 0; } void bowser_initialize_action(void) { - if (o->oBowserUnk88 == 0) + if (o->oBowserUnk88 == 0 && !bowserCutscenePlayed) { o->oAction = 5; - else if (o->oBowserUnk88 == 1) + } else if (o->oBowserUnk88 == 1 && !bowserCutscenePlayed) { o->oAction = 6; - else if (o->oBehParams2ndByte == 1) + } else if (o->oBehParams2ndByte == 1) { + bowserCutscenePlayed = TRUE; o->oAction = 13; - else + if (bowserIsCutscenePlayer) { network_send_object_reliability(o, TRUE); } + } else { + bowserCutscenePlayed = TRUE; o->oAction = 0; + if (bowserIsCutscenePlayer) { network_send_object_reliability(o, TRUE); } + } } void bowser_act_text_wait(void) // not much @@ -374,12 +383,13 @@ void bowser_act_default(void) // only lasts one frame o->oAngleVelYaw = 0; o->oForwardVel = 0.0f; o->oVelY = 0.0f; - if (BITDW) + if (BITDW) { bowser_bitdw_act_controller(); - else if (BITFS) + } else if (BITFS) { bowser_bitfs_act_controller(); - else + } else { bowser_bits_act_controller(); + } // Action 14 commonly follows } @@ -850,12 +860,14 @@ void bowser_spawn_grand_star_key(void) { reward = (prevReward != NULL) ? prevReward : spawn_object(o, MODEL_STAR, bhvGrandStar); gSecondCameraFocus = reward; - if (prevReward == NULL && reward != NULL) { + if (network_owns_object(o) && prevReward == NULL && reward != NULL) { // set the home position reward->oHomeX = reward->oPosX; reward->oHomeY = reward->oPosY; reward->oHomeZ = reward->oPosZ; + network_set_sync_id(reward); + struct Object* spawn_objects[] = { reward }; u32 models[] = { MODEL_STAR }; network_send_spawn_objects(spawn_objects, models, 1); @@ -921,7 +933,6 @@ s32 bowser_dead_wait_for_mario(void) { s32 bowser_dead_twirl_into_trophy(void) { bowserIsDying = TRUE; - s32 ret = 0; if (o->header.gfx.scale[0] < 0.8) o->oAngleVelYaw += 0x80; if (o->header.gfx.scale[0] > 0.2) { @@ -933,11 +944,11 @@ s32 bowser_dead_twirl_into_trophy(void) { o->oGravity = 0.0f; } if (o->header.gfx.scale[1] < 0.5) - ret = 1; + return 1; o->oMoveAngleYaw += o->oAngleVelYaw; if (o->oOpacity >= 3) o->oOpacity -= 2; - return ret; + return 0; } void bowser_dead_hide(void) { @@ -1094,6 +1105,9 @@ void bowser_act_ride_tilting_platform(void) { cur_obj_extend_animation_if_at_end(); } +void bowser_act_nothing(void) { + +} s32 bowser_check_fallen_off_stage(void) // bowser off stage? { @@ -1114,7 +1128,8 @@ void (*sBowserActions[])(void) = { bowser_act_default, bowser_act_thrown_droppe 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 }; + bowser_act_teleport, bowser_act_jump_towards_mario, bowser_act_unused_slow_walk, bowser_act_ride_tilting_platform, + bowser_act_nothing, }; struct SoundState D_8032F5B8[] = { { 0, 0, 0, NO_SOUND }, { 0, 0, 0, NO_SOUND }, { 0, 0, 0, NO_SOUND }, @@ -1257,8 +1272,13 @@ void bhv_bowser_loop(void) { geo_obj_init_animation(&o->header.gfx, &anim); } } + + // If Bowser isn't in a cutscene, It's been played already. + if (!bowserCutscenePlayed && (o->oAction != 5 && o->oAction != 6 && o->oAction != 20)) { + bowserCutscenePlayed = TRUE; + } - s16 angleToMario; // AngleToMario from Bowser's perspective + 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); @@ -1314,6 +1334,13 @@ void bhv_bowser_loop(void) { } void bhv_bowser_override_ownership(u8* shouldOverride, u8* shouldOwn) { + // Nothing state sanity check. + if (o->oAction == 20) { + *shouldOverride = TRUE; + *shouldOwn = FALSE; + return; + } + // tilting platform static u8 tiltingTimer = 0; if (o->oAction == 19) { tiltingTimer = 5; } @@ -1327,6 +1354,7 @@ void bhv_bowser_override_ownership(u8* shouldOverride, u8* shouldOwn) { static u8 bhv_bowser_ignore_if_true(void) { if (bowserIsDying) { return TRUE; } if (o->oAction == 19) { return TRUE; } // let the platform get to a stable state + if (bowserIsCutscenePlayer && (o->oAction == 5 || o->oAction == 6)) { return TRUE; } // Ignore updates till our cutscene is done. return FALSE; } @@ -1346,21 +1374,32 @@ void bhv_bowser_init(void) { 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; - - struct SyncObject* so = network_init_object(o, 8000.0f); - if (so) { - so->override_ownership = bhv_bowser_override_ownership; - so->ignore_if_true = bhv_bowser_ignore_if_true; - so->fullObjectSync = TRUE; - network_init_object_field_with_size(o, &o->header.gfx.node.flags, 16); - network_init_object_field_with_size(o, &o->header.gfx.animInfo.animFrame, 16); - network_init_object_field(o, &networkBowserAnimationIndex); - network_init_object_field(o, &o->header.gfx.scale[0]); - network_init_object_field(o, &o->header.gfx.scale[1]); - network_init_object_field(o, &o->header.gfx.scale[2]); + bowserCutscenePlayed = FALSE; + + // Make sure we're the first to trigger Bowser. + if (!is_other_player_active()) { + bowserIsCutscenePlayer = TRUE; + o->oAction = 5; // bowser_act_text_wait + } else { // If we aren't do nothing till we get our sync. + bowserIsCutscenePlayer = FALSE; + o->oAction = 20; // bowser_act_nothing + } + + if (!network_sync_object_initialized(o)) { + struct SyncObject* so = network_init_object(o, 8000.0f); + if (so) { + so->override_ownership = bhv_bowser_override_ownership; + so->ignore_if_true = bhv_bowser_ignore_if_true; + so->fullObjectSync = TRUE; + network_init_object_field_with_size(o, &o->header.gfx.node.flags, 16); + network_init_object_field_with_size(o, &o->header.gfx.animInfo.animFrame, 16); + network_init_object_field(o, &networkBowserAnimationIndex); + network_init_object_field(o, &o->header.gfx.scale[0]); + network_init_object_field(o, &o->header.gfx.scale[1]); + network_init_object_field(o, &o->header.gfx.scale[2]); + } } } diff --git a/src/game/behaviors/grand_star.inc.c b/src/game/behaviors/grand_star.inc.c index 0f5f4fe9..f97d8991 100644 --- a/src/game/behaviors/grand_star.inc.c +++ b/src/game/behaviors/grand_star.inc.c @@ -1,8 +1,8 @@ // grand_star.c.inc -s32 arc_to_goal_pos(Vec3f a0, Vec3f a1, f32 yVel, f32 gravity) { - f32 dx = a0[0] - a1[0]; - f32 dz = a0[2] - a1[2]; +s32 arc_to_goal_pos(Vec3f empty, Vec3f pos, f32 yVel, f32 gravity) { + f32 dx = empty[0] - pos[0]; + f32 dz = empty[2] - pos[2]; f32 planarDist = sqrtf(dx * dx + dz * dz); o->oMoveAngleYaw = atan2s(dz, dx); o->oVelY = yVel; @@ -22,12 +22,15 @@ void bhv_grand_star_init(void) { struct Object *other = cur_obj_nearest_object_with_behavior(bhvGrandStar); if (other == NULL) { if (!network_sync_object_initialized(o)) { - struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + struct SyncObject *so = network_init_object(o, 4000.0f); if (so) { - network_init_object_field_with_size(o, &o->activeFlags, 16); + network_init_object_field(o, &o->header.gfx.scale[0]); + network_init_object_field(o, &o->header.gfx.scale[1]); + network_init_object_field(o, &o->header.gfx.scale[2]); network_init_object_field(o, &o->oPrevAction); network_init_object_field(o, &o->oAction); network_init_object_field(o, &o->oSubAction); + network_init_object_field(o, &o->oInteractStatus); network_init_object_field(o, &o->oTimer); network_init_object_field(o, &o->oHomeX); network_init_object_field(o, &o->oHomeY); @@ -41,7 +44,7 @@ void bhv_grand_star_init(void) { network_init_object_field(o, &o->oAngleVelYaw); network_init_object_field(o, &o->oMoveAngleYaw); network_init_object_field(o, &o->oFaceAngleYaw); - network_init_object_field(o, &o->oGrandStarUnk108); + network_init_object_field(o, &o->oGraphYOffset); } } return; @@ -66,12 +69,12 @@ void bhv_grand_star_loop(void) { spawn_sparkle_particles(3, 200, 80, -60); } else if (o->oAction == 1) { if (o->oTimer == 0) { - Vec3f sp28; - sp28[0] = sp28[1] = sp28[2] = 0.0f; + Vec3f empty; + empty[0] = empty[1] = empty[2] = 0.0f; cur_obj_play_sound_2(SOUND_GENERAL_GRAND_STAR); cutscene_object(CUTSCENE_STAR_SPAWN, o); - o->oGrandStarUnk108 = arc_to_goal_pos(sp28, &o->oPosX, 80.0f, -2.0f); + o->oGrandStarUnk108 = arc_to_goal_pos(empty, &o->oPosX, 80.0f, -2.0f); } cur_obj_move_using_fvel_and_gravity(); if (o->oSubAction == 0) { @@ -92,13 +95,25 @@ void bhv_grand_star_loop(void) { cur_obj_play_sound_2(SOUND_GENERAL_GRAND_STAR_JUMP); } spawn_sparkle_particles(3, 200, 80, -60); - } else { + } else if (o->oAction == 2) { + // Make our object tangible. cur_obj_become_tangible(); + // Check for if the jumbo star has been collected. if (o->oInteractStatus & INT_STATUS_INTERACTED) { + // Make sure we're in the jumbo star cutscene. if (gMarioStates[0].action != ACT_JUMBO_STAR_CUTSCENE) { set_mario_action(&gMarioStates[0], ACT_JUMBO_STAR_CUTSCENE, 0); } + // Increment our action, The star despawns next action. + o->oAction++; + } + } else { + // The star cutscene has started, Make sure the star is deleted + // if it isn't already deactivated. + if (o->activeFlags != ACTIVE_FLAG_DEACTIVATED) { + // Mark our object for deletion. obj_mark_for_deletion(o); + // Reset our interactive status. o->oInteractStatus = 0; } } @@ -108,4 +123,4 @@ void bhv_grand_star_loop(void) { o->oFaceAngleYaw += o->oAngleVelYaw; cur_obj_scale(2.0f); o->oGraphYOffset = 110.0f; -} +} \ No newline at end of file diff --git a/src/game/camera.c b/src/game/camera.c index abdfd0bc..f82fce7a 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -3336,7 +3336,8 @@ void reset_camera(struct Camera *c) { } void init_camera(struct Camera *c) { - struct Surface *floor = 0; + struct Surface *floor = NULL; + struct Object *obj = NULL; Vec3f marioOffset; s32 i; @@ -3391,18 +3392,34 @@ void init_camera(struct Camera *c) { case LEVEL_BOWSER_1: #ifndef VERSION_JP if (gCurrDemoInput == NULL) { + // Make sure Bowser is in a state that we'd start speaking to him in. + obj = find_object_with_behavior(bhvBowser); + if (obj != NULL && obj->oAction != 5) { break; } + start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA); } else if (gSecondCameraFocus != NULL) { gSecondCameraFocus->oBowserUnk88 = 2; } #else + // Make sure Bowser is in a state that we'd start speaking to him in. + obj = find_object_with_behavior(bhvBowser); + if (obj != NULL && obj->oAction != 5) { break; } + start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA); #endif break; case LEVEL_BOWSER_2: + // Make sure Bowser is in a state that we'd start speaking to him in. + obj = find_object_with_behavior(bhvBowser); + if (obj != NULL && obj->oAction != 5) { break; } + start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA); break; case LEVEL_BOWSER_3: + // Make sure Bowser is in a state that we'd start speaking to him in. + obj = find_object_with_behavior(bhvBowser); + if (obj != NULL && obj->oAction != 5) { break; } + start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA); break; diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index b4e2899e..3a362461 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -536,6 +536,14 @@ u8 is_player_active(struct MarioState* m) { return TRUE; } +u8 is_other_player_active(void) { + for (s32 i = 1; i < MAX_PLAYERS; i++) { + struct MarioState *m = &gMarioStates[i]; + if (is_player_active(m)) { return TRUE; } + } + return FALSE; +} + u8 is_player_in_local_area(struct MarioState* m) { if (gNetworkType == NT_NONE && m == &gMarioStates[0]) { return TRUE; } struct NetworkPlayer* np = &gNetworkPlayers[m->playerIndex]; diff --git a/src/game/obj_behaviors.h b/src/game/obj_behaviors.h index 6f7092d7..5dbb8a82 100644 --- a/src/game/obj_behaviors.h +++ b/src/game/obj_behaviors.h @@ -163,6 +163,7 @@ void bhv_rr_cruiser_wing_init(void); void bhv_rr_cruiser_wing_loop(void); struct Object* spawn_default_star(f32 sp20, f32 sp24, f32 sp28); u8 is_player_active(struct MarioState* m); +u8 is_other_player_active(void); u8 is_player_in_local_area(struct MarioState* m); struct MarioState* nearest_mario_state_to_object(struct Object* obj); struct Object* nearest_player_to_object(struct Object* obj); diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index aa2094d2..5ff9a791 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -1036,6 +1036,22 @@ s32 count_objects_with_behavior(const BehaviorScript *behavior) { return count; } +struct Object *find_object_with_behavior(const BehaviorScript *behavior) { + uintptr_t *behaviorAddr = segmented_to_virtual(behavior); + struct ObjectNode *listHead = &gObjectLists[get_object_list_from_behavior(behaviorAddr)]; + struct ObjectNode *obj = listHead->next; + + while (listHead != obj) { + if (((struct Object *) obj)->behavior == behaviorAddr) { + return (struct Object *)obj; + } + + obj = obj->next; + } + + return NULL; +} + struct Object *cur_obj_find_nearby_held_actor(const BehaviorScript *behavior, f32 maxDist) { const BehaviorScript *behaviorAddr = segmented_to_virtual(behavior); struct ObjectNode *listHead; diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 1da5f893..be3ba4b2 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -146,6 +146,7 @@ f32 cur_obj_dist_to_nearest_object_with_behavior(const BehaviorScript* behavior) struct Object* cur_obj_find_nearest_pole(void); struct Object *cur_obj_find_nearest_object_with_behavior(const BehaviorScript * behavior, f32 *dist); u16 cur_obj_count_objects_with_behavior(const BehaviorScript* behavior, f32 dist); +struct Object *find_object_with_behavior(const BehaviorScript *behavior); struct Object *find_unimportant_object(void); s32 count_unimportant_objects(void); s32 count_objects_with_behavior(const BehaviorScript *behavior);