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 5a64ee5132, 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 <https://github.com/dxx-rebirth/dxx-rebirth/issues/483>
Fixes: 5a64ee5132 ("Add experimental support for larger cooperative games")
This commit is contained in:
Kp 2020-01-06 01:25:35 +00:00
parent ffb4240710
commit b0185e44ad

View file

@ -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_t>(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)