515 lines
20 KiB
C#
515 lines
20 KiB
C#
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<string, GameObject> _taskMarkers = new Dictionary<string, GameObject>();
|
|
private Dictionary<string, GameObject> _bodyMarkers = new Dictionary<string, GameObject>();
|
|
private Dictionary<string, GameObject> _playerAvatars = new Dictionary<string, GameObject>();
|
|
private List<GameObject> _sabotageMarkers = new List<GameObject>();
|
|
|
|
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;
|
|
|
|
/// <summary>Called from OnSceneLoaded when Client.unity is loaded so the
|
|
/// MapCenterPoint (which lives in Client.unity) can be wired at runtime.</summary>
|
|
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<GameObject> toDestroy = new List<GameObject>();
|
|
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<MeshFilter>();
|
|
MeshRenderer meshRenderer = building.AddComponent<MeshRenderer>();
|
|
|
|
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<MeshCollider>();
|
|
return building;
|
|
}
|
|
GameObject BuildPathwayMesh(MapPathway w)
|
|
{
|
|
var path = new GameObject($"Path_{w.Name ?? "Unknown"}");
|
|
|
|
// Použijeme LineRenderer pro jednoduchost
|
|
LineRenderer line = path.AddComponent<LineRenderer>();
|
|
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<MeshFilter>();
|
|
MeshRenderer meshRenderer = area.AddComponent<MeshRenderer>();
|
|
|
|
// 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<Position> 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<Position> 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<int> triangles = new List<int>();
|
|
|
|
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<Position> 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<int> triangles = new List<int>();
|
|
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<GeoSus.Client.GameTask> 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<MeshRenderer>();
|
|
if (mr) mr.material.color = Color.yellow;
|
|
var light = go.AddComponent<Light>();
|
|
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<MeshRenderer>();
|
|
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<string, PlayerPositionInfo> 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<MeshRenderer>();
|
|
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<RepairStationInfo> 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<MeshRenderer>();
|
|
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
|
|
}
|
|
}
|