using GeoSus.Client; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using UnityEngine; using UnityEngine.UI; namespace Subsystems{ [System.Serializable] public class BuildingSettings { public Material ResidentialBuildingsMat; public float ResidentialBuildingHeight; public Material CommercialBuildingsMat; public float CommercialBuildingHeight; public Material IndustrialBuildingsMat; public float IndustrialBuildingHeight; public Material DefaultBuildingMat; public float DefaultBuildingHeight; } [System.Serializable] public class PathwaySettings { public Material FootwayMat; public float FootwayWidth; public Material PathMat; public float PathWidth; public Material StepsMat; public float StepsWidth; public Material CyclewayMat; public float CyclewayWidth; public Material PedestrianMat; public float PedestrianWidth; public Material RoadMat; public float RoadWidth; public Material ServiceMat; public float ServiceWidth; public Material ResidentialMat; public float ResidentialWidth; public Material TrackMat; public float TrackWidth; public Material DefaultMat; public float DefaultWidth; } [System.Serializable] public class AreaSettings { public Material ParkMat; public Material GardenMat; public Material PlaygroundMat; public Material ForestMat; public Material GrassMat; public Material WaterMat; public Material DefaultMat; } public class GameManager_Map { private GameClient _gameClient; private GameObject _mapCenterPoint; private Position _centerPosition; private BuildingSettings _buildingSettings; private PathwaySettings _pathwaySettings; private AreaSettings _areaSettings; private const float _metersPerUnit = 1f; // Runtime marker collections private Dictionary _taskMarkers = new Dictionary(); private Dictionary _bodyMarkers = new Dictionary(); private Dictionary _playerAvatars = new Dictionary(); private List _sabotageMarkers = new List(); public GameManager_Map(GameClient gameClient, GameObject mapCenterPoint, BuildingSettings buildingSettings, PathwaySettings pathwaySettings, AreaSettings areaSettings) { _gameClient = gameClient; _mapCenterPoint = mapCenterPoint; _buildingSettings = buildingSettings; _pathwaySettings = pathwaySettings; _areaSettings = areaSettings; } public bool IsSceneReady => _mapCenterPoint != null; /// Called from OnSceneLoaded when Client.unity is loaded so the /// MapCenterPoint (which lives in Client.unity) can be wired at runtime. public void SetMapCenterPoint(GameObject go) { _mapCenterPoint = go; } public void BuildMap() { if (_mapCenterPoint == null) { Debug.LogWarning("[Map] BuildMap skipped: MapCenterPoint is not yet bound."); return; } if (_gameClient?.CurrentLobbyState?.MapData == null) { Debug.LogWarning("[Map] BuildMap skipped: no MapData in CurrentLobbyState."); return; } ClearChildren(); _centerPosition = _gameClient.CurrentLobbyState.MapData.Center; GameObject buildingsRoot = new GameObject("Buildings"); buildingsRoot.transform.parent = _mapCenterPoint.transform; GameObject pathRoot = new GameObject("Pathways"); pathRoot.transform.parent = _mapCenterPoint.transform; GameObject areaRoot = new GameObject("Areas"); areaRoot.transform.parent = _mapCenterPoint.transform; foreach (var building in _gameClient.CurrentLobbyState.MapData.GetBuildings()) { string buildingType = "Unknown"; try { buildingType = _gameClient.CurrentLobbyState.MapData.BuildingTypes[_gameClient.CurrentLobbyState.MapData.GetBuildings().IndexOf(building)]; } catch (Exception ex) { Debug.Log($"Error: {ex.Message}"); } building.Name = buildingType; GameObject b = BuildBuildingMesh(building); b.transform.parent = buildingsRoot.transform; } foreach (var path in _gameClient.CurrentLobbyState.MapData.GetPathways()) { GameObject p = BuildPathwayMesh(path); p.transform.parent = pathRoot.transform; } foreach (var area in _gameClient.CurrentLobbyState.MapData.GetAreas()) { GameObject a = BuildAreaMesh(area); a.transform.parent = areaRoot.transform; } //TODO: POIs } void ClearChildren() { List toDestroy = new List(); foreach (Transform t in _mapCenterPoint.transform) toDestroy.Add(t.gameObject); foreach (var g in toDestroy) { UnityEngine.Object.DestroyImmediate(g); } } #region Mesh Building GameObject BuildBuildingMesh(MapBuilding b) { var building = new GameObject($"Building_{b.Name ?? "Unknown"}"); // Výpočet středu budovy Vector3 center = CalculatePolygonCenter(b.Outline); building.transform.position = center; // Vytvoření mesh pro budovu MeshFilter meshFilter = building.AddComponent(); MeshRenderer meshRenderer = building.AddComponent(); float height; Material mat; switch (b.BuildingType.ToLower()) { case "residential": mat = _buildingSettings.ResidentialBuildingsMat; height = _buildingSettings.ResidentialBuildingHeight; break; case "commercial": mat = _buildingSettings.CommercialBuildingsMat; height = _buildingSettings.CommercialBuildingHeight; break; case "industrial": mat = _buildingSettings.IndustrialBuildingsMat; height = _buildingSettings.IndustrialBuildingHeight; break; default: mat = _buildingSettings.DefaultBuildingMat; height = _buildingSettings.DefaultBuildingHeight; break; } Mesh mesh = CreateExtrudedPolygonMesh(b.Outline, height); meshFilter.mesh = mesh; //TODO: material by type // Použijeme barvu podle typu budovy meshRenderer.material = mat; // Přidání collideru pro interakci building.AddComponent(); return building; } GameObject BuildPathwayMesh(MapPathway w) { var path = new GameObject($"Path_{w.Name ?? "Unknown"}"); // Použijeme LineRenderer pro jednoduchost LineRenderer line = path.AddComponent(); float width; Material mat; switch (w.PathType) { case PathType.Footway: mat = _pathwaySettings.FootwayMat; width = _pathwaySettings.FootwayWidth; break; case PathType.Path: mat = _pathwaySettings.PathMat; width = _pathwaySettings.PathWidth; break; case PathType.Steps: mat = _pathwaySettings.StepsMat; width = _pathwaySettings.PathWidth; break; case PathType.Cycleway: mat = _pathwaySettings.CyclewayMat; width = _pathwaySettings.CyclewayWidth; break; case PathType.Pedestrian: mat = _pathwaySettings.PedestrianMat; width = _pathwaySettings.PedestrianWidth; break; case PathType.Road: mat = _pathwaySettings.RoadMat; width = _pathwaySettings.RoadWidth; break; case PathType.Service: mat = _pathwaySettings.ServiceMat; width = _pathwaySettings.ServiceWidth; break; case PathType.Residential: mat = _pathwaySettings.ResidentialMat; width = _pathwaySettings.ResidentialWidth; break; case PathType.Track: mat = _pathwaySettings.TrackMat; width = _pathwaySettings.TrackWidth; break; default: mat = _pathwaySettings.DefaultMat; width = _pathwaySettings.DefaultWidth; break; } line.material = mat; line.widthMultiplier = width; // Nastavení bodů cesty line.positionCount = w.Points.Count; for (int i = 0; i < w.Points.Count; i++) { Vector3 pos = w.Points[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center); pos.y = 0.1f; // Mírně nad zemí line.SetPosition(i, pos); } return path; } GameObject BuildAreaMesh(MapArea a) { var area = new GameObject($"Area_{a.Name ?? "Unknown"}"); MeshFilter meshFilter = area.AddComponent(); MeshRenderer meshRenderer = area.AddComponent(); // Vytvoření plochého mesh Mesh mesh = CreateFlatPolygonMesh(a.Outline); meshFilter.mesh = mesh; Material mat; switch (a.AreaType) { case MapAreaType.Park: mat = _areaSettings.ParkMat; break; case MapAreaType.Garden: mat = _areaSettings.GardenMat; break; case MapAreaType.Playground: mat = _areaSettings.PlaygroundMat; break; case MapAreaType.Forest: mat = _areaSettings.ForestMat; break; case MapAreaType.Grass: mat = _areaSettings.GrassMat; break; case MapAreaType.Water: mat = _areaSettings.WaterMat; break; default: mat = _areaSettings.DefaultMat; break; } meshRenderer.material = mat; area.transform.position = new Vector3(0, 0.05f, 0); // Těsně nad zemí return area; } //TODO: POIs #endregion #region Polygon Utils private Vector3 CalculatePolygonCenter(List points) { Vector3 center = Vector3.zero; foreach (var point in points) { center += point.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center); } return center / points.Count; } private Mesh CreateExtrudedPolygonMesh(List outline, float height) { Mesh mesh = new Mesh(); int vertexCount = outline.Count; // Vertices - spodní a horní podstava Vector3[] vertices = new Vector3[vertexCount * 2]; Vector3 center = CalculatePolygonCenter(outline); for (int i = 0; i < vertexCount; i++) { Vector3 pos = outline[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center) - center; vertices[i] = pos; // Spodní vertices[i + vertexCount] = pos + Vector3.up * height; // Horní } // Triangles - jen boční stěny pro jednoduchost List triangles = new List(); for (int i = 0; i < vertexCount; i++) { int next = (i + 1) % vertexCount; // Boční stěna - dva trojúhelníky triangles.Add(i); triangles.Add(i + vertexCount); triangles.Add(next); triangles.Add(next); triangles.Add(i + vertexCount); triangles.Add(next + vertexCount); } // Horní podstava - zjednodušená triangulace (fan) if (vertexCount >= 3) { for (int i = 1; i < vertexCount - 1; i++) { triangles.Add(vertexCount); // Střed (první bod horní) triangles.Add(vertexCount + i); triangles.Add(vertexCount + i + 1); } } mesh.vertices = vertices; mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); mesh.RecalculateBounds(); return mesh; } private Mesh CreateFlatPolygonMesh(List outline) { Mesh mesh = new Mesh(); int vertexCount = outline.Count; Vector3[] vertices = new Vector3[vertexCount]; Vector3 center = CalculatePolygonCenter(outline); for (int i = 0; i < vertexCount; i++) { vertices[i] = outline[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center) - center; } // Triangulace - fan pattern List triangles = new List(); if (vertexCount >= 3) { for (int i = 1; i < vertexCount - 1; i++) { triangles.Add(0); triangles.Add(i); triangles.Add(i + 1); } } mesh.vertices = vertices; mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } #endregion #region Markers public void CreateTaskMarkers(List tasks) { if (_mapCenterPoint == null) return; if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0) { var md = _gameClient?.CurrentLobbyState?.MapData; if (md != null) _centerPosition = md.Center; } if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0) return; foreach (var task in tasks) { if (_taskMarkers.ContainsKey(task.TaskId)) continue; var go = GameObject.CreatePrimitive(PrimitiveType.Sphere); go.name = $"Task_{task.TaskId}"; go.transform.parent = _mapCenterPoint.transform; go.transform.position = task.Location.ToLocalVector3(_centerPosition) + Vector3.up * 1f; // Raised go.transform.localScale = Vector3.one * 8f; // Bigger var mr = go.GetComponent(); if (mr) mr.material.color = Color.yellow; var light = go.AddComponent(); light.color = Color.yellow; light.intensity = 2; light.range = 5; _taskMarkers[task.TaskId] = go; } } public void RemoveTaskMarker(string taskId) { if (_taskMarkers.TryGetValue(taskId, out var go)) { UnityEngine.Object.Destroy(go); _taskMarkers.Remove(taskId); } } public void CreateBodyMarker(string bodyId, Position location) { if (_mapCenterPoint == null) return; if (_bodyMarkers.ContainsKey(bodyId)) return; var go = GameObject.CreatePrimitive(PrimitiveType.Capsule); go.name = $"Body_{bodyId}"; go.transform.parent = _mapCenterPoint?.transform; go.transform.position = location.ToLocalVector3(_centerPosition) + Vector3.up * 0.15f; go.transform.localScale = new Vector3(0.3f, 0.5f, 0.3f); go.transform.rotation = Quaternion.Euler(90, 0, 0); // lying down var mr = go.GetComponent(); if (mr) mr.material.color = Color.red; _bodyMarkers[bodyId] = go; } public void ClearBodyMarkers() { foreach (var go in _bodyMarkers.Values) if (go) UnityEngine.Object.Destroy(go); _bodyMarkers.Clear(); } public void UpdatePlayerAvatars(Dictionary positions, string myUuid) { if (_mapCenterPoint == null) return; if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0) { var md = _gameClient?.CurrentLobbyState?.MapData; if (md != null) _centerPosition = md.Center; } if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0) return; foreach (var kvp in positions) { string uuid = kvp.Key; var info = kvp.Value; if (!_playerAvatars.TryGetValue(uuid, out var go) || go == null) { go = GameObject.CreatePrimitive(PrimitiveType.Capsule); go.name = $"Player_{uuid.Substring(0, Mathf.Min(8, uuid.Length))}"; go.transform.parent = _mapCenterPoint?.transform; go.transform.localScale = Vector3.one * 0.4f; _playerAvatars[uuid] = go; } go.transform.position = info.Position.ToLocalVector3(_centerPosition) + Vector3.up * 1f; var mr = go.GetComponent(); if (mr) { if (uuid == myUuid) mr.material.color = Color.green; else if (info.State == GeoSus.Client.PlayerState.Dead) mr.material.color = Color.grey; else mr.material.color = Color.white; } } } public void CreateSabotageMarkers(List stations) { foreach (var station in stations) { var go = GameObject.CreatePrimitive(PrimitiveType.Cylinder); go.name = $"Sabotage_{station.StationId}"; go.transform.parent = _mapCenterPoint?.transform; go.transform.position = station.Location.ToLocalVector3(_centerPosition) + Vector3.up * 1f; go.transform.localScale = new Vector3(0.5f, 2f, 0.5f); var mr = go.GetComponent(); if (mr) mr.material.color = new Color(1f, 0.5f, 0f); // orange _sabotageMarkers.Add(go); } } public void ClearSabotageMarkers() { foreach (var go in _sabotageMarkers) if (go) UnityEngine.Object.Destroy(go); _sabotageMarkers.Clear(); } #endregion } }