Rename Railroad tech to Railroads (#4567)

* Rename Railroad tech to Railroads

* Rename Railroad tech to Railroads - force check

* Rename Railroad tech to Railroads - patch1

* Rename Railroad tech to Railroads - atlas

* Rename Railroad tech to Railroads - patch2
This commit is contained in:
SomeTroglodyte 2021-07-25 18:04:51 +02:00 committed by GitHub
parent dbc6bc9f30
commit 966f70d0d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 104 additions and 75 deletions

View File

Before

Width:  |  Height:  |  Size: 922 B

After

Width:  |  Height:  |  Size: 922 B

View File

@ -1859,9 +1859,9 @@ TechIcons/Radio
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
TechIcons/Railroad TechIcons/Railroads
rotate: false rotate: false
xy: 1292, 760 xy: 1184, 652
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
@ -2071,14 +2071,14 @@ TileSets/Default/OasisOverlay
index: -1 index: -1
TileSets/Default/Railroad TileSets/Default/Railroad
rotate: false rotate: false
xy: 1184, 652 xy: 1292, 760
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
ImprovementIcons/Railroad ImprovementIcons/Railroad
rotate: false rotate: false
xy: 1184, 652 xy: 1292, 760
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -954,7 +954,7 @@
"isWonder": true, "isWonder": true,
"uniques": ["[+1 Happiness, +2 Culture, +3 Gold] from every [Castle]", "uniques": ["[+1 Happiness, +2 Culture, +3 Gold] from every [Castle]",
"Must have an owned [Mountain] within [2] tiles"], "Must have an owned [Mountain] within [2] tiles"],
"requiredTech": "Railroad", "requiredTech": "Railroads",
"quote": "'...the location is one of the most beautiful to be found, holy and unapproachable, a worthy temple for the divine friend who has brought salvation and true blessing to the world.' - King Ludwig II of Bavaria" "quote": "'...the location is one of the most beautiful to be found, holy and unapproachable, a worthy temple for the divine friend who has brought salvation and true blessing to the world.' - King Ludwig II of Bavaria"
}, },
// Column 12 // Column 12

View File

@ -446,7 +446,7 @@
"quote": "'Aeronautics was neither an industry nor a science. It was a miracle.' - Igor Sikorsky" "quote": "'Aeronautics was neither an industry nor a science. It was a miracle.' - Igor Sikorsky"
}, },
{ {
"name": "Railroad", "name": "Railroads",
"row": 8, "row": 8,
"prerequisites": ["Steam Power","Dynamite"], "prerequisites": ["Steam Power","Dynamite"],
"quote": "'The introduction of so powerful an agent as steam to a carriage on wheels will make a great change in the situation of man.' - Thomas Jefferson" "quote": "'The introduction of so powerful an agent as steam to a carriage on wheels will make a great change in the situation of man.' - Thomas Jefferson"
@ -481,7 +481,7 @@
{ {
"name": "Combustion", "name": "Combustion",
"row": 8, "row": 8,
"prerequisites": ["Railroad"], "prerequisites": ["Railroads"],
"quote": "'Any man who can drive safely while kissing a pretty girl is simply not giving the kiss the attention it deserves.' - Albert Einstein" "quote": "'Any man who can drive safely while kissing a pretty girl is simply not giving the kiss the attention it deserves.' - Albert Einstein"
} }
] ]

View File

