diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index 0588728a15..c281842665 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -23,7 +23,6 @@ import com.unciv.utils.debug import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.isActive import kotlin.math.abs -import kotlin.math.max import kotlin.math.pow import kotlin.math.roundToInt import kotlin.math.sign @@ -342,8 +341,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc // remove the tiles where previous resources have been placed val suitableTiles = candidateTiles .filterNot { it.baseTerrain == Constants.snow && it.isHill() } - .filter { it.resource == null - && resource.terrainsCanBeFoundOn.contains(it.lastTerrain.name) } + .filter { it.resource == null && resource.generatesNaturallyOn(it) } val locations = randomness.chooseSpreadOutLocations(resourcesPerType, suitableTiles, mapRadius) @@ -361,8 +359,10 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc val suitableTiles = tileMap.values .filterNot { it.baseTerrain == Constants.snow && it.isHill() } - .filter { it.resource == null && it.neighbors.none { neighbor -> neighbor.isNaturalWonder() } - && resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.lastTerrain.name) } } + .filter { it.resource == null + && it.neighbors.none { neighbor -> neighbor.isNaturalWonder() } + && resourcesOfType.any { r -> r.generatesNaturallyOn(it) } + } val numberOfResources = tileMap.values.count { it.isLand && !it.isImpassible() } * tileMap.mapParameters.resourceRichness val locations = randomness.chooseSpreadOutLocations(numberOfResources.toInt(), suitableTiles, mapRadius) @@ -370,8 +370,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc val resourceToNumber = Counter() for (tile in locations) { - val possibleResources = resourcesOfType - .filter { it.terrainsCanBeFoundOn.contains(tile.lastTerrain.name) } + val possibleResources = resourcesOfType.filter { it.generatesNaturallyOn(tile) } if (possibleResources.isEmpty()) continue val resourceWithLeastAssignments = possibleResources.minByOrNull { resourceToNumber[it.name] }!! resourceToNumber.add(resourceWithLeastAssignments.name, 1) diff --git a/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegionResources.kt b/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegionResources.kt index b2e3a13d55..c49ec2423b 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegionResources.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegionResources.kt @@ -30,14 +30,11 @@ object MapRegionResources { ResourceType.Luxury -> MapRegions.ImpactType.Luxury } val conditionalTerrain = StateForConditionals(attackedTile = tileList.firstOrNull()) - val weightings = resourceOptions.map { + val weightings = resourceOptions.associateWith { val unique = it.getMatchingUniques(UniqueType.ResourceWeighting, conditionalTerrain).firstOrNull() - if (unique != null) - unique.params[0].toFloat() - else - 1f + val weight = if (unique != null) unique.params[0].toFloat() else 1f + weight } - val testTerrains = (resourceOptions.size == 1) && !forcePlacement val amountToPlace = (tileList.size / frequency) + 1 var amountPlaced = 0 val detailedPlaced = HashMap() @@ -45,17 +42,15 @@ object MapRegionResources { val fallbackTiles = ArrayList() // First pass - avoid impacts entirely for (tile in tileList) { - if (tile.resource != null || - (testTerrains && - (tile.lastTerrain.name !in resourceOptions.first().terrainsCanBeFoundOn || - resourceOptions.first().hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain)) ) || - tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain)) - continue // Can't place here, can't be a fallback tile + if (tile.resource != null) continue + val possibleResourcesForTile = resourceOptions.filter { it.generatesNaturallyOn(tile) } + if (possibleResourcesForTile.isEmpty()) continue + if (tileData[tile.position]!!.impacts.containsKey(impactType)) { fallbackTiles.add(tile) // Taken but might be a viable fallback tile } else { // Add a resource to the tile - val resourceToPlace = resourceOptions.randomWeighted(weightings) + val resourceToPlace = possibleResourcesForTile.randomWeighted(possibleResourcesForTile.map { weightings[it] ?: 0f }) tile.setTileResource(resourceToPlace, majorDeposit) tileData.placeImpact(impactType, tile, baseImpact + Random.nextInt(randomImpact + 1)) amountPlaced++ @@ -70,7 +65,8 @@ object MapRegionResources { // Sorry, we do need to re-sort the list for every pass since new impacts are made with every placement val bestTile = fallbackTiles.minByOrNull { tileData[it.position]!!.impacts[impactType]!! }!! fallbackTiles.remove(bestTile) - val resourceToPlace = resourceOptions.randomWeighted(weightings) + val possibleResourcesForTile = resourceOptions.filter { it.generatesNaturallyOn(bestTile) } + val resourceToPlace = possibleResourcesForTile.randomWeighted(possibleResourcesForTile.map { weightings[it] ?: 0f }) bestTile.setTileResource(resourceToPlace, majorDeposit) tileData.placeImpact(impactType, bestTile, baseImpact + Random.nextInt(randomImpact + 1)) amountPlaced++ @@ -95,17 +91,7 @@ object MapRegionResources { } for (tile in tiles) { - val conditionalTerrain = StateForConditionals(attackedTile = tile) - if (tile.resource == null && - tile.lastTerrain.name in resource.terrainsCanBeFoundOn && - !tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain) && - !resource.hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain) && - - resource.getMatchingUniques(UniqueType.TileGenerationConditions).none { - tile.temperature!! !in it.params[0].toDouble() .. it.params[1].toDouble() - || tile.humidity!! !in it.params[2].toDouble() .. it.params[3].toDouble() - } - ) { + if (tile.resource == null && resource.generatesNaturallyOn(tile)) { if (ratioProgress >= 1f && !(respectImpacts && tileData[tile.position]!!.impacts.containsKey(impactType))) { tile.setTileResource(resource, majorDeposit) diff --git a/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegions.kt b/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegions.kt index a121285eb8..c5c781930a 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegions.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/mapregions/MapRegions.kt @@ -792,7 +792,7 @@ class MapRegions (val ruleset: Ruleset) { continue val weightings = strategicResources.map { if (fallbackStrategic) { - if (tile.lastTerrain.name in it.terrainsCanBeFoundOn) 1f else 0f + if (it.generatesNaturallyOn(tile)) 1f else 0f } else { val uniques = it.getMatchingUniques(UniqueType.MinorDepositWeighting, conditionalTerrain).toList() uniques.sumOf { unique -> unique.params[0].toInt() }.toFloat() @@ -822,10 +822,10 @@ class MapRegions (val ruleset: Ruleset) { for (resource in strategicResources) { val extraNeeded = min(2, regions.size - totalPlaced[resource]!!) if (extraNeeded > 0) { - if (isWaterOnlyResource(resource, ruleset)) - MapRegionResources.tryAddingResourceToTiles(tileData, resource, extraNeeded, tileMap.values.asSequence().filter { it.isWater }.shuffled(), respectImpacts = true) - else - MapRegionResources.tryAddingResourceToTiles(tileData, resource, extraNeeded, landList.asSequence(), respectImpacts = true) + val tilesToAddTo = if (!isWaterOnlyResource(resource, ruleset)) landList.asSequence() + else tileMap.values.asSequence().filter { it.isWater }.shuffled() + + MapRegionResources.tryAddingResourceToTiles(tileData, resource, extraNeeded, tilesToAddTo, respectImpacts = true) } } diff --git a/core/src/com/unciv/logic/map/mapgenerator/mapregions/StartNormalizer.kt b/core/src/com/unciv/logic/map/mapgenerator/mapregions/StartNormalizer.kt index 814b9b05b1..d7c6ea3b33 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/mapregions/StartNormalizer.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/mapregions/StartNormalizer.kt @@ -162,9 +162,7 @@ object StartNormalizer { if (plot.resource != null) continue - val bonusToPlace = - productionBonuses.filter { plot.lastTerrain.name in it.terrainsCanBeFoundOn } - .randomOrNull() + val bonusToPlace = productionBonuses.filter { it.generatesNaturallyOn(plot) }.randomOrNull() if (bonusToPlace != null) { plot.resource = bonusToPlace.name productionBonusesNeeded-- @@ -280,7 +278,7 @@ object StartNormalizer { val validBonuses = ruleset.tileResources.values.filter { it.resourceType == ResourceType.Bonus && it.food >= 1 && - plot.lastTerrain.name in it.terrainsCanBeFoundOn + it.generatesNaturallyOn(plot) } val goodPlotForOasis = canPlaceOasis && plot.lastTerrain.name in oasisEquivalent!!.occursOn diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index 48bc973fb6..40ce598965 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -380,7 +380,7 @@ open class Tile : IsPartOfGameInfoSerialization { return civInfo.isAtWarWith(tileOwner) } - fun isRoughTerrain() = allTerrains.any{ it.isRough() } + fun isRoughTerrain() = allTerrains.any { it.isRough() } /** Checks whether any of the TERRAINS of this tile has a certain unique */ fun terrainHasUnique(uniqueType: UniqueType) = terrainUniqueMap.getUniques(uniqueType).any() diff --git a/core/src/com/unciv/models/ruleset/tile/Terrain.kt b/core/src/com/unciv/models/ruleset/tile/Terrain.kt index 68ed670fc8..8694467b66 100644 --- a/core/src/com/unciv/models/ruleset/tile/Terrain.kt +++ b/core/src/com/unciv/models/ruleset/tile/Terrain.kt @@ -2,6 +2,7 @@ package com.unciv.models.ruleset.tile import com.badlogic.gdx.graphics.Color import com.unciv.Constants +import com.unciv.logic.map.tile.Tile import com.unciv.models.ruleset.Belief import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetStatsObject @@ -143,6 +144,10 @@ class Terrain : RulesetStatsObject() { return textList } + fun canBePlacedOn(tile: Tile){ + + } + fun setTransients() { damagePerTurn = getMatchingUniques(UniqueType.DamagesContainingUnits).sumOf { it.params[0].toInt() } } diff --git a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt index 7288918a7d..8db1280ef4 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt @@ -207,7 +207,7 @@ class TileImprovement : RulesetStatsObject() { val cannotFilters = getMatchingUniques(UniqueType.CannotBuildOnTile).map { it.params[0] }.toSet() val resourcesImprovedByThis = ruleset.tileResources.values.filter { it.isImprovedBy(name) } - val expandedCanBeBuiltOn = sequence { + val expandedTerrainsCanBeBuiltOn = sequence { yieldAll(terrainsCanBeBuiltOn) yieldAll(terrainsCanBeBuiltOn.asSequence().mapNotNull { ruleset.terrains[it] }.flatMap { it.occursOn.asSequence() }) if (hasUnique(UniqueType.CanOnlyImproveResource)) @@ -220,21 +220,21 @@ class TileImprovement : RulesetStatsObject() { }.filter { it !in cannotFilters }.toMutableSet() val terrainsCanBeBuiltOnTypes = sequence { - yieldAll(expandedCanBeBuiltOn.asSequence() + yieldAll(expandedTerrainsCanBeBuiltOn.asSequence() .mapNotNull { ruleset.terrains[it]?.type }) yieldAll(TerrainType.values().asSequence() - .filter { it.name in expandedCanBeBuiltOn }) + .filter { it.name in expandedTerrainsCanBeBuiltOn }) }.filter { it.name !in cannotFilters }.toMutableSet() - if (canOnlyFilters.isNotEmpty() && canOnlyFilters.intersect(expandedCanBeBuiltOn).isEmpty()) { - expandedCanBeBuiltOn.clear() + if (canOnlyFilters.isNotEmpty() && canOnlyFilters.intersect(expandedTerrainsCanBeBuiltOn).isEmpty()) { + expandedTerrainsCanBeBuiltOn.clear() if (terrainsCanBeBuiltOnTypes.none { it.name in canOnlyFilters }) terrainsCanBeBuiltOnTypes.clear() } fun matchesBuildImprovementsFilter(filter: String) = matchesFilter(filter) || - filter in expandedCanBeBuiltOn || + filter in expandedTerrainsCanBeBuiltOn || terrainsCanBeBuiltOnTypes.any { it.name == filter } return ruleset.units.values.asSequence() diff --git a/core/src/com/unciv/models/ruleset/tile/TileResource.kt b/core/src/com/unciv/models/ruleset/tile/TileResource.kt index b5e62eadf7..7996e9e224 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileResource.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileResource.kt @@ -50,9 +50,8 @@ class TileResource : RulesetStatsObject() { if (terrainsCanBeFoundOn.isNotEmpty()) { textList += FormattedLine() if (terrainsCanBeFoundOn.size == 1) { - with (terrainsCanBeFoundOn[0]) { - textList += FormattedLine("{Can be found on} {$this}", link = "Terrain/$this") - } + val terrainName = terrainsCanBeFoundOn[0] + textList += FormattedLine("{Can be found on} {$terrainName}", link = "Terrain/$terrainName") } else { textList += FormattedLine("{Can be found on}:") terrainsCanBeFoundOn.forEach { @@ -142,6 +141,21 @@ class TileResource : RulesetStatsObject() { } } + fun generatesNaturallyOn(tile:Tile): Boolean { + if (tile.lastTerrain.name !in terrainsCanBeFoundOn) return false + val stateForConditionals = StateForConditionals(tile = tile) + if (hasUnique(UniqueType.NoNaturalGeneration, stateForConditionals)) return false + if (tile.allTerrains.any { it.hasUnique(UniqueType.BlocksResources, stateForConditionals) }) return false + + if (tile.temperature!=null && tile.humidity!=null) // Only works when in map generation + for (unique in getMatchingUniques(UniqueType.TileGenerationConditions, stateForConditionals)){ + if (tile.temperature!! !in unique.params[0].toDouble() .. unique.params[1].toDouble()) return false + if (tile.humidity!! !in unique.params[2].toDouble() .. unique.params[3].toDouble()) return false + } + + return true + } + fun isStockpiled() = hasUnique(UniqueType.Stockpiled) class DepositAmount { @@ -149,5 +163,4 @@ class TileResource : RulesetStatsObject() { var default: Int = 2 var abundant: Int = 3 } - } diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index fb68c67581..1e51a0c8fa 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -790,9 +790,6 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Global, Unit -??? example "+30% Strength when fighting City-State units and cities" - Applicable to: Global - ??? example "[amount] additional attacks per turn" Example: "[3] additional attacks per turn" @@ -2026,6 +2023,11 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Conditional +??? example "<vs [combatantFilter]>" + Example: "<vs [City]>" + + Applicable to: Conditional + ??? example "<when fighting units from a Civilization with more Cities than you>" Applicable to: Conditional @@ -2066,23 +2068,23 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Conditional -??? example "<with [amount] to [amount] neighboring [tileFilter] [tileFilter] tiles>" - Example: "<with [3] to [3] neighboring [Farm] [Farm] tiles>" - - Applicable to: Conditional - ??? example "<in [tileFilter] tiles>" Example: "<in [Farm] tiles>" Applicable to: Conditional -??? example "<in [tileFilter] [tileFilter] tiles>" - Example: "<in [Farm] [Farm] tiles>" +??? example "<in tiles without [tileFilter]>" + Example: "<in tiles without [Farm]>" Applicable to: Conditional -??? example "<in tiles without [tileFilter]>" - Example: "<in tiles without [Farm]>" +??? example "<in tiles adjacent to [tileFilter]>" + Example: "<in tiles adjacent to [Farm]>" + + Applicable to: Conditional + +??? example "<in tiles not adjacent to [tileFilter]>" + Example: "<in tiles not adjacent to [Farm]>" Applicable to: Conditional