using UnityEngine; using System; using System.Linq; using System.Threading; using System.IO; using System.Collections; using System.Collections.Generic; namespace WPM { public enum GRID_STYLE { Wireframe = 0, Shaded = 1, ShadedWireframe = 2 } public partial class WorldMapGlobe : MonoBehaviour { #region Hexagonal grid properties [SerializeField] bool _showHexagonalGrid = false; /// /// Enables or disables hexagonal grid on Globe. /// public bool showHexagonalGrid { get { return _showHexagonalGrid; } set { if (_showHexagonalGrid != value) { _showHexagonalGrid = value; if (!_showHexagonalGrid) { if (gridCellsRoot != null) gridCellsRoot.gameObject.SetActive(_showHexagonalGrid); if (gridWireframeRoot != null) gridWireframeRoot.gameObject.SetActive(_showHexagonalGrid); } else { GenerateGrid(); } isDirty = true; } } } [SerializeField] bool _hexaGridUseMask = false; /// /// Enables or disables hexagonal grid masking. /// public bool hexaGridUseMask { get { return _hexaGridUseMask; } set { if (_hexaGridUseMask != value) { _hexaGridUseMask = value; shouldGenerateGrid = true; UpdateGridMaterialProperties(); isDirty = true; } } } [SerializeField] Texture2D _hexaGridMask; public Texture2D hexaGridMask { get { return _hexaGridMask; } set { if (_hexaGridMask != value) { _hexaGridMask = value; isDirty = true; LoadGridMask(); if (_showHexagonalGrid) { GenerateGrid(); } } } } [Range(15, 200)] [SerializeField] int _hexaGridDivisions = 15; public int hexaGridDivisions { get { return _hexaGridDivisions; } set { if (_hexaGridDivisions != value) { _hexaGridDivisions = Mathf.Max(15, value); // less divisions will increase clipping with the sphere. shouldGenerateGrid = true; UpdateGridMaterialProperties(); isDirty = true; } } } [Range(0, 255)] [SerializeField] int _hexaGridMaskThreshold = 11; public int hexaGridMaskThreshold { get { return _hexaGridMaskThreshold; } set { if (_hexaGridMaskThreshold != value) { _hexaGridMaskThreshold = Mathf.Clamp(value, 0, 255); shouldGenerateGrid = true; UpdateGridMaterialProperties(); isDirty = true; } } } [SerializeField] Vector3 _hexaGridRotationShift; /// /// Applies an internal rotation to the positions of the grid cells. Does not change globe rotation. /// public Vector3 hexaGridRotationShift { get { return _hexaGridRotationShift; } set { if (_hexaGridRotationShift != value) { _hexaGridRotationShift = value; shouldGenerateGrid = true; UpdateGridMaterialProperties(); isDirty = true; } } } [SerializeField] Color _hexaGridColor = Color.white; public Color hexaGridColor { get { return _hexaGridColor; } set { if (_hexaGridColor != value) { _hexaGridColor = value; UpdateGridMaterialProperties(); isDirty = true; } } } /// /// Fired when path finding algorithmn evaluates a cell. Return the increased cost for cell. /// public event GridCellEvent OnCellClick; /// /// Fired when cursor enters a cell /// public event GridCellEvent OnCellEnter; /// /// Fired when cursor exits a cell /// public event GridCellEvent OnCellExit; [SerializeField] Color _hexaGridHighlightColor = new Color(0, 0.25f, 1f, 0.8f); public Color hexaGridHighlightColor { get { return _hexaGridHighlightColor; } set { if (_hexaGridHighlightColor != value) { _hexaGridHighlightColor = value; UpdateGridMaterialProperties(); isDirty = true; } } } [SerializeField] [Range(0.1f, 5f)] float _hexaGridHighlightSpeed = 1f; public float hexaGridHighlightSpeed { get { return _hexaGridHighlightSpeed; } set { if (_hexaGridHighlightSpeed != value) { _hexaGridHighlightSpeed = value; isDirty = true; } } } [SerializeField] bool _hexaGridHighlightEnabled = true; public bool hexaGridHighlightEnabled { get { return _hexaGridHighlightEnabled; } set { if (_hexaGridHighlightEnabled != value) { _hexaGridHighlightEnabled = value; if (!_hexaGridHighlightEnabled) { HideHighlightedCell(); } isDirty = true; } } } [SerializeField] float _gridMaxDistance = 1000f; /// /// Maximum distance from grid where it's visible /// public float gridMaxDistance { get { return _gridMaxDistance; } set { if (value != _gridMaxDistance) { _gridMaxDistance = value; isDirty = true; if (_showHexagonalGrid) { AdjustsHexagonalGridMaterial(); } } } } [SerializeField] float _gridMinDistance = 0.01f; /// /// Minimum distance from grid where it's visible /// public float gridMinDistance { get { return _gridMinDistance; } set { if (value != _gridMinDistance) { _gridMinDistance = value; isDirty = true; if (_showHexagonalGrid) { AdjustsHexagonalGridMaterial(); } } } } public int lastHighlightedCellIndex = -1; public Cell lastHighlightedCell; #endregion #region Public API /// /// Gets the cell under the local sphere position /// public int GetCellIndex(Vector3 spherePosition) { if (cells == null) return -1; return GetCellAtLocalPosition(spherePosition, false); } /// /// Gets the cell under the latlon position /// public int GetCellIndex(Vector2 latlon) { if (cells == null) return -1; Vector3 spherePosition = Conversion.GetSpherePointFromLatLon(latlon); return GetCellAtLocalPosition(spherePosition, false); } /// /// Gets the cell index of the cell nearest to a given world position. /// /// The cell at world position. public int GetCellAtWorldPos(Vector3 worldPosition) { Vector3 localPosition = transform.InverseTransformPoint(worldPosition); return GetCellIndex(localPosition); } /// /// Array of generated cells. /// public Cell[] cells; /// /// Returns the index of the cell in the cells list /// public int GetCellIndex(Cell cell) { if (cells == null) return -1; return cell.index; } /// /// Sets the cell material. /// /// true, if cell material was set, false otherwise. /// Cell index. /// Material to be used. /// If set to true the material is not saved anywhere and will be restored to default cell material when cell gets unselected. public bool SetCellMaterial(int cellIndex, Material mat, bool temporary = false) { if (cellIndex < 0 || cellIndex >= cells.Length) return false; Cell cell = cells[cellIndex]; if (cell.renderer == null) { GenerateCellMesh(cellIndex, mat); } else { cell.renderer.sharedMaterial = mat; cell.renderer.enabled = true; } if (mat != hexaGridHighlightMaterial) { if (temporary) { cell.tempMat = mat; } else { cell.customMat = mat; } } if (hexaGridHighlightMaterial != null && cell == lastHighlightedCell) { if (cell.renderer != null) cell.renderer.sharedMaterial = hexaGridHighlightMaterial; if (cell.tempMat != null) { hexaGridHighlightMaterial.SetColor(ShaderParams.Color2, cell.tempMat.color); hexaGridHighlightMaterial.mainTexture = cell.tempMat.mainTexture; } else if (cell.customMat != null) { hexaGridHighlightMaterial.SetColor(ShaderParams.Color2, cell.customMat.color); hexaGridHighlightMaterial.mainTexture = cell.customMat.mainTexture; } } return true; } /// /// Sets the color of the cell. /// /// true, if cell color was set, false otherwise. /// Cell index. /// Color. /// If set to true th_gridDivisionsored temporarily and returns to default color when it gets unselected. public bool SetCellColor(int cellIndex, Color color, bool temporary = false) { Material mat = GetCachedMaterial(color); return SetCellMaterial(cellIndex, mat, temporary); } /// /// Sets the color of a list of cells. /// /// true, if cell color was set, false otherwise. /// Cell index. /// Color. /// If set to true the cell is colored temporarily and returns to default color when it gets unselected. public void SetCellColor(List cellIndices, Color color, bool temporary = false) { if (cellIndices == null) return; Material mat = GetCachedMaterial(color); int tc = cellIndices.Count; for (int k = 0; k < tc; k++) { int cellIndex = cellIndices[k]; SetCellMaterial(cellIndex, mat, temporary); } } /// /// Sets the color of all cells contained in a country main region. /// /// Country object. /// Color. /// If set to true th_gridDivisionsored temporarily and returns to default color when it gets unselected. public void SetCellColor(Country country, Color color, bool temporary = false) { if (country == null) return; Region region = country.mainRegion; for (int k = 0; k < cells.Length; k++) { if (region.Contains(cells[k].latlonCenter)) { SetCellColor(k, color, temporary); } } } /// /// Sets the color of all cells contained in a province main region. /// /// Province object. /// Color. /// If set to true th_gridDivisionsored temporarily and returns to default color when it gets unselected. public void SetCellColor(Province province, Color color, bool temporary = false) { if (province == null) return; Region region = province.mainRegion; for (int k = 0; k < cells.Length; k++) { if (region.Contains(cells[k].latlonCenter)) { SetCellColor(k, color, temporary); } } } /// /// Sets the texture of the cell. /// /// true, if cell color was set, false otherwise. /// Cell index. /// Color. /// If set to true the cell is colored temporarily and returns to default color when it gets unselected. public bool SetCellTexture(int cellIndex, Texture2D texture, bool temporary = false) { return SetCellTexture(cellIndex, texture, Color.white, temporary); } /// /// Sets the texture and tint color of the cell. /// /// true, if cell color was set, false otherwise. /// Cell index. /// Color. /// Optional tint color. /// If set to true the cell is colored temporarily and returns to default color when it gets unselected. public bool SetCellTexture(int cellIndex, Texture2D texture, Color tint, bool temporary = false) { Material mat = GetCachedMaterial(tint, texture); return SetCellMaterial(cellIndex, mat, temporary); } /// /// Returns current cell color. /// public Color GetCellColor(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return Color.white; Cell cell = cells[cellIndex]; if (cell.tempMat != null) return cell.tempMat.color; if (cell.customMat != null) return cell.customMat.color; return Color.white; } /// /// Gets the cell neighbours. /// /// Cell index. public Cell[] GetCellNeighbours(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return null; return cells[cellIndex].neighbours; } /// /// Gets the cell neighbours indices. /// /// Cell index. public int[] GetCellNeighboursIndices(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return null; return cells[cellIndex].neighboursIndices; } /// /// Given a cell, returns the neighbour index of a second cell. If that cell is not a neighbour, this function returns -1 /// /// The cell neighbour index. /// Cell index. /// Cell neighbour index. public int GetCellNeighbourIndex(int cellIndex, int cellNeighbourIndex) { if (cells == null || cellIndex < 0 || cellIndex >= cells.Length || cellNeighbourIndex < 0 || cellNeighbourIndex >= cells.Length) return -1; Cell cell = cells[cellIndex]; int neighbourCount = cell.neighboursIndices.Length; for (int k = 0; k < neighbourCount; k++) { if (cellNeighbourIndex == cell.neighboursIndices[k]) { return k; } } return -1; } /// /// Returns the points of the edge line which is shared by two cells /// /// The cell shared edge. /// Cell index. /// Cell neighbour index. public Vector3[] GetCellSharedEdge(int cellIndex, int cellNeighbourIndex) { if (cellIndex < 0 || cellIndex >= cells.Length || cellNeighbourIndex < 0 || cellNeighbourIndex >= cells.Length) return null; Cell cell1 = cells[cellIndex]; Cell cell2 = cells[cellNeighbourIndex]; Vector3[] edge = new Vector3[2]; int vertexIndex = 0; for (int k = 0; k < cell1.vertexPoints.Length; k++) { for (int j = 0; j < cell2.vertexPoints.Length; j++) { if (cell1.vertexPoints[k] == cell2.vertexPoints[j]) { edge[vertexIndex++] = cell1.vertices[k]; if (vertexIndex > 1) break; } } } return edge; } /// /// Hide a given cell /// public void ClearCell(int cellIndex, bool clearTemporaryColor = false, bool clearAllColors = true, bool clearObstacles = true) { if (cellIndex < 0 || cellIndex >= cells.Length) return; Cell cell = cells[cellIndex]; Renderer cellRenderer = cell.renderer; cell.tempMat = null; if (cellRenderer != null) { cellRenderer.enabled = false; } if (clearAllColors) { cell.customMat = null; } if (clearObstacles) { cell.canCross = true; } } /// /// Hide all cells /// public void ClearCells(bool clearTemporaryColors = false, bool clearAllColors = true, bool clearObstacles = true) { for (int k = 0; k < cells.Length; k++) { ClearCell(k, clearTemporaryColors, clearAllColors, clearObstacles); } ResetHighlightMaterial(); } /// /// Destroys a colored cell /// public void DestroyCell(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return; cells[cellIndex].tempMat = null; cells[cellIndex].customMat = null; if (cells[cellIndex].renderer != null) { DestroyImmediate(cells[cellIndex].renderer.gameObject); cells[cellIndex].renderer = null; } } /// /// Toggles cell visibility /// public bool ToggleCell(int cellIndex, bool visible) { if (cellIndex < 0 || cellIndex >= cells.Length) return false; Cell cell = cells[cellIndex]; if (cell.renderer != null) { cell.renderer.enabled = visible; return true; } if (cell.visible != visible) { cell.visible = visible; shouldGenerateGrid = true; } return false; } /// /// Hides a colored cell /// public bool HideCell(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return false; if (cells[cellIndex].renderer != null) { cells[cellIndex].renderer.enabled = false; return true; } return false; } /// /// Shows a colored cell /// public bool ShowCell(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return false; if (cells[cellIndex].renderer != null) { cells[cellIndex].renderer.enabled = true; return true; } return false; } /// /// Returns true if cell is visible /// /// /// public bool IsCellVisible(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return false; return cells[cellIndex].visible; } /// /// Returns the center of the cell in world space coordinates. /// public Vector3 GetWorldSpaceCellCenter(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return Vector3.zero; Cell cell = cells[cellIndex]; Vector3 cellTop = transform.TransformPoint(cell.sphereCenter); return cellTop; } /// /// Starts navigating to target cell by index in the cells collection with specified duration using NavigationTime property for duration. /// public void FlyToCell(int cellIndex) { if (cellIndex < 0 || cellIndex >= cells.Length) return; FlyToLocation(cells[cellIndex].sphereCenter, _navigationTime, 0, _navigationBounceIntensity); } /// /// Starts navigating to target cell by index in the cells collection with specified duration, ignoring NavigationTime property. /// Set duration to zero to go instantly. /// public void FlyToCell(int cellIndex, float duration) { if (cellIndex < 0 || cellIndex >= cells.Length) return; FlyToLocation(cells[cellIndex].sphereCenter, duration, 0, _navigationBounceIntensity); } /// /// Starts navigating to target cell by index in the cells collection with specified duration, ignoring NavigationTime property. /// Set duration to zero to go instantly. /// Set zoomLevel to a value from 0 to 1 for the destination zoom level. /// public void FlyToCell(int cellIndex, float duration, float zoomLevel) { if (cellIndex < 0 || cellIndex >= cells.Length) return; FlyToLocation(cells[cellIndex].sphereCenter, duration, zoomLevel, _navigationBounceIntensity); } /// /// Starts navigating to target cell by index in the cells collection with specified duration, ignoring NavigationTime property. /// Set duration to zero to go instantly. /// Set zoomLevel to a value from 0 to 1 for the destination zoom level. /// Set bounceIntensity to a value from 0 to 1 for a bouncing effect between current position and destination /// public void FlyToCell(int cellIndex, float duration, float zoomLevel, float bounceIntensity) { if (cellIndex < 0 || cellIndex >= cells.Length) return; FlyToLocation(cells[cellIndex].sphereCenter, duration, zoomLevel, _navigationBounceIntensity); } /// /// Get all cell indices whose center is inside a given region /// public List GetCells(Region region, int enclosedVertexCount = 3) { List results = new List(); for (int k = 0; k < cells.Length; k++) { if (region.Contains(cells[k].latlonCenter)) { results.Add(k); continue; } Cell cell = cells[k]; int vertexCount = cell.latlon.Length; int count = 0; for (int v = 0; v < vertexCount; v++) { if (region.Contains(cell.latlon[v])) { count++; if (count >= enclosedVertexCount) { results.Add(k); break; } } } } return results; } #endregion } }