// Vanilla named settlements supply exclusion areas; no original mission location system. // Shared town/village exclusion for mortar impacts, roadblocks and mine sites. // Fixed distance from mapped settlement centres, independent of mission // activation radii. Scattered buildings and props are not settlements. class ERR_SettlementSafety { static const float RADIUS = 200; string SettlementReason; protected vector m_SafeImpact; protected bool m_SettlementNear; protected vector m_LastSettlementImpact; protected float m_LastSettlementCheck = -1; protected bool m_LastSettlementClear; bool OutsideSettlements(vector impact) { float now = GetGame().GetWorld().GetWorldTime(); if (m_LastSettlementCheck >= 0 && now >= m_LastSettlementCheck && now - m_LastSettlementCheck < 1000 && vector.DistanceXZ(impact, m_LastSettlementImpact) == 0) return m_LastSettlementClear; m_LastSettlementCheck = now; m_LastSettlementImpact = impact; m_LastSettlementClear = false; m_SafeImpact = impact; m_SettlementNear = false; SettlementReason = "clear"; // Also cover named native map settlements, independent of whether the // mission placed a garrison marker there. Generic building entities also // include deer stands, bridge pillars, rubble and electrical cabinets. // Broad vertical bounds keep the 200m distance horizontal on hills. GetGame().GetWorld().QueryEntitiesByAABB(impact - Vector(RADIUS, 5000, RADIUS), impact + Vector(RADIUS, 5000, RADIUS), CheckSettlement, FilterSettlement, EQueryEntitiesFlags.ALL); m_LastSettlementClear = !m_SettlementNear; return m_LastSettlementClear; } protected bool FilterSettlement(IEntity entity) { return entity && entity.FindComponent(MapDescriptorComponent); } protected bool CheckSettlement(IEntity entity) { float radius; MapDescriptorComponent descriptor = MapDescriptorComponent.Cast(entity.FindComponent(MapDescriptorComponent)); if (descriptor) { int type = descriptor.GetBaseType(); if (type == EMapDescriptorType.MDT_NAME_CITY) radius = RADIUS; else if (type == EMapDescriptorType.MDT_NAME_TOWN) radius = RADIUS; else if (type == EMapDescriptorType.MDT_NAME_VILLAGE || type == EMapDescriptorType.MDT_NAME_SETTLEMENT) radius = RADIUS; } if (radius > 0 && vector.DistanceXZ(m_SafeImpact, entity.GetOrigin()) <= radius) { m_SettlementNear = true; SettlementReason = string.Format("named map settlement entity=%1 position=%2 distance=%3m exclusion=%4m", entity, entity.GetOrigin(), vector.DistanceXZ(m_SafeImpact, entity.GetOrigin()), radius); return false; } return true; } }