G&K Neutral Tile Road Maintenance (#8138)

* Redo

* New parameterized and actually used Road Unique

* Remove unneeded

* Update CURRENT_COMPATIBILITY_NUMBER since adding new serialization data

* Make neutralRoads for CivilizationInfo.kt Transient
This commit is contained in:
itanasi 2022-12-24 09:43:37 -08:00 committed by GitHub
parent 8d0acd0647
commit bc483e8984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 28 deletions

View File

@ -1,5 +1,5 @@
{
"name": "Global uniques",
"name": "Global uniques",
"uniques": [
"[-75]% growth [in all cities] <when between [-10] and [0] Happiness>",
"Nullifies Growth [in all cities] <when below [-10] Happiness>",
@ -7,19 +7,19 @@
"[-33]% Strength <for [All] units> <when below [-10] Happiness>",
"Cannot build [Settler] units <when below [-10] Happiness>",
"Rebel units may spawn <when below [-20] Happiness>"
// TODO: Implement the uniques below
// "[+20]% [Culture] [in all cities] <during a golden age>",
// "[+20]% [Production] [in all cities] <during a golden age>",
// "[+10]% growth [in all cities] <during We Love The King Day>",
// "Nullifies All Yield <while is in resistance>",
// "[-25]% [Science] [in pupetted cities]" -- Imo cityFilters should ideally become conditionals anyway
// "[-25]% [Culture] [in pupetted cities]"
// "[+20]% [Production] [in cities connected via railroad]"
// something something unit supply
]
}
}

View File

@ -108,8 +108,7 @@
"terrainsCanBeBuiltOn": ["Land"],
"turnsToBuild": 4,
"techRequired": "The Wheel",
// "Costs [1] gold per turn when in your territory" does nothing and is only to inform the user
"uniques": ["Can be built outside your borders", "Costs [1] gold per turn when in your territory"],
"uniques": ["Can be built outside your borders", "Costs [1] [Gold] per turn"],
"shortcutKey": "R",
"civilopediaText": [
{"text":"Reduces movement cost to ½ if the other tile also has a Road or Railroad"},
@ -122,7 +121,7 @@
"terrainsCanBeBuiltOn": ["Land"],
"turnsToBuild": 4,
"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"],
"shortcutKey": "R",
"civilopediaText": [{"text":"Reduces movement cost to ⅒ if the other tile also has a Railroad"}]
},

View File

@ -108,8 +108,7 @@
"terrainsCanBeBuiltOn": ["Land"],
"turnsToBuild": 4,
"techRequired": "The Wheel",
// "Costs [1] gold per turn when in your territory" does nothing and is only to inform the user
"uniques": ["Can be built outside your borders", "Costs [1] gold per turn when in your territory"],
"uniques": ["Can be built outside your borders", "Costs [1] [Gold] per turn when in your territory"],
"shortcutKey": "R",
"civilopediaText": [
{"text":"Reduces movement cost to ½ if the other tile also has a Road or Railroad"},
@ -122,7 +121,7 @@
"terrainsCanBeBuiltOn": ["Land"],
"turnsToBuild": 4,
"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",
"civilopediaText": [{"text":"Reduces movement cost to ⅒ if the other tile also has a Railroad"}]
},

View File

@ -66,7 +66,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
companion object {
/** The current compatibility version of [GameInfo]. This number is incremented whenever changes are made to the save file structure that guarantee that
* previous versions of the game will not be able to load or play a game normally. */
const val CURRENT_COMPATIBILITY_NUMBER = 2
const val CURRENT_COMPATIBILITY_NUMBER = 3
val CURRENT_COMPATIBILITY_VERSION = CompatibilityVersion(CURRENT_COMPATIBILITY_NUMBER, UncivGame.VERSION)
@ -498,6 +498,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
civInfo.thingsToFocusOnForVictory =
civInfo.getPreferredVictoryTypeObject()?.getThingsToFocus(civInfo) ?: setOf()
}
tileMap.setNeutralTransients() // has to happen after civInfo.setTransients() sets owningCity
convertFortify()

