chore: Split MapRegions into class files, first step of refactor

This commit is contained in:
Yair Morgenstern 2023-10-02 11:10:48 +03:00
parent 9386e4a7ce
commit 1e085eb60a
5 changed files with 189 additions and 154 deletions

View File

@ -8,6 +8,7 @@ import com.unciv.logic.map.MapParameters
import com.unciv.logic.map.MapShape
import com.unciv.logic.map.MapType
import com.unciv.logic.map.TileMap
import com.unciv.logic.map.mapgenerator.mapregions.MapRegions
import com.unciv.logic.map.tile.Tile
import com.unciv.models.Counter
import com.unciv.models.metadata.GameParameters

View File

@ -0,0 +1,94 @@
package com.unciv.logic.map.mapgenerator.mapregions
import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType
import kotlin.math.max
import kotlin.math.min
// Holds a bunch of tile info that is only interesting during map gen
class MapGenTileData(val tile: Tile, val region: Region?, ruleset: Ruleset) {
var closeStartPenalty = 0
val impacts = HashMap<MapRegions.ImpactType, Int>()
var isFood = false
private set
var isProd = false
private set
var isGood = false
private set
var isJunk = false
private set
var isTwoFromCoast = false
private set
var isGoodStart = true
var startScore = 0
init {
evaluate(ruleset)
}
fun addCloseStartPenalty(penalty: Int) {
if (closeStartPenalty == 0)
closeStartPenalty = penalty
else {
// Multiple overlapping values - take the higher one and add 20 %
closeStartPenalty = max(closeStartPenalty, penalty)
closeStartPenalty = min(97, (closeStartPenalty * 1.2f).toInt())
}
}
/** Populates all private-set fields */
private fun evaluate(ruleset: Ruleset) {
// Check if we are two tiles from coast (a bad starting site)
if (!tile.isCoastalTile() && tile.neighbors.any { it.isCoastalTile() })
isTwoFromCoast = true
// Check first available out of unbuildable features, then other features, then base terrain
val terrainToCheck = if (tile.terrainFeatures.isEmpty()) tile.getBaseTerrain()
else tile.terrainFeatureObjects.firstOrNull { it.unbuildable }
?: tile.terrainFeatureObjects.first()
// Add all applicable qualities
for (unique in terrainToCheck.getMatchingUniques(
UniqueType.HasQuality,
StateForConditionals(region = region)
)) {
when (unique.params[0]) {
"Food" -> isFood = true
"Desirable" -> isGood = true
"Production" -> isProd = true
"Undesirable" -> isJunk = true
}
}
// Were there in fact no explicit qualities defined for any region at all? If so let's guess at qualities to preserve mod compatibility.
if (terrainToCheck.uniqueObjects.none { it.type == UniqueType.HasQuality }) {
if (tile.isWater) return // Most water type tiles have no qualities
// is it junk???
if (terrainToCheck.impassable) {
isJunk = true
return // Don't bother checking the rest, junk is junk
}
// Take possible improvements into account
val improvements = ruleset.tileImprovements.values.filter {
terrainToCheck.name in it.terrainsCanBeBuiltOn &&
it.uniqueTo == null &&
!it.hasUnique(UniqueType.GreatImprovement)
}
val maxFood = terrainToCheck.food + (improvements.maxOfOrNull { it.food } ?: 0f)
val maxProd = terrainToCheck.production + (improvements.maxOfOrNull { it.production } ?: 0f)
val bestImprovementValue = improvements.maxOfOrNull { it.food + it.production + it.gold + it.culture + it.science + it.faith } ?: 0f
val maxOverall = terrainToCheck.food + terrainToCheck.production + terrainToCheck.gold +
terrainToCheck.culture + terrainToCheck.science + terrainToCheck.faith + bestImprovementValue
if (maxFood >= 2) isFood = true
if (maxProd >= 2) isProd = true
if (maxOverall >= 3) isGood = true
}
}
}

View File

