// Conservative native volume check; no scenery removal or alternate placement. class ERR_ConvoyPlacement { protected bool m_Obstructed; protected int m_Visited; protected string m_LastReason; protected float m_LastLowest; protected float m_LastHighest; protected IEntity m_FirstObstruction; protected float m_LastTrace = -1; float LastTraceFraction() { return m_LastTrace; } string Diagnostic() { string prefab = "none"; vector position; if (m_FirstObstruction) { prefab = "no prefab"; if (m_FirstObstruction.GetPrefabData()) prefab = m_FirstObstruction.GetPrefabData().GetPrefabName(); position = m_FirstObstruction.GetOrigin(); } return string.Format("%1; terrainY=%2..%3 span=%4; visited=%5; first=%6 at %7; trace=%8", m_LastReason, m_LastLowest, m_LastHighest, m_LastHighest - m_LastLowest, m_Visited, prefab, position, m_LastTrace); } protected bool Occupancy(IEntity entity) { m_Visited++; if (m_Visited > 128) { m_Obstructed = true; m_LastReason = "native query visit cap"; } else if (entity && entity.GetVObject()) { m_Obstructed = true; m_LastReason = "native VObject volume overlap"; m_FirstObstruction = entity; } return !m_Obstructed; } static bool Terrain(float origin, float lowest, float highest) { return ERR_ForceRules.Finite(origin) && ERR_ForceRules.Finite(lowest) && ERR_ForceRules.Finite(highest) && lowest > 0.3 && highest >= lowest && highest - lowest <= 0.75 && origin >= lowest - 0.3 && origin <= highest + 0.3; } bool Clear(vector position, float heading) { m_LastReason = "invalid origin or heading"; m_LastLowest = 1e20; m_LastHighest = -1e20; m_FirstObstruction = null; m_Visited = 0; m_LastTrace = -1; if (!ERR_ForceRules.FiniteVector(position) || !ERR_ForceRules.Finite(heading)) return false; float radians = heading * Math.DEG2RAD; vector forward = Vector(Math.Sin(radians), 0, Math.Cos(radians)); vector right = Vector(forward[2], 0, -forward[0]); float lowest = 1e20, highest = -1e20; vector low = position, high = position; for (int x = -1; x <= 1; x++) { for (int z = -1; z <= 1; z++) { vector sample = ERR_Actors.Ground(position + right * (x * 2.0) + forward * (z * 4.5)); if (!ERR_ForceRules.FiniteVector(sample)) { m_LastReason = "nonfinite terrain sample"; return false; } if (ChimeraWorldUtils.TryGetWaterSurfaceSimple(GetGame().GetWorld(), sample)) { m_LastReason = "water at footprint sample"; return false; } lowest = Math.Min(lowest, sample[1]); highest = Math.Max(highest, sample[1]); low[0] = Math.Min(low[0], sample[0]); low[2] = Math.Min(low[2], sample[2]); high[0] = Math.Max(high[0], sample[0]); high[2] = Math.Max(high[2], sample[2]); } } m_LastLowest = lowest; m_LastHighest = highest; if (!Terrain(position[1], lowest, highest)) { m_LastReason = "terrain origin/height/span check"; return false; } low[1] = lowest + 0.05; high[1] = highest + 3.5; m_Obstructed = false; m_Visited = 0; GetGame().GetWorld().QueryEntitiesByAABB(low, high, Occupancy); // Entity bounding boxes can include distant branches or overhead wires. // Their overlap is diagnostic only; the filled native volume decides collision. vector center = (low + high) * 0.5; TraceBox volume = new TraceBox(); volume.Start = center; volume.End = center + "0 0.01 0"; volume.Mins = low - center; volume.Maxs = high - center; // Nine terrain/water samples already qualify the supporting ground below us. volume.Flags = TraceFlags.ENTS | TraceFlags.OCEAN; m_LastTrace = GetGame().GetWorld().TraceMove(volume, null); m_Obstructed = !ERR_ForceRules.Finite(m_LastTrace) || m_LastTrace < 1; m_FirstObstruction = null; if (m_Obstructed) { m_FirstObstruction = volume.TraceEnt; m_LastReason = "native filled volume collision"; if (!ERR_ForceRules.Finite(m_LastTrace)) m_LastReason = "invalid native trace fraction"; } else m_LastReason = "clear"; return !m_Obstructed; } }