mirror of
https://github.com/astromaddie/HaloCE-VR.git
synced 2026-10-20 10:00:14 +02:00
Import only independently reviewed production changes. Preserve canonical physics and virtual-keyboard repairs; keep B3 and all unresolved holds out. Record exactly four approved canary debits (6987 meaningful bytes), six Halo admissions and two revocations. Keep vendor ioinit/storage work separate. Full build, 8252-row sweep, 1318 tests, park/admission/fake audits and 621-object binding pass within approved debit bounds. Do not publish donor research dumps or private assets. README and unrelated user research remain untouched. See frontier_publication_reconciliation_20260927.md for complete evidence limits and accounting.
690 lines
18 KiB
C
690 lines
18 KiB
C
/*
|
|
PATH_STRUCTURE_BSP.C
|
|
|
|
symbols in this file:
|
|
00051D80 01e0:
|
|
_structure_test_ray2d (0000)
|
|
00051F60 0080:
|
|
_structure_surfaces_are_equivalent (0000)
|
|
00051FE0 0070:
|
|
_clip_empty_interval_by_solid_interval (0000)
|
|
00052050 0450:
|
|
_structure_test_line2d (0000)
|
|
000524A0 0060:
|
|
_structure_surface_index_from_point (0000)
|
|
00052500 0270:
|
|
_structure_test_pill2d (0000)
|
|
0024EC0C 0020:
|
|
_quantized_pathfinding_surface_widths (0000)
|
|
0024EC2C 0020:
|
|
_quantized_pathfinding_surface_heights (0000)
|
|
0024EC4C 0027:
|
|
??_C@_0CH@JLKIDLBA@c?3?2halo?2SOURCE?2ai?2path_structure@ (0000)
|
|
0024EC78 0008:
|
|
__real@3fa99999a0000000 (0000)
|
|
0024EC80 0004:
|
|
__real@3c000000 (0000)
|
|
0024EC88 0048:
|
|
??_C@_0EI@IFBLLPMB@surface_index?$DO?$DN0?5?$CG?$CG?5surface_inde@ (0000)
|
|
0024ECD0 0007:
|
|
??_C@_06KJFADLGN@result?$AA@ (0000)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "path_structure_bsp.h"
|
|
|
|
#include "math/real_math.h"
|
|
#include "physics/breakable_surfaces.h"
|
|
#include "physics/collision_bsp.h"
|
|
#include "physics/collision_bsp_definitions.h"
|
|
#include "structures/structure_bsp_definitions.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
_pathfinding_surface_walkable_bit = 6,
|
|
_pathfinding_surface_breakable_bit = 7,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_collision_surface_breakable_bit = 3,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_path_test_pill_endpoint_near_wall_ok_bit = 0,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
/* ---------- structures */
|
|
|
|
/* ---------- prototypes */
|
|
|
|
/* ---------- globals */
|
|
|
|
static real const quantized_pathfinding_surface_widths[8] =
|
|
{
|
|
0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 1.5f, 2.0f, 4.0f
|
|
};
|
|
|
|
static real const quantized_pathfinding_surface_heights[8] =
|
|
{
|
|
0.25f, 0.5f, 0.75f, 1.0f, 1.5f, 2.0f, 4.0f, 8.0f
|
|
};
|
|
|
|
/* ---------- public code */
|
|
|
|
boolean structure_test_ray2d(
|
|
struct structure_bsp const *structure,
|
|
boolean ignore_broken_surfaces,
|
|
real_point2d const *point,
|
|
long surface_index,
|
|
real_vector2d const *direction,
|
|
real distance,
|
|
struct structure_test_ray2d_result *result)
|
|
{
|
|
struct collision_surface_test_line2d_result surface_result;
|
|
struct collision_bsp const *bsp;
|
|
byte *breakable_surface_flags;
|
|
byte const *pathfinding_surfaces;
|
|
|
|
bsp = TAG_BLOCK_GET_ELEMENT(&structure->collision_bsp, 0, struct collision_bsp);
|
|
breakable_surface_flags = breakable_surface_flags_get();
|
|
pathfinding_surfaces = structure->pathfinding_surfaces.address;
|
|
|
|
collision_surface_test_line2d(
|
|
bsp,
|
|
surface_index,
|
|
_z,
|
|
TRUE,
|
|
point,
|
|
direction,
|
|
&surface_result);
|
|
|
|
/* BUG (preserved for exact matching): January loads
|
|
* pathfinding_surfaces[surface_result.enter_surface_index] (+0x5d..+0x63) and the exit
|
|
* index (+0xe6..+0xec) before any NONE test; the later /Od build does the same (0x4cfe60
|
|
* +0x98, +0x168). For a finite distance both indices come from a crossed edge
|
|
* (collision_surface_test_line2d starts at REAL_MIN/REAL_MAX), so they are NONE only for an
|
|
* edge with no surface on its far side (an open collision BSP). The read then takes the byte
|
|
* before the array: zero, or a value adopted as next_surface_index == NONE, stops at the
|
|
* edge; with the breakable bit set and broken surfaces not ignored, TAG_BLOCK_GET_ELEMENT
|
|
* receives NONE and its index assertion halts. No structure BSP in the shipped
|
|
* 01.10.12.2276 maps has an open edge (0 of 2,066,607 edges in 82 BSPs).
|
|
*/
|
|
while (TRUE)
|
|
{
|
|
long next_surface_index = NONE;
|
|
|
|
if (distance < surface_result.enter_t &&
|
|
pathfinding_surfaces[surface_result.enter_surface_index])
|
|
{
|
|
boolean surface_passable = TRUE;
|
|
|
|
if (!ignore_broken_surfaces &&
|
|
TEST_FLAG(
|
|
pathfinding_surfaces[surface_result.enter_surface_index],
|
|
_pathfinding_surface_breakable_bit))
|
|
{
|
|
struct collision_surface const *collision_surface;
|
|
|
|
collision_surface = TAG_BLOCK_GET_ELEMENT(
|
|
&bsp->surfaces,
|
|
surface_result.enter_surface_index,
|
|
struct collision_surface);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\path_structure_bsp.c",
|
|
105,
|
|
TEST_FLAG(collision_surface->flags, _collision_surface_breakable_bit));
|
|
surface_passable = BIT_VECTOR_TEST_FLAG(
|
|
(long *)breakable_surface_flags,
|
|
collision_surface->breakable_surface_index);
|
|
}
|
|
|
|
if (surface_passable)
|
|
{
|
|
next_surface_index = surface_result.enter_surface_index;
|
|
}
|
|
}
|
|
|
|
if (next_surface_index == NONE &&
|
|
distance > surface_result.exit_t &&
|
|
pathfinding_surfaces[surface_result.exit_surface_index])
|
|
{
|
|
boolean surface_passable = TRUE;
|
|
|
|
if (!ignore_broken_surfaces &&
|
|
TEST_FLAG(
|
|
pathfinding_surfaces[surface_result.exit_surface_index],
|
|
_pathfinding_surface_breakable_bit))
|
|
{
|
|
struct collision_surface const *collision_surface;
|
|
|
|
collision_surface = TAG_BLOCK_GET_ELEMENT(
|
|
&bsp->surfaces,
|
|
surface_result.exit_surface_index,
|
|
struct collision_surface);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\path_structure_bsp.c",
|
|
126,
|
|
TEST_FLAG(collision_surface->flags, _collision_surface_breakable_bit));
|
|
surface_passable = BIT_VECTOR_TEST_FLAG(
|
|
(long *)breakable_surface_flags,
|
|
collision_surface->breakable_surface_index);
|
|
}
|
|
|
|
if (surface_passable)
|
|
{
|
|
next_surface_index = surface_result.exit_surface_index;
|
|
}
|
|
}
|
|
|
|
if (next_surface_index == NONE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
surface_index = next_surface_index;
|
|
collision_surface_test_line2d(
|
|
bsp,
|
|
surface_index,
|
|
_z,
|
|
TRUE,
|
|
point,
|
|
direction,
|
|
&surface_result);
|
|
}
|
|
|
|
if (distance < surface_result.enter_t)
|
|
{
|
|
result->distance = surface_result.enter_t;
|
|
result->surface_index = surface_index;
|
|
result->edge_index = surface_result.enter_edge_index;
|
|
return TRUE;
|
|
}
|
|
|
|
if (distance > surface_result.exit_t)
|
|
{
|
|
result->distance = surface_result.exit_t;
|
|
result->surface_index = surface_index;
|
|
result->edge_index = surface_result.exit_edge_index;
|
|
return TRUE;
|
|
}
|
|
|
|
result->distance = distance;
|
|
result->surface_index = surface_index;
|
|
result->edge_index = NONE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean structure_surfaces_are_equivalent(
|
|
struct structure_bsp const *structure,
|
|
real_point2d const *destination_point,
|
|
long destination_surface_index,
|
|
long test_surface_index)
|
|
{
|
|
struct collision_bsp const *bsp;
|
|
real_point3d destination_point3d;
|
|
real_point3d test_point3d;
|
|
boolean result;
|
|
|
|
bsp = TAG_BLOCK_GET_ELEMENT(&structure->collision_bsp, 0, struct collision_bsp);
|
|
result = FALSE;
|
|
|
|
if (destination_surface_index != NONE && test_surface_index != NONE)
|
|
{
|
|
collision_surface_project_point2d(
|
|
bsp,
|
|
destination_surface_index,
|
|
_z,
|
|
TRUE,
|
|
destination_point,
|
|
&destination_point3d);
|
|
collision_surface_project_point2d(
|
|
bsp,
|
|
test_surface_index,
|
|
_z,
|
|
TRUE,
|
|
destination_point,
|
|
&test_point3d);
|
|
result = fabs(destination_point3d.z - test_point3d.z) < 0.05f;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean structure_test_line2d(
|
|
struct structure_bsp const *structure,
|
|
boolean ignore_broken_surfaces,
|
|
real_point2d const *p0,
|
|
long p0_surface_index,
|
|
real_point2d const *p1,
|
|
long p1_surface_index,
|
|
struct path_collision_result *result)
|
|
{
|
|
struct collision_bsp const *bsp = TAG_BLOCK_GET_ELEMENT(&structure->collision_bsp, 0, struct collision_bsp);
|
|
byte const *pathfinding_surfaces = structure->pathfinding_surfaces.address;
|
|
long const *breakable_surface_flags = (long const *)breakable_surface_flags_get();
|
|
long surface_index = p0_surface_index;
|
|
boolean recursed = FALSE;
|
|
real_vector2d p0p1;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\path_structure_bsp.c", 217, result);
|
|
|
|
vector_from_points2d(p0, p1, &p0p1);
|
|
|
|
while (TRUE)
|
|
{
|
|
struct collision_surface const *surface = TAG_BLOCK_GET_ELEMENT(&bsp->surfaces, surface_index, struct collision_surface);
|
|
long edge_index = surface->first_edge_index;
|
|
real_point3d point_in_surface = *global_origin3d;
|
|
short edge_count = 0;
|
|
boolean crossed_any = FALSE;
|
|
boolean reached_target = FALSE;
|
|
|
|
while (TRUE)
|
|
{
|
|
struct collision_edge const *edge = TAG_BLOCK_GET_ELEMENT(&bsp->edges, edge_index, struct collision_edge);
|
|
boolean on_right_side = surface_index == edge->surface_indices[1];
|
|
struct collision_vertex const *vertex0 = TAG_BLOCK_GET_ELEMENT(
|
|
&bsp->vertices,
|
|
edge->vertex_indices[!on_right_side],
|
|
struct collision_vertex);
|
|
struct collision_vertex const *vertex1 = TAG_BLOCK_GET_ELEMENT(
|
|
&bsp->vertices,
|
|
edge->vertex_indices[on_right_side],
|
|
struct collision_vertex);
|
|
real_vector2d e0e1;
|
|
real_vector2d e0p1;
|
|
real_vector2d p0e0;
|
|
real_vector2d p0e1;
|
|
|
|
vector_from_points2d((real_point2d const *)&vertex0->point, (real_point2d const *)&vertex1->point, &e0e1);
|
|
vector_from_points2d((real_point2d const *)&vertex0->point, p1, &e0p1);
|
|
vector_from_points2d(p0, (real_point2d const *)&vertex0->point, &p0e0);
|
|
vector_from_points2d(p0, (real_point2d const *)&vertex1->point, &p0e1);
|
|
|
|
if (edge->surface_indices[!on_right_side] == p1_surface_index)
|
|
{
|
|
reached_target = TRUE;
|
|
}
|
|
|
|
point_in_surface.x += vertex0->point.x;
|
|
point_in_surface.y += vertex0->point.y;
|
|
point_in_surface.z += vertex0->point.z;
|
|
edge_count++;
|
|
|
|
if (cross_product2d(&e0e1, &e0p1) > 0.0f)
|
|
{
|
|
crossed_any = TRUE;
|
|
if (cross_product2d(&p0p1, &p0e0) > 0.0f &&
|
|
cross_product2d(&p0e1, &p0p1) > 0.0f)
|
|
{
|
|
/* BUG (preserved for exact matching): January loads
|
|
* pathfinding_surfaces[next_surface_index] (+0x207..+0x20b) before any NONE test, as
|
|
* does the later /Od build (0x4cee00 +0x330..+0x339). next_surface_index is NONE only
|
|
* for an open edge; the byte before the array then decides: walkable bit clear reports
|
|
* a collision at this edge; set, NONE reaches TAG_BLOCK_GET_ELEMENT (below, or at the
|
|
* top of the surface loop), whose index assertion halts. No structure BSP in the
|
|
* shipped 01.10.12.2276 maps has an open edge (0 of 2,066,607 edges in 82 BSPs).
|
|
*/
|
|
long next_surface_index = edge->surface_indices[!on_right_side];
|
|
boolean passable = TEST_FLAG(
|
|
pathfinding_surfaces[next_surface_index],
|
|
_pathfinding_surface_walkable_bit);
|
|
|
|
if (!ignore_broken_surfaces &&
|
|
passable &&
|
|
TEST_FLAG(
|
|
pathfinding_surfaces[next_surface_index],
|
|
_pathfinding_surface_breakable_bit))
|
|
{
|
|
struct collision_surface const *collision_surface = TAG_BLOCK_GET_ELEMENT(
|
|
&bsp->surfaces,
|
|
next_surface_index,
|
|
struct collision_surface);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\path_structure_bsp.c",
|
|
274,
|
|
TEST_FLAG(collision_surface->flags, _collision_surface_breakable_bit));
|
|
passable = BIT_VECTOR_TEST_FLAG(
|
|
breakable_surface_flags,
|
|
collision_surface->breakable_surface_index);
|
|
}
|
|
|
|
if (passable)
|
|
{
|
|
surface_index = next_surface_index;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
real t = (cross_product2d(&e0e1, &p0e0) - magnitude2d(&e0e1) * (1.0f / 128.0f)) /
|
|
cross_product2d(&e0e1, &p0p1);
|
|
real_point2d p2d;
|
|
|
|
point_from_line2d(p0, &p0p1, t, &p2d);
|
|
collision_surface_project_point2d(
|
|
bsp,
|
|
surface_index,
|
|
_z,
|
|
TRUE,
|
|
&p2d,
|
|
&result->point);
|
|
result->surface_index = surface_index;
|
|
result->edge_index = edge_index;
|
|
result->collision = TRUE;
|
|
result->t = t;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
edge_index = edge->edge_indices[on_right_side];
|
|
if (edge_index == surface->first_edge_index)
|
|
{
|
|
if (crossed_any)
|
|
{
|
|
struct path_collision_result p0_result;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\ai\\path_structure_bsp.c",
|
|
316,
|
|
surface_index>=0 && surface_index<structure->pathfinding_surfaces.count);
|
|
point_in_surface.x /= edge_count;
|
|
point_in_surface.y /= edge_count;
|
|
|
|
if (!recursed &&
|
|
pathfinding_surfaces[surface_index] &&
|
|
!structure_test_line2d(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
(real_point2d const *)&point_in_surface,
|
|
surface_index,
|
|
p0,
|
|
NONE,
|
|
&p0_result))
|
|
{
|
|
recursed = TRUE;
|
|
surface_index = p0_result.surface_index;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
collision_surface_project_point2d(
|
|
bsp,
|
|
p0_surface_index,
|
|
_z,
|
|
TRUE,
|
|
p0,
|
|
&result->point);
|
|
result->surface_index = NONE;
|
|
result->edge_index = NONE;
|
|
result->collision = TRUE;
|
|
result->t = 0.0f;
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (surface_index == p1_surface_index || reached_target || p1_surface_index == NONE)
|
|
{
|
|
collision_surface_project_point2d(
|
|
bsp,
|
|
surface_index,
|
|
_z,
|
|
TRUE,
|
|
p1,
|
|
&result->point);
|
|
result->surface_index = surface_index;
|
|
result->edge_index = NONE;
|
|
result->collision = FALSE;
|
|
result->t = 1.0f;
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
collision_surface_project_point2d(
|
|
bsp,
|
|
p0_surface_index,
|
|
_z,
|
|
TRUE,
|
|
p0,
|
|
&result->point);
|
|
result->surface_index = NONE;
|
|
result->edge_index = NONE;
|
|
result->collision = TRUE;
|
|
result->t = 0.0f;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean clip_empty_interval_by_solid_interval(
|
|
real *empty_t0,
|
|
real *empty_t1,
|
|
real solid_t0,
|
|
real solid_t1)
|
|
{
|
|
real clipped_solid_t0;
|
|
real clipped_solid_t1;
|
|
|
|
if (*empty_t0 > solid_t1)
|
|
clipped_solid_t1 = *empty_t0;
|
|
else
|
|
clipped_solid_t1 = solid_t1;
|
|
solid_t1 = clipped_solid_t1;
|
|
|
|
clipped_solid_t0 = MIN(solid_t0, *empty_t1);
|
|
|
|
if (*empty_t1 - solid_t1 > clipped_solid_t0 - *empty_t0)
|
|
*empty_t0 = solid_t1;
|
|
else
|
|
*empty_t1 = clipped_solid_t0;
|
|
|
|
return *empty_t0 > *empty_t1;
|
|
}
|
|
|
|
long structure_surface_index_from_point(
|
|
struct structure_bsp const *structure,
|
|
boolean ignore_broken_surfaces,
|
|
real_point2d const *known_point,
|
|
long known_surface_index,
|
|
real_point2d *point)
|
|
{
|
|
struct path_collision_result result;
|
|
|
|
if (known_surface_index != NONE)
|
|
{
|
|
structure_test_line2d(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
known_point,
|
|
known_surface_index,
|
|
point,
|
|
NONE,
|
|
&result);
|
|
|
|
point->x = result.point.x;
|
|
point->y = result.point.y;
|
|
|
|
if (result.surface_index == NONE)
|
|
return known_surface_index;
|
|
|
|
return result.surface_index;
|
|
}
|
|
|
|
return NONE;
|
|
}
|
|
|
|
boolean structure_test_pill2d(
|
|
struct structure_bsp const *structure,
|
|
boolean ignore_broken_surfaces,
|
|
real_point2d const *p0,
|
|
long p0_surface_index,
|
|
real_point2d const *p1,
|
|
long p1_surface_index,
|
|
real radius,
|
|
unsigned long flags,
|
|
struct path_collision_result *result)
|
|
{
|
|
boolean collision = FALSE;
|
|
real_vector2d direction;
|
|
real_vector2d perpendicular;
|
|
|
|
vector_from_points2d(p0, p1, &direction);
|
|
set_real_vector2d(&perpendicular, -direction.j, direction.i);
|
|
if (normalize2d(&perpendicular) > 0.0f)
|
|
{
|
|
real_point2d left_p0;
|
|
real_point2d left_p1;
|
|
real_point2d right_p0;
|
|
real_point2d right_p1;
|
|
long left_p0_surface_index;
|
|
long left_p1_surface_index;
|
|
long right_p0_surface_index;
|
|
long right_p1_surface_index;
|
|
struct path_collision_result left_result;
|
|
struct path_collision_result right_result;
|
|
struct path_collision_result endpoint_result;
|
|
struct path_collision_result *best_result;
|
|
|
|
left_p0_surface_index = structure_surface_index_from_point(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
p0,
|
|
p0_surface_index,
|
|
point_from_line2d(p0, &perpendicular, radius, &left_p0));
|
|
left_p1_surface_index = structure_surface_index_from_point(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
p1,
|
|
p1_surface_index,
|
|
point_from_line2d(p1, &perpendicular, radius, &left_p1));
|
|
right_p0_surface_index = structure_surface_index_from_point(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
p0,
|
|
p0_surface_index,
|
|
point_from_line2d(p0, &perpendicular, -radius, &right_p0));
|
|
right_p1_surface_index = structure_surface_index_from_point(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
p1,
|
|
p1_surface_index,
|
|
point_from_line2d(p1, &perpendicular, -radius, &right_p1));
|
|
|
|
if (left_p0_surface_index == NONE)
|
|
{
|
|
left_result.collision = FALSE;
|
|
}
|
|
else if (structure_test_line2d(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
&left_p0,
|
|
left_p0_surface_index,
|
|
&left_p1,
|
|
left_p1_surface_index,
|
|
&left_result) &&
|
|
left_result.surface_index != NONE &&
|
|
!TEST_FLAG(flags, _path_test_pill_endpoint_near_wall_ok_bit) &&
|
|
!structure_test_line2d(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
(real_point2d const *)&left_result.point,
|
|
left_result.surface_index,
|
|
p1,
|
|
p1_surface_index,
|
|
&endpoint_result))
|
|
{
|
|
left_result.collision = FALSE;
|
|
}
|
|
|
|
if (right_p0_surface_index == NONE)
|
|
{
|
|
right_result.collision = FALSE;
|
|
}
|
|
else if (structure_test_line2d(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
&right_p0,
|
|
right_p0_surface_index,
|
|
&right_p1,
|
|
right_p1_surface_index,
|
|
&right_result) &&
|
|
right_result.surface_index != NONE &&
|
|
!TEST_FLAG(flags, _path_test_pill_endpoint_near_wall_ok_bit) &&
|
|
!structure_test_line2d(
|
|
structure,
|
|
ignore_broken_surfaces,
|
|
(real_point2d const *)&right_result.point,
|
|
right_result.surface_index,
|
|
p1,
|
|
p1_surface_index,
|
|
&endpoint_result))
|
|
{
|
|
right_result.collision = FALSE;
|
|
}
|
|
|
|
if (left_result.collision && right_result.collision)
|
|
{
|
|
if (left_result.t < right_result.t)
|
|
{
|
|
best_result = &left_result;
|
|
}
|
|
else
|
|
{
|
|
best_result = &right_result;
|
|
}
|
|
}
|
|
else if (left_result.collision)
|
|
{
|
|
best_result = &left_result;
|
|
}
|
|
else if (right_result.collision)
|
|
{
|
|
best_result = &right_result;
|
|
}
|
|
else
|
|
{
|
|
best_result = NULL;
|
|
}
|
|
|
|
/* BUG (preserved for exact matching): a side whose p0 surface index is NONE gets only
|
|
* its collision flag, so when p0_surface_index is NONE the copy below returns
|
|
* left_result's point, surface_index, edge_index and t unassigned (January 0x452500
|
|
* writes only the byte [ebp-0x4c] at +0x189 and copies seven dwords from [ebp-0x4c]
|
|
* at +0x24f..+0x25a), and the function returns FALSE. actor_move_try_evasion_vector
|
|
* passes the actor's pathfinding surface as p0 and, on a FALSE return, reads
|
|
* result->point.z to accept or reject the evasion point; actor_find_pathfinding_location
|
|
* leaves that surface NONE for a non-flying actor when no ground surface is found or
|
|
* when the actor's vehicle is not a ground vehicle. A runtime occurrence was not
|
|
* traced. A corrected build should fill both side results before selecting.
|
|
* Source-policy approval pending (2026-09-27 audit). */
|
|
if (!best_result ||
|
|
distance_squared2d((real_point2d const *)&best_result->point, p1) < radius * radius)
|
|
{
|
|
*result = left_result;
|
|
}
|
|
else
|
|
{
|
|
*result = *best_result;
|
|
collision = TRUE;
|
|
}
|
|
}
|
|
|
|
return collision;
|
|
}
|
|
|
|
/* ---------- private code */
|