// Optional HQ destination refinement over delivered memory and authored roads. // No player/target entity reads, forces, props, assignments or independent timer. class ERR_HQRoadIntercept { static bool Eligible(ERR_HQBelief belief, float now) { if (!belief || belief.Kind != ERR_HQMemoryKind.SIGHTING || belief.SampleCount < 2 || belief.MessageId <= 0) return false; if (!ERR_ForceRules.Finite(now) || !ERR_ForceRules.Finite(belief.LastDeliveredAt) || belief.LastDeliveredAt > now || belief.LastDeliveredAt < belief.ObservedAt) return false; if (belief.TravelMode != "on foot" && belief.TravelMode != "in vehicle") return false; if (!ERR_HQPursuitRules.Heading(belief.Velocity, belief.HasVelocity)) return false; vector velocity = belief.Velocity; velocity[1] = 0; if (velocity.Length() < 2) return false; return ERR_HQPursuitRules.Role(belief.Confidence, (now - belief.ObservedAt) / 1000, belief.Radius, true, 0) == ERR_HQPursuitRole.INTERCEPT; } static bool Ahead(vector origin, vector velocity, vector point) { if (!ERR_ForceRules.FiniteVector(origin) || !ERR_ForceRules.FiniteVector(point) || !ERR_HQPursuitRules.Heading(velocity, true)) return false; vector delta = point - origin; delta[1] = 0; velocity[1] = 0; float distance = delta.Length(); return distance >= 40 && distance <= 250 && vector.Dot(delta.Normalized(), velocity.Normalized()) >= 0.8; } static bool Unreserved(vector point, array reserved) { if (!reserved || reserved.Count() > 128) return false; foreach (vector other : reserved) if (!ERR_ForceRules.FiniteVector(other) || vector.DistanceXZ(point, other) < 80) return false; return true; } static bool Better(float score, vector point, float bestScore, vector best) { if (score < bestScore) return true; if (score > bestScore) return false; if (point[0] != best[0]) return point[0] < best[0]; if (point[2] != best[2]) return point[2] < best[2]; return point[1] < best[1]; } // Projection stays on one actual road segment, never a chord through bends. static bool FromPolyline(array points, vector origin, vector velocity, vector seed, array reserved, out vector result, out float score) { result = vector.Zero; score = 1e20; if (!points || points.Count() < 2 || points.Count() > 1024 || !ERR_ForceRules.FiniteVector(seed)) return false; foreach (vector point : points) if (!ERR_ForceRules.FiniteVector(point)) return false; bool found; for (int i = 1; i < points.Count(); i++) { vector delta = points[i] - points[i - 1]; delta[1] = 0; float length = delta.Length(); if (length < 0.01) continue; vector offset = seed - points[i - 1]; offset[1] = 0; float fraction = Math.Clamp(vector.Dot(offset, delta) / (length * length), 0, 1); vector candidate = vector.Lerp(points[i - 1], points[i], fraction); float distance = vector.DistanceXZ(seed, candidate); if (distance > 100 || !Ahead(origin, velocity, candidate) || !Unreserved(candidate, reserved)) continue; if (!found || Better(distance, candidate, score, result)) { result = candidate; score = distance; found = true; } } return found; } static bool Standable(vector position) { if (!ERR_ForceRules.FiniteVector(position) || ChimeraWorldUtils.TryGetWaterSurfaceSimple(GetGame().GetWorld(), position)) return false; array edges = {"0.4 0 0", "-0.4 0 0", "0 0 0.4", "0 0 -0.4"}; foreach (vector edge : edges) { vector sample = ERR_Actors.Ground(position + edge); if (!ERR_ForceRules.FiniteVector(sample) || Math.AbsFloat(sample[1] - position[1]) > 0.4) return false; } TraceBox body = new TraceBox(); body.Start = position + "0 1 0"; body.End = body.Start + "0 0.01 0"; body.Mins = "-0.35 -0.9 -0.35"; body.Maxs = "0.35 0.9 0.35"; body.Flags = TraceFlags.WORLD | TraceFlags.ENTS; return GetGame().GetWorld().TraceMove(body, null) == 1; } static bool Select(ERR_HQBelief belief, float now, array reserved, out vector result) { result = vector.Zero; if (!Replication.IsServer() || !Eligible(belief, now) || !reserved || reserved.Count() > 128) return false; vector seed = ERR_HQPursuitRules.Objective(belief.PredictedPosition, belief.Velocity, true, belief.Confidence, (now - belief.ObservedAt) / 1000, belief.Radius, ERR_HQPursuitRole.INTERCEPT, 0); if (!ERR_ForceRules.FiniteVector(seed)) return false; SCR_AIWorld world = SCR_AIWorld.Cast(GetGame().GetAIWorld()); if (!world || !world.GetRoadNetworkManager()) return false; array roads = {}; world.GetRoadNetworkManager().GetRoadsInAABB(seed + "-100 -1000 -100", seed + "100 1000 100", roads); if (roads.Count() > 128) return false; int segments; bool found; vector best; float bestScore = 1e20; foreach (BaseRoad road : roads) { if (!road || !ERR_ForceRules.Finite(road.GetWidth()) || road.GetWidth() < 4) continue; array points = {}; road.GetPoints(points); if (points.Count() < 2 || points.Count() > 1024) continue; segments += points.Count() - 1; if (segments > 4096) return false; vector candidate; float score; if (!FromPolyline(points, belief.PredictedPosition, belief.Velocity, seed, reserved, candidate, score)) continue; if (!found || Better(score, candidate, bestScore, best)) { best = candidate; bestScore = score; found = true; } } if (!found) return false; vector grounded = ERR_Actors.Ground(best); // Terrain grounding must not turn an elevated road/bridge into an offroad post below it. if (!ERR_ForceRules.FiniteVector(grounded) || Math.AbsFloat(grounded[1] - best[1]) > 2 || !Standable(grounded)) return false; result = grounded; return true; } }