View File

@ -73,8 +73,8 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
return cost.toInt()
}
private fun getTransportationUpkeep(): Int {
var transportationUpkeep = 0f
private fun getTransportationUpkeep(): Stats {
val transportationUpkeep = Stats()
// we no longer use .flatMap, because there are a lot of tiles and keeping them all in a list
// just to go over them once is a waste of memory - there are low-end phones who don't have much ram
@ -88,14 +88,38 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
if (tile.isCityCenter()) continue
if (tile.getUnpillagedRoad() == RoadStatus.None) continue // Cheap checks before pricey checks
if (ignoredTileTypes.any { tile.matchesFilter(it, civInfo) }) continue
transportationUpkeep += tile.getUnpillagedRoad().upkeep
val road = tile.getUnpillagedRoadImprovement()
if (road!!.hasUnique(UniqueType.ImprovementMaintenance, StateForConditionals(civInfo, tile = tile))) {
for(unique in road.getMatchingUniques(UniqueType.ImprovementMaintenance)) {
transportationUpkeep.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
}
}
if (road.hasUnique(UniqueType.ImprovementAllMaintenance, StateForConditionals(civInfo, tile = tile))) {
for(unique in road.getMatchingUniques(UniqueType.ImprovementAllMaintenance)) {
transportationUpkeep.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
}
}
// backwards compatible
if (road.hasUnique(UniqueType.OldImprovementMaintenance, StateForConditionals(civInfo, tile = tile))) {
transportationUpkeep.add(Stat.Gold, tile.getUnpillagedRoad().upkeep.toFloat())
}
}
}
// tabulate neutral roads
for (position in civInfo.neutralRoads) {
val tile = civInfo.gameInfo.tileMap[position]
if (tile.getUnpillagedRoad() == RoadStatus.None) continue // Cheap checks before pricey checks
val road = tile.getUnpillagedRoadImprovement()
if (road!!.hasUnique(UniqueType.ImprovementAllMaintenance, StateForConditionals(civInfo, tile = tile))) {
for(unique in road.getMatchingUniques(UniqueType.ImprovementAllMaintenance)) {
transportationUpkeep.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
}
}
}
for (unique in civInfo.getMatchingUniques(UniqueType.RoadMaintenance))
transportationUpkeep *= unique.params[0].toPercent()
transportationUpkeep.times(unique.params[0].toPercent())
return transportationUpkeep.toInt()
return transportationUpkeep
}
fun getUnitSupply(): Int {
@ -156,7 +180,7 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
}
}
statMap["Transportation upkeep"] = Stats(gold = -getTransportationUpkeep().toFloat())
statMap["Transportation upkeep"] = getTransportationUpkeep() * -1
statMap["Unit upkeep"] = Stats(gold = -getUnitMaintenance().toFloat())

View File

@ -145,6 +145,9 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
@Transient
var thingsToFocusOnForVictory = setOf<Victory.Focus>()
@Transient
var neutralRoads = HashSet<Vector2>()
var playerType = PlayerType.AI
/** Used in online multiplayer for human players */
@ -280,6 +283,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
toReturn.diplomacy[diplomacyManager.otherCivName] = diplomacyManager
toReturn.proximity.putAll(proximity)
toReturn.cities = cities.map { it.clone() }
toReturn.neutralRoads = neutralRoads
// This is the only thing that is NOT switched out, which makes it a source of ConcurrentModification errors.
// Cloning it by-pointer is a horrific move, since the serialization would go over it ANYWAY and still lead to concurrency problems.

View File

