class CfgPlayerRestrictedAreaHandler { private static bool m_Initialized; static ref TStringArray m_PRAFiles = new TStringArray(); static ref CfgPlayerRestrictedAreaJsonData m_Data = new CfgPlayerRestrictedAreaJsonData(); //---------------------------------------------------------- //load, inits, validation static bool LoadData() { if (m_Initialized) return true; m_Initialized = false; if (GetGame().ServerConfigGetInt( "enableCfgGameplayFile" )) //only reach into the CfgGameplayHandler if properly loaded! m_PRAFiles = CfgGameplayHandler.GetPlayerRestrictedAreaFiles(); else m_PRAFiles = g_Game.GetMission().GetWorldData().GetDefaultPRAPaths(); if (!m_PRAFiles) return true; string worldName; GetGame().GetWorldName(worldName); foreach (string filename : m_PRAFiles) { PlayerRestrictedAreaInstance area = new PlayerRestrictedAreaInstance(); string errorMessage, path; path = string.Format("$mission:%1", filename); if (!JsonFileLoader.LoadFile(path, area, errorMessage)) { if (!FileExist(filename)) path = string.Format("dz/worlds/%1/ce/%2", worldName, filename); if (!JsonFileLoader.LoadFile(path, area, errorMessage)) { ErrorEx(errorMessage); continue; } } if (area != null) m_Data.m_Areas.Insert(area); } m_Initialized = m_Data.InitializeAreas(); return true; } static bool IsInitialized() { return m_Initialized; } //---------------------------------------------------------- //synchronization static void SyncDataSend(notnull PlayerIdentity identity) { GetGame().RPCSingleParam(null, ERPCs.RPC_PLAYERRESTRICTEDAREAS_SYNC, new Param1(m_Data), true, identity); } static void OnRPC(ParamsReadContext ctx) { Param1 data = new Param1(null); if (ctx.Read(data)) { m_Data = data.param1; m_Initialized = m_Data.m_ValidatedAreas.Count() > 0; } else { ErrorEx("CfgPlayerRestrictedAreaHandler - client failed to read incoming data"); } } //---------------------------------------------------------- //main area checking methods static bool IsPointInPlayerRestrictedAreaClosest(vector point, out PlayerRestrictedAreaInstance hitArea) { if (!IsInitialized()) return false; PlayerRestrictedAreaInstance area = GetClosestArea(point); if (area && IsCylinderInAreaBox(area,point) || IsPointInAreaPolygon(area,point)) { hitArea = area; return true; } return false; } static bool IsPointInPlayerRestrictedArea(vector point, out PlayerRestrictedAreaInstance hitArea) { if (!IsInitialized()) return false; foreach (PlayerRestrictedAreaInstance area : m_Data.m_ValidatedAreas) { if (IsCylinderInAreaBox(area,point) || IsPointInAreaPolygon(area,point)) { hitArea = area; return true; } } return false; } //---------------------------------------------------------- //support methods private static PlayerRestrictedAreaInstance GetClosestArea(vector point) { PlayerRestrictedAreaInstance ret; float closestDist = float.MAX; float currentDist; foreach (PlayerRestrictedAreaInstance area : m_Data.m_ValidatedAreas) { currentDist = vector.DistanceSq(point,area.GetCenterPos2D()); if (currentDist < closestDist) { ret = area; closestDist = currentDist; } } return ret; } //! default cylinder values sufficient for player detection private static bool IsCylinderInAreaBox(notnull PlayerRestrictedAreaInstance area, vector point, float cylinderRadius = 0.25, float cylinderHeight = 1) { foreach (PRAShapeBoxData boxData : area.m_PRABoxDataTranslated) { vector matPlayer4[4]; Math3D.MatrixIdentity4(matPlayer4); matPlayer4[3] = point; if (Math3D.IntersectCylinderOBB(boxData.m_Mins,boxData.m_Maxs,boxData.m_Mat4,matPlayer4,cylinderRadius,cylinderHeight)) return true; } return false; } private static bool IsPointInAreaPolygon(notnull PlayerRestrictedAreaInstance area, vector point) { array translatedDta = new array; foreach (array> polygonData : area.PRAPolygons) { translatedDta.Clear(); foreach (array vertexPos : polygonData) { translatedDta.Insert(vertexPos[0]); translatedDta.Insert(vertexPos[1]); } if (Math2D.IsPointInPolygon(translatedDta,point[0],point[2])) return true; } return false; } //---------------------------------------------------------- //debugs static private ref array m_DbgShapesBoxes; static private ref array m_DbgShapesPolygonLines; static void DrawBoxesDebug(bool draw) { if (!IsInitialized()) return; if (!m_DbgShapesBoxes) m_DbgShapesBoxes = {}; if (draw && m_DbgShapesBoxes.Count() == 0) { foreach (PlayerRestrictedAreaInstance area : m_Data.m_ValidatedAreas) { foreach (PRAShapeBoxData boxData : area.m_PRABoxDataTranslated) { Shape shp = Debug.DrawBox(boxData.m_Mins,boxData.m_Maxs); shp.SetMatrix(boxData.m_Mat4); m_DbgShapesBoxes.Insert(shp); } } } else if (!draw && m_DbgShapesBoxes.Count() > 0) { foreach (Shape box : m_DbgShapesBoxes) { box.Destroy(); } m_DbgShapesBoxes.Clear(); } } static void DrawPolygonLinesDebug(bool draw) { if (!IsInitialized()) return; if (!m_DbgShapesPolygonLines) m_DbgShapesPolygonLines = {}; if (draw && m_DbgShapesPolygonLines.Count() == 0) { array polygonVectors = {}; foreach (PlayerRestrictedAreaInstance area : m_Data.m_ValidatedAreas) { foreach (array> polygonData : area.PRAPolygons) { vector first; vector current; vector last; foreach (array vertexPos : polygonData) { polygonVectors.Insert(Vector(vertexPos[0],GetGame().SurfaceY(vertexPos[0],vertexPos[1]) + 2,vertexPos[1])); } int count = polygonVectors.Count(); for (int i = 0; i < count; ++i) { current = polygonVectors[i]; if (i == 0) first = polygonVectors[i]; else Debug.DrawLine(last,current,COLOR_WHITE,ShapeFlags.TRANSP|ShapeFlags.NOZWRITE); last = current; } m_DbgShapesPolygonLines.Insert(Debug.DrawLine(current,first,COLOR_RED,ShapeFlags.TRANSP|ShapeFlags.NOZWRITE)); polygonVectors.Clear(); } } } else if (!draw && m_DbgShapesPolygonLines.Count() > 0) { foreach (Shape item : m_DbgShapesPolygonLines) { item.Destroy(); } m_DbgShapesPolygonLines.Clear(); } } }