@ -110,7 +110,7 @@
{ {
"name": "Railroad", "name": "Railroad",
"turnsToBuild": 4, "turnsToBuild": 4,
"techRequired": "Railroad", "techRequired": "Railroads",
"uniques": ["Can be built outside your borders", "Costs [2] gold per turn when in your territory"], "uniques": ["Can be built outside your borders", "Costs [2] gold per turn when in your territory"],
"shortcutKey": "R", "shortcutKey": "R",
"civilopediaText": [{text:"Reduces movement cost to ⅒ if the other tile also has a Railroad"}] "civilopediaText": [{text:"Reduces movement cost to ⅒ if the other tile also has a Railroad"}]

View File

@ -3496,7 +3496,7 @@ Replaceable Parts = Ersetzbare Teile
'Nothing is particularly hard if you divide it into small jobs.' - Henry Ford = 'Keine Aufgabe ist wirklich schwierig, solange man sie in einzelne Schritte aufteilt.' - Henry Ford 'Nothing is particularly hard if you divide it into small jobs.' - Henry Ford = 'Keine Aufgabe ist wirklich schwierig, solange man sie in einzelne Schritte aufteilt.' - Henry Ford
Flight = Flug Flight = Flug
'Aeronautics was neither an industry nor a science. It was a miracle.' - Igor Sikorsky = 'Die Luftfahrt war weder eine Industrie noch eine Wissenschaft. Es war ein Wunder.' - Igor Sikorsky 'Aeronautics was neither an industry nor a science. It was a miracle.' - Igor Sikorsky = 'Die Luftfahrt war weder eine Industrie noch eine Wissenschaft. Es war ein Wunder.' - Igor Sikorsky
Railroad = Eisenbahn Railroads = Eisenbahn
'The introduction of so powerful an agent as steam to a carriage on wheels will make a great change in the situation of man.' - Thomas Jefferson = 'Die Einführung eines so mächtigen Mittels wie Dampf in einen Wagen auf Rädern wird die Situation des Menschen stark verändern.' - Thomas Jefferson 'The introduction of so powerful an agent as steam to a carriage on wheels will make a great change in the situation of man.' - Thomas Jefferson = 'Die Einführung eines so mächtigen Mittels wie Dampf in einen Wagen auf Rädern wird die Situation des Menschen stark verändern.' - Thomas Jefferson
Plastics = Plastik Plastics = Plastik
@ -3659,6 +3659,7 @@ Fort = Festung
Costs [amount] gold per turn when in your territory = Kostet [amount] Gold pro Runde innerhalb des eigenen Territoriums Costs [amount] gold per turn when in your territory = Kostet [amount] Gold pro Runde innerhalb des eigenen Territoriums
Road = Straße Road = Straße
Railroad = Schienen
Remove Forest = Wald abholzen Remove Forest = Wald abholzen

View File

@ -248,7 +248,7 @@ class GameInfo {
/** /**
* [CivilizationInfo.addNotification][Add a notification] to every civilization that have * [CivilizationInfo.addNotification][Add a notification] to every civilization that have
* adopted Honor policy and have explored the [tile] where the Barbarian Encampent has spawned. * adopted Honor policy and have explored the [tile] where the Barbarian Encampment has spawned.
*/ */
fun notifyCivsOfBarbarianEncampment(tile: TileInfo) { fun notifyCivsOfBarbarianEncampment(tile: TileInfo) {
civilizations.filter { civilizations.filter {
@ -273,6 +273,14 @@ class GameInfo {
throw UncivShowableException("Missing mods: [$missingMods]") throw UncivShowableException("Missing mods: [$missingMods]")
} }
// compatibility code translating changed tech name "Railroad" - to be deprecated soon
val (oldTechName, newTechName) = "Railroad" to "Railroads"
if (ruleSet.technologies[oldTechName] == null && ruleSet.technologies[newTechName] != null) {
civilizations.forEach {
it.tech.replaceUpdatedTechName(oldTechName, newTechName)
}
}
removeMissingModReferences() removeMissingModReferences()
tileMap.setTransients(ruleSet) tileMap.setTransients(ruleSet)
@ -391,6 +399,7 @@ class GameInfo {
* This function can be used for backwards compatibility with older save files when a building * This function can be used for backwards compatibility with older save files when a building
* name is changed. * name is changed.
*/ */
@Suppress("unused") // it's OK if there's no deprecation currently needing this
private fun changeBuildingNameIfNotInRuleset(cityConstructions: CityConstructions, oldBuildingName: String, newBuildingName: String) { private fun changeBuildingNameIfNotInRuleset(cityConstructions: CityConstructions, oldBuildingName: String, newBuildingName: String) {
if (ruleSet.buildings.containsKey(oldBuildingName)) if (ruleSet.buildings.containsKey(oldBuildingName))
return return
@ -411,7 +420,24 @@ class GameInfo {
cityConstructions.inProgressConstructions.remove(oldBuildingName) cityConstructions.inProgressConstructions.remove(oldBuildingName)
} }
} }
/** Replace a changed tech name, only temporarily used for breaking ruleset updates */
private fun TechManager.replaceUpdatedTechName(oldTechName: String, newTechName: String) {
if (oldTechName in techsResearched) {
techsResearched.remove(oldTechName)
techsResearched.add(newTechName)
}
val index = techsToResearch.indexOf(oldTechName)
if (index >= 0) {
techsToResearch[index] = newTechName
}
if (oldTechName in techsInProgress) {
techsInProgress[newTechName] = researchOfTech(oldTechName)
techsInProgress.remove(oldTechName)
}
}
fun hasReligionEnabled() = gameParameters.religionEnabled || ruleSet.hasReligion() // Temporary function to check whether religion should be used for this game fun hasReligionEnabled() = gameParameters.religionEnabled || ruleSet.hasReligion() // Temporary function to check whether religion should be used for this game
} }

View File

@ -498,11 +498,11 @@ class CityInfo {
internal fun tryUpdateRoadStatus() { internal fun tryUpdateRoadStatus() {
if (getCenterTile().roadStatus == RoadStatus.None) { if (getCenterTile().roadStatus == RoadStatus.None) {
val roadImprovement = getRuleset().tileImprovements["Road"] val roadImprovement = RoadStatus.Road.improvement(getRuleset())
if (roadImprovement != null && roadImprovement.techRequired in civInfo.tech.techsResearched) if (roadImprovement != null && roadImprovement.techRequired in civInfo.tech.techsResearched)
getCenterTile().roadStatus = RoadStatus.Road getCenterTile().roadStatus = RoadStatus.Road
} else if (getCenterTile().roadStatus != RoadStatus.Railroad) { } else if (getCenterTile().roadStatus != RoadStatus.Railroad) {
val railroadImprovement = getRuleset().tileImprovements["Railroad"] val railroadImprovement = RoadStatus.Railroad.improvement(getRuleset())
if (railroadImprovement != null && railroadImprovement.techRequired in civInfo.tech.techsResearched) if (railroadImprovement != null && railroadImprovement.techRequired in civInfo.tech.techsResearched)
getCenterTile().roadStatus = RoadStatus.Railroad getCenterTile().roadStatus = RoadStatus.Railroad
} }

View File

@ -77,12 +77,12 @@ class CityStats {
private fun getStatPercentBonusesFromRailroad(): Stats { private fun getStatPercentBonusesFromRailroad(): Stats {
val stats = Stats() val stats = Stats()
val railroadImprovement = cityInfo.getRuleset().tileImprovements["Railroad"] val railroadImprovement = RoadStatus.Railroad.improvement(cityInfo.getRuleset())
if (railroadImprovement == null) return stats // for mods ?: return stats // for mods
val techEnablingRailroad = railroadImprovement.techRequired!! val techEnablingRailroad = railroadImprovement.techRequired
// If we conquered enemy cities connected by railroad, but we don't yet have that tech, // If we conquered enemy cities connected by railroad, but we don't yet have that tech,
// we shouldn't get bonuses, it's as if the tracks aare layed out but we can't operate them. // we shouldn't get bonuses, it's as if the tracks are laid out but we can't operate them.
if (cityInfo.civInfo.tech.isResearched(techEnablingRailroad) if ( (techEnablingRailroad == null || cityInfo.civInfo.tech.isResearched(techEnablingRailroad))
&& (cityInfo.isCapital() || isConnectedToCapital(RoadStatus.Railroad))) && (cityInfo.isCapital() || isConnectedToCapital(RoadStatus.Railroad)))
stats.production += 25f stats.production += 25f
return stats return stats
@ -364,8 +364,12 @@ class CityStats {
if (cityInfo.civInfo.cities.count() < 2) return false// first city! if (cityInfo.civInfo.cities.count() < 2) return false// first city!
// Railroad, or harbor from railroad // Railroad, or harbor from railroad
if (roadType == RoadStatus.Railroad) return cityInfo.isConnectedToCapital { it.any { it.contains("Railroad") } } return if (roadType == RoadStatus.Railroad)
else return cityInfo.isConnectedToCapital() cityInfo.isConnectedToCapital {
roadTypes ->
roadTypes.any { it.contains(RoadStatus.Railroad.name) }
}
else cityInfo.isConnectedToCapital()
} }
//endregion //endregion
@ -398,7 +402,7 @@ class CityStats {
newStatPercentBonusList["Buildings"] = getStatPercentBonusesFromUniques(currentConstruction, localBuildingUniques) newStatPercentBonusList["Buildings"] = getStatPercentBonusesFromUniques(currentConstruction, localBuildingUniques)
.plus(cityInfo.cityConstructions.getStatPercentBonuses()) // This function is to be deprecated but it'll take a while. .plus(cityInfo.cityConstructions.getStatPercentBonuses()) // This function is to be deprecated but it'll take a while.
newStatPercentBonusList["Wonders"] = getStatPercentBonusesFromUniques(currentConstruction, cityInfo.civInfo.getCivWideBuildingUniques()) newStatPercentBonusList["Wonders"] = getStatPercentBonusesFromUniques(currentConstruction, cityInfo.civInfo.getCivWideBuildingUniques())
newStatPercentBonusList["Railroad"] = getStatPercentBonusesFromRailroad() newStatPercentBonusList["Railroads"] = getStatPercentBonusesFromRailroad() // Name chosen same as tech, for translation, but theoretically independent
newStatPercentBonusList["Resources"] = getStatPercentBonusesFromResources(currentConstruction) newStatPercentBonusList["Resources"] = getStatPercentBonusesFromResources(currentConstruction)
newStatPercentBonusList["National ability"] = getStatPercentBonusesFromNationUnique(currentConstruction) newStatPercentBonusList["National ability"] = getStatPercentBonusesFromNationUnique(currentConstruction)
newStatPercentBonusList["Puppet City"] = getStatPercentBonusesFromPuppetCity() newStatPercentBonusList["Puppet City"] = getStatPercentBonusesFromPuppetCity()

View File

@ -13,13 +13,14 @@ class CapitalConnectionsFinder(private val civInfo: CivilizationInfo) {
private val allCivCities = civInfo.gameInfo.getCities() private val allCivCities = civInfo.gameInfo.getCities()
private val harbor = "Harbor" // hardcoding at least centralized for this class for now
private val road = RoadStatus.Road.name private val road = RoadStatus.Road.name
private val railroad = RoadStatus.Railroad.name private val railroad = RoadStatus.Railroad.name
private val harborFromRoad = "Harbor-Road" private val harborFromRoad = "$harbor-$road"
private val harborFromRailroad = "Harbor-Railroad" private val harborFromRailroad = "$harbor-$railroad"
private val ruleset = civInfo.gameInfo.ruleSet private val ruleset = civInfo.gameInfo.ruleSet
private val theWheelIsResearched = ruleset.tileImprovements.containsKey(road) && civInfo.tech.isResearched(ruleset.tileImprovements[road]!!.techRequired!!) private val roadIsResearched = ruleset.tileImprovements.containsKey(road) && civInfo.tech.isResearched(ruleset.tileImprovements[road]!!.techRequired!!)
private val railroadIsResearched = ruleset.tileImprovements.containsKey(railroad) && civInfo.tech.isResearched(ruleset.tileImprovements[railroad]!!.techRequired!!) private val railroadIsResearched = ruleset.tileImprovements.containsKey(railroad) && civInfo.tech.isResearched(ruleset.tileImprovements[railroad]!!.techRequired!!)
init { init {
@ -42,7 +43,7 @@ class CapitalConnectionsFinder(private val civInfo: CivilizationInfo) {
if(mediumsReached.contains("Start") || mediumsReached.contains(railroad) || mediumsReached.contains(harborFromRailroad)) if(mediumsReached.contains("Start") || mediumsReached.contains(railroad) || mediumsReached.contains(harborFromRailroad))
checkRailroad(cityToConnectFrom) // This is only relevant for city connection if there is an unbreaking line from the capital checkRailroad(cityToConnectFrom) // This is only relevant for city connection if there is an unbreaking line from the capital
} }
if (theWheelIsResearched) { if (roadIsResearched) {
checkRoad(cityToConnectFrom) checkRoad(cityToConnectFrom)
} }
} }
@ -71,7 +72,7 @@ class CapitalConnectionsFinder(private val civInfo: CivilizationInfo) {
private fun checkHarbor(cityToConnectFrom: CityInfo) { private fun checkHarbor(cityToConnectFrom: CityInfo) {
check( check(
cityToConnectFrom, cityToConnectFrom,
transportType = if(cityToConnectFrom.wasPreviouslyReached("Railroad",null)) harborFromRailroad else harborFromRoad, transportType = if(cityToConnectFrom.wasPreviouslyReached(railroad,null)) harborFromRailroad else harborFromRoad,
overridingTransportType = harborFromRailroad, overridingTransportType = harborFromRailroad,
tileFilter = { tile -> tile.isWater || tile.isCityCenter() }, tileFilter = { tile -> tile.isWater || tile.isCityCenter() },
cityFilter = { city -> city.containsHarbor() } cityFilter = { city -> city.containsHarbor() }
@ -79,7 +80,7 @@ class CapitalConnectionsFinder(private val civInfo: CivilizationInfo) {
} }
private fun CityInfo.containsHarbor() = private fun CityInfo.containsHarbor() =
this.cityConstructions.containsBuildingOrEquivalent("Harbor") this.cityConstructions.containsBuildingOrEquivalent(harbor)
private fun check(cityToConnectFrom: CityInfo, private fun check(cityToConnectFrom: CityInfo,
transportType: String, transportType: String,

View File

@ -1,7 +1,6 @@
package com.unciv.logic.civilization package com.unciv.logic.civilization
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.map.RoadStatus
import com.unciv.models.metadata.BASE_GAME_DURATION_TURNS import com.unciv.models.metadata.BASE_GAME_DURATION_TURNS
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
@ -65,13 +64,7 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
if (tile.isCityCenter()) continue if (tile.isCityCenter()) continue
if (ignoredTileTypes.any { tile.matchesFilter(it, civInfo) }) continue if (ignoredTileTypes.any { tile.matchesFilter(it, civInfo) }) continue
val tileUpkeep = transportationUpkeep += tile.roadStatus.upkeep
when (tile.roadStatus) {
RoadStatus.Road -> 1
RoadStatus.Railroad -> 2
RoadStatus.None -> 0
}
transportationUpkeep += tileUpkeep
} }
} }
for (unique in civInfo.getMatchingUniques("Maintenance on roads & railroads reduced by []%")) for (unique in civInfo.getMatchingUniques("Maintenance on roads & railroads reduced by []%"))
@ -158,8 +151,10 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
.map { it.params[0].toFloat() / 100f }.sum() .map { it.params[0].toFloat() / 100f }.sum()
val luxuriesProvidedByCityStates = val luxuriesProvidedByCityStates =
civInfo.getKnownCivs().filter { it.isCityState() && it.getAllyCiv() == civInfo.civName } civInfo.getKnownCivs().asSequence()
.map { it.getCivResources().map { res -> res.resource } }.flatten().distinct().count { it.resourceType === ResourceType.Luxury } .filter { it.isCityState() && it.getAllyCiv() == civInfo.civName }
.map { it.getCivResources().map { res -> res.resource } }
.flatten().distinct().count { it.resourceType === ResourceType.Luxury }
statMap["City-State Luxuries"] = happinessBonusForCityStateProvidedLuxuries * luxuriesProvidedByCityStates * happinessPerUniqueLuxury statMap["City-State Luxuries"] = happinessBonusForCityStateProvidedLuxuries * luxuriesProvidedByCityStates * happinessPerUniqueLuxury

View File

@ -35,7 +35,7 @@ class TechManager {
// UnitMovementAlgorithms.getMovementCostBetweenAdjacentTiles is a close second =) // UnitMovementAlgorithms.getMovementCostBetweenAdjacentTiles is a close second =)
@Transient @Transient
var movementSpeedOnRoadsImproved = false var movementSpeedOnRoads = 1f
@Transient @Transient
var roadsConnectAcrossRivers = false var roadsConnectAcrossRivers = false
@ -50,8 +50,7 @@ class TechManager {
/** When moving towards a certain tech, the user doesn't have to manually pick every one. */ /** When moving towards a certain tech, the user doesn't have to manually pick every one. */
var techsToResearch = ArrayList<String>() var techsToResearch = ArrayList<String>()
var overflowScience = 0 var overflowScience = 0
private var techsInProgress = HashMap<String, Int>() var techsInProgress = HashMap<String, Int>()
fun scienceSpentOnTech(tech: String): Int = if (tech in techsInProgress) techsInProgress[tech]!! else 0
/** In civ IV, you can auto-convert a certain percentage of gold in cities to science */ /** In civ IV, you can auto-convert a certain percentage of gold in cities to science */
var goldPercentConvertedToScience = 0.6f var goldPercentConvertedToScience = 0.6f
@ -109,10 +108,9 @@ class TechManager {
return if (techsToResearch.isEmpty()) null else techsToResearch[0] return if (techsToResearch.isEmpty()) null else techsToResearch[0]
} }
private fun researchOfTech(TechName: String?): Int { fun researchOfTech(TechName: String?) = techsInProgress[TechName] ?: 0
return if (techsInProgress.containsKey(TechName)) techsInProgress[TechName]!! else 0 // Was once duplicated as fun scienceSpentOnTech(tech: String): Int
}
fun remainingScienceToTech(techName: String) = costOfTech(techName) - researchOfTech(techName) fun remainingScienceToTech(techName: String) = costOfTech(techName) - researchOfTech(techName)
fun turnsToTech(techName: String): String { fun turnsToTech(techName: String): String {
@ -302,7 +300,7 @@ class TechManager {
val text = "[${cities.size}] cities changed production from [$unit] to [${construction.upgradesTo!!}]" val text = "[${cities.size}] cities changed production from [$unit] to [${construction.upgradesTo!!}]"
civInfo.addNotification(text, locationAction, unit, NotificationIcon.Construction, construction.upgradesTo!!) civInfo.addNotification(text, locationAction, unit, NotificationIcon.Construction, construction.upgradesTo!!)
} else { } else {
val text = "[$unit] has become osbolete and was removed from the queue in [${cities.size}] cities!" val text = "[$unit] has become obsolete and was removed from the queue in [${cities.size}] cities!"
civInfo.addNotification(text, locationAction, NotificationIcon.Construction) civInfo.addNotification(text, locationAction, NotificationIcon.Construction)
} }
} }
@ -365,7 +363,8 @@ class TechManager {
unitsCanEmbark = wayfinding || civInfo.hasUnique("Enables embarkation for land units") unitsCanEmbark = wayfinding || civInfo.hasUnique("Enables embarkation for land units")
embarkedUnitsCanEnterOcean = wayfinding || civInfo.hasUnique("Enables embarked units to enter ocean tiles") embarkedUnitsCanEnterOcean = wayfinding || civInfo.hasUnique("Enables embarked units to enter ocean tiles")
movementSpeedOnRoadsImproved = civInfo.hasUnique("Improves movement speed on roads") movementSpeedOnRoads = if (civInfo.hasUnique("Improves movement speed on roads"))
RoadStatus.Road.movementImproved else RoadStatus.Road.movement
roadsConnectAcrossRivers = civInfo.hasUnique("Roads connect tiles across rivers") roadsConnectAcrossRivers = civInfo.hasUnique("Roads connect tiles across rivers")
} }

View File

@ -14,7 +14,6 @@ import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.ruleset.unit.UnitType
import java.text.DecimalFormat import java.text.DecimalFormat
import kotlin.math.pow
import kotlin.random.Random import kotlin.random.Random
/** /**
@ -275,7 +274,7 @@ class MapUnit {
&& canBuildImprovement(getTile().getTileImprovementInProgress()!!)) && canBuildImprovement(getTile().getTileImprovementInProgress()!!))
return false return false
// unique "Can construct roads" deprecated since 3.15.5 // unique "Can construct roads" deprecated since 3.15.5
if (hasUnique("Can construct roads") && currentTile.improvementInProgress == "Road") return false if (hasUnique("Can construct roads") && currentTile.improvementInProgress == RoadStatus.Road.name) return false
// //
if (isFortified()) return false if (isFortified()) return false
if (action == Constants.unitActionExplore || isSleeping() if (action == Constants.unitActionExplore || isSleeping()
@ -459,6 +458,7 @@ class MapUnit {
if (civInfo.isCurrentPlayer()) if (civInfo.isCurrentPlayer())
UncivGame.Current.settings.addCompletedTutorialTask("Construct an improvement") UncivGame.Current.settings.addCompletedTutorialTask("Construct an improvement")
when { when {
tile.improvementInProgress!!.startsWith("Remove ") -> { tile.improvementInProgress!!.startsWith("Remove ") -> {
val removedFeatureName = tile.improvementInProgress!!.removePrefix("Remove ") val removedFeatureName = tile.improvementInProgress!!.removePrefix("Remove ")
@ -473,9 +473,9 @@ class MapUnit {
tile.improvement = null tile.improvement = null
if (tile.resource != null) civInfo.updateDetailedCivResources() // unlikely, but maybe a mod makes a resource improvement dependent on a terrain feature if (tile.resource != null) civInfo.updateDetailedCivResources() // unlikely, but maybe a mod makes a resource improvement dependent on a terrain feature
} }
if (tile.improvementInProgress == "Remove Road" || tile.improvementInProgress == "Remove Railroad") { if (RoadStatus.values().any { tile.improvementInProgress == it.removeAction })
tile.roadStatus = RoadStatus.None tile.roadStatus = RoadStatus.None
} else { else {
val removedFeatureObject = tile.ruleset.terrains[removedFeatureName] val removedFeatureObject = tile.ruleset.terrains[removedFeatureName]
if (removedFeatureObject != null && removedFeatureObject.uniques if (removedFeatureObject != null && removedFeatureObject.uniques
.contains("Provides a one-time Production bonus to the closest city when cut down") .contains("Provides a one-time Production bonus to the closest city when cut down")
@ -485,8 +485,8 @@ class MapUnit {
tile.terrainFeatures.remove(removedFeatureName) tile.terrainFeatures.remove(removedFeatureName)
} }
} }
tile.improvementInProgress == "Road" -> tile.roadStatus = RoadStatus.Road tile.improvementInProgress == RoadStatus.Road.name -> tile.roadStatus = RoadStatus.Road
tile.improvementInProgress == "Railroad" -> tile.roadStatus = RoadStatus.Railroad tile.improvementInProgress == RoadStatus.Railroad.name -> tile.roadStatus = RoadStatus.Railroad
else -> { else -> {
tile.improvement = tile.improvementInProgress tile.improvement = tile.improvementInProgress
if (tile.resource != null) civInfo.updateDetailedCivResources() if (tile.resource != null) civInfo.updateDetailedCivResources()

View File

@ -1,18 +1,23 @@
package com.unciv.logic.map package com.unciv.logic.map
import com.unciv.logic.map.RoadStatus.Railroad
import com.unciv.logic.map.RoadStatus.Road
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
/** /**
* You can use RoadStatus.name to identify [Road] and [Railroad] * You can use RoadStatus.name to identify [Road] and [Railroad]
* in string-based identification, as done in [improvement]. * in string-based identification, as done in [improvement].
*
* Note: Order is important, [ordinal] _is_ compared - please interpret as "roadLevel".
*/ */
enum class RoadStatus { enum class RoadStatus(
val upkeep: Int = 0,
val movement: Float = 1f,
val movementImproved: Float = 1f,
val removeAction: String? = null
) {
None, None,
Road, Road (1, 0.5f, 1/3f, "Remove Road"),
Railroad; Railroad (2, 0.1f, 0.1f, "Remove Railroad");
/** returns null for [None] */ /** returns null for [None] */
fun improvement(ruleset: Ruleset) = ruleset.tileImprovements[this.name] fun improvement(ruleset: Ruleset) = ruleset.tileImprovements[this.name]

View File

@ -406,8 +406,7 @@ open class TileInfo {
} -> false } -> false
// Road improvements can change on tiles with irremovable improvements - nothing else can, though. // Road improvements can change on tiles with irremovable improvements - nothing else can, though.
improvement.name != RoadStatus.Railroad.name && improvement.name != RoadStatus.Railroad.name RoadStatus.values().none { it.name == improvement.name || it.removeAction == improvement.name }
&& improvement.name != "Remove Road" && improvement.name != "Remove Railroad"
&& getTileImprovement().let { it != null && it.hasUnique("Irremovable") } -> false && getTileImprovement().let { it != null && it.hasUnique("Irremovable") } -> false
// Decide cancelImprovementOrder earlier, otherwise next check breaks it // Decide cancelImprovementOrder earlier, otherwise next check breaks it
@ -420,10 +419,8 @@ open class TileInfo {
if (filter == "River") return@any !isAdjacentToRiver() if (filter == "River") return@any !isAdjacentToRiver()
else return@any !neighbors.any { neighbor -> neighbor.matchesFilter(filter) } else return@any !neighbors.any { neighbor -> neighbor.matchesFilter(filter) }
} -> false } -> false
improvement.name == "Road" && roadStatus == RoadStatus.None && !isWater -> true !isWater && RoadStatus.values().any { it.name == improvement.name && it > roadStatus } -> true
improvement.name == "Railroad" && this.roadStatus != RoadStatus.Railroad && !isWater -> true improvement.name == roadStatus.removeAction -> true
improvement.name == "Remove Road" && this.roadStatus == RoadStatus.Road -> true
improvement.name == "Remove Railroad" && this.roadStatus == RoadStatus.Railroad -> true
topTerrain.unbuildable && !improvement.isAllowedOnFeature(topTerrain.name) -> false topTerrain.unbuildable && !improvement.isAllowedOnFeature(topTerrain.name) -> false
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests. // DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.
improvement.hasUnique("Can also be built on tiles adjacent to fresh water") && isAdjacentToFreshwater -> true improvement.hasUnique("Can also be built on tiles adjacent to fresh water") && isAdjacentToFreshwater -> true

View File

@ -25,16 +25,14 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
extraCost += 1 extraCost += 1
if (from.roadStatus == RoadStatus.Railroad && to.roadStatus == RoadStatus.Railroad) if (from.roadStatus == RoadStatus.Railroad && to.roadStatus == RoadStatus.Railroad)
return 1 / 10f + extraCost return RoadStatus.Railroad.movement + extraCost
val areConnectedByRoad = from.hasConnection(civInfo) && to.hasConnection(civInfo) val areConnectedByRoad = from.hasConnection(civInfo) && to.hasConnection(civInfo)
val areConnectedByRiver = from.isConnectedByRiver(to) val areConnectedByRiver = from.isConnectedByRiver(to)
if (areConnectedByRoad && (!areConnectedByRiver || civInfo.tech.roadsConnectAcrossRivers)) if (areConnectedByRoad && (!areConnectedByRiver || civInfo.tech.roadsConnectAcrossRivers))
{ return unit.civInfo.tech.movementSpeedOnRoads + extraCost
if (unit.civInfo.tech.movementSpeedOnRoadsImproved) return 1 / 3f + extraCost
else return 1 / 2f + extraCost
}
if (unit.ignoresTerrainCost) return 1f + extraCost if (unit.ignoresTerrainCost) return 1f + extraCost
if (areConnectedByRiver) return 100f // Rivers take the entire turn to cross if (areConnectedByRiver) return 100f // Rivers take the entire turn to cross

View File

@ -1,6 +1,7 @@
package com.unciv.models.ruleset.tile package com.unciv.models.ruleset.tile
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.RoadStatus
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.Unique import com.unciv.models.ruleset.Unique
import com.unciv.models.stats.NamedStats import com.unciv.models.stats.NamedStats
@ -72,6 +73,7 @@ class TileImprovement : NamedStats(), ICivilopediaText {
fun hasUnique(unique: String) = uniques.contains(unique) fun hasUnique(unique: String) = uniques.contains(unique)
fun isGreatImprovement() = hasUnique("Great Improvement") fun isGreatImprovement() = hasUnique("Great Improvement")
fun isRoad() = RoadStatus.values().any { it != RoadStatus.None && it.name == this.name }
/** /**
* Check: Is this improvement allowed on a [given][name] terrain feature? * Check: Is this improvement allowed on a [given][name] terrain feature?
@ -93,7 +95,7 @@ class TileImprovement : NamedStats(), ICivilopediaText {
return when (filter) { return when (filter) {
name -> true name -> true
"All" -> true "All" -> true
"All Road" -> name == "road" || name == "railroad" "All Road" -> isRoad()
"Great Improvement", "Great" -> isGreatImprovement() "Great Improvement", "Great" -> isGreatImprovement()
else -> false else -> false
} }

View File

@ -141,8 +141,8 @@ class MapEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(CameraS
improvementImage.onClick { improvementImage.onClick {
tileAction = { tileAction = {
when (improvement.name) { when (improvement.name) {
"Road" -> it.roadStatus = RoadStatus.Road RoadStatus.Road.name -> it.roadStatus = RoadStatus.Road
"Railroad" -> it.roadStatus = RoadStatus.Railroad RoadStatus.Railroad.name -> it.roadStatus = RoadStatus.Railroad
else -> it.improvement = improvement.name else -> it.improvement = improvement.name
} }
} }

View File

@ -264,7 +264,7 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo, centerOnTech: Tec
} }
private fun getTechProgressLabel(techs: List<String>): String { private fun getTechProgressLabel(techs: List<String>): String {
val progress = techs.sumBy { tech -> civTech.scienceSpentOnTech(tech) } val progress = techs.sumBy { tech -> civTech.researchOfTech(tech) }
val techCost = techs.sumBy { tech -> civInfo.tech.costOfTech(tech) } val techCost = techs.sumBy { tech -> civInfo.tech.costOfTech(tech) }
return "(${progress}/${techCost})" return "(${progress}/${techCost})"
} }

View File

@ -56,7 +56,6 @@ object UnitActions {
addSpreadReligionActions(unit, actionList, tile) addSpreadReligionActions(unit, actionList, tile)
addToggleActionsAction(unit, actionList, unitTable) addToggleActionsAction(unit, actionList, unitTable)
return actionList return actionList
@ -76,7 +75,6 @@ object UnitActions {
addGiftAction(unit, actionList, tile) addGiftAction(unit, actionList, tile)
addToggleActionsAction(unit, actionList, unitTable) addToggleActionsAction(unit, actionList, unitTable)
return actionList return actionList

View File

@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus
import com.unciv.models.UnitAction import com.unciv.models.UnitAction
import com.unciv.models.translations.equalsPlaceholderText import com.unciv.models.translations.equalsPlaceholderText
import com.unciv.models.translations.getPlaceholderParameters import com.unciv.models.translations.getPlaceholderParameters
@ -60,7 +61,9 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
"Start Golden Age" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Artist"), 'g') "Start Golden Age" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Artist"), 'g')
"Hurry Wonder" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'g') "Hurry Wonder" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'g')
"Conduct Trade Mission" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Merchant"), 'g') "Conduct Trade Mission" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Merchant"), 'g')
"Construct road" -> return UnitIconAndKey(ImageGetter.getImprovementIcon("Road"), 'r') // Deprecated since 3.15.4
"Construct road" -> return UnitIconAndKey(ImageGetter.getImprovementIcon(RoadStatus.Road.name), 'r')
//
"Paradrop" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Paratrooper"), 'p') "Paradrop" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Paratrooper"), 'p')
"Set up" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Catapult"), 't') "Set up" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Catapult"), 't')
"Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x') "Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x')