mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Minimum city distance across continents (#5404)
This commit is contained in:
@ -2,10 +2,7 @@ package com.unciv
|
||||
|
||||
object Constants {
|
||||
const val worker = "Worker"
|
||||
const val canBuildImprovements = "Can build [] improvements on tiles"
|
||||
const val workBoatsUnique = "May create improvements on water resources"
|
||||
const val settler = "Settler"
|
||||
const val settlerUnique = "Founds a new city"
|
||||
const val eraSpecificUnit = "Era Starting Unit"
|
||||
const val spreadReligionAbilityCount = "Spread Religion"
|
||||
const val removeHeresyAbilityCount = "Remove Foreign religions from your own cities"
|
||||
|
@ -262,7 +262,7 @@ object GameStarter {
|
||||
val startingLocations = getStartingLocations(allCivs, tileMap, landTilesInBigEnoughGroup, startScores)
|
||||
|
||||
val settlerLikeUnits = ruleSet.units.filter {
|
||||
it.value.uniqueObjects.any { unique -> unique.placeholderText == Constants.settlerUnique }
|
||||
it.value.hasUnique(UniqueType.FoundCity)
|
||||
}
|
||||
|
||||
// no starting units for Barbarians and Spectators
|
||||
@ -315,9 +315,8 @@ object GameStarter {
|
||||
}
|
||||
if (unit == "Worker" && "Worker" !in ruleSet.units) {
|
||||
val buildableWorkerLikeUnits = ruleSet.units.filter {
|
||||
it.value.uniqueObjects.any { unique -> unique.placeholderText == Constants.canBuildImprovements }
|
||||
&& it.value.isBuildable(civ)
|
||||
&& it.value.isCivilian()
|
||||
it.value.hasUnique(UniqueType.BuildImprovements) &&
|
||||
it.value.isBuildable(civ) && it.value.isCivilian()
|
||||
}
|
||||
if (buildableWorkerLikeUnits.isEmpty()) return null // No workers in this mod
|
||||
return civ.getEquivalentUnit(buildableWorkerLikeUnits.keys.random()).name
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
@ -10,8 +9,8 @@ import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.VictoryType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.equalsPlaceholderText
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -105,7 +104,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this
|
||||
|
||||
val civilianUnit = cityInfo.getCenterTile().civilianUnit
|
||||
if (civilianUnit != null && civilianUnit.hasUnique(Constants.settlerUnique)
|
||||
if (civilianUnit != null && civilianUnit.hasUnique(UniqueType.FoundCity)
|
||||
&& cityInfo.getCenterTile().getTilesInDistance(5).none { it.militaryUnit?.civInfo == civInfo })
|
||||
modifier = 5f // there's a settler just sitting here, doing nothing - BAD
|
||||
|
||||
@ -115,10 +114,10 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
private fun addWorkBoatChoice() {
|
||||
val buildableWorkboatUnits = cityInfo.cityConstructions.getConstructableUnits()
|
||||
.filter { it.uniques.contains(Constants.workBoatsUnique)
|
||||
.filter { it.hasUnique(UniqueType.CreateWaterImprovements)
|
||||
&& Automation.allowSpendingResource(civInfo, it) }
|
||||
val canBuildWorkboat = buildableWorkboatUnits.any()
|
||||
&& !cityInfo.getTiles().any { it.civilianUnit?.hasUnique(Constants.workBoatsUnique) == true }
|
||||
&& !cityInfo.getTiles().any { it.civilianUnit?.hasUnique(UniqueType.CreateWaterImprovements) == true }
|
||||
if (!canBuildWorkboat) return
|
||||
val tilesThatNeedWorkboat = cityInfo.getTiles()
|
||||
.filter { it.isWater && it.hasViewableResource(civInfo) && it.improvement == null }.toList()
|
||||
@ -139,9 +138,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
private fun addWorkerChoice() {
|
||||
val workerEquivalents = civInfo.gameInfo.ruleSet.units.values
|
||||
.filter { it.uniques.any {
|
||||
unique -> unique.equalsPlaceholderText(Constants.canBuildImprovements)
|
||||
} && it.isBuildable(cityConstructions)
|
||||
.filter {
|
||||
it.hasUnique(UniqueType.BuildImprovements)
|
||||
&& it.isBuildable(cityConstructions)
|
||||
&& Automation.allowSpendingResource(civInfo, it) }
|
||||
if (workerEquivalents.isEmpty()) return // for mods with no worker units
|
||||
if (civInfo.getIdleUnits().any { it.isAutomated() && it.hasUniqueToBuildImprovements })
|
||||
|
@ -689,12 +689,12 @@ object NextTurnAutomation {
|
||||
if (civInfo.cities.none() || civInfo.getHappiness() <= civInfo.cities.size + 5) return
|
||||
|
||||
val settlerUnits = civInfo.gameInfo.ruleSet.units.values
|
||||
.filter { it.uniques.contains(Constants.settlerUnique) && it.isBuildable(civInfo) }
|
||||
.filter { it.hasUnique(UniqueType.FoundCity) && it.isBuildable(civInfo) }
|
||||
if (settlerUnits.isEmpty()) return
|
||||
if (civInfo.getCivUnits().none { it.hasUnique(Constants.settlerUnique) }
|
||||
if (civInfo.getCivUnits().none { it.hasUnique(UniqueType.FoundCity) }
|
||||
&& civInfo.cities.none {
|
||||
val currentConstruction = it.cityConstructions.getCurrentConstruction()
|
||||
currentConstruction is BaseUnit && currentConstruction.uniques.contains(Constants.settlerUnique)
|
||||
currentConstruction is BaseUnit && currentConstruction.hasUnique(UniqueType.FoundCity)
|
||||
}) {
|
||||
|
||||
val bestCity = civInfo.cities.maxByOrNull { it.cityStats.currentCityStats.production }!!
|
||||
|
@ -159,17 +159,23 @@ object SpecificUnitAutomation {
|
||||
&& !unit.civInfo.isCityState() // ..unless you're a city state that was unable to settle its city on turn 1
|
||||
&& unit.getDamageFromTerrain() < unit.health) return // Also make sure we won't die waiting
|
||||
|
||||
val tilesNearCities = unit.civInfo.gameInfo.getCities().asSequence()
|
||||
.flatMap {
|
||||
val distanceAwayFromCity =
|
||||
if (unit.civInfo.knows(it.civInfo)
|
||||
val tilesNearCities = sequence {
|
||||
for (city in unit.civInfo.gameInfo.getCities()) {
|
||||
val center = city.getCenterTile()
|
||||
if (unit.civInfo.knows(city.civInfo) &&
|
||||
// If the CITY OWNER knows that the UNIT OWNER agreed not to settle near them
|
||||
&& it.civInfo.getDiplomacyManager(unit.civInfo).hasFlag(DiplomacyFlags.AgreedToNotSettleNearUs))
|
||||
6
|
||||
else 3
|
||||
it.getCenterTile().getTilesInDistance(distanceAwayFromCity)
|
||||
city.civInfo.getDiplomacyManager(unit.civInfo).hasFlag(DiplomacyFlags.AgreedToNotSettleNearUs)
|
||||
) {
|
||||
yieldAll(center.getTilesInDistance(6))
|
||||
continue
|
||||
}
|
||||
.toSet()
|
||||
for (tile in center.getTilesAtDistance(3)) {
|
||||
if (tile.getContinent() == center.getContinent())
|
||||
yield(tile)
|
||||
}
|
||||
yieldAll(center.getTilesInDistance(2))
|
||||
}
|
||||
}.toSet()
|
||||
|
||||
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
|
||||
val nearbyTileRankings = unit.getTile().getTilesInDistance(7)
|
||||
|
@ -101,7 +101,7 @@ object UnitAutomation {
|
||||
if (unit.isCivilian()) {
|
||||
if (tryRunAwayIfNeccessary(unit)) return
|
||||
|
||||
if (unit.hasUnique(Constants.settlerUnique))
|
||||
if (unit.hasUnique(UniqueType.FoundCity))
|
||||
return SpecificUnitAutomation.automateSettlerActions(unit)
|
||||
|
||||
if (unit.hasUniqueToBuildImprovements)
|
||||
@ -119,7 +119,7 @@ object UnitAutomation {
|
||||
)
|
||||
return SpecificUnitAutomation.enhanceReligion(unit)
|
||||
|
||||
if (unit.hasUnique(Constants.workBoatsUnique))
|
||||
if (unit.hasUnique(UniqueType.CreateWaterImprovements))
|
||||
return SpecificUnitAutomation.automateWorkBoats(unit)
|
||||
|
||||
if (unit.hasUnique("Bonus for units in 2 tile radius 15%"))
|
||||
@ -311,7 +311,7 @@ object UnitAutomation {
|
||||
.firstOrNull {
|
||||
val tile = it.currentTile
|
||||
it.isCivilian() &&
|
||||
(it.hasUnique(Constants.settlerUnique) || unit.isGreatPerson())
|
||||
(it.hasUnique(UniqueType.FoundCity) || unit.isGreatPerson())
|
||||
&& tile.militaryUnit == null && unit.movement.canMoveTo(tile) && unit.movement.canReach(tile)
|
||||
} ?: return false
|
||||
unit.movement.headTowards(settlerOrGreatPersonToAccompany.currentTile)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.automation.NextTurnAutomation
|
||||
import com.unciv.logic.civilization.diplomacy.*
|
||||
import com.unciv.models.metadata.GameSpeed
|
||||
@ -23,7 +22,7 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
/** Attempts to initialize the city state, returning true if successful. */
|
||||
fun initCityState(ruleset: Ruleset, startingEra: String, unusedMajorCivs: Collection<String>): Boolean {
|
||||
val cityStateType = ruleset.nations[civInfo.civName]?.cityStateType
|
||||
if (cityStateType == null) return false
|
||||
?: return false
|
||||
|
||||
val startingTechs = ruleset.technologies.values.filter { it.uniques.contains("Starting tech") }
|
||||
for (tech in startingTechs)
|
||||
@ -406,9 +405,8 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
if (!civInfo.isCityState()) throw Exception("You can only demand workers from City-States!")
|
||||
|
||||
val buildableWorkerLikeUnits = civInfo.gameInfo.ruleSet.units.filter {
|
||||
it.value.uniqueObjects.any { unique -> unique.placeholderText == Constants.canBuildImprovements }
|
||||
&& it.value.isCivilian()
|
||||
&& it.value.isBuildable(civInfo)
|
||||
it.value.hasUnique(UniqueType.BuildImprovements) &&
|
||||
it.value.isCivilian() && it.value.isBuildable(civInfo)
|
||||
}
|
||||
if (buildableWorkerLikeUnits.isEmpty()) return // Bad luck?
|
||||
demandingCiv.placeUnitNearTile(civInfo.getCapital().location, buildableWorkerLikeUnits.keys.random())
|
||||
|
@ -72,9 +72,9 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo) {
|
||||
// And so, sequences to the rescue!
|
||||
val ownedTiles = civInfo.cities.asSequence().flatMap { it.getTiles() }
|
||||
newViewableTiles.addAll(ownedTiles)
|
||||
val neighboringUnownedTiles = ownedTiles.flatMap { it.neighbors.filter { it.getOwner() != civInfo } }
|
||||
val neighboringUnownedTiles = ownedTiles.flatMap { tile -> tile.neighbors.filter { it.getOwner() != civInfo } }
|
||||
newViewableTiles.addAll(neighboringUnownedTiles)
|
||||
newViewableTiles.addAll(civInfo.getCivUnits().flatMap { it.viewableTiles.asSequence().filter { it.getOwner() != civInfo } })
|
||||
newViewableTiles.addAll(civInfo.getCivUnits().flatMap { unit -> unit.viewableTiles.asSequence().filter { it.getOwner() != civInfo } })
|
||||
|
||||
if (!civInfo.isCityState()) {
|
||||
for (otherCiv in civInfo.getKnownCivs()) {
|
||||
@ -110,9 +110,7 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo) {
|
||||
}
|
||||
|
||||
if (civInfo.hasUnique("100 Gold for discovering a Natural Wonder (bonus enhanced to 500 Gold if first to discover it)")) {
|
||||
if (!discoveredNaturalWonders.contains(tile.naturalWonder!!))
|
||||
goldGained += 500
|
||||
else goldGained += 100
|
||||
goldGained += if (discoveredNaturalWonders.contains(tile.naturalWonder!!)) 100 else 500
|
||||
}
|
||||
|
||||
if (goldGained > 0) {
|
||||
|
@ -277,7 +277,7 @@ class MapUnit {
|
||||
cannotEnterOceanTiles = hasUnique(UniqueType.CannotEnterOcean)
|
||||
cannotEnterOceanTilesUntilAstronomy = hasUnique(UniqueType.CannotEnterOceanUntilAstronomy)
|
||||
|
||||
hasUniqueToBuildImprovements = hasUnique(Constants.canBuildImprovements)
|
||||
hasUniqueToBuildImprovements = hasUnique(UniqueType.BuildImprovements)
|
||||
canEnterForeignTerrain =
|
||||
hasUnique("May enter foreign tiles without open borders, but loses [] religious strength each turn it ends there")
|
||||
|| hasUnique("May enter foreign tiles without open borders")
|
||||
@ -1051,7 +1051,7 @@ class MapUnit {
|
||||
}
|
||||
|
||||
fun canBuildImprovement(improvement: TileImprovement, tile: TileInfo = currentTile): Boolean {
|
||||
val matchingUniques = getMatchingUniques(Constants.canBuildImprovements)
|
||||
val matchingUniques = getMatchingUniques(UniqueType.BuildImprovements)
|
||||
return matchingUniques.any { improvement.matchesFilter(it.params[0]) || tile.matchesTerrainFilter(it.params[0]) }
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import com.unciv.models.stats.Stat
|
||||
|
||||
// parameterName values should be compliant with autogenerated values in TranslationFileWriter.generateStringsFromJSONs
|
||||
// Eventually we'll merge the translation generation to take this as the source of that
|
||||
@Suppress("unused") // Some are used only via enumerating the enum matching on parameterName
|
||||
enum class UniqueParameterType(val parameterName:String) {
|
||||
Number("amount") {
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
@ -145,6 +146,16 @@ enum class UniqueParameterType(val parameterName:String) {
|
||||
else -> UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||
}
|
||||
},
|
||||
/** should mirror TileImprovement.matchesFilter exactly */
|
||||
ImprovementFilter("improvementFilter") {
|
||||
private val knownValues = setOf("All", "All Road", "Great Improvement", "Great")
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueComplianceErrorSeverity? {
|
||||
if (parameterText in knownValues) return null
|
||||
if (ruleset.tileImprovements.containsKey(parameterText)) return null
|
||||
return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||
}
|
||||
},
|
||||
Resource("resource") {
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueComplianceErrorSeverity? = when (parameterText) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.unciv.models.ruleset.unique
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.*
|
||||
import com.unciv.logic.map.MapUnit
|
||||
@ -33,7 +34,7 @@ object UniqueTriggerActivation {
|
||||
OneTimeFreeUnit -> {
|
||||
val unitName = unique.params[0]
|
||||
val unit = civInfo.gameInfo.ruleSet.units[unitName]
|
||||
if (chosenCity == null || unit == null || (unit.uniques.contains("Founds a new city") && civInfo.isOneCityChallenger()))
|
||||
if (chosenCity == null || unit == null || (unit.hasUnique(UniqueType.FoundCity) && civInfo.isOneCityChallenger()))
|
||||
return false
|
||||
|
||||
val placedUnit = civInfo.addUnit(unitName, chosenCity)
|
||||
@ -49,7 +50,7 @@ object UniqueTriggerActivation {
|
||||
OneTimeAmountFreeUnits -> {
|
||||
val unitName = unique.params[1]
|
||||
val unit = civInfo.gameInfo.ruleSet.units[unitName]
|
||||
if (chosenCity == null || unit == null || (unit.uniques.contains("Founds a new city") && civInfo.isOneCityChallenger()))
|
||||
if (chosenCity == null || unit == null || (unit.hasUnique(UniqueType.FoundCity) && civInfo.isOneCityChallenger()))
|
||||
return false
|
||||
|
||||
val tilesUnitsWerePlacedOn: MutableList<Vector2> = mutableListOf()
|
||||
|
@ -124,6 +124,9 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
||||
|
||||
///////////////////////////////////////// UNIT UNIQUES /////////////////////////////////////////
|
||||
|
||||
FoundCity("Founds a new city", UniqueTarget.Unit),
|
||||
BuildImprovements("Can build [improvementFilter/terrainFilter] improvements on tiles", UniqueTarget.Unit),
|
||||
CreateWaterImprovements("May create improvements on water resources", UniqueTarget.Unit),
|
||||
|
||||
Strength("[amount]% Strength", UniqueTarget.Unit, UniqueTarget.Global),
|
||||
StrengthNearCapital("[amount]% Strength decreasing with distance from the capital", UniqueTarget.Unit),
|
||||
|
@ -403,7 +403,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
})
|
||||
}
|
||||
|
||||
if (uniques.contains(Constants.settlerUnique) &&
|
||||
if (hasUnique(UniqueType.FoundCity) &&
|
||||
(civInfo.isCityState() || civInfo.isOneCityChallenger())
|
||||
)
|
||||
rejectionReasons.add(RejectionReason.NoSettlerForOneCityPlayers)
|
||||
|
@ -128,7 +128,7 @@ object UnitActions {
|
||||
|
||||
fun getWaterImprovementAction(unit: MapUnit): UnitAction? {
|
||||
val tile = unit.currentTile
|
||||
if (!tile.isWater || !unit.hasUnique(Constants.workBoatsUnique) || tile.resource == null) return null
|
||||
if (!tile.isWater || !unit.hasUnique(UniqueType.CreateWaterImprovements) || tile.resource == null) return null
|
||||
|
||||
val improvementName = tile.getTileResource().improvement ?: return null
|
||||
val improvement = tile.ruleset.tileImprovements[improvementName] ?: return null
|
||||
@ -161,9 +161,11 @@ object UnitActions {
|
||||
* (no movement left, too close to another city).
|
||||
*/
|
||||
fun getFoundCityAction(unit: MapUnit, tile: TileInfo): UnitAction? {
|
||||
if (!unit.hasUnique("Founds a new city") || tile.isWater || tile.isImpassible()) return null
|
||||
if (!unit.hasUnique(UniqueType.FoundCity) || tile.isWater || tile.isImpassible()) return null
|
||||
|
||||
if (unit.currentMovement <= 0 || tile.getTilesInDistance(3).any { it.isCityCenter() })
|
||||
if (unit.currentMovement <= 0 ||
|
||||
tile.getTilesInDistance(2).any { it.isCityCenter() } ||
|
||||
tile.getTilesAtDistance(3).any { it.isCityCenter() && it.getContinent() == tile.getContinent() })
|
||||
return UnitAction(UnitActionType.FoundCity, action = null)
|
||||
|
||||
val foundAction = {
|
||||
|
@ -14,6 +14,7 @@ import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.metadata.Player
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
@ -68,7 +69,7 @@ class SerializationTests {
|
||||
|
||||
// Found a city otherwise too many classes have no instance and are not tested
|
||||
val civ = game.getCurrentPlayerCivilization()
|
||||
val unit = civ.getCivUnits().first { it.hasUnique(Constants.settlerUnique) }
|
||||
val unit = civ.getCivUnits().first { it.hasUnique(UniqueType.FoundCity) }
|
||||
val tile = unit.getTile()
|
||||
unit.civInfo.addCity(tile.position)
|
||||
if (tile.ruleset.tileImprovements.containsKey("City center"))
|
||||
|
Reference in New Issue
Block a user