@ -44,10 +44,20 @@ open class TileInfo : IsPartOfGameInfoSerialization {
private set
fun setOwningCity(city:CityInfo?){
if (city != null) {
if (roadStatus != RoadStatus.None && roadOwner != "") {
// remove previous neutral tile owner
getRoadOwner()!!.neutralRoads.remove(this.position)
}
roadOwner = city.civInfo.civName // only when taking control, otherwise last owner
} else {
if (roadStatus != RoadStatus.None && owningCity != null) {
// previous tile owner still owns road, add to tracker
owningCity!!.civInfo.neutralRoads.add(this.position)
}
}
owningCity = city
isCityCenterInternal = getCity()?.location == position
if (city != null) // only when taking control, otherwise last owner
roadOwner = city.civInfo.civName
}
@Transient
@ -272,6 +282,10 @@ open class TileInfo : IsPartOfGameInfoSerialization {
else
roadStatus
}
fun getUnpillagedRoadImprovement(): TileImprovement? {
return if (getUnpillagedRoad() == RoadStatus.None) null
else ruleset.tileImprovements[getUnpillagedRoad().name]
}
fun changeImprovement(improvementStr: String?) {
improvementIsPillaged = false
@ -282,16 +296,20 @@ open class TileInfo : IsPartOfGameInfoSerialization {
fun addRoad(roadType: RoadStatus, unitCivInfo: CivilizationInfo) {
roadStatus = roadType
roadIsPillaged = false
roadOwner = if (getOwner() == null)
unitCivInfo.civName // neutral tile, use building unit
else
getOwner()!!.civName
if (getOwner() == null) {
roadOwner = unitCivInfo.civName // neutral tile, use building unit
unitCivInfo.neutralRoads.add(this.position)
} else {
roadOwner = getOwner()!!.civName
}
}
// function handling when removing a road from the tile
fun removeRoad() {
roadStatus = RoadStatus.None
roadIsPillaged = false
if (owningCity == null)
getRoadOwner()!!.neutralRoads.remove(this.position)
}
fun getShownImprovement(viewingCiv: CivilizationInfo?): String? {
@ -1165,6 +1183,7 @@ open class TileInfo : IsPartOfGameInfoSerialization {
fun setTransients() {
setTerrainTransients()
setUnitTransients(true)
setOwnerTransients()
}
fun setTerrainTransients() {
@ -1194,6 +1213,11 @@ open class TileInfo : IsPartOfGameInfoSerialization {
}
}
fun setOwnerTransients() {
if (owningCity == null && roadOwner != "")
getRoadOwner()!!.neutralRoads.add(this.position)
}
fun stripUnits() {
for (unit in this.getUnits()) removeUnit(unit)
}

View File

@ -459,6 +459,14 @@ class TileMap : IsPartOfGameInfoSerialization {
}
}
/** Initialize based on TileInfo which Civ has neutral tile roads
*/
fun setNeutralTransients() {
for (tileInfo in values) {
tileInfo.setOwnerTransients()
}
}
fun removeMissingTerrainModReferences(ruleSet: Ruleset) {
for (tile in this.values) {
for (terrainFeature in tile.terrainFeatures.filter { !ruleSet.terrains.containsKey(it) })

View File

@ -565,7 +565,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
RemovesFeaturesIfBuilt("Removes removable features when built", UniqueTarget.Improvement),
DefensiveBonus("Gives a defensive bonus of [relativeAmount]%", UniqueTarget.Improvement),
ImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // Unused
ImprovementMaintenance("Costs [amount] [stat] per turn when in your territory", UniqueTarget.Improvement), // Roads
ImprovementAllMaintenance("Costs [amount] [stat] per turn", UniqueTarget.Improvement), // Roads
//@Deprecated("as of 4.3.9", ReplaceWith("Costs [amount] [stats] per turn when in your territory"), DeprecationLevel.ERROR)
OldImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // unused
DamagesAdjacentEnemyUnits("Adjacent enemy units ending their turn take [amount] damage", UniqueTarget.Improvement),
TakeOverTilesAroundWhenBuilt("Constructing it will take over the tiles around it and assign them to your closest city", UniqueTarget.Improvement),