Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"scope": "Recover owned original mine site policy in the existing finite delivered-post selector. No changes to actor/stock/task/attempt/controller behavior.",
|
||||
"provenance": {
|
||||
"original": "/home/jakub/devel/escapists/escapists-ai/Scripts/Game/EscapistsAI/EAI_HQRoadblocks.c",
|
||||
"sha256": "128776f0b448162ee36cfc71b2a38d2cd06d6d6e4679253f215896aa3fcabb98",
|
||||
"road_exclusion_line": 385,
|
||||
"settlement_exclusion_line": 386,
|
||||
"successor_baseline": "Centerline candidate tests codify existing implementation, but no explicit overriding user policy found. Shared ERR_SettlementSafety comment explicitly names mine sites as intended consumer."
|
||||
},
|
||||
"design": {
|
||||
"positions": "Both \u00b115.01m parallel flanks of each actual eligible road segment; reuse bounded projection/endpoints/annulus-crossings.",
|
||||
"post_distance_m": [
|
||||
25,
|
||||
60
|
||||
],
|
||||
"road_centerline_minimum_m": 15,
|
||||
"settlement_exclusion_m": 200,
|
||||
"query": "One AABB query\u00b180m XZ,\u00b11000mY. Every roadcenterline point within15m of a candidate<=60m frompost iswithin75m XZ; query adds5m margin.",
|
||||
"hazards": "Every returned originalsegment inclnarrowroads andzero-length points; finite positive-length projection uses squared>0 toretain shortsegment endpoints.",
|
||||
"bounds": {
|
||||
"roads": 128,
|
||||
"points_per_road": 1024,
|
||||
"segments": 4096,
|
||||
"candidates": 128,
|
||||
"cached_segment_comparisons": 65536
|
||||
},
|
||||
"failure": "Unknown/malformed roads, bounds overflow, exhausted clearance budget or all rejected candidates returnfalse withresultzero. No partially checked candidate can succeed.",
|
||||
"determinism": "Canonical local segment endpoints via existing Better(0,b,0,a) before offset generation and clearance projection; original native polylines remain unchanged."
|
||||
},
|
||||
"tests": "Final source d838f569: independent actual geometry harness passes; baseline compiles and fails at runtime. See /tmp/erri-mine-flank-review-0402/review.json and /tmp/erri-mine-flank-tests-0402.",
|
||||
"limits": [
|
||||
"Inherited policy is15m from road centerlines, not road edges: a very wide road is not guaranteed to be outside its carriageway.",
|
||||
"Vertical query\u00b11000m, existing2mroad-groundheight check and Standable do not prove semantic bridge classification or native engineer walking/arming.",
|
||||
"Bounded nearest-candidate retention may conservatively refuse despite a different unexamined feasible location.",
|
||||
"65536 comparison cap is a bound, not a measured FPS/performance claim."
|
||||
],
|
||||
"target_edits": false,
|
||||
"native_run": false,
|
||||
"compiler_run_by_author": false,
|
||||
"independent_review": "/tmp/erri-mine-flank-review-0402/review.json"
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMineSite.c
|
||||
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMineSite.c
|
||||
@@ -9,7 +9,7 @@
|
||||
return distance >= 25 && distance <= 60;
|
||||
}
|
||||
// Keep the nearest bounded candidates, with stable ties independent of native
|
||||
- // road enumeration order. Candidates always belong to an actual road segment.
|
||||
+ // road enumeration order. Candidates lie beside actual road segments, never a chord across a bend.
|
||||
static void Keep(vector post, vector point, array<vector> candidates)
|
||||
{
|
||||
if (!candidates || candidates.Count() > 128 || !InBand(post, point) || candidates.Contains(point)) return;
|
||||
@@ -35,6 +35,19 @@
|
||||
if (first >= 0 && first <= 1) Keep(post, vector.Lerp(a, b, first), candidates);
|
||||
if (second >= 0 && second <= 1) Keep(post, vector.Lerp(a, b, second), candidates);
|
||||
}
|
||||
+ // Use the existing finite circle/projection candidates on each road flank.
|
||||
+ static void FromSegment(vector a, vector b, vector post, array<vector> candidates)
|
||||
+ {
|
||||
+ vector direction = b - a; direction[1] = 0;
|
||||
+ float squared = vector.Dot(direction, direction);
|
||||
+ if (!ERR_ForceRules.Finite(squared) || squared < 0.0001) return;
|
||||
+ vector offset = post - a; offset[1] = 0;
|
||||
+ float fraction = Math.Clamp(vector.Dot(offset, direction) / squared, 0, 1);
|
||||
+ Keep(post, vector.Lerp(a, b, fraction), candidates);
|
||||
+ Keep(post, a, candidates); Keep(post, b, candidates);
|
||||
+ Crossings(a, b, post, 25.01, candidates);
|
||||
+ Crossings(a, b, post, 59.99, candidates);
|
||||
+ }
|
||||
static bool FromPolyline(array<vector> points, vector post, array<vector> candidates)
|
||||
{
|
||||
if (!points || points.Count() < 2 || points.Count() > 1024 || !candidates || candidates.Count() > 128) return false;
|
||||
@@ -43,17 +56,41 @@
|
||||
for (int i = 1; i < points.Count(); i++)
|
||||
{
|
||||
vector a = points[i - 1], b = points[i];
|
||||
+ // Canonical orientation makes flank/circle rounding independent of native
|
||||
+ // polyline direction, so equally near candidates retain stable ties.
|
||||
+ if (ERR_HQRoadIntercept.Better(0, b, 0, a))
|
||||
+ { vector swap = a; a = b; b = swap; }
|
||||
vector direction = b - a; direction[1] = 0;
|
||||
float squared = vector.Dot(direction, direction);
|
||||
if (!ERR_ForceRules.Finite(squared) || squared < 0.0001) continue;
|
||||
- vector offset = post - a; offset[1] = 0;
|
||||
- float fraction = Math.Clamp(vector.Dot(offset, direction) / squared, 0, 1);
|
||||
- Keep(post, vector.Lerp(a, b, fraction), candidates);
|
||||
- Keep(post, a, candidates); Keep(post, b, candidates);
|
||||
- // One-centimetre inward margins keep rounding at circle intersections from
|
||||
- // authorizing <25m placement or silently discarding the only valid crossing.
|
||||
- Crossings(a, b, post, 25.01, candidates);
|
||||
- Crossings(a, b, post, 59.99, candidates);
|
||||
+ vector flank = Vector(-direction[2], 0, direction[0]) * (15.01 / Math.Sqrt(squared));
|
||||
+ FromSegment(a + flank, b + flank, post, candidates);
|
||||
+ FromSegment(a - flank, b - flank, post, candidates);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Every queried road participates, including narrow roads and degenerate
|
||||
+ // segment points. A bounded exhausted scan cannot authorize a candidate.
|
||||
+ static bool FarFromRoads(vector point, array<vector> starts, array<vector> ends, inout int comparisons)
|
||||
+ {
|
||||
+ if (!ERR_ForceRules.FiniteVector(point) || comparisons < 0 || !starts || !ends || starts.IsEmpty() || starts.Count() != ends.Count() || starts.Count() > 4096) return false;
|
||||
+ for (int i = 0; i < starts.Count(); i++)
|
||||
+ {
|
||||
+ if (comparisons >= 65536) return false;
|
||||
+ comparisons++;
|
||||
+ vector a = starts[i], b = ends[i];
|
||||
+ if (!ERR_ForceRules.FiniteVector(a) || !ERR_ForceRules.FiniteVector(b)) return false;
|
||||
+ if (ERR_HQRoadIntercept.Better(0, b, 0, a)) { vector swap = a; a = b; b = swap; }
|
||||
+ vector direction = b - a; direction[1] = 0;
|
||||
+ float squared = vector.Dot(direction, direction);
|
||||
+ if (!ERR_ForceRules.Finite(squared)) return false;
|
||||
+ float fraction;
|
||||
+ if (squared > 0)
|
||||
+ {
|
||||
+ vector offset = point - a; offset[1] = 0;
|
||||
+ fraction = Math.Clamp(vector.Dot(offset, direction) / squared, 0, 1);
|
||||
+ }
|
||||
+ if (vector.DistanceXZ(point, vector.Lerp(a, b, fraction)) < 15) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -63,21 +100,29 @@
|
||||
if (!Replication.IsServer() || !ERR_ForceRules.FiniteVector(post) || !GetGame() || !GetGame().GetWorld()) return false;
|
||||
SCR_AIWorld world = SCR_AIWorld.Cast(GetGame().GetAIWorld());
|
||||
if (!world || !world.GetRoadNetworkManager()) return false;
|
||||
+ // Any road within15m of a point in the60m band intersects the75m
|
||||
+ // horizontal neighbourhood. The80m query includes a5m margin; query once.
|
||||
array<BaseRoad> roads = {};
|
||||
- world.GetRoadNetworkManager().GetRoadsInAABB(post + "-60 -1000 -60", post + "60 1000 60", roads);
|
||||
+ world.GetRoadNetworkManager().GetRoadsInAABB(post + "-80 -1000 -80", post + "80 1000 80", roads);
|
||||
if (roads.Count() > 128) return false;
|
||||
int segments;
|
||||
- array<vector> candidates = {};
|
||||
+ array<vector> candidates = {}, starts = {}, ends = {};
|
||||
foreach (BaseRoad road : roads)
|
||||
{
|
||||
- if (!road || !ERR_ForceRules.Finite(road.GetWidth()) || road.GetWidth() < 4) continue;
|
||||
+ if (!road || !ERR_ForceRules.Finite(road.GetWidth()) || road.GetWidth() < 0) return false;
|
||||
array<vector> points = {}; road.GetPoints(points);
|
||||
if (points.Count() < 2 || points.Count() > 1024) return false;
|
||||
segments += points.Count() - 1;
|
||||
- if (segments > 4096 || !FromPolyline(points, post, candidates)) return false;
|
||||
+ if (segments > 4096) return false;
|
||||
+ foreach (vector original : points) if (!ERR_ForceRules.FiniteVector(original)) return false;
|
||||
+ for (int segment = 1; segment < points.Count(); segment++)
|
||||
+ { starts.Insert(points[segment - 1]); ends.Insert(points[segment]); }
|
||||
+ if (road.GetWidth() >= 4 && !FromPolyline(points, post, candidates)) return false;
|
||||
}
|
||||
- // Evaluate at most128 ground/body checks, nearest candidates first. A blocked
|
||||
- // nearest point cannot hide another retained, physically valid road point.
|
||||
+ // At most128 candidates,65536 cached segment comparisons and128 native
|
||||
+ // settlement/ground/body checks. No per-candidate road queries.
|
||||
+ int comparisons;
|
||||
+ ERR_SettlementSafety settlements = new ERR_SettlementSafety();
|
||||
while (!candidates.IsEmpty())
|
||||
{
|
||||
int best;
|
||||
@@ -86,6 +131,9 @@
|
||||
vector point = candidates[best]; candidates.Remove(best);
|
||||
vector grounded = ERR_Actors.Ground(point);
|
||||
if (!InBand(post, grounded) || Math.AbsFloat(grounded[1] - point[1]) > 2) continue;
|
||||
+ if (!FarFromRoads(grounded, starts, ends, comparisons))
|
||||
+ { if (comparisons >= 65536) return false; continue; }
|
||||
+ if (!settlements.OutsideSettlements(grounded)) continue;
|
||||
// BaseRoad has no bridge-type accessor: retain existing road-height and
|
||||
// standability rejection, including water and physical bridge obstruction.
|
||||
if (!ERR_HQRoadIntercept.Standable(grounded)) continue;
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"addons/core/Scripts/Game/ERR/10_AI/ERR_EngineerMineSite.c": "d838f5693d4fa5d252d529e59777f560674c02532f1041a5598e938b3a850839",
|
||||
"fix.patch": "a3fa4e97c5e6ce92790da0030c28a9b114b093f6f1655870cbb27275965ae061"
|
||||
}
|
||||
Reference in New Issue
Block a user