diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c index f7fa7740d..aae60a52f 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 0f5f4fe9c..f97d89913 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 abdfd0bcd..f82fce7a2 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 b4e2899e4..3a3624619 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 6f7092d71..5dbb8a829 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 aa2094d25..5ff9a791b 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 1da5f8933..be3ba4b21 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);