@ -1,4 +1,4 @@
package com.unciv.logic.map.mapgenerator
package com.unciv.logic.map.mapgenerator.mapregions
import com.badlogic.gdx.math.Rectangle
import com.badlogic.gdx.math.Vector2
@ -7,6 +7,7 @@ import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.MapResources
import com.unciv.logic.map.MapShape
import com.unciv.logic.map.TileMap
import com.unciv.logic.map.mapgenerator.mapregions.MapRegions.BiasTypes.PositiveFallback
import com.unciv.logic.map.tile.Tile
import com.unciv.models.metadata.GameParameters
import com.unciv.models.ruleset.Ruleset
@ -245,8 +246,7 @@ class MapRegions (val ruleset: Ruleset){
// Generate tile data for all tiles
for (tile in tileMap.values) {
val newData = MapGenTileData(tile, regions.firstOrNull { it.tiles.contains(tile) })
newData.evaluate(ruleset)
val newData = MapGenTileData(tile, regions.firstOrNull { it.tiles.contains(tile) }, ruleset)
tileData[tile.position] = newData
}
@ -1628,156 +1628,6 @@ class MapRegions (val ruleset: Ruleset){
MinorCiv,
}
// Holds a bunch of tile info that is only interesting during map gen
class MapGenTileData(val tile: Tile, val region: Region?) {
var closeStartPenalty = 0
val impacts = HashMap<ImpactType, Int>()
var isFood = false
var isProd = false
var isGood = false
var isJunk = false
var isTwoFromCoast = false
var isGoodStart = true
var startScore = 0
fun addCloseStartPenalty(penalty: Int) {
if (closeStartPenalty == 0)
closeStartPenalty = penalty
else {
// Multiple overlapping values - take the higher one and add 20 %
closeStartPenalty = max(closeStartPenalty, penalty)
closeStartPenalty = min(97, (closeStartPenalty * 1.2f).toInt())
}
}
fun evaluate(ruleset: Ruleset) {
// Check if we are two tiles from coast (a bad starting site)
if (!tile.isCoastalTile() && tile.neighbors.any { it.isCoastalTile() })
isTwoFromCoast = true
// Check first available out of unbuildable features, then other features, then base terrain
val terrainToCheck = if (tile.terrainFeatures.isEmpty()) tile.getBaseTerrain()
else tile.terrainFeatureObjects.firstOrNull { it.unbuildable }
?: tile.terrainFeatureObjects.first()
// Add all applicable qualities
for (unique in terrainToCheck.getMatchingUniques(UniqueType.HasQuality, StateForConditionals(region = region))) {
when (unique.params[0]) {
"Food" -> isFood = true
"Desirable" -> isGood = true
"Production" -> isProd = true
"Undesirable" -> isJunk = true
}
}
// Were there in fact no explicit qualities defined for any region at all? If so let's guess at qualities to preserve mod compatibility.
if (terrainToCheck.uniqueObjects.none { it.type == UniqueType.HasQuality }) {
if (tile.isWater) return // Most water type tiles have no qualities
// is it junk???
if (terrainToCheck.impassable) {
isJunk = true
return // Don't bother checking the rest, junk is junk
}
// Take possible improvements into account
val improvements = ruleset.tileImprovements.values.filter {
terrainToCheck.name in it.terrainsCanBeBuiltOn &&
it.uniqueTo == null &&
!it.hasUnique(UniqueType.GreatImprovement)
}
val maxFood = terrainToCheck.food + (improvements.maxOfOrNull { it.food } ?: 0f)
val maxProd = terrainToCheck.production + (improvements.maxOfOrNull { it.production } ?: 0f)
val bestImprovementValue = improvements.maxOfOrNull { it.food + it.production + it.gold + it.culture + it.science + it.faith } ?: 0f
val maxOverall = terrainToCheck.food + terrainToCheck.production + terrainToCheck.gold +
terrainToCheck.culture + terrainToCheck.science + terrainToCheck.faith + bestImprovementValue
if (maxFood >= 2) isFood = true
if (maxProd >= 2) isProd = true
if (maxOverall >= 3) isGood = true
}
}
}
}
class Region (val tileMap: TileMap, val rect: Rectangle, val continentID: Int = -1) {
val tiles = HashSet<Tile>()
val terrainCounts = HashMap<String, Int>()
var totalFertility = 0
var type = "Hybrid" // being an undefined or indeterminate type
var luxury: String? = null
var startPosition: Vector2? = null
val assignedMinorCivs = ArrayList<Civilization>()
var affectedByWorldWrap = false
/** Recalculates tiles and fertility */
fun updateTiles(trim: Boolean = true) {
totalFertility = 0
var minColumn = 99999f
var maxColumn = -99999f
var minRow = 99999f
var maxRow = -99999f
val columnHasTile = HashSet<Int>()
tiles.clear()
for (tile in tileMap.getTilesInRectangle(rect).filter {
continentID == -1 || it.getContinent() == continentID } ) {
val fertility = tile.getTileFertility(continentID != -1)
tiles.add(tile)
totalFertility += fertility
if (affectedByWorldWrap)
columnHasTile.add(tile.getColumn())
if (trim) {
val row = tile.getRow().toFloat()
val column = tile.getColumn().toFloat()
minColumn = min(minColumn, column)
maxColumn = max(maxColumn, column)
minRow = min(minRow, row)
maxRow = max(maxRow, row)
}
}
if (trim) {
if (affectedByWorldWrap) // Need to be more thorough with origin longitude
rect.x = columnHasTile.filter { !columnHasTile.contains(it - 1) }.maxOf { it }.toFloat()
else
rect.x = minColumn // ez way for non-wrapping regions
rect.y = minRow
rect.height = maxRow - minRow + 1
if (affectedByWorldWrap && minColumn < rect.x) { // Thorough way
rect.width = columnHasTile.size.toFloat()
} else {
rect.width = maxColumn - minColumn + 1 // ez way
affectedByWorldWrap = false // also we're not wrapping anymore
}
}
}
/** Counts the terrains in the Region for type and start determination */
fun countTerrains() {
// Count terrains in the region
terrainCounts.clear()
for (tile in tiles) {
val terrainsToCount = if (tile.terrainHasUnique(UniqueType.IgnoreBaseTerrainForRegion))
tile.terrainFeatureObjects.map { it.name }.asSequence()
else
tile.allTerrains.map { it.name }
for (terrain in terrainsToCount) {
terrainCounts[terrain] = (terrainCounts[terrain] ?: 0) + 1
}
if (tile.isCoastalTile())
terrainCounts["Coastal"] = (terrainCounts["Coastal"] ?: 0) + 1
}
}
/** Returns number terrains with [name] */
fun getTerrainAmount(name: String) = terrainCounts[name] ?: 0
override fun toString() = "Region($type, ${tiles.size} tiles, ${terrainCounts.entries.joinToString { "${it.value} ${it.key}" }})"
}

