Trade routes now travel between harbors over water, and are calculated once per civilization instead of per city

This commit is contained in:
Yair Morgenstern 2018-11-01 19:27:57 +02:00
parent 60b86decfb
commit 5a6e5043b3
9 changed files with 79 additions and 26 deletions

View File

@ -280,7 +280,8 @@
name:"Harbor", name:"Harbor",
maintenance:2, maintenance:2,
hurryCostModifier:25, hurryCostModifier:25,
uniques:["+1 production from all sea resources worked by the city","Connects trade routes over water"] // todo - trade routes over water! uniques:["+1 production from all sea resources worked by the city",
"Connects trade routes over water","Can only be built in coastal cities"]
requiredTech:"Compass" requiredTech:"Compass"
}, },
{ {

View File

@ -299,7 +299,7 @@
] ]
] ]
Worker:[ WorkerTrained:[
[ [
"You have trained a worker!", "You have trained a worker!",
"Workers are vital to your cities' growth, since only they can onstruct improvements on tiles", "Workers are vital to your cities' growth, since only they can onstruct improvements on tiles",

View File

@ -21,8 +21,8 @@ android {
applicationId "com.unciv.game" applicationId "com.unciv.game"
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 152 versionCode 153
versionName "2.9.6" versionName "2.9.7"
} }
buildTypes { buildTypes {
release { release {

View File

@ -17,7 +17,7 @@ class UnCivGame : Game() {
* This exists so that when debugging we can see the entire map. * This exists so that when debugging we can see the entire map.
* Remember to turn this to false before commit and upload! * Remember to turn this to false before commit and upload!
*/ */
val viewEntireMapForDebug = true val viewEntireMapForDebug = false
lateinit var worldScreen: WorldScreen lateinit var worldScreen: WorldScreen

View File

@ -15,6 +15,7 @@ import kotlin.math.min
class CityInfo { class CityInfo {
@Transient lateinit var civInfo: CivilizationInfo @Transient lateinit var civInfo: CivilizationInfo
@Transient var isConnectedToCapital = false
var location: Vector2 = Vector2.Zero var location: Vector2 = Vector2.Zero
var name: String = "" var name: String = ""
var health = 200 var health = 200
@ -78,6 +79,7 @@ class CityInfo {
toReturn.tiles.addAll(tiles) toReturn.tiles.addAll(tiles)
toReturn.workedTiles.addAll(workedTiles) toReturn.workedTiles.addAll(workedTiles)
toReturn.isBeingRazed=isBeingRazed toReturn.isBeingRazed=isBeingRazed
toReturn.isConnectedToCapital = isConnectedToCapital
return toReturn return toReturn
} }

View File

@ -224,32 +224,14 @@ class CityStats {
fun isConnectedToCapital(roadType: RoadStatus): Boolean { fun isConnectedToCapital(roadType: RoadStatus): Boolean {
if (cityInfo.civInfo.cities.count() < 2) return false// first city! 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 capitalTile = cityInfo.civInfo.getCapital().getCenterTile()
val BFS = val BFS = BFS(capitalTile){it.roadStatus == roadType}
if(roadType==RoadStatus.Road) BFS(capitalTile){it.roadStatus!=RoadStatus.None}
else BFS(capitalTile){it.roadStatus == roadType}
val cityTile = cityInfo.getCenterTile() val cityTile = cityInfo.getCenterTile()
BFS.stepUntilDestination(cityTile) BFS.stepUntilDestination(cityTile)
return BFS.tilesReached.containsKey(cityTile) return BFS.tilesReached.containsKey(cityTile)
// val tilesReached = HashSet<TileInfo>()
// var tilesToCheck: List<TileInfo> = listOf(cityInfo.getCenterTile())
// while (tilesToCheck.isNotEmpty()) {
// val newTiles = tilesToCheck
// .flatMap { it.neighbors }.distinct()
// .filter {
// !tilesReached.contains(it)
// && !tilesToCheck.contains(it)
// && (roadType !== RoadStatus.Road || it.roadStatus !== RoadStatus.None)
// && (roadType !== RoadStatus.Railroad || it.roadStatus === roadType)
// }
//
// if (newTiles.contains(capitalTile)) return true
// tilesReached.addAll(tilesToCheck)
// tilesToCheck = newTiles
// }
// return false
} }
//endregion //endregion

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.unciv.logic.GameInfo import com.unciv.logic.GameInfo
import com.unciv.logic.city.CityInfo import com.unciv.logic.city.CityInfo
import com.unciv.logic.map.BFS
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
@ -15,6 +16,8 @@ import com.unciv.models.gamebasics.tile.TileResource
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.ui.utils.getRandom import com.unciv.ui.utils.getRandom
import com.unciv.ui.utils.tr import com.unciv.ui.utils.tr
import java.util.*
import kotlin.collections.HashMap
import kotlin.math.max import kotlin.math.max
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -255,6 +258,7 @@ class CivilizationInfo {
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the civInfo cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the civInfo
cityInfo.setTransients() cityInfo.setTransients()
} }
setCitiesConnectedToCapitalTransients()
} }
fun endTurn() { fun endTurn() {
@ -296,6 +300,7 @@ class CivilizationInfo {
fun startTurn(){ fun startTurn(){
getViewableTiles() // adds explored tiles so that the units will be able to perform automated actions better getViewableTiles() // adds explored tiles so that the units will be able to perform automated actions better
setCitiesConnectedToCapitalTransients()
for (city in cities) for (city in cities)
city.cityStats.update() city.cityStats.update()
happiness = getHappinessForNextTurn().values.sum().roundToInt() happiness = getHappinessForNextTurn().values.sum().roundToInt()
@ -328,5 +333,62 @@ class CivilizationInfo {
newCity.cityConstructions.chooseNextConstruction() newCity.cityConstructions.chooseNextConstruction()
} }
fun setCitiesConnectedToCapitalTransients(){
if(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(getCapital())
citiesReachedToMediums[getCapital()] = arrayListOf("Start")
while(citiesToCheck.isNotEmpty() && citiesReachedToMediums.size<cities.size){
val newCitiesToCheck = mutableListOf<CityInfo>()
for(cityToConnectFrom in citiesToCheck){
val reachedMediums = citiesReachedToMediums[cityToConnectFrom]!!
// This is copypasta and can be cleaned up
if(!reachedMediums.contains("Road")){
val roadBfs = BFS(cityToConnectFrom.getCenterTile()){it.roadStatus!=RoadStatus.None}
roadBfs.stepToEnd()
val reachedCities = cities.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 = cities.filter { seaBfs.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("Harbor"))
cityReachedByMediums.add("Harbor")
}
citiesReachedToMediums[cityToConnectFrom]!!.add("Harbor")
}
}
citiesToCheck = newCitiesToCheck
}
for(city in cities){
city.isConnectedToCapital = citiesReachedToMediums.containsKey(city)
}
}
//endregion //endregion
} }

View File

@ -12,6 +12,11 @@ class BFS(val startingPoint: TileInfo, val predicate : (TileInfo) -> Boolean){
tilesReached.put(startingPoint,startingPoint) tilesReached.put(startingPoint,startingPoint)
} }
fun stepToEnd(){
while(tilesToCheck.isNotEmpty())
nextStep()
}
fun stepUntilDestination(destination: TileInfo){ fun stepUntilDestination(destination: TileInfo){
while(!tilesReached.containsKey(destination) && tilesToCheck.isNotEmpty()) while(!tilesReached.containsKey(destination) && tilesToCheck.isNotEmpty())
nextStep() nextStep()

View File

@ -94,6 +94,7 @@ class WorldScreen : CameraStageBaseScreen() {
val cloneCivilization = gameClone.getPlayerCivilization() val cloneCivilization = gameClone.getPlayerCivilization()
kotlin.concurrent.thread { kotlin.concurrent.thread {
civInfo.happiness = gameClone.getPlayerCivilization().getHappinessForNextTurn().values.sum().toInt() civInfo.happiness = gameClone.getPlayerCivilization().getHappinessForNextTurn().values.sum().toInt()
gameInfo.civilizations.forEach { it.setCitiesConnectedToCapitalTransients() }
} }
if(bottomBar.unitTable.selectedUnit!=null){ if(bottomBar.unitTable.selectedUnit!=null){