From b0185e44adafb36c380ffcb29a7d1e76b7c1e112 Mon Sep 17 00:00:00 2001 From: Kp Date: Mon, 6 Jan 2020 01:25:35 +0000 Subject: [PATCH] Fix displacing players when synthesizing player start positions TORRaven reported an issue where `Descent: First Strike` level 4 in cooperative games would cause the player 0 ship to be unable to move. On investigation, this occurs when the host has enabled extra player starts, and the resulting synthesis of new player start positions moves the player 0 start out of its origin segment. The physics engine reacts poorly to this situation, causing the object to be unable to move and to report HIT_BAD_P0 in debug builds. Add a test to cancel the displacement if get_seg_masks reports that the center point would move outside the origin segment. More clever logic might be appropriate, but since this is the first report of problems with the feature since its introduction in 5a64ee5132fb, this commit just eliminates the immediate problem. Possible future improvements if the existing displacement causes collisions: - Reduce the magnitude of the existing displacement, instead of abandoning it entirely. - Move the player start to the segment's center before displacing it. - Add logic to move the player into a nearby logically connected segment. Reported-by: TORRaven Fixes: 5a64ee5132fb6028f987bf46f5ab125fcda0a735 ("Add experimental support for larger cooperative games") --- similar/main/gameseq.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/similar/main/gameseq.cpp b/similar/main/gameseq.cpp index c1d4bb8f2..05163bb0e 100644 --- a/similar/main/gameseq.cpp +++ b/similar/main/gameseq.cpp @@ -272,7 +272,7 @@ static unsigned generate_extra_starts_by_displacement_within_segment(const unsig if (vm_vec_dist2(v0, vcvertptr(seg.verts[4])) > size2) capacity_flag |= capacity_z; player_init_segment_capacity_flag[i] = capacity_flag; - con_printf(CON_NORMAL, "Original player %u has size %u and segment capacity flags %x.", i, old_player_obj.size, capacity_flag); + con_printf(CON_NORMAL, "Original player %u has size %u, starts in segment #%hu, and has segment capacity flags %x.", i, old_player_obj.size, static_cast(segnum), capacity_flag); if (capacity_flag) ++segments_with_spare_capacity; } @@ -350,8 +350,14 @@ static unsigned generate_extra_starts_by_displacement_within_segment(const unsig return; vm_vec_normalize(disp); vm_vec_scale(disp, fixmul(old_player_obj.size, size_scalar >> 1)); - con_printf(CON_NORMAL, "Displace player %u at {%i, %i, %i} by {%i, %i, %i}.", plridx, plrobj.pos.x, plrobj.pos.y, plrobj.pos.z, disp.x, disp.y, disp.z); - vm_vec_add2(Player_init[plridx].pos, disp); + const auto target_position = vm_vec_add(Player_init[plridx].pos, disp); + if (const auto sidemask = get_seg_masks(vcvertptr, target_position, vcsegptr(plrobj.segnum), 1).sidemask) + { + con_printf(CON_NORMAL, "Cannot displace player %u at {%i, %i, %i} to {%i, %i, %i}: would be outside segment for sides %x.", plridx, plrobj.pos.x, plrobj.pos.y, plrobj.pos.z, target_position.x, target_position.y, target_position.z, sidemask); + return; + } + con_printf(CON_NORMAL, "Displace player %u at {%i, %i, %i} by {%i, %i, %i} to {%i, %i, %i}.", plridx, plrobj.pos.x, plrobj.pos.y, plrobj.pos.z, disp.x, disp.y, disp.z, target_position.x, target_position.y, target_position.z); + Player_init[plridx].pos = target_position; plrobj.pos = Player_init[plridx].pos; }; for (unsigned extra_player_idx = preplaced_starts, displacements = 0; extra_player_idx < k; ++extra_player_idx)