View File

@ -0,0 +1,90 @@
package com.unciv.logic.map.mapgenerator.mapregions
import com.badlogic.gdx.math.Rectangle
import com.badlogic.gdx.math.Vector2
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.TileMap
import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.UniqueType
import kotlin.math.max
import kotlin.math.min
class Region (val tileMap: TileMap, val rect: Rectangle, val continentID: Int = -1) {
val tiles = HashSet<Tile>()
val terrainCounts = HashMap<String, Int>()
var totalFertility = 0
var type = "Hybrid" // being an undefined or indeterminate type
var luxury: String? = null
var startPosition: Vector2? = null
val assignedMinorCivs = ArrayList<Civilization>()
var affectedByWorldWrap = false
/** Recalculates tiles and fertility */
fun updateTiles(trim: Boolean = true) {
totalFertility = 0
var minColumn = 99999f
var maxColumn = -99999f
var minRow = 99999f
var maxRow = -99999f
val columnHasTile = HashSet<Int>()
tiles.clear()
for (tile in tileMap.getTilesInRectangle(rect).filter {
continentID == -1 || it.getContinent() == continentID } ) {
val fertility = tile.getTileFertility(continentID != -1)
tiles.add(tile)
totalFertility += fertility
if (affectedByWorldWrap)
columnHasTile.add(tile.getColumn())
if (trim) {
val row = tile.getRow().toFloat()
val column = tile.getColumn().toFloat()
minColumn = min(minColumn, column)
maxColumn = max(maxColumn, column)
minRow = min(minRow, row)
maxRow = max(maxRow, row)
}
}
if (trim) {
if (affectedByWorldWrap) // Need to be more thorough with origin longitude
rect.x = columnHasTile.filter { !columnHasTile.contains(it - 1) }.maxOf { it }.toFloat()
else
rect.x = minColumn // ez way for non-wrapping regions
rect.y = minRow
rect.height = maxRow - minRow + 1
if (affectedByWorldWrap && minColumn < rect.x) { // Thorough way
rect.width = columnHasTile.size.toFloat()
} else {
rect.width = maxColumn - minColumn + 1 // ez way
affectedByWorldWrap = false // also we're not wrapping anymore
}
}
}
/** Counts the terrains in the Region for type and start determination */
fun countTerrains() {
// Count terrains in the region
terrainCounts.clear()
for (tile in tiles) {
val terrainsToCount = if (tile.terrainHasUnique(UniqueType.IgnoreBaseTerrainForRegion))
tile.terrainFeatureObjects.map { it.name }.asSequence()
else
tile.allTerrains.map { it.name }
for (terrain in terrainsToCount) {
terrainCounts[terrain] = (terrainCounts[terrain] ?: 0) + 1
}
if (tile.isCoastalTile())
terrainCounts["Coastal"] = (terrainCounts["Coastal"] ?: 0) + 1
}
}
/** Returns number terrains with [name] */
fun getTerrainAmount(name: String) = terrainCounts[name] ?: 0
override fun toString() = "Region($type, ${tiles.size} tiles, ${terrainCounts.entries.joinToString { "${it.value} ${it.key}" }})"
}

View File

@ -4,7 +4,7 @@ import com.unciv.logic.battle.CombatAction
import com.unciv.logic.battle.ICombatant
import com.unciv.logic.city.City
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.mapgenerator.Region
import com.unciv.logic.map.mapgenerator.mapregions.Region
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.logic.map.tile.Tile