From 13b216ea5d56ca7687eaea7bad90405d2cec0d0a Mon Sep 17 00:00:00 2001 From: MysterD Date: Mon, 24 Aug 2020 21:38:49 -0700 Subject: [PATCH] Synchronized Boo --- src/game/behaviors/boo.inc.c | 158 +++++++++++++++++++------ src/game/behaviors/boo_cage.inc.c | 11 +- src/game/behaviors/chain_chomp.inc.c | 3 +- src/game/level_update.c | 2 +- src/pc/network/network.h | 1 + src/pc/network/packets/packet_object.c | 23 +++- 6 files changed, 154 insertions(+), 44 deletions(-) diff --git a/src/game/behaviors/boo.inc.c b/src/game/behaviors/boo.inc.c index a95ff67e6..26e0c1822 100644 --- a/src/game/behaviors/boo.inc.c +++ b/src/game/behaviors/boo.inc.c @@ -19,6 +19,25 @@ static s16 sCourtyardBooTripletPositions[][3] = { {-210, 70, -210} }; +void boo_network_init_object(void) { + network_init_object(o, 4000.0f); + network_init_object_field(o, &o->oBooBaseScale); + network_init_object_field(o, &o->oBooNegatedAggressiveness); + network_init_object_field(o, &o->oBooOscillationTimer); + network_init_object_field(o, &o->oBooTargetOpacity); + network_init_object_field(o, &o->oBooTurningSpeed); + network_init_object_field(o, &o->oFaceAngleRoll); + network_init_object_field(o, &o->oFaceAngleYaw); + network_init_object_field(o, &o->oFlags); + network_init_object_field(o, &o->oForwardVel); + network_init_object_field(o, &o->oHealth); + network_init_object_field(o, &o->oInteractStatus); + network_init_object_field(o, &o->oInteractType); + network_init_object_field(o, &o->oOpacity); + network_init_object_field(o, &o->oRoom); + network_init_object_field(o, &o->oBigBooNumMinionBoosKilled); +} + static void boo_stop(void) { o->oForwardVel = 0.0f; o->oVelY = 0.0f; @@ -52,6 +71,15 @@ static s32 boo_should_be_stopped(void) { } static s32 boo_should_be_active(void) { + struct MarioState* marioState = nearest_mario_state_to_object(o); + int distanceToPlayer = dist_between_objects(o, marioState->marioObj); + + u8 inRoom = FALSE; + for (int i = 0; i < MAX_PLAYERS; i++) { + if (marioState->floor == NULL) { continue; } + inRoom = inRoom || (marioState->floor->room == o->oRoom); + } + f32 activationRadius; if (cur_obj_has_behavior(bhvBalconyBigBoo)) { @@ -67,14 +95,11 @@ static s32 boo_should_be_active(void) { return FALSE; } } else if (o->oRoom == -1) { - if (o->oDistanceToMario < activationRadius) { + if (distanceToPlayer < activationRadius) { return TRUE; } } else if (!boo_should_be_stopped()) { - if ( - o->oDistanceToMario < activationRadius && - (o->oRoom == gMarioCurrentRoom || gMarioCurrentRoom == 0) - ) { + if (distanceToPlayer < activationRadius && (inRoom || gMarioCurrentRoom == 0)) { return TRUE; } } @@ -141,8 +166,11 @@ static void boo_oscillate(s32 ignoreOpacity) { } static s32 boo_vanish_or_appear(void) { - s16 relativeAngleToMario = abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw); - s16 relativeMarioFaceAngle = abs_angle_diff(o->oMoveAngleYaw, gMarioObject->oFaceAngleYaw); + struct Object* player = nearest_player_to_object(o); + int angleToPlayer = obj_angle_to_object(o, player); + + s16 relativeAngleToMario = abs_angle_diff(angleToPlayer, o->oMoveAngleYaw); + s16 relativeMarioFaceAngle = abs_angle_diff(o->oMoveAngleYaw, player->oFaceAngleYaw); // magic? s16 relativeAngleToMarioThreshhold = 0x1568; s16 relativeMarioFaceAngleThreshhold = 0x6b58; @@ -150,10 +178,7 @@ static s32 boo_vanish_or_appear(void) { o->oVelY = 0.0f; - if ( - relativeAngleToMario > relativeAngleToMarioThreshhold || - relativeMarioFaceAngle < relativeMarioFaceAngleThreshhold - ) { + if (relativeAngleToMario > relativeAngleToMarioThreshhold || relativeMarioFaceAngle < relativeMarioFaceAngleThreshhold) { if (o->oOpacity == 40) { o->oBooTargetOpacity = 255; cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_LONG); @@ -170,14 +195,17 @@ static s32 boo_vanish_or_appear(void) { } static void boo_set_move_yaw_for_during_hit(s32 hurt) { + struct Object* player = nearest_player_to_object(o); + int angleToPlayer = obj_angle_to_object(o, player); + cur_obj_become_intangible(); o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW; o->oBooMoveYawBeforeHit = (f32) o->oMoveAngleYaw; if (hurt != FALSE) { - o->oBooMoveYawDuringHit = gMarioObject->oMoveAngleYaw; - } else if (coss((s16)o->oMoveAngleYaw - (s16)o->oAngleToMario) < 0.0f) { + o->oBooMoveYawDuringHit = player->oMoveAngleYaw; + } else if (coss((s16)o->oMoveAngleYaw - (s16)angleToPlayer) < 0.0f) { o->oBooMoveYawDuringHit = o->oMoveAngleYaw; } else { o->oBooMoveYawDuringHit = (s16)(o->oMoveAngleYaw + 0x8000); @@ -261,11 +289,13 @@ static s32 big_boo_update_during_nonlethal_hit(f32 a0) { // called every frame once mario lethally hits the boo until the boo is deleted, // returns whether death is complete static s32 boo_update_during_death(void) { + struct Object* player = nearest_player_to_object(o); + struct Object *parentBigBoo; if (o->oTimer == 0) { o->oForwardVel = 40.0f; - o->oMoveAngleYaw = gMarioObject->oMoveAngleYaw; + o->oMoveAngleYaw = player->oMoveAngleYaw; o->oBooDeathStatus = BOO_DEATH_STATUS_DYING; o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW; } else { @@ -280,13 +310,17 @@ static s32 boo_update_during_death(void) { if (o->oBooParentBigBoo != NULL) { parentBigBoo = o->oBooParentBigBoo; + struct MarioState* marioState = nearest_mario_state_to_object(o); + if (marioState->playerIndex == 0) { #ifndef VERSION_JP - if (!cur_obj_has_behavior(bhvBoo)) { - parentBigBoo->oBigBooNumMinionBoosKilled++; - } + if (!cur_obj_has_behavior(bhvBoo)) { + parentBigBoo->oBigBooNumMinionBoosKilled++; + } #else - parentBigBoo->oBigBooNumMinionBoosKilled++; + parentBigBoo->oBigBooNumMinionBoosKilled++; #endif + network_send_object_reliability(parentBigBoo, TRUE); + } } return TRUE; @@ -334,29 +368,33 @@ static s32 boo_get_attack_status(void) { // boo idle/chasing movement? static void boo_chase_mario(f32 a0, s16 a1, f32 a2) { + struct MarioState* marioState = nearest_mario_state_to_object(o); + struct Object* player = marioState->marioObj; + int angleToPlayer = obj_angle_to_object(o, player); + f32 sp1C; s16 sp1A; if (boo_vanish_or_appear()) { o->oInteractType = 0x8000; - if (cur_obj_lateral_dist_from_mario_to_home() > 1500.0f) { + if (cur_obj_lateral_dist_from_obj_to_home(player) > 1500.0f) { sp1A = cur_obj_angle_to_home(); } else { - sp1A = o->oAngleToMario; + sp1A = angleToPlayer; } cur_obj_rotate_yaw_toward(sp1A, a1); o->oVelY = 0.0f; - if (mario_is_in_air_action(&gMarioStates[0]) == 0) { - sp1C = o->oPosY - gMarioObject->oPosY; + if (mario_is_in_air_action(marioState) == 0) { + sp1C = o->oPosY - player->oPosY; if (a0 < sp1C && sp1C < 500.0f) { - o->oVelY = increment_velocity_toward_range(o->oPosY, gMarioObject->oPosY + 50.0f, 10.f, 2.0f); + o->oVelY = increment_velocity_toward_range(o->oPosY, player->oPosY + 50.0f, 10.f, 2.0f); } } - cur_obj_set_vel_from_mario_vel(10.0f - o->oBooNegatedAggressiveness, a2); + cur_obj_set_vel_from_mario_vel(marioState, 10.0f - o->oBooNegatedAggressiveness, a2); if (o->oForwardVel != 0.0f) { boo_oscillate(FALSE); @@ -464,7 +502,8 @@ static void boo_act_4(void) { dialogID = DIALOG_107; } - if (cur_obj_update_dialog(&gMarioState[0], 2, 2, dialogID, 0)) { + struct MarioState* marioState = nearest_mario_state_to_object(o); + if (marioState->playerIndex == 0 && cur_obj_update_dialog(&gMarioState[0], 2, 2, dialogID, 0)) { create_sound_spawner(SOUND_OBJ_DYING_ENEMY1); obj_mark_for_deletion(o); @@ -484,6 +523,7 @@ static void (*sBooActions[])(void) = { }; void bhv_boo_loop(void) { + if (!network_sync_object_initialized(o)) { boo_network_init_object(); } //PARTIAL_UPDATE cur_obj_update_floor_and_walls(); @@ -494,6 +534,7 @@ void bhv_boo_loop(void) { if (obj_has_behavior(o->parentObj, bhvMerryGoRoundBooManager)) { if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { o->parentObj->oMerryGoRoundBooManagerNumBoosKilled++; + network_send_object(o->parentObj); } } @@ -638,10 +679,13 @@ static void big_boo_act_4(void) { boo_stop(); #endif + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + if (o->oBehParams2ndByte == 0) { obj_set_pos(o, 973, 0, 626); - if (o->oTimer > 60 && o->oDistanceToMario < 600.0f) { + if (o->oTimer > 60 && distanceToPlayer < 600.0f) { obj_set_pos(o, 973, 0, 717); spawn_object_relative(0, 0, 0, 0, o, MODEL_BBH_STAIRCASE_STEP, bhvBooBossSpawnedBridge); @@ -664,6 +708,7 @@ static void (*sBooGivingStarActions[])(void) = { }; void bhv_big_boo_loop(void) { + if (!network_sync_object_initialized(o)) { boo_network_init_object(); } //PARTIAL_UPDATE obj_set_hitbox(o, &sBooGivingStarHitbox); @@ -741,8 +786,8 @@ static void (*sBooWithCageActions[])(void) = { boo_with_cage_act_3 }; -void bhv_boo_with_cage_loop(void) -{ +void bhv_boo_with_cage_loop(void) { + if (!network_sync_object_initialized(o)) { boo_network_init_object(); } //PARTIAL_UPDATE cur_obj_update_floor_and_walls(); @@ -754,14 +799,31 @@ void bhv_boo_with_cage_loop(void) } void bhv_merry_go_round_boo_manager_loop(void) { + if (!network_sync_object_initialized(o)) { + network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oMerryGoRoundBooManagerNumBoosSpawned); + network_init_object_field(o, &o->oMerryGoRoundBooManagerNumBoosKilled); + } + + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + switch (o->oAction) { case 0: - if (o->oDistanceToMario < 1000.0f) { - if (o->oMerryGoRoundBooManagerNumBoosKilled < 5) { - if (o->oMerryGoRoundBooManagerNumBoosSpawned != 5) { + if (distanceToPlayer < 1000.0f) { + if (networkType == NT_SERVER && o->oMerryGoRoundBooManagerNumBoosKilled < 5) { + if (o->oMerryGoRoundBooManagerNumBoosSpawned < 5) { if (o->oMerryGoRoundBooManagerNumBoosSpawned - o->oMerryGoRoundBooManagerNumBoosKilled < 2) { - spawn_object(o, MODEL_BOO, bhvMerryGoRoundBoo); + struct Object* boo = spawn_object(o, MODEL_BOO, bhvMerryGoRoundBoo); + + network_set_sync_id(boo); + struct Object* spawn_objects[] = { boo }; + u32 models[] = { MODEL_BOO }; + network_send_spawn_objects(spawn_objects, models, 1); + o->oMerryGoRoundBooManagerNumBoosSpawned++; + network_send_object(o); } } @@ -769,11 +831,18 @@ void bhv_merry_go_round_boo_manager_loop(void) { } if (o->oMerryGoRoundBooManagerNumBoosKilled > 4) { - struct Object *boo = spawn_object(o, MODEL_BOO, bhvMerryGoRoundBigBoo); - obj_copy_behavior_params(boo, o); + if (networkType == NT_SERVER) { + struct Object* boo = spawn_object(o, MODEL_BOO, bhvMerryGoRoundBigBoo); + obj_copy_behavior_params(boo, o); - o->oAction = 2; + network_set_sync_id(boo); + struct Object* spawn_objects[] = { boo }; + u32 models[] = { MODEL_BOO }; + network_send_spawn_objects(spawn_objects, models, 1); + o->oAction = 2; + network_send_object(o); + } #ifndef VERSION_JP play_puzzle_jingle(); #else @@ -803,6 +872,19 @@ void bhv_animated_texture_loop(void) { } void bhv_boo_in_castle_loop(void) { + if (!network_sync_object_initialized(o)) { boo_network_init_object(); } + + struct MarioState* marioState = nearest_mario_state_to_object(o); + struct Object* player = marioState->marioObj; + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); + + u8 inRoom = FALSE; + for (int i = 0; i < MAX_PLAYERS; i++) { + if (marioState->floor == NULL) { continue; } + inRoom = inRoom || (marioState->floor->room == 1); + } + s16 targetAngle; o->oBooBaseScale = 2.0f; @@ -814,7 +896,7 @@ void bhv_boo_in_castle_loop(void) { obj_mark_for_deletion(o); } - if (gMarioCurrentRoom == 1) { + if (inRoom) { o->oAction++; } } else if (o->oAction == 1) { @@ -826,13 +908,13 @@ void bhv_boo_in_castle_loop(void) { cur_obj_scale(o->oBooBaseScale); } - if (o->oDistanceToMario < 1000.0f) { + if (distanceToPlayer < 1000.0f) { o->oAction++; cur_obj_play_sound_2(SOUND_OBJ_BOO_LAUGH_LONG); } o->oForwardVel = 0.0f; - targetAngle = o->oAngleToMario; + targetAngle = angleToPlayer; } else { cur_obj_forward_vel_approach_upward(32.0f, 1.0f); diff --git a/src/game/behaviors/boo_cage.inc.c b/src/game/behaviors/boo_cage.inc.c index 24e4e0622..4daf87b06 100644 --- a/src/game/behaviors/boo_cage.inc.c +++ b/src/game/behaviors/boo_cage.inc.c @@ -25,6 +25,15 @@ static struct ObjectHitbox sBooCageHitbox = { * Update function for bhvBooCage. */ void bhv_boo_cage_loop(void) { + if (!network_sync_object_initialized(o)) { + struct SyncObject* so = network_init_object(o, 4000.0f); + network_init_object_field(o, &o->oVelY); + network_init_object_field(o, &o->oFaceAnglePitch); + network_init_object_field(o, &o->oFaceAngleRoll); + network_init_object_field(o, &o->oMoveFlags); + so->maxUpdateRate = 5.0f; + } + UNUSED s32 unused; obj_set_hitbox(o, &sBooCageHitbox); @@ -86,7 +95,7 @@ void bhv_boo_cage_loop(void) { cur_obj_scale(1.0f); // Set the action to BOO_CAGE_ACT_MARIO_JUMPING_IN when Mario jumps in. - if (obj_check_if_collided_with_object(o, gMarioObject)) { + if (obj_check_if_collided_with_object(o, gMarioStates[0].marioObj)) { o->oAction++; } diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c index cd5cb376d..380ffb62e 100644 --- a/src/game/behaviors/chain_chomp.inc.c +++ b/src/game/behaviors/chain_chomp.inc.c @@ -492,7 +492,8 @@ static void chain_chomp_act_unload_chain(void) { */ void bhv_chain_chomp_update(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, 1000.0f); + struct SyncObject* so = network_init_object(o, 1000.0f); + so->syncDeathEvent = FALSE; network_init_object_field(o, &o->oChainChompUnk104); network_init_object_field(o, &o->header.gfx.unk38.animFrame); } diff --git a/src/game/level_update.c b/src/game/level_update.c index da308ff96..3d68d9009 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -711,7 +711,7 @@ s16 level_trigger_warp(struct MarioState *m, s32 warpOp) { // only allow for local player if (m != &gMarioStates[0]) { return 0; } - gControlPainting = FALSE; + gControlPainting = TRUE; s32 val04 = TRUE; if (sDelayedWarpOp == WARP_OP_NONE) { diff --git a/src/pc/network/network.h b/src/pc/network/network.h index 872b45267..40c825368 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -94,6 +94,7 @@ void network_set_sync_id(struct Object* o); bool network_sync_object_initialized(struct Object* o); void network_update_objects(void); void network_send_object(struct Object* o); +void network_send_object_reliability(struct Object* o, bool reliable); void network_receive_object(struct Packet* p); void network_send_spawn_objects(struct Object* objects[], u32 models[], u8 objectCount); diff --git a/src/pc/network/packets/packet_object.c b/src/pc/network/packets/packet_object.c index 63f9bfbc9..c4634a2c6 100644 --- a/src/pc/network/packets/packet_object.c +++ b/src/pc/network/packets/packet_object.c @@ -278,13 +278,30 @@ void network_send_object(struct Object* o) { return; } + bool reliable = (o->activeFlags == ACTIVE_FLAG_DEACTIVATED || so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS); + network_send_object_reliability(o, reliable); +} + +void network_send_object_reliability(struct Object* o, bool reliable) { + // sanity check SyncObject + if (!network_sync_object_initialized(o)) { return; } + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so == NULL) { return; } + if (o->behavior != so->behavior) { + printf("network_send_object() BEHAVIOR MISMATCH!\n"); + forget_sync_object(so); + return; + } + if (o != so->o) { + printf("network_send_object() OBJECT MISMATCH!\n"); + forget_sync_object(so); + return; + } + // always send a new event ID so->onEventId++; so->clockSinceUpdate = clock(); - // reliable packets when sending a dead object or event - bool reliable = (o->activeFlags == ACTIVE_FLAG_DEACTIVATED || so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS); - // write the packet data struct Packet p; packet_init(&p, PACKET_OBJECT, reliable);