mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-11 11:28:03 +07:00
chore: More luxury assignment into the new object
This commit is contained in:
parent
2582d913d3
commit
3eff497bd8
@ -1,5 +1,8 @@
|
||||
package com.unciv.logic.map.mapgenerator.mapregions
|
||||
|
||||
import com.unciv.logic.map.MapResources
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
@ -7,8 +10,11 @@ import com.unciv.models.ruleset.tile.TileResource
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.components.extensions.randomWeighted
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.random.Random
|
||||
|
||||
object LuxuryResourcePlacementLogic {
|
||||
|
||||
@ -88,8 +94,7 @@ object LuxuryResourcePlacementLogic {
|
||||
100 - min(tileData.size.toFloat().pow(0.2f) * 16, 100f).toInt() // Approximately
|
||||
val targetDisabledLuxuries = (ruleset.tileResources.values
|
||||
.count { it.resourceType == ResourceType.Luxury } * disabledPercent) / 100
|
||||
val randomLuxuries = remainingLuxuries.drop(targetDisabledLuxuries)
|
||||
return randomLuxuries
|
||||
return remainingLuxuries.drop(targetDisabledLuxuries)
|
||||
}
|
||||
|
||||
private fun getCandidateLuxuries(
|
||||
@ -160,4 +165,260 @@ object LuxuryResourcePlacementLogic {
|
||||
}
|
||||
return cityStateLuxuries
|
||||
}
|
||||
|
||||
|
||||
/** Places all Luxuries onto [tileMap]. Assumes that assignLuxuries and placeMinorCivs have been called. */
|
||||
fun placeLuxuries(
|
||||
regions: ArrayList<Region>,
|
||||
tileMap: TileMap,
|
||||
tileData: TileDataMap,
|
||||
ruleset: Ruleset,
|
||||
cityStateLuxuries: List<String>,
|
||||
randomLuxuries: List<String>
|
||||
) {
|
||||
|
||||
placeLuxuriesAtMajorCivStartLocations(regions, tileMap, ruleset, tileData, randomLuxuries)
|
||||
placeLuxuriesAtMinorCivStartLocations(tileMap, ruleset, regions, randomLuxuries, cityStateLuxuries, tileData)
|
||||
addRegionalLuxuries(tileData, regions, tileMap, ruleset)
|
||||
addRandomLuxuries(randomLuxuries, tileData, tileMap, regions, ruleset)
|
||||
|
||||
|
||||
val specialLuxuries = ruleset.tileResources.values.filter {
|
||||
it.resourceType == ResourceType.Luxury &&
|
||||
it.hasUnique(UniqueType.LuxurySpecialPlacement)
|
||||
}
|
||||
val placedSpecials = HashMap<String, Int>()
|
||||
specialLuxuries.forEach { placedSpecials[it.name] = 0 } // init map
|
||||
|
||||
addExtraLuxuryToStarts(
|
||||
tileMap,
|
||||
regions,
|
||||
randomLuxuries,
|
||||
specialLuxuries,
|
||||
cityStateLuxuries,
|
||||
tileData,
|
||||
ruleset,
|
||||
placedSpecials
|
||||
)
|
||||
|
||||
fillSpecialLuxuries(specialLuxuries, tileMap, regions, placedSpecials, tileData)
|
||||
}
|
||||
|
||||
/** top up marble-type specials if needed */
|
||||
private fun fillSpecialLuxuries(
|
||||
specialLuxuries: List<TileResource>,
|
||||
tileMap: TileMap,
|
||||
regions: ArrayList<Region>,
|
||||
placedSpecials: HashMap<String, Int>,
|
||||
tileData: TileDataMap
|
||||
) {
|
||||
for (special in specialLuxuries) {
|
||||
val targetNumber = when (tileMap.mapParameters.mapResources) {
|
||||
MapResources.sparse -> (regions.size * 0.5f).toInt()
|
||||
MapResources.abundant -> (regions.size * 0.9f).toInt()
|
||||
else -> (regions.size * 0.75f).toInt()
|
||||
}
|
||||
val numberToPlace = max(2, targetNumber - placedSpecials[special.name]!!)
|
||||
MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData, special, numberToPlace, tileMap.values.asSequence().shuffled(), 1f,
|
||||
true, 6, 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addExtraLuxuryToStarts(
|
||||
tileMap: TileMap,
|
||||
regions: ArrayList<Region>,
|
||||
randomLuxuries: List<String>,
|
||||
specialLuxuries: List<TileResource>,
|
||||
cityStateLuxuries: List<String>,
|
||||
tileData: TileDataMap,
|
||||
ruleset: Ruleset,
|
||||
placedSpecials: HashMap<String, Int>
|
||||
) {
|
||||
if (tileMap.mapParameters.mapResources == MapResources.sparse) return
|
||||
for (region in regions) {
|
||||
val tilesToCheck = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
||||
val candidateLuxuries = randomLuxuries.shuffled().toMutableList()
|
||||
if (tileMap.mapParameters.mapResources != MapResources.strategicBalance)
|
||||
candidateLuxuries += specialLuxuries.shuffled()
|
||||
.map { it.name } // Include marble!
|
||||
candidateLuxuries += cityStateLuxuries.shuffled()
|
||||
candidateLuxuries += regions.mapNotNull { it.luxury }.shuffled()
|
||||
for (luxury in candidateLuxuries) {
|
||||
if (MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData,
|
||||
ruleset.tileResources[luxury]!!,
|
||||
1,
|
||||
tilesToCheck
|
||||
) > 0
|
||||
) {
|
||||
if (placedSpecials.containsKey(luxury)) // Keep track of marble-type specials as they may be placed now.
|
||||
placedSpecials[luxury] = placedSpecials[luxury]!! + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addRandomLuxuries(
|
||||
randomLuxuries: List<String>,
|
||||
tileData: TileDataMap,
|
||||
tileMap: TileMap,
|
||||
regions: ArrayList<Region>,
|
||||
ruleset: Ruleset
|
||||
) {
|
||||
if (randomLuxuries.isEmpty()) return
|
||||
var targetRandomLuxuries = tileData.size.toFloat().pow(0.45f).toInt() // Approximately
|
||||
targetRandomLuxuries *= when (tileMap.mapParameters.mapResources) {
|
||||
MapResources.sparse -> 80
|
||||
MapResources.abundant -> 133
|
||||
else -> 100
|
||||
}
|
||||
targetRandomLuxuries /= 100
|
||||
targetRandomLuxuries += Random.nextInt(regions.size) // Add random number based on number of civs
|
||||
val minimumRandomLuxuries = tileData.size.toFloat().pow(0.2f).toInt() // Approximately
|
||||
val worldTiles = tileMap.values.asSequence().shuffled()
|
||||
for ((index, luxury) in randomLuxuries.shuffled().withIndex()) {
|
||||
val targetForThisLuxury = if (randomLuxuries.size > 8) targetRandomLuxuries / 10
|
||||
else {
|
||||
val minimum = max(3, minimumRandomLuxuries - index)
|
||||
max(
|
||||
minimum,
|
||||
(targetRandomLuxuries * MapRegions.randomLuxuryRatios[randomLuxuries.size]!![index] + 0.5f).toInt()
|
||||
)
|
||||
}
|
||||
MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData,
|
||||
ruleset.tileResources[luxury]!!,
|
||||
targetForThisLuxury,
|
||||
worldTiles,
|
||||
0.25f,
|
||||
true,
|
||||
4,
|
||||
2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addRegionalLuxuries(
|
||||
tileData: TileDataMap,
|
||||
regions: ArrayList<Region>,
|
||||
tileMap: TileMap,
|
||||
ruleset: Ruleset
|
||||
) {
|
||||
val idealCivsForMapSize = max(2, tileData.size / 500)
|
||||
var regionTargetNumber =
|
||||
(tileData.size / 600) - (0.3f * abs(regions.size - idealCivsForMapSize)).toInt()
|
||||
regionTargetNumber += when (tileMap.mapParameters.mapResources) {
|
||||
MapResources.abundant -> 1
|
||||
MapResources.sparse -> -1
|
||||
else -> 0
|
||||
}
|
||||
regionTargetNumber = max(1, regionTargetNumber)
|
||||
for (region in regions) {
|
||||
val resource = ruleset.tileResources[region.luxury] ?: continue
|
||||
fun Tile.isShoreOfContinent(continent: Int) =
|
||||
isWater && neighbors.any { it.getContinent() == continent }
|
||||
|
||||
val candidates = if (isWaterOnlyResource(resource, ruleset))
|
||||
tileMap.getTilesInRectangle(region.rect)
|
||||
.filter { it.isShoreOfContinent(region.continentID) }
|
||||
else region.tiles.asSequence()
|
||||
MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData,
|
||||
resource,
|
||||
regionTargetNumber,
|
||||
candidates.shuffled(),
|
||||
0.4f,
|
||||
true,
|
||||
4,
|
||||
2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun placeLuxuriesAtMinorCivStartLocations(
|
||||
tileMap: TileMap,
|
||||
ruleset: Ruleset,
|
||||
regions: ArrayList<Region>,
|
||||
randomLuxuries: List<String>,
|
||||
cityStateLuxuries: List<String>,
|
||||
tileData: TileDataMap
|
||||
) {
|
||||
for (startLocation in tileMap.startingLocationsByNation
|
||||
.filterKeys { ruleset.nations[it]!!.isCityState }.map { it.value.first() }) {
|
||||
val region = regions.firstOrNull { startLocation in it.tiles }
|
||||
val tilesToCheck = startLocation.getTilesInDistanceRange(1..2)
|
||||
// 75% probability that we first attempt to place a "city state" luxury, then a random or regional one
|
||||
// 25% probability of going the other way around
|
||||
val globalLuxuries =
|
||||
if (region?.luxury != null) randomLuxuries + listOf(region.luxury) else randomLuxuries
|
||||
val candidateLuxuries = if (Random.nextInt(100) >= 25)
|
||||
cityStateLuxuries.shuffled() + globalLuxuries.shuffled()
|
||||
else
|
||||
globalLuxuries.shuffled() + cityStateLuxuries.shuffled()
|
||||
// Now try adding one until we are successful
|
||||
for (luxury in candidateLuxuries) {
|
||||
if (MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData,
|
||||
ruleset.tileResources[luxury]!!,
|
||||
1,
|
||||
tilesToCheck
|
||||
) > 0
|
||||
) break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun placeLuxuriesAtMajorCivStartLocations(
|
||||
regions: ArrayList<Region>,
|
||||
tileMap: TileMap,
|
||||
ruleset: Ruleset,
|
||||
tileData: TileDataMap,
|
||||
randomLuxuries: List<String>
|
||||
) {
|
||||
val averageFertilityDensity =
|
||||
regions.sumOf { it.totalFertility } / regions.sumOf { it.tiles.size }.toFloat()
|
||||
for (region in regions) {
|
||||
var targetLuxuries = 1
|
||||
if (tileMap.mapParameters.mapResources == MapResources.legendaryStart)
|
||||
targetLuxuries++
|
||||
if (region.totalFertility / region.tiles.size.toFloat() < averageFertilityDensity) {
|
||||
targetLuxuries++
|
||||
}
|
||||
|
||||
val luxuryToPlace = ruleset.tileResources[region.luxury] ?: continue
|
||||
// First check 2 inner rings
|
||||
val firstPass = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
||||
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
||||
targetLuxuries -= MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData,
|
||||
luxuryToPlace,
|
||||
targetLuxuries,
|
||||
firstPass,
|
||||
0.5f
|
||||
) // Skip every 2nd tile on first pass
|
||||
|
||||
if (targetLuxuries > 0) {
|
||||
val secondPass = firstPass + tileMap[region.startPosition!!].getTilesAtDistance(3)
|
||||
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
||||
targetLuxuries -= MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData,
|
||||
luxuryToPlace,
|
||||
targetLuxuries,
|
||||
secondPass
|
||||
)
|
||||
}
|
||||
if (targetLuxuries > 0) {
|
||||
// Try adding in 1 luxury from the random rotation as compensation
|
||||
for (luxury in randomLuxuries) {
|
||||
if (MapRegionResources.tryAddingResourceToTiles(
|
||||
tileData, ruleset.tileResources[luxury]!!, 1, firstPass) > 0
|
||||
) break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import com.unciv.utils.Tag
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -660,7 +659,7 @@ class MapRegions (val ruleset: Ruleset){
|
||||
|
||||
val (cityStateLuxuries, randomLuxuries) = LuxuryResourcePlacementLogic.assignLuxuries(regions, tileData, ruleset)
|
||||
MinorCivPlacer.placeMinorCivs(regions, tileMap, minorCivs, usingArchipelagoRegions, tileData, ruleset)
|
||||
placeLuxuries(tileMap, cityStateLuxuries, randomLuxuries)
|
||||
LuxuryResourcePlacementLogic.placeLuxuries(regions, tileMap, tileData, ruleset, cityStateLuxuries, randomLuxuries)
|
||||
placeStrategicAndBonuses(tileMap)
|
||||
}
|
||||
|
||||
@ -675,137 +674,6 @@ class MapRegions (val ruleset: Ruleset){
|
||||
}
|
||||
|
||||
|
||||
/** Places all Luxuries onto [tileMap]. Assumes that assignLuxuries and placeMinorCivs have been called. */
|
||||
private fun placeLuxuries(
|
||||
tileMap: TileMap,
|
||||
cityStateLuxuries: List<String>,
|
||||
randomLuxuries: List<String>
|
||||
) {
|
||||
|
||||
// First place luxuries at major civ start locations
|
||||
val averageFertilityDensity = regions.sumOf { it.totalFertility } / regions.sumOf { it.tiles.size }.toFloat()
|
||||
for (region in regions) {
|
||||
var targetLuxuries = 1
|
||||
if (tileMap.mapParameters.mapResources == MapResources.legendaryStart)
|
||||
targetLuxuries++
|
||||
if (region.totalFertility / region.tiles.size.toFloat() < averageFertilityDensity) {
|
||||
targetLuxuries++
|
||||
}
|
||||
|
||||
val luxuryToPlace = ruleset.tileResources[region.luxury] ?: continue
|
||||
// First check 2 inner rings
|
||||
val firstPass = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
||||
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
||||
targetLuxuries -= MapRegionResources.tryAddingResourceToTiles(tileData, luxuryToPlace, targetLuxuries, firstPass, 0.5f) // Skip every 2nd tile on first pass
|
||||
|
||||
if (targetLuxuries > 0) {
|
||||
val secondPass = firstPass + tileMap[region.startPosition!!].getTilesAtDistance(3)
|
||||
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
||||
targetLuxuries -= MapRegionResources.tryAddingResourceToTiles(tileData, luxuryToPlace, targetLuxuries, secondPass)
|
||||
}
|
||||
if (targetLuxuries > 0) {
|
||||
// Try adding in 1 luxury from the random rotation as compensation
|
||||
for (luxury in randomLuxuries) {
|
||||
if (MapRegionResources.tryAddingResourceToTiles(tileData, ruleset.tileResources[luxury]!!, 1, firstPass) > 0) break
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second place one (1) luxury at minor civ start locations
|
||||
// Check only ones that got a start location
|
||||
for (startLocation in tileMap.startingLocationsByNation
|
||||
.filterKeys { ruleset.nations[it]!!.isCityState }.map { it.value.first() }) {
|
||||
val region = regions.firstOrNull { startLocation in it.tiles }
|
||||
val tilesToCheck = startLocation.getTilesInDistanceRange(1..2)
|
||||
// 75% probability that we first attempt to place a "city state" luxury, then a random or regional one
|
||||
// 25% probability of going the other way around
|
||||
val globalLuxuries = if (region?.luxury != null) randomLuxuries + listOf(region.luxury) else randomLuxuries
|
||||
val candidateLuxuries = if (Random.nextInt(100) >= 25)
|
||||
cityStateLuxuries.shuffled() + globalLuxuries.shuffled()
|
||||
else
|
||||
globalLuxuries.shuffled() + cityStateLuxuries.shuffled()
|
||||
// Now try adding one until we are successful
|
||||
for (luxury in candidateLuxuries) {
|
||||
if (MapRegionResources.tryAddingResourceToTiles(tileData, ruleset.tileResources[luxury]!!, 1, tilesToCheck) > 0) break
|
||||
}
|
||||
}
|
||||
// Third add regional luxuries
|
||||
// The target number depends on map size and how close we are to an "ideal number" of civs for the map
|
||||
val idealCivs = max(2, tileData.size / 500)
|
||||
var regionTargetNumber = (tileData.size / 600) - (0.3f * abs(regions.size - idealCivs)).toInt()
|
||||
regionTargetNumber += when (tileMap.mapParameters.mapResources) {
|
||||
MapResources.abundant -> 1
|
||||
MapResources.sparse -> -1
|
||||
else -> 0
|
||||
}
|
||||
regionTargetNumber = max(1, regionTargetNumber)
|
||||
for (region in regions) {
|
||||
val resource = ruleset.tileResources[region.luxury] ?: continue
|
||||
fun Tile.isShoreOfContinent(continent: Int) = isWater && neighbors.any { it.getContinent() == continent }
|
||||
val candidates = if (isWaterOnlyResource(resource, ruleset))
|
||||
tileMap.getTilesInRectangle(region.rect).filter { it.isShoreOfContinent(region.continentID) }
|
||||
else region.tiles.asSequence()
|
||||
MapRegionResources.tryAddingResourceToTiles(tileData, resource, regionTargetNumber, candidates.shuffled(), 0.4f, true, 4, 2)
|
||||
}
|
||||
// Fourth add random luxuries
|
||||
if (randomLuxuries.isNotEmpty()) {
|
||||
var targetRandomLuxuries = tileData.size.toFloat().pow(0.45f).toInt() // Approximately
|
||||
targetRandomLuxuries *= when (tileMap.mapParameters.mapResources) {
|
||||
MapResources.sparse -> 80
|
||||
MapResources.abundant -> 133
|
||||
else -> 100
|
||||
}
|
||||
targetRandomLuxuries /= 100
|
||||
targetRandomLuxuries += Random.nextInt(regions.size) // Add random number based on number of civs
|
||||
val minimumRandomLuxuries = tileData.size.toFloat().pow(0.2f).toInt() // Approximately
|
||||
val worldTiles = tileMap.values.asSequence().shuffled()
|
||||
for ((index, luxury) in randomLuxuries.shuffled().withIndex()) {
|
||||
val targetForThisLuxury = if (randomLuxuries.size > 8) targetRandomLuxuries / 10
|
||||
else {
|
||||
val minimum = max(3, minimumRandomLuxuries - index)
|
||||
max(minimum, (targetRandomLuxuries * randomLuxuryRatios[randomLuxuries.size]!![index] + 0.5f).toInt())
|
||||
}
|
||||
MapRegionResources.tryAddingResourceToTiles(tileData, ruleset.tileResources[luxury]!!, targetForThisLuxury, worldTiles, 0.25f,
|
||||
true, 4, 2)
|
||||
}
|
||||
}
|
||||
val specialLuxuries = ruleset.tileResources.values.filter {
|
||||
it.resourceType == ResourceType.Luxury &&
|
||||
it.hasUnique(UniqueType.LuxurySpecialPlacement)
|
||||
}
|
||||
val placedSpecials = HashMap<String, Int>()
|
||||
specialLuxuries.forEach { placedSpecials[it.name] = 0 } // init map
|
||||
|
||||
// Fifth, on resource settings other than sparse, add an extra luxury to starts
|
||||
if (tileMap.mapParameters.mapResources != MapResources.sparse) {
|
||||
for (region in regions) {
|
||||
val tilesToCheck = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
||||
val candidateLuxuries = randomLuxuries.shuffled().toMutableList()
|
||||
if (tileMap.mapParameters.mapResources != MapResources.strategicBalance)
|
||||
candidateLuxuries += specialLuxuries.shuffled().map { it.name } // Include marble!
|
||||
candidateLuxuries += cityStateLuxuries.shuffled()
|
||||
candidateLuxuries += regions.mapNotNull { it.luxury }.shuffled()
|
||||
for (luxury in candidateLuxuries) {
|
||||
if (MapRegionResources.tryAddingResourceToTiles(tileData, ruleset.tileResources[luxury]!!, 1, tilesToCheck) > 0) {
|
||||
if (placedSpecials.containsKey(luxury)) // Keep track of marble-type specials as they may be placed now.
|
||||
placedSpecials[luxury] = placedSpecials[luxury]!! + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sixth, top up marble-type specials if needed
|
||||
for (special in specialLuxuries) {
|
||||
val targetNumber = when (tileMap.mapParameters.mapResources) {
|
||||
MapResources.sparse -> (regions.size * 0.5f).toInt()
|
||||
MapResources.abundant -> (regions.size * 0.9f).toInt()
|
||||
else -> (regions.size * 0.75f).toInt()
|
||||
}
|
||||
val numberToPlace = max(2, targetNumber - placedSpecials[special.name]!!)
|
||||
MapRegionResources.tryAddingResourceToTiles(tileData, special, numberToPlace, tileMap.values.asSequence().shuffled(), 1f,
|
||||
true, 6, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun placeStrategicAndBonuses(tileMap: TileMap) {
|
||||
val strategicResources = ruleset.tileResources.values.filter { it.resourceType == ResourceType.Strategic }
|
||||
// As usual, if there are any relevant json definitions, assume they are complete
|
||||
|
Loading…
Reference in New Issue
Block a user