mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-07 17:43:54 +07:00
Trade routes (Railroad) fixes (#2013)
* Changes: - worker automation to build Railroad overseas (currently they do not improve roads/build new) - recognize harbor connection and add Railroad production bonus - extracted and refactored connections to capital lookup * CR fixes
This commit is contained in:
parent
5cc39b1020
commit
e062947e7e
@ -128,7 +128,10 @@ class CityInfo {
|
||||
fun getWorkableTiles() = getTiles().filter { it in tilesInRange }
|
||||
|
||||
fun isCapital() = cityConstructions.isBuilt("Palace")
|
||||
fun isConnectedToCapital() = civInfo.citiesConnectedToCapital.contains(this)
|
||||
fun isConnectedToCapital(connectionTypePredicate: (Set<String>) -> Boolean = {true}): Boolean {
|
||||
val mediumTypes = civInfo.citiesConnectedToCapitalToMediums[this] ?: return false
|
||||
return connectionTypePredicate(mediumTypes)
|
||||
}
|
||||
fun isInResistance() = resistanceCounter>0
|
||||
|
||||
|
||||
@ -396,7 +399,7 @@ class CityInfo {
|
||||
// The city could be producing something that puppets shouldn't, like units
|
||||
cityConstructions.currentConstructionIsUserSet = false
|
||||
cityConstructions.constructionQueue.clear()
|
||||
cityConstructions.chooseNextConstruction()
|
||||
cityConstructions.chooseNextConstruction()
|
||||
}
|
||||
|
||||
private fun diplomaticRepercussionsForConqueringCity(oldCiv: CivilizationInfo, conqueringCiv: CivilizationInfo) {
|
||||
|
@ -353,14 +353,7 @@ class CityStats {
|
||||
fun isConnectedToCapital(roadType: RoadStatus): Boolean {
|
||||
if (cityInfo.civInfo.cities.count() < 2) return false// first city!
|
||||
|
||||
if (roadType == RoadStatus.Road) return cityInfo.isConnectedToCapital() // this transient is not applicable to connection via railroad.
|
||||
|
||||
val capitalTile = cityInfo.civInfo.getCapital().getCenterTile()
|
||||
val bfs = BFS(capitalTile) { it.roadStatus == roadType }
|
||||
|
||||
val cityTile = cityInfo.getCenterTile()
|
||||
bfs.stepUntilDestination(cityTile)
|
||||
return bfs.tilesReached.containsKey(cityTile)
|
||||
return cityInfo.isConnectedToCapital { it.contains(roadType.name) || it.contains("Harbor") }
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
@ -0,0 +1,112 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import kotlin.collections.set
|
||||
|
||||
class CapitalConnectionsFinder(private val civInfo: CivilizationInfo) {
|
||||
private val citiesReachedToMediums = HashMap<CityInfo, MutableSet<String>>()
|
||||
private var citiesToCheck = mutableListOf(civInfo.getCapital())
|
||||
private lateinit var newCitiesToCheck: MutableList<CityInfo>
|
||||
|
||||
private val allCivCities = civInfo.gameInfo.getCities()
|
||||
|
||||
private val theWheelIsResearched = civInfo.tech.isResearched("The Wheel")
|
||||
private val railroadIsResearched = civInfo.tech.isResearched("Railroad")
|
||||
|
||||
private val road = "Road"
|
||||
private val railroad = "Railroad"
|
||||
private val harbor = "Harbor"
|
||||
|
||||
init {
|
||||
citiesReachedToMediums[civInfo.getCapital()] = hashSetOf("Start")
|
||||
}
|
||||
|
||||
fun find(): Map<CityInfo, Set<String>> {
|
||||
// We map which cities we've reached, to the mediums they've been reached by -
|
||||
// this is so we know that if we've seen which cities can be connected by port A, and one
|
||||
// of those is city B, then we don't need to check the cities that B can connect to by port,
|
||||
// since we'll get the same cities we got from A, since they're connected to the same sea.
|
||||
while (citiesToCheck.isNotEmpty() && citiesReachedToMediums.size < allCivCities.size) {
|
||||
newCitiesToCheck = mutableListOf()
|
||||
for (cityToConnectFrom in citiesToCheck) {
|
||||
if (cityToConnectFrom.containsHarbor()) {
|
||||
checkHarbor(cityToConnectFrom)
|
||||
}
|
||||
if (railroadIsResearched) {
|
||||
checkRailroad(cityToConnectFrom)
|
||||
}
|
||||
if (theWheelIsResearched) {
|
||||
checkRoad(cityToConnectFrom)
|
||||
}
|
||||
}
|
||||
citiesToCheck = newCitiesToCheck
|
||||
}
|
||||
return citiesReachedToMediums
|
||||
}
|
||||
|
||||
private fun checkRoad(cityToConnectFrom: CityInfo) {
|
||||
check(
|
||||
cityToConnectFrom = cityToConnectFrom,
|
||||
transportType = road,
|
||||
overridingTransportType = railroad,
|
||||
tileFilter = { tile -> tile.hasRoad(civInfo) || tile.hasRailroad() || tile.isCityCenter() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkRailroad(cityToConnectFrom: CityInfo) {
|
||||
check(
|
||||
cityToConnectFrom = cityToConnectFrom,
|
||||
transportType = railroad,
|
||||
tileFilter = { tile -> tile.hasRailroad() || tile.isCityCenter() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkHarbor(cityToConnectFrom: CityInfo) {
|
||||
check(
|
||||
cityToConnectFrom = cityToConnectFrom,
|
||||
transportType = harbor,
|
||||
tileFilter = { tile -> tile.isWater || tile.isCityCenter() },
|
||||
cityFilter = { city -> city.containsHarbor() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun CityInfo.containsHarbor() =
|
||||
this.cityConstructions.containsBuildingOrEquivalent(harbor)
|
||||
|
||||
private fun check(cityToConnectFrom: CityInfo,
|
||||
transportType: String,
|
||||
overridingTransportType: String? = null,
|
||||
tileFilter: (TileInfo) -> Boolean,
|
||||
cityFilter: (CityInfo) -> Boolean = { true }) {
|
||||
val bfs = BFS(cityToConnectFrom.getCenterTile(), tileFilter)
|
||||
bfs.stepToEnd()
|
||||
val reachedCities = allCivCities.filter {
|
||||
bfs.hasReachedTile(it.getCenterTile()) && cityFilter(it)
|
||||
}
|
||||
for (reachedCity in reachedCities) {
|
||||
addCityIfFirstEncountered(reachedCity)
|
||||
if (reachedCity == cityToConnectFrom) continue
|
||||
if (reachedCity.wasNotPreviouslyReached(transportType, overridingTransportType))
|
||||
reachedCity.addMedium(transportType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCityIfFirstEncountered(reachedCity: CityInfo) {
|
||||
if (!citiesReachedToMediums.containsKey(reachedCity)) {
|
||||
newCitiesToCheck.add(reachedCity)
|
||||
citiesReachedToMediums[reachedCity] = mutableSetOf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CityInfo.wasNotPreviouslyReached(transportType: String, overridingTransportType: String?): Boolean {
|
||||
val mediums = citiesReachedToMediums[this]!!
|
||||
return !mediums.contains(transportType) && !mediums.contains(overridingTransportType)
|
||||
}
|
||||
|
||||
private fun CityInfo.addMedium(transportType: String) {
|
||||
citiesReachedToMediums[this]!!.add(transportType)
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +1,11 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.set
|
||||
|
||||
/** CivInfo class was getting too crowded */
|
||||
class CivInfoTransientUpdater(val civInfo: CivilizationInfo){
|
||||
class CivInfoTransientUpdater(val civInfo: CivilizationInfo) {
|
||||
|
||||
// This is a big performance
|
||||
fun updateViewableTiles() {
|
||||
@ -112,89 +106,30 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo){
|
||||
}
|
||||
}
|
||||
|
||||
fun updateHasActiveGreatWall(){
|
||||
fun updateHasActiveGreatWall() {
|
||||
civInfo.hasActiveGreatWall = !civInfo.tech.isResearched("Dynamite") &&
|
||||
civInfo.containsBuildingUnique("Enemy land units must spend 1 extra movement point when inside your territory (obsolete upon Dynamite)")
|
||||
}
|
||||
|
||||
|
||||
fun setCitiesConnectedToCapitalTransients(initialSetup:Boolean=false){
|
||||
if(civInfo.cities.isEmpty()) return // eg barbarians
|
||||
fun setCitiesConnectedToCapitalTransients(initialSetup: Boolean = false) {
|
||||
if (civInfo.cities.isEmpty()) return // eg barbarians
|
||||
|
||||
// We map which cities we've reached, to the mediums they've been reached by -
|
||||
// this is so we know that if we've seen which cities can be connected by port A, and one
|
||||
// of those is city B, then we don't need to check the cities that B can connect to by port,
|
||||
// since we'll get the same cities we got from A, since they're connected to the same sea.
|
||||
val citiesReachedToMediums = HashMap<CityInfo, ArrayList<String>>()
|
||||
var citiesToCheck = mutableListOf(civInfo.getCapital())
|
||||
citiesReachedToMediums[civInfo.getCapital()] = arrayListOf("Start")
|
||||
val allCivCities = civInfo.gameInfo.getCities()
|
||||
val citiesReachedToMediums = CapitalConnectionsFinder(civInfo).find()
|
||||
|
||||
val theWheelIsResearched = civInfo.tech.isResearched("The Wheel")
|
||||
if (!initialSetup) { // In the initial setup we're loading an old game state, so it doesn't really count
|
||||
for (city in citiesReachedToMediums.keys)
|
||||
if (city !in civInfo.citiesConnectedToCapitalToMediums && city.civInfo == civInfo && city != civInfo.getCapital())
|
||||
civInfo.addNotification("[${city.name}] has been connected to your capital!", city.location, Color.GOLD)
|
||||
|
||||
val road = "Road"
|
||||
val harbor = "Harbor"
|
||||
|
||||
while(citiesToCheck.isNotEmpty() && citiesReachedToMediums.size<allCivCities.size){
|
||||
val newCitiesToCheck = mutableListOf<CityInfo>()
|
||||
for(cityToConnectFrom in citiesToCheck){
|
||||
val reachedMediums = citiesReachedToMediums[cityToConnectFrom]!!
|
||||
|
||||
// This is copypasta and can be cleaned up
|
||||
if(theWheelIsResearched && !reachedMediums.contains(road)){
|
||||
|
||||
val roadBfs = BFS(cityToConnectFrom.getCenterTile()) { it.hasRoad(civInfo) }
|
||||
roadBfs.stepToEnd()
|
||||
val reachedCities = allCivCities.filter { roadBfs.tilesReached.containsKey(it.getCenterTile())}
|
||||
for(reachedCity in reachedCities){
|
||||
if(!citiesReachedToMediums.containsKey(reachedCity)){
|
||||
newCitiesToCheck.add(reachedCity)
|
||||
citiesReachedToMediums[reachedCity] = arrayListOf()
|
||||
}
|
||||
val cityReachedByMediums = citiesReachedToMediums[reachedCity]!!
|
||||
if(!cityReachedByMediums.contains(road))
|
||||
cityReachedByMediums.add(road)
|
||||
}
|
||||
citiesReachedToMediums[cityToConnectFrom]!!.add(road)
|
||||
}
|
||||
|
||||
if(!reachedMediums.contains(harbor)
|
||||
&& cityToConnectFrom.cityConstructions.containsBuildingOrEquivalent(harbor)){
|
||||
val seaBfs = BFS(cityToConnectFrom.getCenterTile()) { it.isWater || it.isCityCenter() }
|
||||
seaBfs.stepToEnd()
|
||||
val reachedCities = allCivCities.filter {
|
||||
seaBfs.tilesReached.containsKey(it.getCenterTile())
|
||||
&& it.cityConstructions.containsBuildingOrEquivalent(harbor)
|
||||
}
|
||||
for(reachedCity in reachedCities){
|
||||
if(!citiesReachedToMediums.containsKey(reachedCity)){
|
||||
newCitiesToCheck.add(reachedCity)
|
||||
citiesReachedToMediums[reachedCity] = arrayListOf()
|
||||
}
|
||||
val cityReachedByMediums = citiesReachedToMediums[reachedCity]!!
|
||||
if(!cityReachedByMediums.contains(harbor))
|
||||
cityReachedByMediums.add(harbor)
|
||||
}
|
||||
citiesReachedToMediums[cityToConnectFrom]!!.add(harbor)
|
||||
}
|
||||
}
|
||||
citiesToCheck = newCitiesToCheck
|
||||
for (city in civInfo.citiesConnectedToCapitalToMediums.keys)
|
||||
if (!citiesReachedToMediums.containsKey(city) && city.civInfo == civInfo)
|
||||
civInfo.addNotification("[${city.name}] has been disconnected from your capital!", city.location, Color.GOLD)
|
||||
}
|
||||
|
||||
if(!initialSetup){ // In the initial setup we're loading an old game state, so it doesn't really count
|
||||
for(city in citiesReachedToMediums.keys)
|
||||
if(city !in civInfo.citiesConnectedToCapital && city.civInfo == civInfo && city != civInfo.getCapital())
|
||||
civInfo.addNotification("[${city.name}] has been connected to your capital!",city.location, Color.GOLD)
|
||||
|
||||
for(city in civInfo.citiesConnectedToCapital)
|
||||
if(!citiesReachedToMediums.containsKey(city) && city.civInfo==civInfo)
|
||||
civInfo.addNotification("[${city.name}] has been disconnected from your capital!",city.location, Color.GOLD)
|
||||
}
|
||||
|
||||
civInfo.citiesConnectedToCapital = citiesReachedToMediums.keys.toList()
|
||||
civInfo.citiesConnectedToCapitalToMediums = citiesReachedToMediums
|
||||
}
|
||||
|
||||
|
||||
fun updateDetailedCivResources() {
|
||||
val newDetailedCivResources = ResourceSupplyList()
|
||||
for (city in civInfo.cities) newDetailedCivResources.add(city.getCityResources())
|
||||
|
@ -44,8 +44,8 @@ class CivilizationInfo {
|
||||
@Transient var viewableTiles = setOf<TileInfo>()
|
||||
@Transient var viewableInvisibleUnitsTiles = setOf<TileInfo>()
|
||||
|
||||
/** Contains cities from ALL civilizations connected by trade routes to the capital */
|
||||
@Transient var citiesConnectedToCapital = listOf<CityInfo>()
|
||||
/** Contains mapping of cities to travel mediums from ALL civilizations connected by trade routes to the capital */
|
||||
@Transient var citiesConnectedToCapitalToMediums = mapOf<CityInfo, Set<String>>()
|
||||
|
||||
/** This is for performance since every movement calculation depends on this, see MapUnit comment */
|
||||
@Transient var hasActiveGreatWall = false
|
||||
|
@ -49,4 +49,7 @@ class BFS(val startingPoint: TileInfo, val predicate : (TileInfo) -> Boolean){
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
fun hasReachedTile(tile: TileInfo) =
|
||||
tilesReached.containsKey(tile)
|
||||
}
|
@ -354,11 +354,17 @@ open class TileInfo {
|
||||
return false
|
||||
}
|
||||
|
||||
fun hasRoad(civInfo: CivilizationInfo): Boolean {
|
||||
if(roadStatus != RoadStatus.None) return true
|
||||
if(civInfo.nation.forestsAndJunglesAreRoads && (terrainFeature==Constants.jungle || terrainFeature==Constants.forest))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
fun hasConnection(civInfo: CivilizationInfo) =
|
||||
roadStatus != RoadStatus.None || forestOrJungleAreRoads(civInfo)
|
||||
|
||||
fun hasRoad(civInfo: CivilizationInfo) =
|
||||
roadStatus == RoadStatus.Road || forestOrJungleAreRoads(civInfo)
|
||||
|
||||
fun hasRailroad() =
|
||||
roadStatus == RoadStatus.Railroad
|
||||
|
||||
private fun forestOrJungleAreRoads(civInfo: CivilizationInfo) =
|
||||
civInfo.nation.forestsAndJunglesAreRoads
|
||||
&& (terrainFeature == Constants.jungle || terrainFeature == Constants.forest)
|
||||
//endregion
|
||||
}
|
@ -21,7 +21,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
if (from.roadStatus === RoadStatus.Railroad && to.roadStatus === RoadStatus.Railroad)
|
||||
return 1 / 10f + extraCost
|
||||
|
||||
if (from.hasRoad(civInfo) && to.hasRoad(civInfo))
|
||||
if (from.hasConnection(civInfo) && to.hasConnection(civInfo))
|
||||
{
|
||||
if (unit.civInfo.tech.movementSpeedOnRoadsImproved) return 1 / 3f + extraCost
|
||||
else return 1 / 2f + extraCost
|
||||
|
@ -180,7 +180,7 @@ class Building : NamedStats(), IConstruction{
|
||||
stats.science = 50f
|
||||
|
||||
if(uniques.contains("+5% Production for every Trade Route with a City-State in the empire"))
|
||||
stats.production += 5*civInfo.citiesConnectedToCapital.count { it.civInfo.isCityState() }
|
||||
stats.production += 5*civInfo.citiesConnectedToCapitalToMediums.count { it.key.civInfo.isCityState() }
|
||||
|
||||
return stats
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||
"\n Click 'Create improvement' (above the unit table, bottom left)" +
|
||||
"\n > Choose the farm > \n Leave the worker there until it's finished"
|
||||
if(!completedTasks.contains("Create a trade route")
|
||||
&& viewingCiv.citiesConnectedToCapital.any { it.civInfo==viewingCiv })
|
||||
&& viewingCiv.citiesConnectedToCapitalToMediums.any { it.key.civInfo==viewingCiv })
|
||||
game.settings.addCompletedTutorialTask("Create a trade route")
|
||||
if(viewingCiv.cities.size>1 && !completedTasks.contains("Create a trade route"))
|
||||
return "Create a trade route!\nConstruct roads between your capital and another city" +
|
||||
|
Loading…
Reference in New Issue
Block a user