1638 lines
70 KiB
C#
1638 lines
70 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.Text;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using TGS.Geom;
|
|
using TGS.PathFinding;
|
|
|
|
namespace TGS {
|
|
|
|
public partial class TerrainGridSystem : MonoBehaviour {
|
|
|
|
[NonSerialized]
|
|
public readonly List<Territory> territories = new List<Territory>();
|
|
|
|
public Texture2D territoriesTexture;
|
|
[ColorUsage(true, true)]
|
|
public Color territoriesTextureNeutralColor;
|
|
|
|
[SerializeField]
|
|
bool _territoriesHideNeutralCells;
|
|
|
|
/// <summary>
|
|
/// Gets or sets if neutral cells are visible.
|
|
/// </summary>
|
|
public bool territoriesHideNeutralCells {
|
|
get { return _territoriesHideNeutralCells; }
|
|
set {
|
|
if (_territoriesHideNeutralCells != value) {
|
|
_territoriesHideNeutralCells = value;
|
|
Redraw();
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
int _numTerritories = 3;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of initial territories.
|
|
/// </summary>
|
|
public int numTerritories {
|
|
get { return _numTerritories; }
|
|
set {
|
|
if (_numTerritories != value) {
|
|
_numTerritories = Mathf.Clamp(value, 0, MAX_TERRITORIES);
|
|
needGenerateMap = true;
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recreate territories and optionally keep current cells
|
|
/// </summary>
|
|
/// <param name="numTerritories"></param>
|
|
/// <param name="preserveCells">If true, cells and their visibility state will be preserved</param>
|
|
public void SetNumTerritories(int numTerritories, bool preserveCells) {
|
|
if (_numTerritories == numTerritories) return;
|
|
_numTerritories = numTerritories;
|
|
isDirty = true;
|
|
if (preserveCells) {
|
|
GenerateMap(reuseTerrainData: true, keepCells: true);
|
|
} else {
|
|
needGenerateMap = true;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
int _territoriesMaxIterations;
|
|
|
|
/// <summary>
|
|
/// Maximum number of iterations when generation territories
|
|
/// </summary>
|
|
public int territoriesMaxIterations {
|
|
get { return _territoriesMaxIterations; }
|
|
set {
|
|
if (_territoriesMaxIterations != value) {
|
|
_territoriesMaxIterations = Mathf.Max(0, value);
|
|
needGenerateMap = true;
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
float _territoriesAsymmetry;
|
|
|
|
/// <summary>
|
|
/// Determines the distribution of cells among territories
|
|
/// </summary>
|
|
public float territoriesAsymmetry {
|
|
get { return _territoriesAsymmetry; }
|
|
set {
|
|
if (_territoriesAsymmetry != value) {
|
|
_territoriesAsymmetry = Mathf.Clamp01(value);
|
|
needGenerateMap = true;
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[SerializeField]
|
|
float _territoriesOrganic;
|
|
|
|
/// <summary>
|
|
/// Makes territory contour more organic
|
|
/// </summary>
|
|
public float territoriesOrganic {
|
|
get { return _territoriesOrganic; }
|
|
set {
|
|
if (_territoriesOrganic != value) {
|
|
_territoriesOrganic = Mathf.Clamp01(value);
|
|
needGenerateMap = true;
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
bool
|
|
_showTerritories;
|
|
|
|
/// <summary>
|
|
/// Toggle frontiers visibility.
|
|
/// </summary>
|
|
public bool showTerritories {
|
|
get {
|
|
return _showTerritories;
|
|
}
|
|
set {
|
|
if (value != _showTerritories) {
|
|
_showTerritories = value;
|
|
isDirty = true;
|
|
if (territoryLayer != null) {
|
|
territoryLayer.gameObject.SetActive(_showTerritories);
|
|
ClearLastOver();
|
|
} else {
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
bool
|
|
_showTerritoriesInteriorBorders;
|
|
|
|
/// <summary>
|
|
/// Toggle interior frontiers visibility.
|
|
/// </summary>
|
|
public bool showTerritoriesInteriorBorders {
|
|
get {
|
|
return _showTerritoriesInteriorBorders;
|
|
}
|
|
set {
|
|
if (value != _showTerritoriesInteriorBorders) {
|
|
_showTerritoriesInteriorBorders = value;
|
|
isDirty = true;
|
|
if (territoryInteriorBorderLayer != null) {
|
|
territoryInteriorBorderLayer.gameObject.SetActive(_showTerritoriesInteriorBorders);
|
|
} else {
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
bool
|
|
_territoryInteriorBorderOnTop;
|
|
|
|
/// <summary>
|
|
/// Show interior borders on top of regular territory borders.
|
|
/// </summary>
|
|
public bool territoryInteriorBorderOnTop {
|
|
get {
|
|
return _territoryInteriorBorderOnTop;
|
|
}
|
|
set {
|
|
if (value != _territoryInteriorBorderOnTop) {
|
|
_territoryInteriorBorderOnTop = value;
|
|
isDirty = true;
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[SerializeField]
|
|
bool
|
|
_territoryInteriorBorderIncludeEnclaves = true;
|
|
|
|
/// <summary>
|
|
/// Show interior borders on top of regular territory borders.
|
|
/// </summary>
|
|
public bool territoryInteriorBorderIncludeEnclaves {
|
|
get {
|
|
return _territoryInteriorBorderIncludeEnclaves;
|
|
}
|
|
set {
|
|
if (value != _territoryInteriorBorderIncludeEnclaves) {
|
|
_territoryInteriorBorderIncludeEnclaves = value;
|
|
isDirty = true;
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
bool _colorizeTerritories;
|
|
|
|
/// <summary>
|
|
/// Toggle colorize countries.
|
|
/// </summary>
|
|
public bool colorizeTerritories {
|
|
get {
|
|
return _colorizeTerritories;
|
|
}
|
|
set {
|
|
if (value != _colorizeTerritories) {
|
|
_colorizeTerritories = value;
|
|
isDirty = true;
|
|
if (!_colorizeTerritories && surfacesLayer != null) {
|
|
DestroyTerritorySurfaces();
|
|
} else {
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
float _colorizedTerritoriesAlpha = 0.7f;
|
|
|
|
public float colorizedTerritoriesAlpha {
|
|
get { return _colorizedTerritoriesAlpha; }
|
|
set {
|
|
if (_colorizedTerritoriesAlpha != value) {
|
|
_colorizedTerritoriesAlpha = value;
|
|
isDirty = true;
|
|
UpdateColorizedTerritoriesAlpha();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
[ColorUsage(true, true)]
|
|
Color
|
|
_territoryHighlightColor = new Color(1, 0, 0, 0.8f);
|
|
|
|
/// <summary>
|
|
/// Fill color to use when the mouse hovers a territory's region.
|
|
/// </summary>
|
|
public Color territoryHighlightColor {
|
|
get {
|
|
return _territoryHighlightColor;
|
|
}
|
|
set {
|
|
if (value != _territoryHighlightColor) {
|
|
_territoryHighlightColor = value;
|
|
isDirty = true;
|
|
if (hudMatTerritoryOverlay != null && _territoryHighlightColor != hudMatTerritoryOverlay.color) {
|
|
hudMatTerritoryOverlay.color = _territoryHighlightColor;
|
|
}
|
|
if (hudMatTerritoryGround != null && _territoryHighlightColor != hudMatTerritoryGround.color) {
|
|
hudMatTerritoryGround.color = _territoryHighlightColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
[ColorUsage(true, true)]
|
|
Color
|
|
_territoryHighlightColor2 = new Color(0, 1, 0, 0.8f);
|
|
|
|
/// <summary>
|
|
/// Alternate fill color to use when the mouse hovers a territory's region.
|
|
/// </summary>
|
|
public Color territoryHighlightColor2 {
|
|
get {
|
|
return _territoryHighlightColor2;
|
|
}
|
|
set {
|
|
if (value != _territoryHighlightColor2) {
|
|
_territoryHighlightColor2 = value;
|
|
isDirty = true;
|
|
if (hudMatTerritoryOverlay != null) {
|
|
hudMatTerritoryOverlay.SetColor(ShaderParams.Color2, _territoryHighlightColor2);
|
|
}
|
|
if (hudMatTerritoryGround != null) {
|
|
hudMatTerritoryGround.SetColor(ShaderParams.Color2, _territoryHighlightColor2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
[ColorUsage(true, true)]
|
|
Color
|
|
_territoryFrontierColor = new Color(0, 1, 0, 1.0f);
|
|
|
|
/// <summary>
|
|
/// Territories border color
|
|
/// </summary>
|
|
public Color territoryFrontiersColor {
|
|
get {
|
|
if (territoriesMat != null) {
|
|
return territoriesMat.color;
|
|
} else {
|
|
return _territoryFrontierColor;
|
|
}
|
|
}
|
|
set {
|
|
if (value != _territoryFrontierColor) {
|
|
_territoryFrontierColor = value;
|
|
isDirty = true;
|
|
if (territoriesThinMat != null && _territoryFrontierColor != territoriesThinMat.color) {
|
|
territoriesThinMat.color = _territoryFrontierColor;
|
|
}
|
|
if (territoriesGeoMat != null && _territoryFrontierColor != territoriesGeoMat.color) {
|
|
territoriesGeoMat.color = _territoryFrontierColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public float territoryFrontiersAlpha {
|
|
get {
|
|
return _territoryFrontierColor.a;
|
|
}
|
|
set {
|
|
if (_territoryFrontierColor.a != value) {
|
|
_territoryFrontierColor = new Color(_territoryFrontierColor.r, _territoryFrontierColor.g, _territoryFrontierColor.b, value);
|
|
}
|
|
if (_territoryDisputedFrontierColor.a != value) {
|
|
_territoryDisputedFrontierColor = new Color(_territoryDisputedFrontierColor.r, _territoryDisputedFrontierColor.g, _territoryDisputedFrontierColor.b, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
float
|
|
_territoryFrontiersThickness = 1f;
|
|
|
|
/// <summary>
|
|
/// Territory frontier thickness
|
|
/// </summary>
|
|
public float territoryFrontiersThickness {
|
|
get {
|
|
return _territoryFrontiersThickness;
|
|
}
|
|
set {
|
|
if (value != _territoryFrontiersThickness) {
|
|
_territoryFrontiersThickness = Mathf.Max(1f, value);
|
|
UpdateMaterialThickness();
|
|
if (_showTerritories) {
|
|
DrawTerritoryFrontiers();
|
|
}
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
float
|
|
_territoryInteriorBorderThickness = 3f;
|
|
|
|
/// <summary>
|
|
/// Territory interior border thickness
|
|
/// </summary>
|
|
public float territoryInteriorBorderThickness {
|
|
get {
|
|
return _territoryInteriorBorderThickness;
|
|
}
|
|
set {
|
|
if (value != territoryInteriorBorderThickness) {
|
|
_territoryInteriorBorderThickness = Mathf.Max(1f, value);
|
|
if (_showTerritoriesInteriorBorders) {
|
|
DrawInteriorTerritoryFrontiers();
|
|
}
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
float
|
|
_territoryInteriorBorderPadding = -0.6f;
|
|
|
|
/// <summary>
|
|
/// Territory interior border padding
|
|
/// </summary>
|
|
public float territoryInteriorBorderPadding {
|
|
get {
|
|
return _territoryInteriorBorderPadding;
|
|
}
|
|
set {
|
|
if (value != _territoryInteriorBorderPadding) {
|
|
_territoryInteriorBorderPadding = value;
|
|
if (_showTerritoriesInteriorBorders) {
|
|
DrawInteriorTerritoryFrontiers();
|
|
}
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
[ColorUsage(true, true)]
|
|
Color
|
|
_territoryDisputedFrontierColor = new Color(0, 1, 0, 1.0f);
|
|
|
|
/// <summary>
|
|
/// Territories disputed borders color
|
|
/// </summary>
|
|
public Color territoryDisputedFrontierColor {
|
|
get {
|
|
if (territoriesDisputedMat != null) {
|
|
return territoriesDisputedMat.color;
|
|
} else {
|
|
return _territoryDisputedFrontierColor;
|
|
}
|
|
}
|
|
set {
|
|
if (value != _territoryDisputedFrontierColor) {
|
|
_territoryDisputedFrontierColor = value;
|
|
isDirty = true;
|
|
if (territoriesDisputedThinMat != null && _territoryDisputedFrontierColor != territoriesDisputedThinMat.color) {
|
|
territoriesDisputedThinMat.color = _territoryDisputedFrontierColor;
|
|
}
|
|
if (territoriesDisputedGeoMat != null && _territoryDisputedFrontierColor != territoriesDisputedGeoMat.color) {
|
|
territoriesDisputedGeoMat.color = _territoryDisputedFrontierColor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
bool _showTerritoriesOuterBorder = true;
|
|
|
|
/// <summary>
|
|
/// Shows perimetral/outer border of territories?
|
|
/// </summary>
|
|
/// <value><c>true</c> if show territories outer borders; otherwise, <c>false</c>.</value>
|
|
public bool showTerritoriesOuterBorders {
|
|
get { return _showTerritoriesOuterBorder; }
|
|
set {
|
|
if (_showTerritoriesOuterBorder != value) {
|
|
_showTerritoriesOuterBorder = value;
|
|
isDirty = true;
|
|
Redraw();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
bool _allowTerritoriesInsideTerritories;
|
|
|
|
/// <summary>
|
|
/// Set this property to true to allow territories to be surrounded by other territories.
|
|
/// </summary>
|
|
public bool allowTerritoriesInsideTerritories {
|
|
get { return _allowTerritoriesInsideTerritories; }
|
|
set {
|
|
if (_allowTerritoriesInsideTerritories != value) {
|
|
_allowTerritoriesInsideTerritories = value;
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[SerializeField]
|
|
bool _territoriesIgnoreHiddenCells;
|
|
|
|
/// <summary>
|
|
/// Set this property to true to allow territories to be surrounded by other territories.
|
|
/// </summary>
|
|
public bool territoriesIgnoreHiddenCells {
|
|
get { return _territoriesIgnoreHiddenCells; }
|
|
set {
|
|
if (_territoriesIgnoreHiddenCells != value) {
|
|
_territoriesIgnoreHiddenCells = value;
|
|
isDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns Territory under mouse position or null if none.
|
|
/// </summary>
|
|
public Territory territoryHighlighted { get { return _territoryHighlighted; } }
|
|
|
|
/// <summary>
|
|
/// Returns currently highlighted territory index in the countries list.
|
|
/// </summary>
|
|
public int territoryHighlightedIndex { get { return _territoryHighlightedIndex; } }
|
|
|
|
/// <summary>
|
|
/// Returns currently highlighted territory region index.
|
|
/// </summary>
|
|
public int territoryHighlightedRegionIndex { get { return _territoryHighlightedRegionIndex; } }
|
|
|
|
/// <summary>
|
|
/// Returns Territory index which has been clicked
|
|
/// </summary>
|
|
public int territoryLastClickedIndex { get { return _territoryLastClickedIndex; } }
|
|
|
|
/// <summary>
|
|
/// Returns Territory region index which has been clicked
|
|
/// </summary>
|
|
public int territoryRegionLastClickedIndex { get { return _territoryRegionLastClickedIndex; } }
|
|
|
|
|
|
#region Public Territories Functions
|
|
|
|
/// <summary>
|
|
/// Uncolorize/hide all territories.
|
|
/// </summary>
|
|
public void TerritoryHideRegionSurfaces() {
|
|
int terrCount = territories.Count;
|
|
for (int k = 0; k < terrCount; k++) {
|
|
TerritoryHideRegionSurface(k);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Uncolorize/hide specified territory by index in the territories collection.
|
|
/// </summary>
|
|
public void TerritoryHideRegionSurface(int territoryIndex, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex)) return;
|
|
if ((_territoryHighlightedIndex != territoryIndex && _territoryHighlightedRegionIndex != regionIndex) || _highlightedObj == null) {
|
|
int cacheIndex = GetCacheIndexForTerritoryRegion(territoryIndex, regionIndex);
|
|
GameObject surf;
|
|
if (surfaces.TryGetValue(cacheIndex, out surf)) {
|
|
if (surf == null) {
|
|
surfaces.Remove(cacheIndex);
|
|
} else {
|
|
surf.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
territories[territoryIndex].regions[regionIndex].customMaterial = null;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Assigns a custom material to a territory
|
|
/// </summary>
|
|
public GameObject TerritorySetMaterial(int territoryIndex, Material material, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex)) return null;
|
|
GameObject o = TerritoryToggleRegionSurface(territoryIndex, true, Color.white, regionIndex: regionIndex);
|
|
Region region = territories[territoryIndex].regions[regionIndex];
|
|
region.customMaterial = material;
|
|
region.customRotateInLocalSpace = false;
|
|
region.customTextureOffset = Vector2.zero;
|
|
region.customTextureRotation = 0;
|
|
region.customTextureScale = Vector2.one;
|
|
ApplyMaterialToSurface(region, material);
|
|
return o;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the color of a territory
|
|
/// </summary>
|
|
public GameObject TerritorySetColor(Territory territory, Color color, int regionIndex = 0) {
|
|
int territoryIndex = TerritoryGetIndex(territory);
|
|
return TerritorySetColor(territoryIndex, color, regionIndex);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the color of a territory
|
|
/// </summary>
|
|
public GameObject TerritorySetColor(int territoryIndex, Color color, int regionIndex = 0) {
|
|
return TerritoryToggleRegionSurface(territoryIndex, true, color, regionIndex: regionIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the texture of a territory
|
|
/// </summary>
|
|
public GameObject TerritorySetTexture(Territory territory, Texture2D texture, int regionIndex = 0) {
|
|
int territoryIndex = TerritoryGetIndex(territory);
|
|
return TerritorySetTexture(territoryIndex, texture, regionIndex);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the texture of a territory
|
|
/// </summary>
|
|
public GameObject TerritorySetTexture(int territoryIndex, Texture2D texture, int regionIndex = 0) {
|
|
return TerritoryToggleRegionSurface(territoryIndex, true, Color.white, false, texture, regionIndex);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Colorize specified territory by index.
|
|
/// </summary>
|
|
/// <param name="territory">Territory.</param>
|
|
/// <param name="visible">If the colored surface will be visible or not.</param>
|
|
/// <param name="color">Color.</param>
|
|
public GameObject TerritoryToggleRegionSurface(Territory territory, bool visible, Color color, bool refreshGeometry = false, int regionIndex = 0) {
|
|
int territoryIndex = TerritoryGetIndex(territory);
|
|
return TerritoryToggleRegionSurface(territoryIndex, visible, color, refreshGeometry, null, Misc.Vector2one, Misc.Vector2zero, 0, false, regionIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Colorize specified territory by index.
|
|
/// </summary>
|
|
/// <param name="territoryIndex">Territory index.</param>
|
|
/// <param name="visible">If the colored surface will be visible or not.</param>
|
|
/// <param name="color">Color.</param>
|
|
public GameObject TerritoryToggleRegionSurface(int territoryIndex, bool visible, Color color, bool refreshGeometry = false, int regionIndex = 0) {
|
|
return TerritoryToggleRegionSurface(territoryIndex, visible, color, refreshGeometry, null, Misc.Vector2one, Misc.Vector2zero, 0, false, regionIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Colorize specified territory by index.
|
|
/// </summary>
|
|
/// <param name="territoryIndex">Territory index.</param>
|
|
/// <param name="visible">If the colored surface will be visible or not.</param>
|
|
/// <param name="color">Color.</param>
|
|
/// <param name="refreshGeometry">If set to <c>true</c> any cached surface will be destroyed and regenerated. Usually you pass false to improve performance.</param>
|
|
/// <param name="texture">Texture, which will be tinted according to the color. Use Color.white to preserve original texture colors.</param>
|
|
public GameObject TerritoryToggleRegionSurface(int territoryIndex, bool visible, Color color, bool refreshGeometry, Texture2D texture, int regionIndex = 0) {
|
|
return TerritoryToggleRegionSurface(territoryIndex, visible, color, refreshGeometry, texture, Misc.Vector2one, Misc.Vector2zero, 0, false, regionIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Colorize specified territory by index.
|
|
/// </summary>
|
|
/// <param name="territoryIndex">Territory index.</param>
|
|
/// <param name="visible">If the colored surface will be visible or not.</param>
|
|
/// <param name="color">Color.</param>
|
|
/// <param name="refreshGeometry">If set to <c>true</c> any cached surface will be destroyed and regenerated. Usually you pass false to improve performance.</param>
|
|
/// <param name="texture">Texture, which will be tinted according to the color. Use Color.white to preserve original texture colors.</param>
|
|
/// <param name="textureScale">Texture scale.</param>
|
|
/// <param name="textureOffset">Texture offset.</param>
|
|
/// <param name="textureRotation">Texture rotation.</param>
|
|
public GameObject TerritoryToggleRegionSurface(int territoryIndex, bool visible, Color color, bool refreshGeometry, Texture2D texture, Vector2 textureScale, Vector2 textureOffset, float textureRotation, bool rotateInLocalSpace, int regionIndex = 0) {
|
|
return TerritoryToggleRegionSurface(territoryIndex, visible, color, refreshGeometry, texture, textureScale, textureOffset, textureRotation, false, rotateInLocalSpace, regionIndex);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Colorize specified territory by index.
|
|
/// </summary>
|
|
/// <param name="territory">Territory.</param>
|
|
/// <param name="visible">If the colored surface will be visible or not.</param>
|
|
/// <param name="color">Color.</param>
|
|
/// <param name="refreshGeometry">If set to <c>true</c> any cached surface will be destroyed and regenerated. Usually you pass false to improve performance.</param>
|
|
/// <param name="texture">Texture, which will be tinted according to the color. Use Color.white to preserve original texture colors.</param>
|
|
/// <param name="textureScale">Texture scale.</param>
|
|
/// <param name="textureOffset">Texture offset.</param>
|
|
/// <param name="textureRotation">Texture rotation.</param>
|
|
/// <param name="overlay">If set to <c>true</c> the colored surface will be shown over any object.</param>
|
|
public GameObject TerritoryToggleRegionSurface(Territory territory, bool visible, Color color, bool refreshGeometry, Texture2D texture, Vector2 textureScale, Vector2 textureOffset, float textureRotation, bool overlay, bool rotateInLocalSpace, int regionIndex = 0) {
|
|
int territoryIndex = TerritoryGetIndex(territory);
|
|
return TerritoryToggleRegionSurface(territoryIndex, visible, color, refreshGeometry, texture, textureScale, textureOffset, textureRotation, overlay, rotateInLocalSpace, regionIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Colorize specified territory by index.
|
|
/// </summary>
|
|
/// <param name="territoryIndex">Territory index.</param>
|
|
/// <param name="visible">If the colored surface will be visible or not.</param>
|
|
/// <param name="color">Color.</param>
|
|
/// <param name="refreshGeometry">If set to <c>true</c> any cached surface will be destroyed and regenerated. Usually you pass false to improve performance.</param>
|
|
/// <param name="texture">Texture, which will be tinted according to the color. Use Color.white to preserve original texture colors.</param>
|
|
/// <param name="textureScale">Texture scale.</param>
|
|
/// <param name="textureOffset">Texture offset.</param>
|
|
/// <param name="textureRotation">Texture rotation.</param>
|
|
/// <param name="overlay">If set to <c>true</c> the colored surface will be shown over any object.</param>
|
|
public GameObject TerritoryToggleRegionSurface(int territoryIndex, bool visible, Color color, bool refreshGeometry, Texture2D texture, Vector2 textureScale, Vector2 textureOffset, float textureRotation, bool overlay, bool rotateInLocalSpace, int regionIndex = 0) {
|
|
|
|
if (needGenerateMap || needResortCells || issueRedraw != RedrawType.None) {
|
|
CheckGridChanges();
|
|
}
|
|
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex))
|
|
return null;
|
|
|
|
if (!visible) {
|
|
TerritoryHideRegionSurface(territoryIndex);
|
|
return null;
|
|
}
|
|
|
|
Region region = territories[territoryIndex].regions[regionIndex];
|
|
int cacheIndex = GetCacheIndexForTerritoryRegion(territoryIndex, regionIndex);
|
|
|
|
GameObject surf;
|
|
// 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
|
|
bool existsInCache = surfaces.TryGetValue(cacheIndex, out surf);
|
|
if (existsInCache && surf == null) {
|
|
surfaces.Remove(cacheIndex);
|
|
existsInCache = false;
|
|
}
|
|
if (refreshGeometry && existsInCache) {
|
|
surfaces.Remove(cacheIndex);
|
|
DestroyImmediate(surf);
|
|
existsInCache = false;
|
|
surf = null;
|
|
}
|
|
|
|
Material coloredMat = overlay ? coloredMatOverlayTerritory : coloredMatGroundTerritory;
|
|
Material texturizedMat = overlay ? texturizedMatOverlayTerritory : texturizedMatGroundTerritory;
|
|
|
|
// Should the surface be recreated?
|
|
Material surfMaterial;
|
|
if (surf != null) {
|
|
surfMaterial = surf.GetComponent<Renderer>().sharedMaterial;
|
|
if (texture != null && (region.customMaterial == null || textureScale != region.customTextureScale || textureOffset != region.customTextureOffset ||
|
|
textureRotation != region.customTextureRotation || !region.customMaterial.name.Equals(texturizedMat.name))) {
|
|
surfaces.Remove(cacheIndex);
|
|
DestroyImmediate(surf);
|
|
surf = null;
|
|
}
|
|
}
|
|
// If it exists, activate and check proper material, if not create surface
|
|
bool isHighlighted = territoryHighlightedIndex == territoryIndex && _highlightEffect != HighlightEffect.None;
|
|
if (surf != null) {
|
|
if (!surf.activeSelf) {
|
|
surf.SetActive(true);
|
|
}
|
|
// Check if material is ok
|
|
Renderer renderer = surf.GetComponent<Renderer>();
|
|
surfMaterial = renderer.sharedMaterial;
|
|
if ((texture == null && !surfMaterial.name.Equals(coloredMat.name)) || (texture != null && !surfMaterial.name.Equals(texturizedMat.name))
|
|
|| (surfMaterial.color != color && !isHighlighted) || (texture != null && (region.customMaterial == null || region.customMaterial.mainTexture != texture))) {
|
|
Material goodMaterial = GetColoredTexturedMaterialForTerritory(region, color, texture, overlay);
|
|
region.customMaterial = goodMaterial;
|
|
ApplyMaterialToSurface(region, goodMaterial);
|
|
}
|
|
} else {
|
|
surfMaterial = GetColoredTexturedMaterialForTerritory(region, color, texture, overlay);
|
|
surf = GenerateTerritoryRegionSurface(territoryIndex, surfMaterial, textureScale, textureOffset, textureRotation, rotateInLocalSpace, regionIndex);
|
|
region.customMaterial = surfMaterial;
|
|
region.customTextureOffset = textureOffset;
|
|
region.customTextureRotation = textureRotation;
|
|
region.customTextureScale = textureScale;
|
|
region.customRotateInLocalSpace = rotateInLocalSpace;
|
|
}
|
|
// If it was highlighted, highlight it again
|
|
if (isHighlighted && region.customMaterial != null && _highlightedObj != null) {
|
|
if (hudMatTerritory.HasProperty(ShaderParams.MainTex)) {
|
|
if (region.customMaterial != null) {
|
|
hudMatTerritory.mainTexture = region.customMaterial.mainTexture;
|
|
} else {
|
|
hudMatTerritory.mainTexture = null;
|
|
}
|
|
}
|
|
surf.GetComponent<Renderer>().sharedMaterial = hudMatTerritory;
|
|
_highlightedObj = surf;
|
|
}
|
|
return surf;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies if a given territory border is visible.
|
|
/// </summary>
|
|
public void TerritorySetBorderVisible(int territoryIndex, bool visible) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count)
|
|
return;
|
|
territories[territoryIndex].borderVisible = visible;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns a list of neighbour territories for specificed cell index.
|
|
/// </summary>
|
|
public List<Territory> TerritoryGetNeighbours(int territoryIndex) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return null;
|
|
return territories[territoryIndex].neighbours;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of neighbour territories for specificed territory index in the territories parameter.
|
|
/// </summary>
|
|
public int TerritoryGetNeighbours(int territoryIndex, List<Territory> territories) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return 0;
|
|
territories.Clear();
|
|
territories.AddRange(territories[territoryIndex].neighbours);
|
|
return territories.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of cells that form the frontiers of a given territory
|
|
/// </summary>
|
|
/// <returns>The number of cells found.</returns>
|
|
/// <param name="territoryIndex">Territory index.</param>
|
|
/// <param name="cellIndices">Cells that form the frontier. You need to pass an already initialized list, which will be cleared and filled with the cells.</param>
|
|
/// <param name="regionIndex">If the territory has several regions, the index of the region</param>
|
|
/// <param name="includeGridEdges">If a cell is located in the edge of the grid, include it in the results</param>
|
|
public int TerritoryGetFrontierCells(int territoryIndex, List<int> cellIndices, int regionIndex = -1, bool includeGridEdges = false) {
|
|
return TerritoryGetFrontierCells(territoryIndex, -1, cellIndices, regionIndex, includeGridEdges);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns a copy of all cells belonging to a territory. Use Territory.cells to access the list without making a copy
|
|
/// </summary>
|
|
public List<Cell> TerritoryGetCells(int territoryIndex) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return null;
|
|
List<Cell> cells = new List<Cell>();
|
|
Territory terr = territories[territoryIndex];
|
|
if (terr.cells != null) {
|
|
cells.AddRange(terr.cells);
|
|
}
|
|
return cells;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all cells belonging to a territory into an user given list. See alto territory.cells list.
|
|
/// </summary>
|
|
public void TerritoryGetCells(int territoryIndex, List<Cell> cells) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return;
|
|
cells.Clear();
|
|
Territory terr = territories[territoryIndex];
|
|
if (terr.cells != null) {
|
|
cells.AddRange(terr.cells);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all cells belonging to a territory region
|
|
/// </summary>
|
|
public List<Cell> TerritoryGetCells(int territoryIndex, int regionIndex) {
|
|
List<Cell> cells = new List<Cell>();
|
|
TerritoryGetCells(territoryIndex, regionIndex, cells);
|
|
return cells;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all cells belonging to a territory region into an user given cell list
|
|
/// </summary>
|
|
public void TerritoryGetCells(int territoryIndex, int regionIndex, List<Cell> cells) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex)) return;
|
|
Territory territory = territories[territoryIndex];
|
|
Region region = territory.regions[regionIndex];
|
|
if (territory.isDirty || region.cells == null || region.cells.Count == 0) {
|
|
ComputeTerritoryRegionCells(region);
|
|
}
|
|
cells.Clear();
|
|
cells.AddRange(region.cells);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds and updates the region.cells list based on the location of the cells inside territories
|
|
/// </summary>
|
|
public void ComputeTerritoryRegionCells(Region region) {
|
|
Territory territory = (Territory)region.entity;
|
|
// Optimization: if territory only contains one region, make region.cells point to territory.cells
|
|
if (territory.regions.Count == 1) {
|
|
region.cells = territory.cells;
|
|
return;
|
|
}
|
|
if (region.cells == null || region.cells == territory.cells) {
|
|
region.cells = new List<Cell>();
|
|
} else {
|
|
region.cells.Clear();
|
|
}
|
|
int cellsCount = territory.cells.Count;
|
|
for (int k = 0; k < cellsCount; k++) {
|
|
Cell cell = territory.cells[k];
|
|
Vector2 scaledCenter = cell.scaledCenter;
|
|
if (region.Contains(scaledCenter.x, scaledCenter.y)) {
|
|
region.cells.Add(cell);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a list of cells that forms the frontiers between a given territory and another one.
|
|
/// </summary>
|
|
/// <returns>The get frontier cells.</returns>
|
|
/// <param name="territoryIndex">Territory index.</param>
|
|
/// <param name="otherTerritoryIndex">Other territory index.</param>
|
|
/// <param name="cellIndices">Cells that form the frontier. You need to pass an already initialized list, which will be cleared and filled with the cells.</param>
|
|
/// <param name="regionIndex">Limit search to a given region. -1 means include all regions.</param>
|
|
/// <param name="includeGridEdges">If a cell is located in the edge of the grid, include it in the results</param>
|
|
public int TerritoryGetFrontierCells(int territoryIndex, int otherTerritoryIndex, List<int> cellIndices, int regionIndex = -1, bool includeGridEdges = false) {
|
|
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count || territories[territoryIndex].cells == null || cells == null)
|
|
return 0;
|
|
|
|
List<Cell> regionCells = null;
|
|
if (regionIndex >= 0) regionCells = TerritoryGetCells(territoryIndex, regionIndex);
|
|
|
|
cellIndices.Clear();
|
|
foreach (KeyValuePair<Segment, Frontier> kv in territoryNeighbourHit) {
|
|
Segment segment = kv.Key;
|
|
Frontier frontier = kv.Value;
|
|
if (frontier.region1 == null || frontier.region2 == null)
|
|
continue;
|
|
Cell cell1 = (Cell)frontier.region1.entity;
|
|
Cell cell2 = (Cell)frontier.region2.entity;
|
|
if (cell1.territoryIndex == territoryIndex && (otherTerritoryIndex < 0 || cell2.territoryIndex == otherTerritoryIndex)) {
|
|
if (!cellIndices.Contains(cell1.index) && (regionIndex < 0 || regionCells.Contains(cell1))) {
|
|
cellIndices.Add(cell1.index);
|
|
}
|
|
} else if (cell2.territoryIndex == territoryIndex && (otherTerritoryIndex < 0 || cell1.territoryIndex == otherTerritoryIndex)) {
|
|
if (!cellIndices.Contains(cell2.index) && (regionIndex < 0 || regionCells.Contains(cell2))) {
|
|
cellIndices.Add(cell2.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (includeGridEdges) {
|
|
Territory territory = territories[territoryIndex];
|
|
foreach (Cell cell in territory.cells) {
|
|
if (cellIndices.Contains(cell.index)) continue;
|
|
if (CellIsBorder(cell.index)) {
|
|
cellIndices.Add(cell.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return cellIndices.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Similar to TerritoryGetFrontierCells but returns the cells of the adjacent territory
|
|
/// </summary>
|
|
public int TerritoryGetAdjacentCells(int territoryIndex, List<int> cellIndices, int regionIndex = -1, int otherTerritoryIndex = -1) {
|
|
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count || territories[territoryIndex].cells == null || cells == null)
|
|
return 0;
|
|
|
|
List<Cell> regionCells = null;
|
|
if (regionIndex >= 0) regionCells = TerritoryGetCells(territoryIndex, regionIndex);
|
|
|
|
cellIndices.Clear();
|
|
foreach (KeyValuePair<Segment, Frontier> kv in territoryNeighbourHit) {
|
|
Segment segment = kv.Key;
|
|
Frontier frontier = kv.Value;
|
|
if (frontier.region1 == null || frontier.region2 == null)
|
|
continue;
|
|
Cell cell1 = (Cell)frontier.region1.entity;
|
|
Cell cell2 = (Cell)frontier.region2.entity;
|
|
if (cell1.territoryIndex == territoryIndex && (otherTerritoryIndex < 0 || cell2.territoryIndex == otherTerritoryIndex)) {
|
|
if (!cellIndices.Contains(cell2.index) && (regionIndex < 0 || regionCells.Contains(cell1))) {
|
|
cellIndices.Add(cell2.index);
|
|
}
|
|
} else if (cell2.territoryIndex == territoryIndex && (otherTerritoryIndex < 0 || cell1.territoryIndex == otherTerritoryIndex)) {
|
|
if (!cellIndices.Contains(cell1.index) && (regionIndex < 0 || regionCells.Contains(cell2))) {
|
|
cellIndices.Add(cell1.index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return cellIndices.Count;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Colors a territory and fades it out during "duration" in seconds.
|
|
/// </summary>
|
|
public void TerritoryFadeOut(int territoryIndex, Color color, float duration, int repetitions = 1) {
|
|
TerritoryAnimate(FaderStyle.FadeOut, territoryIndex, color, duration, repetitions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Flashes a territory with "color" and "duration" in seconds.
|
|
/// </summary>
|
|
public void TerritoryFlash(int territoryIndex, Color color, float duration, int repetitions = 1) {
|
|
TerritoryAnimate(FaderStyle.Flash, territoryIndex, color, duration, repetitions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Blinks a territory with "color" and "duration" in seconds.
|
|
/// </summary>
|
|
public void TerritoryBlink(int territoryIndex, Color color, float duration, int repetitions = 1) {
|
|
TerritoryAnimate(FaderStyle.Blink, territoryIndex, color, duration, repetitions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Temporarily colors a territory for "duration" in seconds.
|
|
/// </summary>
|
|
public void TerritoryColorTemp(int territoryIndex, Color color, float duration) {
|
|
TerritoryAnimate(FaderStyle.ColorTemp, territoryIndex, color, duration, 1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancels any ongoing visual effect on a territory
|
|
/// </summary>
|
|
/// <param name="cellIndex">Cell index.</param>
|
|
public void TerritoryCancelAnimations(int territoryIndex, float fadeOutDuration = 0) {
|
|
TerritoryCancelAnimation(territoryIndex, fadeOutDuration);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Specifies if a given territory is visible.
|
|
/// </summary>
|
|
public void TerritorySetVisible(int territoryIndex, bool visible) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return;
|
|
territories[territoryIndex].visible = visible;
|
|
if (territoryIndex == _territoryLastOverIndex) {
|
|
ClearLastOver();
|
|
}
|
|
needUpdateTerritories = true;
|
|
issueRedraw = RedrawType.Full;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if territory is visible
|
|
/// </summary>
|
|
public bool TerritoryIsVisible(int territoryIndex) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count)
|
|
return false;
|
|
return territories[territoryIndex].visible;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies if a given territory is neutral.
|
|
/// </summary>
|
|
public void TerritorySetNeutral(int territoryIndex, bool neutral) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count)
|
|
return;
|
|
territories[territoryIndex].neutral = neutral;
|
|
needUpdateTerritories = true;
|
|
issueRedraw = RedrawType.Full;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if territory is neutral
|
|
/// </summary>
|
|
public bool TerritoryIsNeutral(int territoryIndex) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count)
|
|
return false;
|
|
return territories[territoryIndex].neutral;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Specifies the color of the territory borders.
|
|
/// </summary>
|
|
public void TerritorySetFrontierColor(int territoryIndex, Color color) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count)
|
|
return;
|
|
Territory terr = territories[territoryIndex];
|
|
if (terr.frontierColor != color) {
|
|
terr.frontierColor = color;
|
|
DrawTerritoryFrontiers();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a gameobject with the frontier for the given territory. Optionally, the frontier could be limited to those segments adjacent to another territory.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public GameObject TerritoryDrawFrontier(Territory territory, int adjacentTerritoryIndex = -1, Material material = null, Color color = default(Color), float thickness = 0) {
|
|
int territoryIndex = TerritoryGetIndex(territory);
|
|
return TerritoryDrawFrontier(territoryIndex, adjacentTerritoryIndex, material, color, thickness);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a gameobject with the frontier for the given territory. Optionally, the frontier could be limited to those segments adjacent to another territory.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public GameObject TerritoryDrawFrontier(int territoryIndex, int adjacentTerritoryIndex = -1, Material material = null, Color color = default(Color), float thickness = 0) {
|
|
|
|
if (needGenerateMap || needResortCells || issueRedraw != RedrawType.None) {
|
|
CheckGridChanges();
|
|
}
|
|
|
|
TerritoryMesh tm = new TerritoryMesh { territoryIndex = territoryIndex };
|
|
|
|
if (!GenerateTerritoryMesh(tm, true, adjacentTerritoryIndex)) return null;
|
|
|
|
if (material == null) {
|
|
if (tm.territoryIndex < 0) {
|
|
material = territoriesDisputedMat;
|
|
} else {
|
|
if (territoryIndex >= territories.Count) return null;
|
|
Color frontierColor = territories[tm.territoryIndex].frontierColor;
|
|
if (frontierColor.a == 0 && frontierColor.r == 0 && frontierColor.g == 0 && frontierColor.b == 0) {
|
|
material = territoriesMat;
|
|
} else {
|
|
material = GetFrontierColorMaterial(frontierColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
material = Instantiate(material);
|
|
material.renderQueue--; // ensure it writes to stencil before normal territory material
|
|
if (color != default(Color)) {
|
|
material.color = color;
|
|
}
|
|
if (thickness > 0) {
|
|
UpdateMaterialTerritoryThickness(material, thickness);
|
|
} else {
|
|
thickness = _territoryFrontiersThickness;
|
|
}
|
|
bool useVertexDisplacement = thickness > 1f & !canUseGeometryShaders;
|
|
|
|
Transform root = CheckTerritoriesCustomFrontiersRoot();
|
|
|
|
GameObject go = DrawTerritoryFrontier(tm, material, root, TERRITORY_FRONTIER_NAME, useVertexDisplacement);
|
|
Territory territory = territories[territoryIndex];
|
|
if (territory.customFrontiersGameObject != null) DestroyImmediate(territory.customFrontiersGameObject);
|
|
territory.customFrontiersGameObject = go;
|
|
return go;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hides all territories interior boders
|
|
/// </summary>
|
|
public void TerritoryHideInteriorBorders() {
|
|
if (territoryInteriorBorderLayer != null) {
|
|
DestroyImmediate(territoryInteriorBorderLayer.gameObject);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hides territory border of a territory
|
|
/// </summary>
|
|
/// <param name="regionIndex">-1 will destroy all interior borders for the territory</param>
|
|
public void TerritoryHideInteriorBorder(int territoryIndex, int regionIndex = -1) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return;
|
|
Territory territory = territories[territoryIndex];
|
|
if (regionIndex >= 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex)) return;
|
|
foreach (Region region in territory.regions) {
|
|
if (region.customBorderGameObject != null) {
|
|
DestroyImmediate(region.customBorderGameObject);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
{
|
|
Region region = territory.regions[regionIndex];
|
|
if (region.customBorderGameObject != null) {
|
|
DestroyImmediate(region.customBorderGameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a gameobject with the interior border for the given territory with optional padding and thickness.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public GameObject TerritoryDrawInteriorBorder(int territoryIndex, float padding, float thickness, Color color = default(Color), Color secondColor = default(Color), int regionIndex = 0, float animationSpeed = 0, bool includeEnclaves = false) {
|
|
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex)) return null;
|
|
Territory territory = territories[territoryIndex];
|
|
return TerritoryDrawInteriorBorder(territory, padding, thickness, color, secondColor, regionIndex, animationSpeed, includeEnclaves);
|
|
}
|
|
|
|
public GameObject ForceDrawTerritoryDrawInteriorBorder(Territory territory, float padding, float thickness, Color color = default(Color), Color secondColor = default(Color), int regionIndex = 0, float animationSpeed = 0, bool includeEnclaves = false)
|
|
{
|
|
return TerritoryDrawInteriorBorder(territory, padding, thickness, color, secondColor, regionIndex, animationSpeed, includeEnclaves);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a gameobject with the interior border for the given territory with optional padding and thickness.
|
|
/// </summary>
|
|
/// <param name="includeEnclaves">Draw additional interior borders for other regions contained inside this territory region</param>
|
|
/// <returns></returns>
|
|
public GameObject TerritoryDrawInteriorBorder(Territory territory, float padding = -0.7f, float thickness = 3f, Color color = default(Color), Color secondColor = default(Color), int regionIndex = 0, float animationSpeed = 0, bool includeEnclaves = false) {
|
|
|
|
GetInteriorBorderColors(territory, ref color, ref secondColor);
|
|
GameObject border = TerritoryDrawInteriorBorderSingle(territory, padding, thickness, color, secondColor, regionIndex, animationSpeed);
|
|
if (border == null) return null;
|
|
|
|
// Check other regions inside this region
|
|
if (includeEnclaves) {
|
|
Region thisRegion = territory.regions[regionIndex];
|
|
int territoryCount = territories.Count;
|
|
for (int k = 0; k < territoryCount; k++) {
|
|
Territory otherTerritory = territories[k];
|
|
if (otherTerritory == territory) continue;
|
|
int otherRegionCount = otherTerritory.regions.Count;
|
|
for (int r = 0; r < otherRegionCount; r++) {
|
|
Region otherRegion = otherTerritory.regions[r];
|
|
if (thisRegion.ContainsRegion(otherRegion)) {
|
|
GameObject enclaveBorder = TerritoryDrawInteriorBorderSingle(otherTerritory, -padding, thickness, secondColor, color, r, animationSpeed);
|
|
enclaveBorder.transform.SetParent(border.transform);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return border;
|
|
}
|
|
|
|
|
|
void GetInteriorBorderColors(Territory territory, ref Color color, ref Color secondColor) {
|
|
if (color == default) {
|
|
color = territory.fillColor;
|
|
}
|
|
if (secondColor == default) {
|
|
secondColor = territory.fillColor;
|
|
secondColor.r *= 0.5f;
|
|
secondColor.g *= 0.5f;
|
|
secondColor.b *= 0.5f;
|
|
secondColor.a = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a gameobject with the interior border for the given territory with optional padding and thickness.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
GameObject TerritoryDrawInteriorBorderSingle(Territory territory, float padding = -0.7f, float thickness = 3f, Color color = default(Color), Color secondColor = default(Color), int regionIndex = 0, float animationSpeed = 0) {
|
|
|
|
if (territory == null) return null;
|
|
|
|
if (needGenerateMap || needResortCells || issueRedraw != RedrawType.None) {
|
|
CheckGridChanges();
|
|
}
|
|
|
|
if (regionIndex < 0 || territory.regions == null || regionIndex >= territory.regions.Count) return null;
|
|
|
|
TerritoryMesh tm = new TerritoryMesh();
|
|
if (!GenerateRegionMesh(tm, territory.regions[regionIndex], thickness)) return null;
|
|
|
|
Material material = Instantiate(territoriesGradientMat);
|
|
GetInteriorBorderColors(territory, ref color, ref secondColor);
|
|
material.color = color;
|
|
material.SetColor(ShaderParams.TerritorySecondColor, secondColor);
|
|
material.renderQueue += _territoryInteriorBorderOnTop ? 3 : 1; // ensure it draws behind regular territory frontiers
|
|
material.SetFloat(ShaderParams.TerritoryAnimationSpeed, animationSpeed);
|
|
material.EnableKeyword(ShaderParams.SKW_GRADIENT);
|
|
material.DisableKeyword(ShaderParams.SKW_NEAR_CLIP_FADE);
|
|
SetBlend(territoriesThickHackMat);
|
|
UpdateMaterialTerritoryThickness(material, thickness);
|
|
|
|
territoryInteriorBorderLayer = transform.Find(TERRITORIES_CUSTOM_FRONTIERS_ROOT);
|
|
if (territoryInteriorBorderLayer == null) {
|
|
GameObject rootGO = new GameObject(TERRITORIES_CUSTOM_FRONTIERS_ROOT);
|
|
disposalManager.MarkForDisposal(rootGO);
|
|
rootGO.layer = gameObject.layer;
|
|
territoryInteriorBorderLayer = rootGO.transform;
|
|
territoryInteriorBorderLayer.SetParent(transform, false);
|
|
territoryInteriorBorderLayer.localRotation = Quaternion.Euler(Misc.Vector3zero);
|
|
}
|
|
territoryInteriorBorderLayer.gameObject.SetActive(true);
|
|
territoryInteriorBorderLayer.localPosition = new Vector3(0, 0, -0.001f * _gridMinElevationMultiplier);
|
|
|
|
GameObject go = DrawTerritoryFrontier(tm, material, territoryInteriorBorderLayer, TERRITORY_INTERIOR_BORDER_NAME, useVertexDisplacementForTerritoryThickness: true, usesGradient: true, thickness, padding);
|
|
Region region = territory.regions[regionIndex];
|
|
if (region.customBorderGameObject != null) DestroyImmediate(region.customBorderGameObject);
|
|
region.customBorderGameObject = go;
|
|
return go;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the territory object under position in local coordinates
|
|
/// </summary>
|
|
public Territory TerritoryGetAtPosition(Vector2 localPosition) {
|
|
return GetTerritoryAtPoint(localPosition, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the territory object under position in local or worldSpace coordinates
|
|
/// </summary>
|
|
public Territory TerritoryGetAtPosition(Vector3 position, bool worldSpace) {
|
|
return GetTerritoryAtPoint(position, worldSpace);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the territory's center position in world space.
|
|
/// </summary>
|
|
/// <param name="centroidType">The accuracy of the algorithm. Defaults to betterCentroId which is slower but more accurate.</param>
|
|
public Vector3 TerritoryGetPosition(int territoryIndex, CentroidType centroidType = CentroidType.BetterCentroid, bool worldSpace = true, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return Misc.Vector3zero;
|
|
Vector3 territoryCenter;
|
|
if (centroidType == CentroidType.BetterCentroid) {
|
|
territoryCenter = territories[territoryIndex].GetBetterCentroid(regionIndex);
|
|
} else if (centroidType == CentroidType.Centroid) {
|
|
territoryCenter = territories[territoryIndex].GetCentroid(regionIndex);
|
|
} else {
|
|
territoryCenter = territories[territoryIndex].scaledCenter;
|
|
}
|
|
if (worldSpace) {
|
|
territoryCenter = GetWorldSpacePosition(territoryCenter);
|
|
}
|
|
return territoryCenter;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the territory's center position in world space.
|
|
/// </summary>
|
|
/// <param name="centroidType">The accuracy of the algorithm. Defaults to betterCentroId which is slower but more accurate.</param>
|
|
public Vector3 TerritoryGetPosition(Territory territory, CentroidType centroidType = CentroidType.BetterCentroid, bool worldSpace = true, int regionIndex = 0) {
|
|
int territoryIndex = TerritoryGetIndex(territory);
|
|
return TerritoryGetPosition(territoryIndex, centroidType, worldSpace, regionIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the rect enclosing the territory in world space
|
|
/// </summary>
|
|
public Bounds TerritoryGetRectWorldSpace(int territoryIndex, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex))
|
|
return new Bounds(Misc.Vector3zero, Misc.Vector3zero);
|
|
Rect rect = territories[territoryIndex].regions[regionIndex].rect2D;
|
|
Vector3 min = GetWorldSpacePosition(rect.min);
|
|
Vector3 max = GetWorldSpacePosition(rect.max);
|
|
Bounds bounds = new Bounds((min + max) * 0.5f, max - min);
|
|
return bounds;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the number of vertices of the territory
|
|
/// </summary>
|
|
public int TerritoryGetVertexCount(int territoryIndex, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex))
|
|
return 0;
|
|
return territories[territoryIndex].regions[regionIndex].points.Count;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the world space position of the vertex of a territory
|
|
/// </summary>
|
|
public Vector3 TerritoryGetVertexPosition(int territoryIndex, int vertexIndex, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex))
|
|
return Misc.Vector3zero;
|
|
Vector2 localPosition = territories[territoryIndex].regions[regionIndex].points[vertexIndex];
|
|
return GetWorldSpacePosition(localPosition);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the shape/surface gameobject of the territory.
|
|
/// </summary>
|
|
/// <returns>The get game object.</returns>
|
|
/// <param name="cellIndex">Cell index.</param>
|
|
public GameObject TerritoryGetGameObject(int territoryIndex, int regionIndex) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex))
|
|
return null;
|
|
Territory territory = territories[territoryIndex];
|
|
if (territory == null) return null;
|
|
if (territory.regions[regionIndex].surfaceGameObject != null) {
|
|
return territory.regions[regionIndex].surfaceGameObject;
|
|
}
|
|
GameObject go = TerritoryToggleRegionSurface(territoryIndex, true, Misc.ColorNull);
|
|
TerritoryToggleRegionSurface(territoryIndex, false, Misc.ColorNull);
|
|
return go;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Automatically generates territories based on the different colors included in the texture.
|
|
/// </summary>
|
|
/// <param name="neutral">This color won't generate any texture.</param>
|
|
public void CreateTerritories(Texture2D texture, Color neutral, bool hideNeutralCells = false) {
|
|
|
|
if (texture == null || cells == null)
|
|
return;
|
|
|
|
List<Color> dsColors = new List<Color>();
|
|
Dictionary<Color, int> dsColorDict = new Dictionary<Color, int>();
|
|
|
|
int cellCount = cells.Count;
|
|
Color[] colors;
|
|
try {
|
|
colors = texture.GetPixels();
|
|
} catch {
|
|
Debug.Log("Texture used to create territories is not readable. Check import settings.");
|
|
return;
|
|
}
|
|
for (int k = 0; k < cellCount; k++) {
|
|
Cell cell = cells[k];
|
|
if (cell == null) continue;
|
|
cell.territoryIndex = -1;
|
|
if (!cell.visible)
|
|
continue;
|
|
Vector2 uv = cell.center;
|
|
uv.x += 0.5f;
|
|
uv.y += 0.5f;
|
|
|
|
int x = (int)(uv.x * texture.width);
|
|
int y = (int)(uv.y * texture.height);
|
|
int pos = y * texture.width + x;
|
|
if (pos < 0 || pos >= colors.Length)
|
|
continue;
|
|
Color pixelColor = colors[pos];
|
|
int territoryIndex;
|
|
if (!dsColorDict.TryGetValue(pixelColor, out territoryIndex)) {
|
|
dsColors.Add(pixelColor);
|
|
territoryIndex = dsColors.Count - 1;
|
|
dsColorDict[pixelColor] = territoryIndex;
|
|
}
|
|
cell.territoryIndex = (short)territoryIndex;
|
|
if (territoryIndex >= MAX_TERRITORIES - 1)
|
|
break;
|
|
}
|
|
needUpdateTerritories = true;
|
|
|
|
if (dsColors.Count > 0) {
|
|
_numTerritories = dsColors.Count;
|
|
_showTerritories = true;
|
|
|
|
territories.Clear();
|
|
for (int c = 0; c < _numTerritories; c++) {
|
|
Territory territory = new Territory(c.ToString());
|
|
Color territoryColor = dsColors[c];
|
|
if (territoryColor.r != neutral.r || territoryColor.g != neutral.g || territoryColor.b != neutral.b) {
|
|
territory.fillColor = territoryColor;
|
|
} else {
|
|
territory.fillColor = new Color(0, 0, 0, 0);
|
|
territory.visible = false;
|
|
}
|
|
// Add cells to territories
|
|
for (int k = 0; k < cellCount; k++) {
|
|
Cell cell = cells[k];
|
|
if (cell.territoryIndex == c) {
|
|
territory.cells.Add(cell);
|
|
territory.center += cell.center;
|
|
}
|
|
}
|
|
if (territory.cells.Count > 0) {
|
|
territory.center /= territory.cells.Count;
|
|
// Ensure center belongs to territory
|
|
Cell cellAtCenter = CellGetAtPosition(territory.center);
|
|
if (cellAtCenter != null && cellAtCenter.territoryIndex != c) {
|
|
territory.center = territory.cells[0].center;
|
|
}
|
|
}
|
|
territories.Add(territory);
|
|
}
|
|
|
|
isDirty = true;
|
|
issueRedraw = RedrawType.Full;
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Scales the gameobject of a colored/textured surface
|
|
/// </summary>
|
|
public void TerritoryScaleSurface(int territoryIndex, float scale, int regionIndex = 0) {
|
|
if (!ValidTerritoryIndex(territoryIndex, regionIndex)) return;
|
|
Territory territory = territories[territoryIndex];
|
|
GameObject surf = territory.regions[regionIndex].surfaceGameObject;
|
|
ScaleSurface(surf, territory.center, scale);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Exports all territories as independent meshes
|
|
/// </summary>
|
|
public void ExportTerritoriesMesh() {
|
|
int territoryCount = territories.Count;
|
|
for (int t = 0; t < territoryCount; t++) {
|
|
Territory terr = territories[t];
|
|
if (terr.regions == null) continue;
|
|
int regionCount = terr.regions.Count;
|
|
for (int tr = 0; tr < regionCount; tr++) {
|
|
ExportTerritoryMesh(t, tr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exports the specified territory mesh
|
|
/// </summary>
|
|
/// <param name="territoryIndex"></param>
|
|
/// <param name="regionIndex"></param>
|
|
public void ExportTerritoryMesh(int territoryIndex, int regionIndex = 0) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count || regionIndex < 0 || regionIndex >= territories[territoryIndex].regions.Count) return;
|
|
|
|
GameObject surf = TerritoryGetGameObject(territoryIndex, regionIndex);
|
|
if (surf == null)
|
|
return;
|
|
MeshFilter mf = surf.GetComponent<MeshFilter>();
|
|
if (mf == null || mf.sharedMesh == null)
|
|
return;
|
|
|
|
Mesh mesh = mf.sharedMesh;
|
|
if (mesh != null && (mesh.uv == null || mesh.uv.Length == 0)) {
|
|
// forces mesh to have UV mappings
|
|
Material oldMat = territories[territoryIndex].regions[regionIndex].customMaterial;
|
|
Color color = oldMat != null ? oldMat.color : Misc.ColorNull;
|
|
surf = TerritoryToggleRegionSurface(territoryIndex, true, color, true, Texture2D.whiteTexture);
|
|
TerritoryToggleRegionSurface(territoryIndex, false, Misc.ColorNull);
|
|
|
|
mf = surf.GetComponent<MeshFilter>();
|
|
if (mf == null || mf.sharedMesh == null)
|
|
return;
|
|
}
|
|
|
|
mesh = Instantiate<Mesh>(mf.sharedMesh);
|
|
mesh.name = "Territory " + territoryIndex;
|
|
mesh.hideFlags = 0;
|
|
|
|
GameObject newSurf = new GameObject("Copy of territory " + territoryIndex);
|
|
newSurf.layer = gameObject.layer;
|
|
newSurf.transform.position = transform.position;
|
|
newSurf.transform.rotation = transform.rotation;
|
|
newSurf.transform.localScale = transform.localScale;
|
|
mf = newSurf.AddComponent<MeshFilter>();
|
|
mf.mesh = mesh;
|
|
MeshRenderer mr = newSurf.AddComponent<MeshRenderer>();
|
|
mr.material = new Material(Shader.Find("Diffuse"));
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the points that form the frontier of a territory/region (a territory usually has a single region but could include more than one if it gets split)
|
|
/// Points returned already have the offset and scale of the grid applied
|
|
/// </summary>
|
|
public List<Vector2> TerritoryGetFrontier(int territoryIndex, int regionIndex) {
|
|
if (territoryIndex < 0 || territoryIndex >= territories.Count || regionIndex < 0 || regionIndex >= territories[territoryIndex].regions.Count) return null;
|
|
return territories[territoryIndex].regions[regionIndex].points;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Create a new territory with a single cell
|
|
/// </summary>
|
|
/// <returns>The newly created territory</returns>
|
|
public Territory TerritoryCreate(Cell cell) {
|
|
if (cell == null) return null;
|
|
Territory territory = new Territory();
|
|
territories.Add(territory);
|
|
_numTerritories = territories.Count;
|
|
int terrIndex = _numTerritories - 1;
|
|
lastTerritoryLookupCount = -1;
|
|
CellSetTerritory(cell.index, terrIndex);
|
|
return territory;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new territory with a single cell
|
|
/// </summary>
|
|
/// <returns>The newly created territory</returns>
|
|
public Territory TerritoryCreate(int cellIndex) {
|
|
if (!ValidCellIndex(cellIndex)) return null;
|
|
return TerritoryCreate(cells[cellIndex]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new territory from a list of cells
|
|
/// </summary>
|
|
/// <returns>The newly created territory</returns>
|
|
public Territory TerritoryCreate(List<Cell> cells) {
|
|
tempListCells.Clear();
|
|
foreach (Cell cell in cells) {
|
|
if (cell != null) tempListCells.Add(cell.index);
|
|
}
|
|
if (tempListCells.Count == 0) return null;
|
|
return TerritoryCreate(tempListCells);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Create a new territory from a list of cells
|
|
/// </summary>
|
|
/// <returns>The newly created territory</returns>
|
|
public Territory TerritoryCreate(List<int> cellIndices) {
|
|
Territory territory = new Territory();
|
|
territories.Add(territory);
|
|
_numTerritories = territories.Count;
|
|
int terrIndex = _numTerritories - 1;
|
|
lastTerritoryLookupCount = -1;
|
|
CellSetTerritory(cellIndices, terrIndex);
|
|
return territory;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an existing territory. Cells belonging to the territory will be freed.
|
|
/// </summary>
|
|
/// <returns>True if successful</returns>
|
|
public bool TerritoryDestroy(int territoryIndex) {
|
|
if (!ValidTerritoryIndex(territoryIndex)) return false;
|
|
Territory territory = territories[territoryIndex];
|
|
foreach (Cell cell in territory.cells) {
|
|
cell.territoryIndex = -1;
|
|
}
|
|
DestroyTerritorySurfaces(territoryIndex);
|
|
territories.RemoveAt(territoryIndex);
|
|
// update territory indices for other cells
|
|
foreach (Cell cell in cells) {
|
|
if (cell.territoryIndex >= territoryIndex) {
|
|
cell.territoryIndex--;
|
|
}
|
|
}
|
|
_numTerritories = territories.Count;
|
|
lastTerritoryLookupCount = -1;
|
|
needUpdateTerritories = true;
|
|
issueRedraw = RedrawType.Full;
|
|
return true;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Destroys all territories
|
|
/// </summary>
|
|
public void TerritoryDestroyAll() {
|
|
foreach (Cell cell in cells) {
|
|
cell.territoryIndex = -1;
|
|
}
|
|
DestroyTerritorySurfaces();
|
|
TerritoryHideInteriorBorders();
|
|
territories.Clear();
|
|
_numTerritories = 0;
|
|
lastTerritoryLookupCount = -1;
|
|
needUpdateTerritories = true;
|
|
issueRedraw = RedrawType.IncrementalTerritories;
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|