// World Political Map - Globe Edition for Unity - Main Script // Created by Ramiro Oliva (Kronnect) // Don't modify this script - changes could be lost if you upgrade to a more recent version of WPM // *************************************************************************** // This is the public API file - every property or public method belongs here // *************************************************************************** using UnityEngine; using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Text; using WPM.ClipperLib; namespace WPM { public delegate void ProvinceBeforeEnter(int provinceIndex, int regionIndex, ref bool ignoreProvince); public delegate void ProvinceEvent(int provinceIndex, int regionIndex); /* Public WPM Class */ public partial class WorldMapGlobe : MonoBehaviour { public event ProvinceBeforeEnter OnProvinceBeforeEnter; public event ProvinceEvent OnProvinceEnter; public event ProvinceEvent OnProvinceExit; public event ProvinceEvent OnProvincePointerDown; public event ProvinceEvent OnProvincePointerUp; public event ProvinceEvent OnProvinceClick; Province[] _provinces; /// /// Complete array of states and provinces and the country name they belong to. /// public Province[] provinces { get { if (_provinces == null) ReadProvincesPackedString(); return _provinces; } set { _provinces = value; lastProvinceLookupCount = -1; } } Province _provinceHighlighted; /// /// Returns Province under mouse position or null if none. /// public Province provinceHighlighted { get { return _provinceHighlighted; } } int _provinceHighlightedIndex = -1; /// /// Returns current highlighted province index. /// public int provinceHighlightedIndex { get { return _provinceHighlightedIndex; } } [SerializeField] bool _enableProvinceHighlight = true; /// /// Enable/disable province highlight when mouse is over. /// public bool enableProvinceHighlight { get { return _enableProvinceHighlight; } set { if (_enableProvinceHighlight != value) { _enableProvinceHighlight = value; isDirty = true; } } } [SerializeField] float _provinceHighlightMaxScreenAreaSize = 1f; /// /// Defines the maximum area of a highlighted province. To prevent filling the whole screen with the highlight color, you can reduce this value and if the highlighted screen area size is greater than this factor (1=whole screen) the province won't be filled (it will behave as selected though) /// public float provinceHighlightMaxScreenAreaSize { get { return _provinceHighlightMaxScreenAreaSize; } set { if (_provinceHighlightMaxScreenAreaSize != value) { _provinceHighlightMaxScreenAreaSize = value; isDirty = true; } } } int _provinceLastClicked = -1; /// /// Returns the last clicked province index. /// public int provinceLastClicked { get { return _provinceLastClicked; } } int _provinceRegionLastClicked = -1; /// /// Returns the last clicked province region index. /// public int provinceRegionLastClicked { get { return _provinceRegionLastClicked; } } Region _provinceRegionHighlighted; /// /// Returns currently highlightd province's region. /// /// The country region highlighted. public Region provinceRegionHighlighted { get { return _provinceRegionHighlighted; } } int _provinceRegionHighlightedIndex = -1; /// /// Returns current highlighted province's region index. /// public int provinceRegionHighlightedIndex { get { return _provinceRegionHighlightedIndex; } } [SerializeField] bool _showProvinces = false; /// /// Toggle frontiers visibility. /// public bool showProvinces { get { return _showProvinces; } set { if (value != _showProvinces) { _showProvinces = value; isDirty = true; if (_showProvinces) { if (_provinces == null) { ReadProvincesPackedString(); } if (_drawAllProvinces) { DrawAllProvinceBorders(true); } } else { HideProvinces(); } } } } [SerializeField] bool _drawAllProvinces = false; /// /// Forces drawing of all provinces and not only thouse of currently selected country. /// public bool drawAllProvinces { get { return _drawAllProvinces; } set { if (value != _drawAllProvinces) { _drawAllProvinces = value; isDirty = true; DrawAllProvinceBorders(true); } } } [SerializeField] Color _provincesFillColor = new Color(0, 0, 1, 0.7f); /// /// Fill color to use when the mouse hovers a country's region. /// public Color provincesFillColor { get { if (hudMatProvince != null) { return hudMatProvince.color; } else { return _provincesFillColor; } } set { if (value != _provincesFillColor) { _provincesFillColor = value; isDirty = true; if (hudMatProvince != null && _provincesFillColor != hudMatProvince.color) { hudMatProvince.color = _provincesFillColor; } } } } [SerializeField] Color _provincesColor = Color.white; /// /// Global color for provinces. /// public Color provincesColor { get { return _provincesColor; } set { if (value != _provincesColor) { _provincesColor = value; isDirty = true; UpdateProvincesMat(); } } } [SerializeField] bool _enableProvinceEnclaves = false; /// /// Allows a province to be surrounded by another province /// public bool enableProvinceEnclaves { get { return _enableProvinceEnclaves; } set { if (value != _enableProvinceEnclaves) { _enableProvinceEnclaves = value; isDirty = true; } } } string _provinceAttributeFile = PROVINCE_ATTRIB_DEFAULT_FILENAME; public string provinceAttributeFile { get { return _provinceAttributeFile; } set { if (value != _provinceAttributeFile) { _provinceAttributeFile = value; if (_provinceAttributeFile == null) _provinceAttributeFile = PROVINCE_ATTRIB_DEFAULT_FILENAME; isDirty = true; ReloadProvincesAttributes(); } } } #region Public API area /// /// Draws the borders of the provinces/states a country by its id. Returns true is country is found, false otherwise. /// Note: if you need persistent provinces, call DrawProvinces(List...) instead. /// public bool DrawProvince(int countryIndex, bool includeNeighbours, bool forceRefresh) { if (countryIndex >= 0) { return mDrawProvinces(countryIndex, includeNeighbours, forceRefresh); } return false; } /// /// Draws the borders of provinces of a list of countries. Province borders remain regardless of country selection. /// /// public void DrawProvinces(List countryIndices) { if (provinces == null) ReadProvincesPackedString(); for (int k = 0; k < _countries.Length; k++) { Country c = _countries[k]; c.allowShowProvinces = countryIndices.Contains(c); } _drawAllProvinces = true; _showProvinces = true; DrawAllProvinceBorders(true); } /// /// Hides all provinces. /// public void HideProvinces() { if (provincesObj != null) { DestroyImmediate(provincesObj); } countryProvincesDrawnIndex = -1; HideProvinceRegionHighlight(); } /// /// Gets the province object by its index. This function equals to map.provinces[provinceIndex]. /// /// The province. public Province GetProvince(int provinceIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return null; return _provinces[provinceIndex]; } /// /// Gets the province object by its name and country name. /// /// The province. /// Country name. /// Province name. public Province GetProvince(string countryName, string provinceName) { int provinceIndex = GetProvinceIndex(countryName, provinceName); if (provinceIndex >= 0) return _provinces[provinceIndex]; return null; } /// /// Returns the index of a province in the provinces array by its reference. /// public int GetProvinceIndex(Province province) { int provinceIndex; if (provinceLookup.TryGetValue(province, out provinceIndex)) return provinceIndex; else return -1; } /// /// Returns the index of a province in the global provinces array. /// public int GetProvinceIndex(string countryName, string provinceName) { int countryIndex = GetCountryIndex(countryName); return GetProvinceIndex(countryIndex, provinceName); } /// /// Returns the index of a province in the global provinces array. /// public int GetProvinceIndex(int countryIndex, string provinceName) { if (countryIndex < 0 || countryIndex >= countries.Length) return -1; Country country = countries[countryIndex]; if (country.provinces == null) return -1; for (int k = 0; k < country.provinces.Length; k++) { if (country.provinces[k].name.Equals(provinceName)) { return GetProvinceIndex(country.provinces[k]); } } return -1; } /// /// Returns the province index by screen position. /// public bool GetProvinceIndex(int countryIndex, Ray ray, out int provinceIndex, out int regionIndex) { Vector3 hitPos; if (GetGlobeIntersection(ray, out hitPos)) { Vector3 localHit = transform.InverseTransformPoint(hitPos); if (GetProvinceUnderMouse(countryIndex, localHit, out provinceIndex, out regionIndex)) return true; } provinceIndex = -1; regionIndex = -1; return false; } /// /// Gets the index of the province that contains the provided map coordinates. This will ignore hidden countries. /// /// The province index. public int GetProvinceIndex(Vector3 spherePosition) { // verify if hitPos is inside any country polygon int countryIndex = GetCountryIndex(spherePosition); if (countryIndex >= 0) { int provinceIndex, provinceRegionIndex; if (GetProvinceUnderSpherePosition(countryIndex, spherePosition, out provinceIndex, out provinceRegionIndex)) { return provinceIndex; } } return -1; } /// /// Gets the province that contains a given map coordinate or the province whose center is nearest to that coordinate. /// public int GetProvinceNearPoint(Vector3 spherePosition) { int provinceIndex = GetProvinceIndex(spherePosition); if (provinceIndex >= 0) return provinceIndex; float minDist = float.MaxValue; for (int k = 0; k < _provinces.Length; k++) { float dist = FastVector.SqrDistanceByValue(_provinces[k].localPosition, spherePosition); // Vector3.SqrMagnitude (_provinces [k].sphereCenter - spherePosition); if (dist < minDist) { minDist = dist; provinceIndex = k; } } return provinceIndex; } /// /// Returns the province located in the sphere point provided (must provide the country index to which the province belongs). See also GetCountryUnderSpherePosition. /// public bool GetProvinceUnderSpherePosition(int countryIndex, Vector3 spherePoint, out int provinceIndex, out int provinceRegionIndex) { return GetProvinceUnderMouse(countryIndex, spherePoint, out provinceIndex, out provinceRegionIndex); } /// /// Returns an array of province objects for the specified country. /// public Province[] GetProvinces(string countryName) { int countryIndex = GetCountryIndex(countryName); return GetProvinces(countryIndex); } /// /// Returns an array of province objects for the specified country. /// public Province[] GetProvinces(Country country) { if (country == null || provinces == null) { return null; } return country.provinces; } /// /// Returns an array of province objects for the specified country. /// public Province[] GetProvinces(int countryIndex) { if (countryIndex < 0 || countryIndex >= countries.Length) { return null; } return GetProvinces(_countries[countryIndex]); } /// /// Returns a list of provinces whose center is contained in a given region /// public void GetProvinces(Region region, List provinces) { int provCount = provinces.Count; provinces.Clear(); for (int k = 0; k < provCount; k++) { if (region.Contains(_provinces[k].latlonCenter)) provinces.Add(_provinces[k]); } } /// /// Returns an array of province names. The returning list can be grouped by country. /// public string[] GetProvinceNames(bool groupByCountry) { List c = new List(provinces.Length + countries.Length); if (provinces == null) return c.ToArray(); bool[] countriesAdded = new bool[countries.Length]; for (int k = 0; k < provinces.Length; k++) { Province province = provinces[k]; if (province != null) { // could be null if country doesn't exist in this level of quality if (groupByCountry) { if (!countriesAdded[province.countryIndex]) { countriesAdded[province.countryIndex] = true; c.Add(countries[province.countryIndex].name); } c.Add(countries[province.countryIndex].name + "|" + province.name + " (" + k + ")"); } else { c.Add(province.name + " (" + k + ")"); } } } c.Sort(); if (groupByCountry) { int k = -1; while (++k < c.Count) { int i = c[k].IndexOf('|'); if (i > 0) { c[k] = " " + c[k].Substring(i + 1); } } } return c.ToArray(); } /// /// Returns an array of province names for the specified country. /// public string[] GetProvinceNames(int countryIndex) { List c = new List(100); if (provinces == null || countryIndex < 0 || countryIndex >= countries.Length) return c.ToArray(); for (int k = 0; k < provinces.Length; k++) { Province province = provinces[k]; if (province.countryIndex == countryIndex) { c.Add(province.name + " (" + k + ")"); } } c.Sort(); return c.ToArray(); } /// /// Returns proposedName if it's unique in the country collection. Otherwise it adds a suffix to the name to make it unique. /// /// The country unique name. public string GetProvinceUniqueName(string proposedName) { string n = proposedName; int iteration = 2; bool repeat = true; while (repeat) { repeat = false; for (int k = 0; k < _provinces.Length; k++) { if (_provinces[k].name.Equals(proposedName)) { proposedName = n + " " + iteration++; repeat = true; break; } } } return proposedName; } public string[] GetProvinceCountriesNeighboursNames(int provinceIndex, bool includeProvinceIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return null; List c = new List(50); Region region = provinces[provinceIndex].mainRegion; int countryIndex = provinces[provinceIndex].countryIndex; int nc = region.neighbours.Count; if (nc == 0) return c.ToArray(); for (int k = 0; k < nc; k++) { Region nr = region.neighbours[k]; Province op = (Province)nr.entity; int cIndex = op.countryIndex; if (cIndex == countryIndex) continue; string s; if (includeProvinceIndex) { s = " " + countries[cIndex].name + " (" + cIndex + ")"; } else { s = " " + countries[cIndex].name; } if (!c.Contains(s)) c.Add(s); } if (c.Count > 0) { c.Sort(); c.Insert(0, "Neighbours of " + provinces[provinceIndex].name); } return c.ToArray(); } /// /// Returns a list of provinces whose attributes matches predicate /// public void GetProvinces(AttribPredicate predicate, List results) { if (results == null) return; int provinceCount = provinces.Length; for (int k = 0; k < provinceCount; k++) { Province province = _provinces[k]; if (province.hasAttributes && predicate(province.attrib)) results.Add(province); } } /// /// Gets XML attributes of all provinces in jSON format. /// public string GetProvincesAttributes(bool prettyPrint = true) { if (provinces == null) return null; return GetProvincesAttributes(new List(provinces), prettyPrint); } /// /// Gets XML attributes of provided provinces in jSON format. /// public string GetProvincesAttributes(List provinces, bool prettyPrint = true) { JSONObject composed = new JSONObject(); int provinceCount = provinces.Count; for (int k = 0; k < provinceCount; k++) { Province province = _provinces[k]; if (province.hasAttributes && province.attrib.keys != null) composed.AddField(k.ToString(), province.attrib); } return composed.Print(prettyPrint); } /// /// Sets provinces attributes from a jSON formatted string. /// public void SetProvincesAttributes(string jSON) { JSONObject composed = new JSONObject(jSON); if (composed.keys == null) return; int keyCount = composed.keys.Count; for (int k = 0; k < keyCount; k++) { int provinceIndex = int.Parse(composed.keys[k]); if (provinceIndex >= 0) { provinces[provinceIndex].attrib = composed[k]; } } } /// /// Adds a new province which has been properly initialized. Used by the Map Editor. Name must be unique. /// /// true if province was added, false otherwise. public bool ProvinceAdd(Province province) { if (province.countryIndex < 0 || province.countryIndex >= countries.Length) return false; Province[] newProvinces = new Province[provinces.Length + 1]; for (int k = 0; k < provinces.Length; k++) { newProvinces[k] = provinces[k]; } newProvinces[newProvinces.Length - 1] = province; provinces = newProvinces; lastProvinceLookupCount = -1; // add the new province to the country internal list Country country = countries[province.countryIndex]; if (country.provinces == null) country.provinces = new Province[0]; Province[] newCountryProvinces = new Province[country.provinces.Length + 1]; for (int k = 0; k < country.provinces.Length; k++) { newCountryProvinces[k] = country.provinces[k]; } newCountryProvinces[newCountryProvinces.Length - 1] = province; country.provinces = newCountryProvinces; return true; } /// /// Renames the province. Name must be unique, different from current and one letter minimum. /// /// true if country was renamed, false otherwise. public bool ProvinceRename(int countryIndex, string oldName, string newName) { if (newName == null || newName.Length == 0) return false; int provinceIndex = GetProvinceIndex(countryIndex, oldName); int newProvinceIndex = GetProvinceIndex(countryIndex, newName); if (provinceIndex < 0 || newProvinceIndex >= 0) return false; provinces[provinceIndex].name = newName; // Updates all cities that depends on this province int cityCount = cities.Count; for (int k = 0; k < cityCount; k++) { if (_cities[k].province.Equals(oldName)) { _cities[k].province = newName; } } lastProvinceLookupCount = -1; return true; } /// /// Delete all provinces from specified continent. /// public void ProvincesDeleteOfSameContinent(string continentName) { HideProvinceRegionHighlights(true); if (provinces == null) return; int numProvinces = _provinces.Length; List newProvinces = new List(numProvinces); for (int k = 0; k < numProvinces; k++) { if (_provinces[k] != null) { int c = _provinces[k].countryIndex; if (!countries[c].continent.Equals(continentName)) { newProvinces.Add(_provinces[k]); } } } provinces = newProvinces.ToArray(); } /// /// Returns all neighbour provinces /// public List ProvinceNeighbours(int provinceIndex) { List provinceNeighbours = new List(); // Get province object Province province = provinces[provinceIndex]; // Iterate for all regions (a province can have several separated regions) for (int provinceRegionIndex = 0; provinceRegionIndex < province.regions.Count; provinceRegionIndex++) { Region provinceRegion = province.regions[provinceRegionIndex]; // Get the neighbours for this region for (int neighbourIndex = 0; neighbourIndex < provinceRegion.neighbours.Count; neighbourIndex++) { Region neighbour = provinceRegion.neighbours[neighbourIndex]; Province neighbourProvince = (Province)neighbour.entity; if (!provinceNeighbours.Contains(neighbourProvince)) { provinceNeighbours.Add(neighbourProvince); } } } return provinceNeighbours; } /// /// Get neighbours of the main region of a province /// public List ProvinceNeighboursOfMainRegion(int provinceIndex) { List provinceNeighbours = new List(); // Get main region Province province = provinces[provinceIndex]; Region provinceRegion = province.regions[province.mainRegionIndex]; // Get the neighbours for this region for (int neighbourIndex = 0; neighbourIndex < provinceRegion.neighbours.Count; neighbourIndex++) { Region neighbour = provinceRegion.neighbours[neighbourIndex]; Province neighbourProvince = (Province)neighbour.entity; if (!provinceNeighbours.Contains(neighbourProvince)) { provinceNeighbours.Add(neighbourProvince); } } return provinceNeighbours; } /// /// Get neighbours of the currently selected region /// public List ProvinceNeighboursOfCurrentRegion() { List provinceNeighbours = new List(); // Get main region Region selectedRegion = provinceRegionHighlighted; if (selectedRegion == null) return provinceNeighbours; // Get the neighbours for this region for (int neighbourIndex = 0; neighbourIndex < selectedRegion.neighbours.Count; neighbourIndex++) { Region neighbour = selectedRegion.neighbours[neighbourIndex]; Province neighbourProvince = (Province)neighbour.entity; if (!provinceNeighbours.Contains(neighbourProvince)) { provinceNeighbours.Add(neighbourProvince); } } return provinceNeighbours; } /// /// Starts navigation to target province/state. Returns false if not found. /// public CallbackHandler FlyToProvince(string name) { for (int k = 0; k < provinces.Length; k++) { if (name.Equals(provinces[k].name)) { return FlyToProvince(k, _navigationTime); } } return null; } /// /// Starts navigation to target province/state by index in the provinces collection. /// public CallbackHandler FlyToProvince(int provinceIndex) { return FlyToProvince(provinceIndex, _navigationTime); } /// /// Starts navigation to target province/state by index in the provinces collection and providing the duration in seconds. /// public CallbackHandler FlyToProvince(int provinceIndex, float duration) { return FlyToLocation(provinces[provinceIndex].localPosition, duration); } /// /// Starts navigating to target province/state by index in the provinces 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 CallbackHandler FlyToProvince(int provinceIndex, float duration, float zoomLevel) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return CallbackHandler.Null; return FlyToLocation(_provinces[provinceIndex].localPosition, duration, zoomLevel); } /// /// Starts navigating to target province/state by index in the provinces 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 CallbackHandler FlyToProvince(int provinceIndex, float duration, float zoomLevel, float bounceIntensity) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return CallbackHandler.Null; return FlyToLocation(_provinces[provinceIndex].localPosition, duration, zoomLevel, bounceIntensity); } /// /// Colorize all regions of specified province/state. Returns false if not found. /// public bool ToggleProvinceSurface(string countryName, string provinceName, bool visible, Color color) { int provinceIndex = GetProvinceIndex(countryName, provinceName); return ToggleProvinceSurface(provinceIndex, visible, color); } /// /// Colorize all regions of specified province/state. /// public bool ToggleProvinceSurface(Province province, bool visible, Color color) { int provinceIndex = GetProvinceIndex(province); return ToggleProvinceSurface(provinceIndex, visible, color); } /// /// Colorize all regions of specified province/state by index in the provinces collection. /// public bool ToggleProvinceSurface(int provinceIndex, bool visible, Color color) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return false; if (!visible) { HideProvinceSurfaces(provinceIndex); return true; } int regionCount = provinces[provinceIndex].regions.Count; for (int r = 0; r < regionCount; r++) { ToggleProvinceRegionSurface(provinceIndex, r, visible, color); } return true; } /// /// Colorize the main region of specified province/state by index in the provinces collection. /// public void ToggleProvinceMainRegionSurface(int provinceIndex, bool visible, Color color) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return; int regionIndex = provinces[provinceIndex].mainRegionIndex; ToggleProvinceRegionSurface(provinceIndex, regionIndex, visible, color); } /// /// Colorize the main region of specified province/state by index in the provinces collection. /// public void ToggleProvinceMainRegionSurface(int provinceIndex, bool visible, Color color, Texture2D texture) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return; int regionIndex = provinces[provinceIndex].mainRegionIndex; ToggleProvinceRegionSurface(provinceIndex, regionIndex, visible, color, texture, Misc.Vector2one, Misc.Vector2zero, 0, false); } /// /// Colorize the main region of specified province/state by index in the provinces collection. /// public void ToggleProvinceMainRegionSurface(int provinceIndex, bool visible, Color color, Texture2D texture, Vector2 textureScale, Vector2 textureOffset, float textureRotation) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return; int regionIndex = provinces[provinceIndex].mainRegionIndex; ToggleProvinceRegionSurface(provinceIndex, regionIndex, visible, color, texture, textureScale, textureOffset, textureRotation, false); } /// /// Colorize or texture a region of specified province/state by index in the provinces collection. /// public void ToggleProvinceRegionSurface(int provinceIndex, int regionIndex, bool visible, Color color) { ToggleProvinceRegionSurface(provinceIndex, regionIndex, visible, color, null, Vector2.one, Vector2.zero, 0, false); } /// /// Colorize or texture a region of specified province/state by index in the provinces collection. /// public void ToggleProvinceRegionSurface(int provinceIndex, int regionIndex, bool visible, Color color, Texture2D texture) { ToggleProvinceRegionSurface(provinceIndex, regionIndex, visible, color, texture, Misc.Vector2one, Misc.Vector2zero, 0, false); } /// /// Colorize or texture a region of specified province/state by index in the provinces collection. /// public GameObject ToggleProvinceRegionSurface(int provinceIndex, int regionIndex, bool visible, Color color, Texture2D texture, Vector2 textureScale, Vector2 textureOffset, float textureRotation, bool temporary) { if (!visible) { HideProvinceRegionSurface(provinceIndex, regionIndex); return null; } GameObject surf = null; Region region = provinces[provinceIndex].regions[regionIndex]; int cacheIndex = GetCacheIndexForProvinceRegion(provinceIndex, regionIndex); // Checks if current cached surface contains a material with a texture, if it exists but it has not texture, destroy it to recreate with uv mappings surfaces.TryGetValue(cacheIndex, out surf); // Should the surface be recreated? Material surfMaterial; if (surf != null) { surfMaterial = surf.GetComponent().sharedMaterial; if (texture != null && (region.customMaterial == null || textureScale != region.customTextureScale || textureOffset != region.customTextureOffset || textureRotation != region.customTextureRotation || !region.customMaterial.name.Equals(provinceTexturizedMat.name))) { surfaces.Remove(cacheIndex); DestroyImmediate(surf); surf = null; } } // If it exists, activate and check proper material, if not create surface bool isHighlighted = provinceHighlightedIndex == provinceIndex && provinceRegionHighlightedIndex == regionIndex && _enableProvinceHighlight; if (surf != null) { bool needMaterial = false; if (!surf.activeSelf) { surf.SetActive(true); needMaterial = true; UpdateSurfaceCount(); } else { // Check if material is ok surfMaterial = surf.GetComponent().sharedMaterial; if ((texture == null && !surfMaterial.name.Equals(provinceColoredMat.name)) || (texture != null && !surfMaterial.name.Equals(provinceTexturizedMat.name)) || (surfMaterial.color != color && !isHighlighted) || (texture != null && region.customMaterial.mainTexture != texture)) needMaterial = true; } if (needMaterial) { Material goodMaterial = GetProvinceColoredTexturedMaterial(color, texture); region.customMaterial = goodMaterial; region.customTextureOffset = textureOffset; region.customTextureRotation = textureRotation; region.customTextureScale = textureScale; ApplyMaterialToSurface(surf, goodMaterial); } } else { surfMaterial = GetProvinceColoredTexturedMaterial(color, texture); surf = GenerateProvinceRegionSurface(provinceIndex, regionIndex, surfMaterial, textureScale, textureOffset, textureRotation, temporary); } // If it was highlighted, highlight it again if (region.customMaterial != null && isHighlighted && region.customMaterial.color != hudMatProvince.color) { Material clonedMat = Instantiate(region.customMaterial); clonedMat.name = region.customMaterial.name; clonedMat.color = hudMatProvince.color; surf.GetComponent().sharedMaterial = clonedMat; provinceRegionHighlightedObj = surf; } return surf; } /// /// Disables all province regions highlights. This doesn't destroy custom materials. /// public void HideProvinceRegionHighlights(bool destroyCachedSurfaces) { HideProvinceRegionHighlight(); if (_provinces == null) return; int provincesCount = provinces.Length; for (int c = 0; c < provincesCount; c++) { Province province = _provinces[c]; if (province == null || province.regions == null) continue; for (int cr = 0; cr < province.regions.Count; cr++) { Region region = province.regions[cr]; int cacheIndex = GetCacheIndexForProvinceRegion(c, cr); GameObject surf; if (surfaces.TryGetValue(cacheIndex, out surf)) { if (surf == null) { surfaces.Remove(cacheIndex); } else { if (destroyCachedSurfaces) { surfaces.Remove(cacheIndex); DestroyImmediate(surf); } else { if (region.customMaterial == null) { surf.SetActive(false); } else { ApplyMaterialToSurface(surf, region.customMaterial); } } } } } } } /// /// Hides all colorized regions of all provinces/states. /// public void HideProvinceSurfaces() { if (provinces == null) return; for (int p = 0; p < provinces.Length; p++) { HideProvinceSurfaces(p); } } /// /// Hides all colorized regions of one province/state. /// public void HideProvinceSurfaces(int provinceIndex, bool destroyCachedSurface = false) { if (provinces[provinceIndex].regions == null) return; for (int r = 0; r < provinces[provinceIndex].regions.Count; r++) { HideProvinceRegionSurface(provinceIndex, r, destroyCachedSurface); } } /// /// Hides all regions of one province. /// public void HideProvinceRegionSurface(int provinceIndex, int regionIndex, bool destroyCachedSurface = false) { int cacheIndex = GetCacheIndexForProvinceRegion(provinceIndex, regionIndex); GameObject surf = null; if (surfaces.TryGetValue(cacheIndex, out surf)) { if (surf == null) { surfaces.Remove(cacheIndex); } else if (destroyCachedSurface) { DestroyImmediate(surf); surfaces.Remove(cacheIndex); } else { surf.SetActive(false); } UpdateSurfaceCount(); } provinces[provinceIndex].regions[regionIndex].customMaterial = null; } /// /// Flashes specified province by index in the global province array. /// public void BlinkProvince(int provinceIndex, Color color1, Color color2, float duration, float blinkingSpeed, bool smoothBlink = false) { int mainRegionIndex = provinces[provinceIndex].mainRegionIndex; BlinkProvince(provinceIndex, mainRegionIndex, color1, color2, duration, blinkingSpeed, smoothBlink); } /// /// Flashes specified province's region. /// public void BlinkProvince(int provinceIndex, int regionIndex, Color color1, Color color2, float duration, float blinkingSpeed, bool smoothBlink = false) { int cacheIndex = GetCacheIndexForProvinceRegion(provinceIndex, regionIndex); GameObject surf; bool disableAtEnd; if (surfaces.ContainsKey(cacheIndex)) { surf = surfaces[cacheIndex]; disableAtEnd = !surf.activeSelf; } else { surf = GenerateProvinceRegionSurface(provinceIndex, regionIndex, hudMatProvince, true); disableAtEnd = true; } surf.SetActive(true); SurfaceBlinker sb = surf.AddComponent(); sb.blinkMaterial = hudMatProvince; sb.color1 = color1; sb.color2 = color2; sb.duration = duration; sb.speed = blinkingSpeed; sb.disableAtEnd = disableAtEnd; sb.customizableSurface = provinces[provinceIndex].regions[regionIndex]; sb.smoothBlink = smoothBlink; } /// /// Returns the colored surface (game object) of a province. If it has not been colored yet, it will return null. /// public GameObject GetProvinceRegionSurfaceGameObject(int provinceIndex, int regionIndex) { int cacheIndex = GetCacheIndexForProvinceRegion(provinceIndex, regionIndex); GameObject surf = null; surfaces.TryGetValue(cacheIndex, out surf); return surf; } /// /// Returns the zoom level which shows the province main region in full screen /// /// The province region zoom level. /// Country index. public float GetProvinceMainRegionZoomExtents(int provinceIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return 0; Province province = _provinces[provinceIndex]; return GetProvinceRegionZoomExtents(provinceIndex, province.mainRegionIndex); } /// /// Returns the zoom level which shows the province region in full screen /// /// The province region zoom level. /// Province index. /// Region index. public float GetProvinceRegionZoomExtents(int provinceIndex, int regionIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return 0; Province province = _provinces[provinceIndex]; if (regionIndex < 0 || regionIndex >= province.regions.Count) return 0; return GetRegionZoomExtents(province.regions[regionIndex]); } #endregion /// /// Returns a list of provinces that are visible (front facing camera) /// public List GetVisibleProvinces() { if (provinces == null) return null; List vc = GetVisibleCountries(); List vp = new List(30); Camera cam = mainCamera; for (int k = 0; k < vc.Count; k++) { Country country = vc[k]; if (country.provinces == null) continue; for (int p = 0; p < country.provinces.Length; p++) { Province prov = country.provinces[p]; Vector3 center = transform.TransformPoint(prov.localPosition); Vector3 dir = center - transform.position; float d = Vector3.Dot(cam.transform.forward, dir); if (d < -0.2f) { Vector3 vpos = cam.WorldToViewportPoint(center); float viewportMinX = cam.rect.xMin; float viewportMaxX = cam.rect.xMax; float viewportMinY = cam.rect.yMin; float viewportMaxY = cam.rect.yMax; if (vpos.x >= viewportMinX && vpos.x <= viewportMaxX && vpos.y >= viewportMinY && vpos.y <= viewportMaxY) { vp.Add(prov); } } } } return vp; } /// /// Returns a list of provinces that are visible and overlaps the rectangle defined by two given sphere points /// public List GetVisibleProvinces(Vector3 rectTopLeft, Vector3 rectBottomRight) { Vector2 latlon0, latlon1; latlon0 = Conversion.GetBillboardPosFromSpherePoint(rectTopLeft); latlon1 = Conversion.GetBillboardPosFromSpherePoint(rectBottomRight); Rect rect = new Rect(latlon0.x, latlon1.y, latlon1.x - latlon0.x, latlon0.y - latlon1.y); List selectedProvinces = new List(); if (_provinces == null) ReadProvincesPackedString(); List countries = GetVisibleCountries(rectTopLeft, rectBottomRight); int countryCount = countries.Count; for (int k = 0; k < countryCount; k++) { Country country = countries[k]; if (country.hidden) continue; if (country.provinces == null) continue; for (int p = 0; p < country.provinces.Length; p++) { Province province = country.provinces[p]; if (selectedProvinces.Contains(province)) continue; // Check if any of province's regions is inside rect if (province.regions == null) ReadProvincePackedString(province); if (province.regions == null) continue; int crc = province.regions.Count; for (int cr = 0; cr < crc; cr++) { Region region = province.regions[cr]; if (rect.Overlaps(region.rect2Dbillboard)) { selectedProvinces.Add(province); break; } } } } return selectedProvinces; } /// /// Makes provinceIndex absorb sourceProvinceIndex. All regions are transfered to target province. /// This function is quite slow with high definition frontiers. Note that the province indices may change after this operation. /// /// Province index of the conquering province. /// The index for the source province to be absorved. public bool ProvinceMerge(int provinceIndex, int sourceProvinceIndex, bool redraw) { if (provinceIndex < 0 || provinceIndex >= provinces.Length || sourceProvinceIndex < 0 || sourceProvinceIndex >= provinces.Length) { return false; } Region sourceProvinceRegion = provinces[sourceProvinceIndex].mainRegion; return ProvinceTransferProvinceRegion(provinceIndex, sourceProvinceRegion, redraw); } /// /// Make the first province absorb the rest of provinces. This function is quite slow with high definition frontiers. Note that the province indices may change after this operation. /// public bool ProvincesMerge(List provinces, bool redraw, bool redrawNeighbours = false) { if (provinces == null || provinces.Count < 2) return false; Province firstProvince = provinces[0]; List affectedCountries = BufferPool.Get(); affectedCountries.Add(firstProvince.countryIndex); for (int k=1;k.Release(affectedCountries); return false; } } } if (redraw) { for (int k = 0; k < affectedCountries.Count; k++) { DrawProvince(affectedCountries[k], redrawNeighbours, true); } } BufferPool.Release(affectedCountries); return true; } /// /// Makes provinceIndex absorb another province providing any of its regions. All regions are transfered to target province. /// This function is quite slow with high definition frontiers. /// /// Province index of the conquering province. /// Source region of the loosing province. public bool ProvinceTransferProvinceRegion(int provinceIndex, Region sourceProvinceRegion, bool redraw) { int sourceProvinceIndex = GetProvinceIndex((Province)sourceProvinceRegion.entity); if (provinceIndex < 0 || sourceProvinceIndex < 0 || provinceIndex == sourceProvinceIndex) return false; // Transfer cities Province sourceProvince = provinces[sourceProvinceIndex]; Province targetProvince = provinces[provinceIndex]; if (sourceProvince.countryIndex != targetProvince.countryIndex) { // Transfer source province to target country province if (!CountryTransferProvinceRegion(targetProvince.countryIndex, sourceProvinceRegion)) { return false; } } int cityCount = cities.Count; for (int k = 0; k < cityCount; k++) { if (_cities[k].countryIndex == sourceProvince.countryIndex && _cities[k].province.Equals(sourceProvince.name)) _cities[k].province = targetProvince.name; } // Transfer mount points int mountPointCount = mountPoints.Count; for (int k = 0; k < mountPointCount; k++) { if (mountPoints[k].provinceIndex == sourceProvinceIndex) mountPoints[k].provinceIndex = provinceIndex; } // Transfer regions if (sourceProvince.regions.Count > 0) { List targetRegions = new List(targetProvince.regions); for (int k = 0; k < sourceProvince.regions.Count; k++) { targetRegions.Add(sourceProvince.regions[k]); } targetProvince.regions = targetRegions; } // Fusion any adjacent regions that results from merge operation MergeAdjacentRegions(targetProvince); RegionSanitize(targetProvince.regions, false); targetProvince.mainRegionIndex = 0; // will be updated on RefreshProvinceDefinition // Finish operation ProvinceDeleteAndDependencies(sourceProvinceIndex); if (provinceIndex > sourceProvinceIndex) { provinceIndex--; } if (redraw) { RefreshProvinceDefinition(provinceIndex); } else { RefreshProvinceGeometry(provinceIndex); } return true; } /// /// Deletes current region or province if this was the last region. /// public void ProvinceDeleteAndDependencies(int provinceIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) return; // Clears references from mount points if (mountPoints != null) { for (int k = 0; k < mountPoints.Count; k++) { if (mountPoints[k].provinceIndex == provinceIndex) { mountPoints[k].provinceIndex = -1; } } } List newProvinces = new List(_provinces.Length); // Clears references from cities int countryIndex = _provinces[provinceIndex].countryIndex; if (countryIndex >= 0 && countryIndex < _countries.Length) { string provinceName = _provinces[provinceIndex].name; if (cities != null) { for (int k = 0; k < _cities.Count; k++) { if (_cities[k].countryIndex == countryIndex && _cities[k].name.Equals(provinceName)) { _cities[k].name = ""; } } } // Remove it from the country array Country country = _countries[countryIndex]; if (country.provinces != null) { for (int k = 0; k < country.provinces.Length; k++) { if (!country.provinces[k].name.Equals(provinceName)) newProvinces.Add(country.provinces[k]); } newProvinces.Sort(ProvinceSizeComparer); country.provinces = newProvinces.ToArray(); } } // Remove from the global array newProvinces.Clear(); for (int k = 0; k < _provinces.Length; k++) { if (k != provinceIndex) { newProvinces.Add(_provinces[k]); } } provinces = newProvinces.ToArray(); } /// /// Makes provinceIndex absorb an hexagonal portion of the map. If that portion belong to another province, it will be substracted from that province as well. /// This function is quite slow with high definition frontiers. /// /// Province index of the conquering province. /// Index of the cell to add to the province. public bool ProvinceTransferCell(int provinceIndex, int cellIndex, bool redraw = true) { if (provinceIndex < 0 || cellIndex < 0 || cells == null || cellIndex >= cells.Length) return false; // Start process Province province = provinces[provinceIndex]; Cell cell = cells[cellIndex]; // Create a region for the cell Region sourceRegion = new Region(province, province.regions.Count); // Convert cell points to latlon coordinates sourceRegion.UpdatePointsAndRect(cell.latlon); // Transfer cities List citiesInCell = GetCities(sourceRegion); int cityCount = citiesInCell.Count; for (int k = 0; k < cityCount; k++) { City city = citiesInCell[k]; if (city.countryIndex != province.countryIndex) { city.countryIndex = province.countryIndex; city.province = ""; // clear province since it does not apply anymore } } // Transfer mount points List mountPointsInCell = new List(); int mountPointCount = GetMountPoints(sourceRegion, mountPointsInCell); for (int k = 0; k < mountPointCount; k++) { MountPoint mp = mountPointsInCell[k]; if (mp.countryIndex != province.countryIndex) { mp.countryIndex = province.countryIndex; mp.provinceIndex = -1; // same as cities - province cleared in case it's informed since it does not apply anymore } } // Add region to target country's polygon - only if the country is touching or crossing target country frontier Region targetRegion = province.mainRegion; RegionMagnet(sourceRegion, targetRegion); Clipper clipper = new Clipper(); clipper.AddPath(sourceRegion, PolyType.ptClip); clipper.AddPaths(province.regions, PolyType.ptSubject); clipper.Execute(ClipType.ctUnion, province); // Finish operation with the country RegionSanitize(province.regions, true); RefreshProvinceGeometry(provinceIndex); // Substract cell region from any other country for (int k = 0; k < _provinces.Length; k++) { Province otherProvince = _provinces[k]; if (otherProvince == province || !otherProvince.Overlaps(sourceRegion)) continue; clipper = new Clipper(); clipper.AddPath(sourceRegion, PolyType.ptClip); clipper.AddPaths(otherProvince.regions, PolyType.ptSubject); clipper.Execute(ClipType.ctDifference, otherProvince); RegionSanitize(otherProvince.regions, true); int otherProvinceIndex = GetProvinceIndex(otherProvince); if (otherProvince.regions.Count == 0) { ProvinceDeleteAndDependencies(otherProvinceIndex); if (k >= otherProvinceIndex) k--; } else { RefreshProvinceGeometry(otherProvinceIndex); } } OptimizeFrontiers(); if (redraw) Redraw(); return true; } } }