private IntIntPair SelectProvinceCenter(int provinceId, int radius) { IntIntPair quadrant = null; int midX = (int)(0.5 * _provinceMap.GetLength(0)); int midY = (int)(0.5 * _provinceMap.GetLength(1)); if (provinceId == 1) { quadrant = new IntIntPair(midX, midY); MarkProvinceCell(quadrant.first, quadrant.second, provinceId, _provinceMap, _quadrants, false); } else { quadrant = AddRandomNeighbor(provinceId, _provinceMap, _quadrants, false); } if (quadrant == null) { return(null); } _quadrants.Add(quadrant); return(new IntIntPair(2 * radius * (quadrant.first - midX) + (int)(0.5 * _cellMap.GetLength(0)) + UnityEngine.Random.Range(-2, 2), 2 * radius * (quadrant.second - midY) + (int)(0.5 * _cellMap.GetLength(1)) + UnityEngine.Random.Range(-2, 2))); }
private void ResizeShapesToExtrude(SceneSelectedEntity selectedNode, int nodeIndex, int subFace) { SortedDictionary <int, IntIntPair> subCollection; Node extrudeNode; if (_extrudesList.TryGetValue(nodeIndex, out extrudeNode)) { var extrudeBuilder = new NodeBuilder(extrudeNode); var sourceFaceNodeIndex = extrudeBuilder[0].Reference.Index; _shapesToExtrude.Remove(sourceFaceNodeIndex); return; } if (!_shapesToExtrude.TryGetValue(nodeIndex, out subCollection)) { subCollection = new SortedDictionary <int, IntIntPair>(); _shapesToExtrude[nodeIndex] = subCollection; } if (subCollection.ContainsKey(subFace)) { //subCollection.Remove(subFace); //if (subCollection.Count == 0) // _shapesToExtrude.Remove(nodeIndex); } else { subCollection[subFace] = new IntIntPair(selectedNode); } }
/// <summary> /// Bribe a province garrison to change sides /// </summary> /// <param name="province">The garrisoned province</param> /// <returns>Whether the opration was successful</returns> public bool BribeProvince(Province province) { IntIntPair costs = CalculateBriberyCosts(province); if (GetCurrentFaction().SubtractCost(costs.second)) { ChangeProvinceOwnership(province, GetCurrentFaction()); return(true); } return(false); }
/// <summary> /// Bribe a province garrison to disband /// </summary> /// <param name="province">The garrisoned province</param> /// <returns>Whether the opration was successful</returns> public bool DisbandGarrison(Province province) { IntIntPair costs = CalculateBriberyCosts(province); if (GetCurrentFaction().SubtractCost(costs.first)) { province.RemoveAllUnits(); return(true); } return(false); }
private bool MarkProvinceCell(int x, int y, int provinceId, int[,] tileMap, List <IntIntPair> neighbors, bool useStricterRules) { if (tileMap[x, y] > 0 || tileMap[x, y] == provinceId) { return(false); } tileMap[x, y] = provinceId; HexBorder[] borders = HexBorder.GetBorderDirections(x % 2 == 1); for (int i = 0; i < borders.Length; i++) { int neighborX = x + borders[i].GetDeltaX(); int neighborY = y + borders[i].GetDeltaY(); IntIntPair neighbor = new IntIntPair(neighborX, neighborY); bool passesBasicRules = IsInnerCell(neighborX, neighborY, tileMap) && tileMap[neighborX, neighborY] <= 0; // stricter rules means having at least two neighboring cells // belonging to the same province // it's used to avoid serpentine forms bool passesStricterRules = false; if (useStricterRules) { int sameProvinceNeighbors = 0; HexBorder[] newBorders = HexBorder.GetBorderDirections(neighborX % 2 == 1); for (int j = 0; j < newBorders.Length; j++) { int xx = neighborX + newBorders[j].GetDeltaX(); int yy = neighborY + newBorders[j].GetDeltaY(); if (IsInnerCell(xx, yy, tileMap) && tileMap[xx, yy] == provinceId) { sameProvinceNeighbors++; if (sameProvinceNeighbors >= 2) { passesStricterRules = true; break; } } } } // is within the map area, is a water cell, and either doesn't care // about stricter rules or satisfies them if (passesBasicRules && (!useStricterRules || passesStricterRules)) { neighbors.Add(neighbor); } } return(true); }
/// <summary> /// Class constructor /// </summary> /// <param name="data">Serialized race data</param> /// <param name="unitTypes">Hash of unit type id => unit type of racial units</param> public Race(RaceData data, Dictionary <int, UnitType> unitTypes) { _data = data; _lowercaseName = _data.name.ToLower(); _racialUnits = new List <UnitType>(); for (int i = 0; i < _data.units.Length; i++) { UnitType unitType = unitTypes[_data.units[i]]; if (unitType != null) { _racialUnits.Add(unitType); } } _magicUpgrades = new Dictionary <UnitType, UnitType>(); if (_data.magicUpgrades != null) { for (int i = 0; i < _data.magicUpgrades.Length; i++) { IntIntPair upgrade = _data.magicUpgrades[i]; _magicUpgrades[unitTypes[upgrade.first]] = unitTypes[upgrade.second]; } } _divineAdditions = new List <UnitType>(); if (_data.divineAdditions != null) { for (int i = 0; i < _data.divineAdditions.Length; i++) { int newUnit = _data.divineAdditions[i]; _divineAdditions.Add(unitTypes[newUnit]); } } _divineUpgrades = new Dictionary <UnitType, UnitType>(); if (_data.divineUpgrades != null) { for (int i = 0; i < _data.divineUpgrades.Length; i++) { IntIntPair upgrade = _data.divineUpgrades[i]; _divineUpgrades[unitTypes[upgrade.first]] = unitTypes[upgrade.second]; } } }
private void ConnectIsland(int index) { FileLogger.Trace("MAP", "Connecting province #" + (index + 1).ToString() + " to the mainland"); int targetCellsWithNeighbors = 3; int addedCells = 0; while (addedCells < targetCellsWithNeighbors) { IntIntPair addition = AddRandomNeighbor(index + 1, _cellMap, _neighbors[index], false, true); if (addition == null) { // nothing can be done - the list of potential neighbors exhausted return; } if (SearchForNeighbors(addition.first, addition.second, true)) { addedCells++; } } }
private IntIntPair AddRandomNeighbor(int provinceId, int[,] tileMap, List <IntIntPair> neighbors, bool useStricterRules, bool towardCenterOnly = false) { IntIntPair newProvinceCell = null; // more accurately, the center of the province #1 IntIntPair mapCenter = _centers[0]; bool found = false; while (!found) { // a safety valve - should not happen if (neighbors.Count == 0) { return(null); } int randomIndex = UnityEngine.Random.Range(0, neighbors.Count - 1); newProvinceCell = neighbors[randomIndex]; neighbors.RemoveAt(randomIndex); // when deepening the ocean (provinceId = -1), only 0 satisfies the criteria // otherwise, both -1 and 0 work if (tileMap[newProvinceCell.first, newProvinceCell.second] <= 0 && tileMap[newProvinceCell.first, newProvinceCell.second] != provinceId) { found = !towardCenterOnly || CalculateDistance(mapCenter, newProvinceCell) <= CalculateDistance(mapCenter, _centers[provinceId - 1]); } } if (MarkProvinceCell(newProvinceCell.first, newProvinceCell.second, provinceId, tileMap, neighbors, useStricterRules)) { return(newProvinceCell); } return(null); }
private int CalculateDistance(IntIntPair one, IntIntPair two) { // http://www.redblobgames.com/grids/hexagons/ return(Math.Max(Math.Abs(one.first - two.first), Math.Abs(one.second - two.second))); }
public Game CreateScenario(GameData data) { data.turn = 1; int height = 42; int width = 48; // all cells within this radius should ideally belong to the same province int radius = 3; int cellsPerProvince = radius * (radius + 1) * 3 + 1; // the number of provinces is calculated with +/- 5% variance int numberOfProvinces = (int)(height * width * LAND_FRACTION / cellsPerProvince * (UnityEngine.Random.Range(0, 10) * 0.01 + 0.95)); _cellMap = new int[width, height]; _centers = new List <IntIntPair>(); int provinceMapXSize = (int)(width * 0.5 / radius); int provinceMapYSize = (int)(height * 0.5 / radius); _provinceMap = new int[provinceMapXSize, provinceMapYSize]; _quadrants = new List <IntIntPair>(); // list of (current number of cells, total number of cells) per province List <IntIntPair> cellQtys = new List <IntIntPair>(); // the number of cells the largest province will have int maxCellQty = 0; // can't really have more than this number of provinces - the map would be too cluttered numberOfProvinces = Math.Min(numberOfProvinces, provinceMapXSize * provinceMapYSize); FileLogger.Trace("MAP", "The world will have " + numberOfProvinces + " provinces "); _neighbors = new List <List <IntIntPair> >(); for (int i = 0; i < numberOfProvinces; i++) { // each province get its own list of neighboring cells coordinates _neighbors.Add(new List <IntIntPair>()); } int provinceId = 1; int failedTries = 0; // placing province centers while (_centers.Count < numberOfProvinces && failedTries < 10) { IntIntPair provinceCenter = SelectProvinceCenter(provinceId, radius); if (provinceCenter != null) { _centers.Add(provinceCenter); MarkProvinceCell(provinceCenter.first, provinceCenter.second, provinceId, _cellMap, _neighbors[provinceId - 1], false); //FileLogger.Trace("MAP", "The center of province #" + provinceId + " is [" + provinceCenter.first + ", " + provinceCenter.second + "]"); // reset failures count failedTries = 0; // move to the next province provinceId++; } else { failedTries++; } } FileLogger.Trace("MAP", "The number of provinces changes from " + numberOfProvinces + " to " + _centers.Count); numberOfProvinces = _centers.Count; // calculating the number of cells to create per province for (int i = 0; i < numberOfProvinces; i++) { IntIntPair provinceCenter = _centers[i]; //int markedCells = MakeProvince(provinceCenter.first, provinceCenter.second, i + 1, radius, cellMap, neighbors[i]); //FileLogger.Trace("MAP", "The bulk of province #" + (i + 1).ToString() + " conists of " + markedCells + " cells"); int cellQty = cellsPerProvince + UnityEngine.Random.Range(-4, 6); if (provinceCenter.first > 3 * radius && provinceCenter.first < width - 3 * radius && provinceCenter.second > 3 * radius && provinceCenter.second < height - 3 * radius) { cellQty += UnityEngine.Random.Range(4, 10); } // can't be fewer than we already marked //cellQty = Math.Max(cellQty, markedCells); cellQtys.Add(new IntIntPair(1, cellQty)); maxCellQty = Math.Max(maxCellQty, cellQty); //FileLogger.Trace("MAP", "Province #" + (i + 1).ToString() + " will have " + cellQty + " cells with the center at [" + provinceCenter.first + ", " + provinceCenter.second + "]"); } //FileLogger.Trace("MAP", "The largest province should have " + maxCellQty + " cells"); // try to reach the desired number of cells per province for (int i = 1; i < maxCellQty; i++) { //FileLogger.Trace("MAP", "Adding cells: pass #" + i); for (int p = 0; p < numberOfProvinces; p++) { if (cellQtys[p].first < cellQtys[p].second && _neighbors[p].Count > 0) { if (AddRandomNeighbor(p + 1, _cellMap, _neighbors[p], true) != null) { cellQtys[p].first++; } } } } _distancesInProvinces = new int[numberOfProvinces, numberOfProvinces]; // init distances in provinces for (int i = 0; i < numberOfProvinces; i++) { for (int j = 0; j < numberOfProvinces; j++) { _distancesInProvinces[i, j] = int.MaxValue; } _distancesInProvinces[i, i] = 0; } for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (_cellMap[x, y] > 0) { SearchForNeighbors(x, y, false); } } } // we only need distances from province #1 UpdateDistancesForProvince(0); /* * for (int i = 0; i < numberOfProvinces - 1; i++) * { * for (int j = i + 1; j < numberOfProvinces; j++) * { * FileLogger.Trace("MAP", "Distance between provinces #" + (i + 1).ToString() + " and #" + (j + 1).ToString() + " is " + distancesInProvinces[i, j]); * } * } */ for (int i = 1; i < numberOfProvinces; i++) { if (_distancesInProvinces[0, i] == int.MaxValue) { ConnectIsland(i); } } // for connected water tiles, set province id to -1 // so that lakes and inner seas can be identified // and eliminated for (int x = 1; x < width - 1; x++) { DeepenTheOcean(x, 1); DeepenTheOcean(x, height - 1); } for (int y = 1; y < height - 1; y++) { DeepenTheOcean(1, y); DeepenTheOcean(width - 1, y); } // dry lakes and inner seas - water tiles that are not // connected to the ocean for (int x = 1; x < _cellMap.GetLength(0) - 1; x++) { for (int y = 1; y < _cellMap.GetLength(1) - 1; y++) { if (_cellMap[x, y] == 0) { DryLake(x, y); } } } _distancesInCells = new int[numberOfProvinces, numberOfProvinces]; // calculate distances in cells for (int i = 0; i < numberOfProvinces; i++) { _distancesInCells[i, i] = 0; for (int j = i + 1; j < numberOfProvinces; j++) { int distance = CalculateDistance(_quadrants[i], _quadrants[j]); _distancesInCells[i, j] = distance; _distancesInCells[j, i] = distance; } } // converting the cells list into an array List <MapCellData> cells = new List <MapCellData>(); for (int x = 1; x < width - 1; x++) { for (int y = 1; y < height - 1; y++) { if (_cellMap[x, y] > 0) { cells.Add(new MapCellData(x, y, _cellMap[x, y])); } } } data.cells = cells.ToArray(); // setting up province data ProvinceData[] templates = data.provinces; data.provinces = new ProvinceData[numberOfProvinces]; // set up Utopia in the center of the map data.provinces[0] = templates[0].CloneAs(1); // calculate max distance to potential faction capitals List <int> orcishCapitalCandidates = new List <int>(); List <int> elvenCapitalCandidates = new List <int>(); List <int> dwarvenCapitalCandidates = new List <int>(); int maxOrcishDistance = 0; int maxElvenDistance = 0; int maxDwarvenDistance = 0; IntIntPair mapCenter = _centers[0]; for (int i = 1; i < numberOfProvinces; i++) { if (_distancesInProvinces[0, i] > 1) { IntIntPair provinceCenter = _centers[i]; if (provinceCenter.first > mapCenter.first) { // orcs: lower-right corner if (provinceCenter.second < mapCenter.second) { orcishCapitalCandidates.Add(i); maxOrcishDistance = Math.Max(maxOrcishDistance, _distancesInProvinces[0, i]); } else { // dwarves: upper-right corner dwarvenCapitalCandidates.Add(i); maxDwarvenDistance = Math.Max(maxDwarvenDistance, _distancesInProvinces[0, i]); } } else { // elves: a 90 degrees sector in the left half of the map if (Math.Abs(provinceCenter.first - mapCenter.first) > Math.Abs(provinceCenter.second - mapCenter.second)) { elvenCapitalCandidates.Add(i); maxElvenDistance = Math.Max(maxElvenDistance, _distancesInProvinces[0, i]); } } } } // we should be able to find a province for each faction at this distance from the center int targetDistance = Math.Min(maxOrcishDistance, Math.Min(maxElvenDistance, maxDwarvenDistance)); // select candidates for the orcish capital List <int> provinceCandidates = new List <int>(); for (int i = 0; i < orcishCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, orcishCapitalCandidates[i]] == targetDistance) { provinceCandidates.Add(orcishCapitalCandidates[i]); } } // if for some reason there is no suitable province at the target distance, search farther if (provinceCandidates.Count == 0) { for (int i = 0; i < orcishCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, orcishCapitalCandidates[i]] > targetDistance) { provinceCandidates.Add(orcishCapitalCandidates[i]); } } } // start with the first candidate, search for the one // with the largest x and smallest y int orcishCapitalIndex = provinceCandidates[0]; int distance1 = Math.Abs(_centers[orcishCapitalIndex].first - _centers[orcishCapitalIndex].second); for (int i = 1; i < provinceCandidates.Count; i++) { int distance2 = Math.Abs(_centers[provinceCandidates[i]].first - _centers[provinceCandidates[i]].second); if (distance2 > distance1) { orcishCapitalIndex = provinceCandidates[i]; } } data.provinces[orcishCapitalIndex] = templates[1].CloneAs(orcishCapitalIndex + 1); // select candidates for the elven capital provinceCandidates = new List <int>(); for (int i = 0; i < elvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, elvenCapitalCandidates[i]] == targetDistance) { provinceCandidates.Add(elvenCapitalCandidates[i]); } } // if for some reason there is no suitable province at the target distance, search farther if (provinceCandidates.Count == 0) { for (int i = 0; i < elvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, elvenCapitalCandidates[i]] > targetDistance) { provinceCandidates.Add(elvenCapitalCandidates[i]); } } } // start with the first candidate, search for the one with the smallest x int elvenCapitalIndex = provinceCandidates[0]; for (int i = 1; i < provinceCandidates.Count; i++) { if (_centers[provinceCandidates[i]].first < _centers[elvenCapitalIndex].first) { elvenCapitalIndex = provinceCandidates[i]; } } data.provinces[elvenCapitalIndex] = templates[2].CloneAs(elvenCapitalIndex + 1); // select candidates for the dwarven capital provinceCandidates = new List <int>(); for (int i = 0; i < dwarvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, dwarvenCapitalCandidates[i]] == targetDistance) { provinceCandidates.Add(dwarvenCapitalCandidates[i]); } } // if for some reason there is no suitable province at the target distance, search farther if (provinceCandidates.Count == 0) { for (int i = 0; i < dwarvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, dwarvenCapitalCandidates[i]] > targetDistance) { provinceCandidates.Add(dwarvenCapitalCandidates[i]); } } } // start with the first candidate, search for the one with the largest x int dwarvenCapitalIndex = provinceCandidates[0]; for (int i = 1; i < provinceCandidates.Count; i++) { if (_centers[provinceCandidates[i]].first + _centers[provinceCandidates[i]].second > _centers[dwarvenCapitalIndex].first + _centers[dwarvenCapitalIndex].second) { dwarvenCapitalIndex = provinceCandidates[i]; } } data.provinces[dwarvenCapitalIndex] = templates[3].CloneAs(dwarvenCapitalIndex + 1); // select provinces that can be a secondary orcish province provinceCandidates = new List <int>(); for (int i = 0; i < orcishCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, orcishCapitalCandidates[i]] == 2 && _distancesInProvinces[orcishCapitalIndex, orcishCapitalCandidates[i]] == 2) { provinceCandidates.Add(orcishCapitalCandidates[i]); } } // if there is no suitable province at distance of 2 from Utopia, search at distance of 1 if (provinceCandidates.Count == 0) { for (int i = 0; i < orcishCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, orcishCapitalCandidates[i]] >= 1 && _distancesInProvinces[orcishCapitalIndex, orcishCapitalCandidates[i]] >= 2) { provinceCandidates.Add(orcishCapitalCandidates[i]); } } } int randomIndex; int provinceIndex; // pick one at random if (provinceCandidates.Count > 0) { randomIndex = UnityEngine.Random.Range(0, provinceCandidates.Count - 1); provinceIndex = provinceCandidates[randomIndex]; data.provinces[provinceIndex] = templates[4].CloneAs(provinceIndex + 1); } // select provinces that can be a secondary elven province provinceCandidates = new List <int>(); for (int i = 0; i < elvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, elvenCapitalCandidates[i]] == 2 && _distancesInProvinces[elvenCapitalIndex, elvenCapitalCandidates[i]] == 2) { provinceCandidates.Add(elvenCapitalCandidates[i]); } } if (provinceCandidates.Count == 0) { for (int i = 0; i < elvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, elvenCapitalCandidates[i]] >= 1 && _distancesInProvinces[elvenCapitalIndex, elvenCapitalCandidates[i]] >= 2) { provinceCandidates.Add(elvenCapitalCandidates[i]); } } } // pick one at random if (provinceCandidates.Count > 0) { randomIndex = UnityEngine.Random.Range(0, provinceCandidates.Count - 1); provinceIndex = provinceCandidates[randomIndex]; data.provinces[provinceIndex] = templates[5].CloneAs(provinceIndex + 1); } // select provinces that can be a secondary dwarven province provinceCandidates = new List <int>(); for (int i = 0; i < dwarvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, dwarvenCapitalCandidates[i]] == 2 && _distancesInProvinces[dwarvenCapitalIndex, dwarvenCapitalCandidates[i]] == 2) { provinceCandidates.Add(dwarvenCapitalCandidates[i]); } } if (provinceCandidates.Count == 0) { for (int i = 0; i < dwarvenCapitalCandidates.Count; i++) { if (_distancesInProvinces[0, dwarvenCapitalCandidates[i]] >= 1 && _distancesInProvinces[dwarvenCapitalIndex, dwarvenCapitalCandidates[i]] >= 2) { provinceCandidates.Add(dwarvenCapitalCandidates[i]); } } } // pick one at random if (provinceCandidates.Count > 0) { randomIndex = UnityEngine.Random.Range(0, provinceCandidates.Count - 1); provinceIndex = provinceCandidates[randomIndex]; data.provinces[provinceIndex] = templates[6].CloneAs(provinceIndex + 1); } _humanProvinceNames = new List <string>(); TextAsset resourceFile = Resources.Load("HumanNames") as TextAsset; StringReader reader = new StringReader(resourceFile.text); if (reader == null) { FileLogger.Error("MAP", "Can't load human province names"); } else { string currentLine; while ((currentLine = reader.ReadLine()) != null) { _humanProvinceNames.Add(currentLine); } } // select provinces neighboring to Utopia and not used for another purpose yet provinceCandidates = new List <int>(); for (int i = 1; i < numberOfProvinces; i++) { if (_distancesInProvinces[0, i] == 1 && data.provinces[i] == null) { provinceCandidates.Add(i); } } // put fighter-producing provinces in the select spots int counter = 1; while (provinceCandidates.Count > 0) { provinceIndex = provinceCandidates[0]; data.provinces[provinceIndex] = templates[7].CloneAs(provinceIndex + 1); data.provinces[provinceIndex].name = CreateProvinceName(provinceIndex + 1); counter++; provinceCandidates.RemoveAll(province => _distancesInProvinces[province, provinceIndex] < 2); } for (int i = 1; i < numberOfProvinces; i++) { if (data.provinces[i] == null) { randomIndex = UnityEngine.Random.Range(8, templates.Length - 1); data.provinces[i] = templates[randomIndex].CloneAs(i + 1); data.provinces[i].name = CreateProvinceName(i + 1); } } return(new Game(data)); }