diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index 36b382676..e0687edc3 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj +++ b/build-windows-visual-studio/sm64ex.vcxproj @@ -3949,6 +3949,7 @@ + diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index a44e70813..2dcc0a82c 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -14955,6 +14955,12 @@ Source Files\src\pc\network\packets + + Source Files\src\pc\network\packets + + + Source Files\src\pc\network\packets + diff --git a/levels/wf/script.c b/levels/wf/script.c index 6697b4973..50a0b2cbe 100644 --- a/levels/wf/script.c +++ b/levels/wf/script.c @@ -104,6 +104,7 @@ const LevelScript level_wf_entry[] = { LOAD_RAW( /*seg*/ 0x0F, _common0_geoSegmentRomStart, _common0_geoSegmentRomEnd), ALLOC_LEVEL_POOL(), MARIO(/*model*/ MODEL_MARIO, /*behParam*/ 0x00000001, /*beh*/ bhvMario), + LUIGI(/*model*/ MODEL_LUIGI, /*behParam*/ 0x00000002, /*beh*/ bhvLuigi), JUMP_LINK(script_func_global_1), JUMP_LINK(script_func_global_2), JUMP_LINK(script_func_global_15), diff --git a/src/game/behaviors/bobomb.inc.c b/src/game/behaviors/bobomb.inc.c index 2770a3363..e5b5faf46 100644 --- a/src/game/behaviors/bobomb.inc.c +++ b/src/game/behaviors/bobomb.inc.c @@ -17,7 +17,7 @@ void bhv_bobomb_init(void) { o->oFriction = 0.8; o->oBuoyancy = 1.3; o->oInteractionSubtype = INT_SUBTYPE_KICKABLE; - network_init_object(o, 4000); + network_init_object(o, 4000.0f); } void bobomb_spawn_coin(void) { diff --git a/src/game/behaviors/breakable_box_small.inc.c b/src/game/behaviors/breakable_box_small.inc.c index 639c96dab..fc4ec33b3 100644 --- a/src/game/behaviors/breakable_box_small.inc.c +++ b/src/game/behaviors/breakable_box_small.inc.c @@ -20,7 +20,7 @@ void bhv_breakable_box_small_init(void) { obj_set_hitbox(o, &sBreakableBoxSmallHitbox); o->oAnimState = 1; o->activeFlags |= ACTIVE_FLAG_UNK9; - network_init_object(o, 500); + network_init_object(o, 500.0f); } void small_breakable_box_spawn_dust(void) { diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c index 5f93f277e..babe7e959 100644 --- a/src/game/behaviors/goomba.inc.c +++ b/src/game/behaviors/goomba.inc.c @@ -127,7 +127,7 @@ void bhv_goomba_init(void) { o->oGravity = -8.0f / 3.0f * o->oGoombaScale; - network_init_object(o, 4000); + network_init_object(o, 4000.0f); network_init_object_field(o, &o->oGoombaTargetYaw); network_init_object_field(o, &o->oGoombaWalkTimer); } diff --git a/src/game/behaviors/koopa.inc.c b/src/game/behaviors/koopa.inc.c index 0c8302125..6eb05f774 100644 --- a/src/game/behaviors/koopa.inc.c +++ b/src/game/behaviors/koopa.inc.c @@ -84,6 +84,12 @@ void bhv_koopa_init(void) { } else { o->oKoopaAgility = 1.0f; } + + network_init_object(o, 4000.0f); + network_init_object_field(o, &o->oSubAction); + network_init_object_field(o, &o->oKoopaTargetYaw); + network_init_object_field(o, &o->oKoopaCountdown); + network_init_object_field(o, &o->oKoopaMovementType); } /** @@ -105,8 +111,10 @@ static void koopa_play_footstep_sound(s8 animFrame1, s8 animFrame2) { * running away. */ static s32 koopa_check_run_from_mario(void) { - if (o->oKoopaDistanceToMario < 300.0f - && abs_angle_diff(o->oKoopaAngleToMario, o->oMoveAngleYaw) < 0x3000) { + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); + if (distanceToPlayer < 300.0f && abs_angle_diff(angleToPlayer, o->oMoveAngleYaw) < 0x3000) { o->oAction = KOOPA_SHELLED_ACT_RUN_FROM_MARIO; return TRUE; } @@ -170,9 +178,12 @@ static void koopa_shelled_act_walk(void) { if (o->oKoopaTurningAwayFromWall) { o->oKoopaTurningAwayFromWall = obj_resolve_collisions_and_turn(o->oKoopaTargetYaw, 0x200); } else { + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); // If far from home, then begin turning toward home - if (o->oDistanceToMario >= 25000.0f) { - o->oKoopaTargetYaw = o->oAngleToMario; + if (distanceToPlayer >= 25000.0f) { + o->oKoopaTargetYaw = angleToPlayer; } o->oKoopaTurningAwayFromWall = obj_bounce_off_walls_edges_objects(&o->oKoopaTargetYaw); @@ -202,18 +213,22 @@ static void koopa_shelled_act_run_from_mario(void) { cur_obj_init_animation_with_sound(1); koopa_play_footstep_sound(0, 11); + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); + // If far from home, run toward it - if (o->oDistanceToMario >= 25000.0f) { - o->oAngleToMario += 0x8000; - o->oDistanceToMario = 0.0f; + if (distanceToPlayer >= 25000.0f) { + angleToPlayer += 0x8000; + distanceToPlayer = 0.0f; } - if (o->oTimer > 30 && o->oDistanceToMario > 800.0f) { + if (o->oTimer > 30 && distanceToPlayer > 800.0f) { if (obj_forward_vel_approach(0.0f, 1.0f)) { o->oAction = KOOPA_SHELLED_ACT_STOPPED; } } else { - cur_obj_rotate_yaw_toward(o->oAngleToMario + 0x8000, 0x400); + cur_obj_rotate_yaw_toward(angleToPlayer + 0x8000, 0x400); obj_forward_vel_approach(17.0f, 1.0f); } } @@ -265,7 +280,9 @@ void shelled_koopa_attack_handler(s32 attackType) { // If attacked from the side, get knocked away from mario if (attackType != ATTACK_FROM_ABOVE && attackType != ATTACK_GROUND_POUND_OR_TWIRL) { - o->oMoveAngleYaw = obj_angle_to_object(gMarioObject, o); + struct Object* player = nearest_player_to_object(o); + int angleToPlayer = obj_angle_to_object(o, player); + o->oMoveAngleYaw = angleToPlayer; } cur_obj_set_model(MODEL_KOOPA_WITHOUT_SHELL); @@ -285,6 +302,10 @@ void shelled_koopa_attack_handler(s32 attackType) { * Update function for both regular and tiny shelled koopa. */ static void koopa_shelled_update(void) { + if (!cur_obj_has_model(MODEL_KOOPA_WITH_SHELL)) { + cur_obj_set_model(MODEL_KOOPA_WITH_SHELL); + } + cur_obj_update_floor_and_walls(); obj_update_blinking(&o->oKoopaBlinkTimer, 20, 50, 4); @@ -336,9 +357,13 @@ static void koopa_unshelled_act_run(void) { if (o->oKoopaTurningAwayFromWall) { o->oKoopaTurningAwayFromWall = obj_resolve_collisions_and_turn(o->oKoopaTargetYaw, 0x600); } else { + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); + // If far from home, then turn toward home - if (o->oDistanceToMario >= 25000.0f) { - o->oKoopaTargetYaw = o->oAngleToMario; + if (distanceToPlayer >= 25000.0f) { + o->oKoopaTargetYaw = angleToPlayer; } // If shell exists, then turn toward shell @@ -358,9 +383,9 @@ static void koopa_unshelled_act_run(void) { // If mario is far away, or our running away from mario coincides with // running toward the shell - if (o->oDistanceToMario > 800.0f + if (distanceToPlayer > 800.0f || (shell != NULL - && abs_angle_diff(o->oKoopaTargetYaw, o->oAngleToMario + 0x8000) < 0x2000)) { + && abs_angle_diff(o->oKoopaTargetYaw, angleToPlayer + 0x8000) < 0x2000)) { // then turn toward the shell cur_obj_rotate_yaw_toward(o->oKoopaTargetYaw, 0x600); } else { @@ -395,6 +420,10 @@ static void koopa_unshelled_act_dive(void) { if (o->oTimer > 10) { shell = cur_obj_find_nearest_object_with_behavior(bhvKoopaShell, &distToShell); + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); + // If we got the shell and mario didn't, put on the shell //! The shell comes after koopa in processing order, and the shell is // responsible for positioning itself under mario. @@ -403,8 +432,7 @@ static void koopa_unshelled_act_dive(void) { // units behind mario. // Using this, we can get the koopa to pick up and despawn its shell // while mario is riding it. - if (shell != NULL && dist_between_objects(shell, gMarioObject) > 200.0f - && distToShell < 50.0f) { + if (shell != NULL && distanceToPlayer && distToShell < 50.0f) { o->oKoopaMovementType = KOOPA_BP_NORMAL; o->oAction = KOOPA_SHELLED_ACT_LYING; o->oForwardVel *= 0.5f; @@ -444,6 +472,10 @@ static void koopa_unshelled_act_unused3(void) { * Update function for koopa after losing his shell. */ static void koopa_unshelled_update(void) { + if (!cur_obj_has_model(MODEL_KOOPA_WITHOUT_SHELL)) { + cur_obj_set_model(MODEL_KOOPA_WITHOUT_SHELL); + } + cur_obj_update_floor_and_walls(); obj_update_blinking(&o->oKoopaBlinkTimer, 10, 15, 3); @@ -615,7 +647,10 @@ static void koopa_the_quick_act_race(void) { case KOOPA_THE_QUICK_SUB_ACT_RUN: koopa_the_quick_animate_footsteps(); - if (o->parentObj->oKoopaRaceEndpointRaceStatus != 0 && o->oDistanceToMario > 1500.0f + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + + if (o->parentObj->oKoopaRaceEndpointRaceStatus != 0 && distanceToPlayer > 1500.0f && (o->oPathedPrevWaypointFlags & WAYPOINT_MASK_00FF) < 28) { // Move faster if mario has already finished the race or // cheated by shooting from cannon @@ -796,8 +831,12 @@ void bhv_koopa_update(void) { if (o->oKoopaMovementType >= KOOPA_BP_KOOPA_THE_QUICK_BASE) { koopa_the_quick_update(); } else if (obj_update_standard_actions(o->oKoopaAgility * 1.5f)) { - o->oKoopaDistanceToMario = o->oDistanceToMario; - o->oKoopaAngleToMario = o->oAngleToMario; + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + int angleToPlayer = obj_angle_to_object(o, player); + + o->oKoopaDistanceToMario = distanceToPlayer; + o->oKoopaAngleToMario = angleToPlayer; treat_far_home_as_mario(1000.0f); switch (o->oKoopaMovementType) { @@ -824,7 +863,9 @@ void bhv_koopa_update(void) { */ void bhv_koopa_race_endpoint_update(void) { if (o->oKoopaRaceEndpointRaceBegun && !o->oKoopaRaceEndpointRaceEnded) { - if (o->oKoopaRaceEndpointKoopaFinished || o->oDistanceToMario < 400.0f) { + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + if (o->oKoopaRaceEndpointKoopaFinished || distanceToPlayer < 400.0f) { o->oKoopaRaceEndpointRaceEnded = TRUE; level_control_timer(TIMER_CONTROL_STOP); diff --git a/src/game/behaviors/koopa_shell.inc.c b/src/game/behaviors/koopa_shell.inc.c index c408bed76..7c4237bd7 100644 --- a/src/game/behaviors/koopa_shell.inc.c +++ b/src/game/behaviors/koopa_shell.inc.c @@ -16,7 +16,7 @@ void koopa_shell_spawn_water_drop(void) { UNUSED s32 unused; struct Object *drop; spawn_object(o, MODEL_WAVE_TRAIL, bhvObjectWaveTrail); - if (gMarioStates->forwardVel > 10.0f) { + if (gMarioStates[o->heldByPlayerIndex].forwardVel > 10.0f) { drop = spawn_object_with_scale(o, MODEL_WHITE_PARTICLE_SMALL, bhvWaterDroplet, 1.5f); drop->oVelY = random_float() * 30.0f; obj_translate_xz_random(drop, 110.0f); @@ -52,21 +52,32 @@ void koopa_shell_spawn_sparkles(f32 a) { } void bhv_koopa_shell_loop(void) { + if (o->oSyncID == 0) { + network_init_object(o, 500.0f); + network_init_object_field(o, &o->oInteractStatus); + network_init_object_field(o, &o->oAction); + } + struct Surface *sp34; obj_set_hitbox(o, &sKoopaShellHitbox); cur_obj_scale(1.0f); + struct Object* player = NULL; switch (o->oAction) { case 0: cur_obj_update_floor_and_walls(); cur_obj_if_hit_wall_bounce_away(); - if (o->oInteractStatus & INT_STATUS_INTERACTED) + if (o->oInteractStatus & INT_STATUS_INTERACTED) { o->oAction++; + player = nearest_player_to_object(o); + o->heldByPlayerIndex = (player == gMarioObject) ? 0 : 1; + } o->oFaceAngleYaw += 0x1000; cur_obj_move_standard(-20); koopa_shell_spawn_sparkles(10.0f); break; case 1: - obj_copy_pos(o, gMarioObject); + player = gMarioStates[o->heldByPlayerIndex].marioObj; + obj_copy_pos(o, player); sp34 = cur_obj_update_floor_height_and_get_floor(); if (absf(find_water_level(o->oPosX, o->oPosZ) - o->oPosY) < 10.0f) koopa_shell_spawn_water_drop(); @@ -77,7 +88,7 @@ void bhv_koopa_shell_loop(void) { koopa_shell_spawn_sparkles(10.0f); } else koopa_shell_spawn_sparkles(10.0f); - o->oFaceAngleYaw = gMarioObject->oMoveAngleYaw; + o->oFaceAngleYaw = player->oMoveAngleYaw; if (o->oInteractStatus & INT_STATUS_STOP_RIDING) { obj_mark_for_deletion(o); spawn_mist_particles(); diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index ec61c714d..2c1e2c05b 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -515,6 +515,27 @@ s32 is_point_within_radius_of_mario(f32 x, f32 y, f32 z, s32 dist) { return FALSE; } +/** + * Returns either gMarioObject or gLuigiObject depending on what is closer + */ +struct MarioState* nearest_mario_state_to_object(struct Object *obj) { + f32 mx = gMarioState[0].marioObj->header.gfx.pos[0] - obj->oPosX; + f32 my = gMarioState[0].marioObj->header.gfx.pos[1] - obj->oPosY; + f32 mz = gMarioState[0].marioObj->header.gfx.pos[2] - obj->oPosZ; + mx *= mx; + my *= my; + mz *= mz; + + f32 lx = gMarioState[1].marioObj->header.gfx.pos[0] - obj->oPosX; + f32 ly = gMarioState[1].marioObj->header.gfx.pos[1] - obj->oPosY; + f32 lz = gMarioState[1].marioObj->header.gfx.pos[2] - obj->oPosZ; + lx *= lx; + ly *= ly; + lz *= lz; + + return (mx + my + mz <= lx + ly + lz) ? &gMarioState[0] : &gMarioState[1]; +} + /** * Returns either gMarioObject or gLuigiObject depending on what is closer */ diff --git a/src/pc/network/network.h b/src/pc/network/network.h index dcb43668b..22af5d3f6 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -35,6 +35,7 @@ struct SyncObject { float maxSyncDistance; bool owned; unsigned int ticksSinceUpdate; + void* behavior; u8 extraFieldCount; void* extraFields[MAX_SYNC_OBJECT_FIELDS]; }; diff --git a/src/pc/network/packets/packet_object.c b/src/pc/network/packets/packet_object.c index 20b8341f4..a627a9d6d 100644 --- a/src/pc/network/packets/packet_object.c +++ b/src/pc/network/packets/packet_object.c @@ -23,6 +23,7 @@ void network_init_object(struct Object *o, float maxSyncDistance) { so->owned = false; so->ticksSinceUpdate = -1; so->extraFieldCount = 0; + so->behavior = o->behavior; memset(so->extraFields, 0, sizeof(void*) * MAX_SYNC_OBJECT_FIELDS); } @@ -41,6 +42,7 @@ void network_send_object(struct Object* o) { struct Packet p; packet_init(&p, PACKET_OBJECT, reliable); packet_write(&p, &o->oSyncID, 4); + packet_write(&p, &so->behavior, sizeof(void*)); packet_write(&p, &o->activeFlags, 2); packet_write(&p, &o->oPosX, 28); packet_write(&p, &o->oAction, 4); @@ -57,6 +59,10 @@ void network_send_object(struct Object* o) { if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { forget_sync_object(so); } + if (o->behavior != so->behavior) { + printf("network_send_object() BEHAVIOR MISMATCH!\n"); + } + network_send(&p); } @@ -91,6 +97,7 @@ void network_receive_object(struct Packet* p) { } // write object flags + packet_read(p, &so->behavior, sizeof(void*)); packet_read(p, &o->activeFlags, 2); packet_read(p, &o->oPosX, 28); packet_read(p, &o->oAction, 4); @@ -106,11 +113,14 @@ void network_receive_object(struct Packet* p) { packet_read(p, so->extraFields[i], 4); } + if (o->behavior != so->behavior) { + printf("network_receive_object() BEHAVIOR MISMATCH!\n"); + } + // deactivated if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { forget_sync_object(so); } - } float player_distance(struct MarioState* marioState, struct Object* o) {