mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 13:55:54 -04:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
3103984250
BIN
android/Images/OtherIcons/Cultured.png
Normal file
BIN
android/Images/OtherIcons/Cultured.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
BIN
android/Images/OtherIcons/Maritime.png
Normal file
BIN
android/Images/OtherIcons/Maritime.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
BIN
android/Images/OtherIcons/Mercantile.png
Normal file
BIN
android/Images/OtherIcons/Mercantile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
BIN
android/Images/OtherIcons/Militaristic.png
Normal file
BIN
android/Images/OtherIcons/Militaristic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
android/Images/OtherIcons/Religious.png
Normal file
BIN
android/Images/OtherIcons/Religious.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
@ -16,13 +16,13 @@
|
||||
"Cultured": ["Provides [3] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [2] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [6] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [2] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [255, 87, 35]
|
||||
},
|
||||
@ -42,13 +42,13 @@
|
||||
"Cultured": ["Provides [3] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [2] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [6] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [2] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [233, 31, 99]
|
||||
},
|
||||
@ -70,13 +70,13 @@
|
||||
"Cultured": ["Provides [6] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [12] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [157, 39, 176]
|
||||
},
|
||||
@ -99,13 +99,13 @@
|
||||
"Cultured": ["Provides [6] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [12] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [104, 58, 183]
|
||||
},
|
||||
@ -129,13 +129,13 @@
|
||||
"Cultured": ["Provides [13] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [26] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [63, 81, 182],
|
||||
"uniques": ["May not generate great prophet equivalents naturally",
|
||||
@ -164,13 +164,13 @@
|
||||
"Cultured": ["Provides [13] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [26] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [33, 150, 243],
|
||||
"uniques": ["May not generate great prophet equivalents naturally",
|
||||
@ -200,13 +200,13 @@
|
||||
"Cultured": ["Provides [13] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [26] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [0, 150, 136],
|
||||
"uniques": ["May not generate great prophet equivalents naturally",
|
||||
@ -240,13 +240,13 @@
|
||||
"Cultured": ["Provides [13] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [26] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [76, 176, 81],
|
||||
"uniques": ["May not generate great prophet equivalents naturally",
|
||||
@ -279,13 +279,13 @@
|
||||
"Cultured": ["Provides [13] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]"],
|
||||
"Mercantile": ["Provides [3] Happiness"],
|
||||
"Militaristic": ["Provides military units every [20] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[20] turns"]
|
||||
},
|
||||
"allyBonus": {
|
||||
"Cultured": ["Provides [26] [Culture] per turn"],
|
||||
"Maritime": ["Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]"],
|
||||
"Mercantile": ["Provides [3] Happiness", "Provides a unique luxury"],
|
||||
"Militaristic": ["Provides military units every [17] turns"]
|
||||
"Militaristic": ["Provides military units every ≈[17] turns"]
|
||||
},
|
||||
"iconRGB": [76, 176, 81],
|
||||
"uniques": ["May not generate great prophet equivalents naturally",
|
||||
|
@ -163,6 +163,7 @@ Pledge to protect =
|
||||
Declare Protection of [cityStateName]? =
|
||||
Build [improvementName] on [resourceName] (200 Gold) =
|
||||
Gift Improvement =
|
||||
[civName] is able to provide [unitName] once [techName] is researched. =
|
||||
|
||||
Diplomatic Marriage ([amount] Gold) =
|
||||
We have married into the ruling family of [civName], bringing them under our control. =
|
||||
|
131
core/src/com/unciv/logic/BackwardCompatibility.kt
Normal file
131
core/src/com/unciv/logic/BackwardCompatibility.kt
Normal file
@ -0,0 +1,131 @@
|
||||
package com.unciv.logic
|
||||
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
import com.unciv.logic.civilization.TechManager
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
|
||||
/**
|
||||
* Container for all temporarily used code managing transitions from deprecated elements to their replacements.
|
||||
*
|
||||
* Please place ***all*** such code here and call it _only_ from [GameInfo.setTransients].
|
||||
* Functions are allowed to remain once no longer used if you think they might serve as template for
|
||||
* similar usecases in the future. Please comment sufficiently :)
|
||||
*/
|
||||
@Suppress("unused") // as mentioned above
|
||||
object BackwardCompatibility {
|
||||
|
||||
/**
|
||||
* Mods can change, leading to things on the map that are no longer defined in the mod.
|
||||
* This function removes them so the game doesn't crash when it tries to access them.
|
||||
*/
|
||||
fun GameInfo.removeMissingModReferences() {
|
||||
for (tile in tileMap.values) {
|
||||
for (terrainFeature in tile.terrainFeatures.filter{ !ruleSet.terrains.containsKey(it) })
|
||||
tile.terrainFeatures.remove(terrainFeature)
|
||||
if (tile.resource != null && !ruleSet.tileResources.containsKey(tile.resource!!))
|
||||
tile.resource = null
|
||||
if (tile.improvement != null && !ruleSet.tileImprovements.containsKey(tile.improvement!!))
|
||||
tile.improvement = null
|
||||
|
||||
for (unit in tile.getUnits()) {
|
||||
if (!ruleSet.units.containsKey(unit.name)) tile.removeUnit(unit)
|
||||
|
||||
for (promotion in unit.promotions.promotions.toList())
|
||||
if (!ruleSet.unitPromotions.containsKey(promotion))
|
||||
unit.promotions.promotions.remove(promotion)
|
||||
}
|
||||
}
|
||||
|
||||
for (city in civilizations.asSequence().flatMap { it.cities.asSequence() }) {
|
||||
|
||||
for (building in city.cityConstructions.builtBuildings.toHashSet())
|
||||
if (!ruleSet.buildings.containsKey(building))
|
||||
city.cityConstructions.builtBuildings.remove(building)
|
||||
|
||||
fun isInvalidConstruction(construction: String) =
|
||||
!ruleSet.buildings.containsKey(construction)
|
||||
&& !ruleSet.units.containsKey(construction)
|
||||
&& !PerpetualConstruction.perpetualConstructionsMap.containsKey(construction)
|
||||
|
||||
// Remove invalid buildings or units from the queue - don't just check buildings and units because it might be a special construction as well
|
||||
for (construction in city.cityConstructions.constructionQueue.toList()) {
|
||||
if (isInvalidConstruction(construction))
|
||||
city.cityConstructions.constructionQueue.remove(construction)
|
||||
}
|
||||
// And from being in progress
|
||||
for (construction in city.cityConstructions.inProgressConstructions.keys.toList())
|
||||
if (isInvalidConstruction(construction))
|
||||
city.cityConstructions.inProgressConstructions.remove(construction)
|
||||
}
|
||||
|
||||
for (civInfo in civilizations) {
|
||||
for (tech in civInfo.tech.techsResearched.toList())
|
||||
if (!ruleSet.technologies.containsKey(tech))
|
||||
civInfo.tech.techsResearched.remove(tech)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of [oldBuildingName] in [cityConstructions] with [newBuildingName]
|
||||
* if the former is not contained in the ruleset.
|
||||
*/
|
||||
private fun changeBuildingNameIfNotInRuleset(
|
||||
ruleSet: Ruleset,
|
||||
cityConstructions: CityConstructions,
|
||||
oldBuildingName: String,
|
||||
newBuildingName: String
|
||||
) {
|
||||
if (ruleSet.buildings.containsKey(oldBuildingName))
|
||||
return
|
||||
// Replace in built buildings
|
||||
if (cityConstructions.builtBuildings.contains(oldBuildingName)) {
|
||||
cityConstructions.builtBuildings.remove(oldBuildingName)
|
||||
cityConstructions.builtBuildings.add(newBuildingName)
|
||||
}
|
||||
// Replace in construction queue
|
||||
if (!cityConstructions.builtBuildings.contains(newBuildingName) && !cityConstructions.constructionQueue.contains(newBuildingName))
|
||||
cityConstructions.constructionQueue = cityConstructions.constructionQueue
|
||||
.map { if (it == oldBuildingName) newBuildingName else it }
|
||||
.toMutableList()
|
||||
else
|
||||
cityConstructions.constructionQueue.remove(oldBuildingName)
|
||||
// Replace in in-progress constructions
|
||||
if (cityConstructions.inProgressConstructions.containsKey(oldBuildingName)) {
|
||||
if (!cityConstructions.builtBuildings.contains(newBuildingName) && !cityConstructions.inProgressConstructions.containsKey(newBuildingName))
|
||||
cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[oldBuildingName]!!
|
||||
cityConstructions.inProgressConstructions.remove(oldBuildingName)
|
||||
}
|
||||
}
|
||||
|
||||
/** Replace a changed tech name */
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/** Replace a deprecated DiplomacyFlags instance */
|
||||
fun GameInfo.replaceDiplomacyFlag(old: DiplomacyFlags, new: DiplomacyFlags) {
|
||||
fun DiplomacyManager.replaceFlag() {
|
||||
if (hasFlag(old)) {
|
||||
val value = getFlag(old)
|
||||
removeFlag(old)
|
||||
setFlag(new, value)
|
||||
}
|
||||
}
|
||||
civilizations.flatMap { civ -> civ.diplomacy.values }.forEach { it.replaceFlag() }
|
||||
}
|
||||
|
||||
}
|
@ -2,10 +2,11 @@ package com.unciv.logic
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.BackwardCompatibility.removeMissingModReferences
|
||||
import com.unciv.logic.BackwardCompatibility.replaceDiplomacyFlag
|
||||
import com.unciv.logic.automation.NextTurnAutomation
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
import com.unciv.logic.civilization.*
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.Religion
|
||||
@ -18,20 +19,7 @@ import java.util.*
|
||||
class UncivShowableException(missingMods: String) : Exception(missingMods)
|
||||
|
||||
class GameInfo {
|
||||
@Transient
|
||||
lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn
|
||||
|
||||
@Transient
|
||||
lateinit var currentPlayerCiv: CivilizationInfo // this is called thousands of times, no reason to search for it with a find{} every time
|
||||
|
||||
/** This is used in multiplayer games, where I may have a saved game state on my phone
|
||||
* that is inconsistent with the saved game on the cloud */
|
||||
@Transient
|
||||
var isUpToDate = false
|
||||
|
||||
@Transient
|
||||
lateinit var ruleSet: Ruleset
|
||||
|
||||
//region Fields - Serialized
|
||||
var civilizations = mutableListOf<CivilizationInfo>()
|
||||
var religions: HashMap<String, Religion> = hashMapOf()
|
||||
var difficulty = "Chieftain" // difficulty is game-wide, think what would happen if 2 human players could play on different difficulties?
|
||||
@ -54,15 +42,36 @@ class GameInfo {
|
||||
@Volatile
|
||||
var customSaveLocation: String? = null
|
||||
|
||||
//endregion
|
||||
//region Fields - Transient
|
||||
|
||||
@Transient
|
||||
lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn
|
||||
|
||||
@Transient
|
||||
lateinit var currentPlayerCiv: CivilizationInfo // this is called thousands of times, no reason to search for it with a find{} every time
|
||||
|
||||
/** This is used in multiplayer games, where I may have a saved game state on my phone
|
||||
* that is inconsistent with the saved game on the cloud */
|
||||
@Transient
|
||||
var isUpToDate = false
|
||||
|
||||
@Transient
|
||||
lateinit var ruleSet: Ruleset
|
||||
|
||||
/** Simulate until any player wins,
|
||||
* or turns exceeds indicated number
|
||||
* Does not update World View until finished.
|
||||
* Should be set manually on each new game start.
|
||||
*/
|
||||
@Transient
|
||||
var simulateMaxTurns: Int = 1000
|
||||
@Transient
|
||||
var simulateUntilWin = false
|
||||
|
||||
//region pure functions
|
||||
//endregion
|
||||
//region Pure functions
|
||||
|
||||
fun clone(): GameInfo {
|
||||
val toReturn = GameInfo()
|
||||
toReturn.tileMap = tileMap.clone()
|
||||
@ -103,7 +112,14 @@ class GameInfo {
|
||||
fun getCities() = civilizations.asSequence().flatMap { it.cities }
|
||||
fun getAliveCityStates() = civilizations.filter { it.isAlive() && it.isCityState() }
|
||||
fun getAliveMajorCivs() = civilizations.filter { it.isAlive() && it.isMajorCiv() }
|
||||
|
||||
fun hasReligionEnabled() =
|
||||
// Temporary function to check whether religion should be used for this game
|
||||
(gameParameters.religionEnabled || ruleSet.hasReligion())
|
||||
&& (ruleSet.eras.isEmpty() || !ruleSet.eras[gameParameters.startingEra]!!.hasUnique("Starting in this era disables religion"))
|
||||
|
||||
//endregion
|
||||
//region State changing functions
|
||||
|
||||
fun nextTurn() {
|
||||
val previousHumanPlayer = getCurrentPlayerCivilization()
|
||||
@ -236,7 +252,7 @@ class GameInfo {
|
||||
return tile
|
||||
}
|
||||
|
||||
fun placeBarbarianUnit(tileToPlace: TileInfo) {
|
||||
private fun placeBarbarianUnit(tileToPlace: TileInfo) {
|
||||
// if we don't make this into a separate list then the retain() will happen on the Tech keys,
|
||||
// which effectively removes those techs from the game and causes all sorts of problems
|
||||
val allResearchedTechs = ruleSet.technologies.keys.toMutableList()
|
||||
@ -252,10 +268,9 @@ class GameInfo {
|
||||
val landUnits = unitList.filter { it.isLandUnit() }
|
||||
val waterUnits = unitList.filter { it.isWaterUnit() }
|
||||
|
||||
val unit: String
|
||||
if (waterUnits.isNotEmpty() && tileToPlace.isCoastalTile() && Random().nextBoolean())
|
||||
unit = waterUnits.random().name
|
||||
else unit = landUnits.random().name
|
||||
val unit: String = if (waterUnits.isNotEmpty() && tileToPlace.isCoastalTile() && Random().nextBoolean())
|
||||
waterUnits.random().name
|
||||
else landUnits.random().name
|
||||
|
||||
tileMap.placeUnitNearTile(tileToPlace.position, unit, getBarbarianCivilization())
|
||||
}
|
||||
@ -264,7 +279,7 @@ class GameInfo {
|
||||
* [CivilizationInfo.addNotification][Add a notification] to every civilization that have
|
||||
* adopted Honor policy and have explored the [tile] where the Barbarian Encampment has spawned.
|
||||
*/
|
||||
fun notifyCivsOfBarbarianEncampment(tile: TileInfo) {
|
||||
private fun notifyCivsOfBarbarianEncampment(tile: TileInfo) {
|
||||
civilizations.filter {
|
||||
it.hasUnique("Notified of new Barbarian encampments")
|
||||
&& it.exploredTiles.contains(tile.position)
|
||||
@ -289,6 +304,8 @@ class GameInfo {
|
||||
|
||||
removeMissingModReferences()
|
||||
|
||||
replaceDiplomacyFlag(DiplomacyFlags.Denunceation, DiplomacyFlags.Denunciation)
|
||||
|
||||
for (baseUnit in ruleSet.units.values)
|
||||
baseUnit.ruleset = ruleSet
|
||||
|
||||
@ -338,105 +355,7 @@ class GameInfo {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mods can change, leading to things on the map that are no longer defined in the mod.
|
||||
// So we remove them so the game doesn't crash when it tries to access them.
|
||||
private fun removeMissingModReferences() {
|
||||
for (tile in tileMap.values) {
|
||||
for (terrainFeature in tile.terrainFeatures.filter{ !ruleSet.terrains.containsKey(it) })
|
||||
tile.terrainFeatures.remove(terrainFeature)
|
||||
if (tile.resource != null && !ruleSet.tileResources.containsKey(tile.resource!!))
|
||||
tile.resource = null
|
||||
if (tile.improvement != null && !ruleSet.tileImprovements.containsKey(tile.improvement!!))
|
||||
tile.improvement = null
|
||||
|
||||
for (unit in tile.getUnits()) {
|
||||
if (!ruleSet.units.containsKey(unit.name)) tile.removeUnit(unit)
|
||||
|
||||
for (promotion in unit.promotions.promotions.toList())
|
||||
if (!ruleSet.unitPromotions.containsKey(promotion))
|
||||
unit.promotions.promotions.remove(promotion)
|
||||
}
|
||||
}
|
||||
|
||||
for (city in civilizations.asSequence().flatMap { it.cities.asSequence() }) {
|
||||
|
||||
for (building in city.cityConstructions.builtBuildings.toHashSet())
|
||||
if (!ruleSet.buildings.containsKey(building))
|
||||
city.cityConstructions.builtBuildings.remove(building)
|
||||
|
||||
fun isInvalidConstruction(construction: String) =
|
||||
!ruleSet.buildings.containsKey(construction)
|
||||
&& !ruleSet.units.containsKey(construction)
|
||||
&& !PerpetualConstruction.perpetualConstructionsMap.containsKey(construction)
|
||||
|
||||
// Remove invalid buildings or units from the queue - don't just check buildings and units because it might be a special construction as well
|
||||
for (construction in city.cityConstructions.constructionQueue.toList()) {
|
||||
if (isInvalidConstruction(construction))
|
||||
city.cityConstructions.constructionQueue.remove(construction)
|
||||
}
|
||||
// And from being in progress
|
||||
for (construction in city.cityConstructions.inProgressConstructions.keys.toList())
|
||||
if (isInvalidConstruction(construction))
|
||||
city.cityConstructions.inProgressConstructions.remove(construction)
|
||||
}
|
||||
|
||||
for (civinfo in civilizations) {
|
||||
for (tech in civinfo.tech.techsResearched.toList())
|
||||
if (!ruleSet.technologies.containsKey(tech))
|
||||
civinfo.tech.techsResearched.remove(tech)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of [oldBuildingName] in [cityConstructions] with [newBuildingName]
|
||||
* if the former is not contained in the ruleset.
|
||||
* This function can be used for backwards compatibility with older save files when a building
|
||||
* 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) {
|
||||
if (ruleSet.buildings.containsKey(oldBuildingName))
|
||||
return
|
||||
// Replace in built buildings
|
||||
if (cityConstructions.builtBuildings.contains(oldBuildingName)) {
|
||||
cityConstructions.builtBuildings.remove(oldBuildingName)
|
||||
cityConstructions.builtBuildings.add(newBuildingName)
|
||||
}
|
||||
// Replace in construction queue
|
||||
if (!cityConstructions.builtBuildings.contains(newBuildingName) && !cityConstructions.constructionQueue.contains(newBuildingName))
|
||||
cityConstructions.constructionQueue = cityConstructions.constructionQueue.map{ if (it == oldBuildingName) newBuildingName else it }.toMutableList()
|
||||
else
|
||||
cityConstructions.constructionQueue.remove(oldBuildingName)
|
||||
// Replace in in-progress constructions
|
||||
if (cityConstructions.inProgressConstructions.containsKey(oldBuildingName)) {
|
||||
if (!cityConstructions.builtBuildings.contains(newBuildingName) && !cityConstructions.inProgressConstructions.containsKey(newBuildingName))
|
||||
cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[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() =
|
||||
// Temporary function to check whether religion should be used for this game
|
||||
(gameParameters.religionEnabled || ruleSet.hasReligion())
|
||||
&& (ruleSet.eras.isEmpty() || !ruleSet.eras[gameParameters.startingEra]!!.hasUnique("Starting in this era disables religion"))
|
||||
//endregion
|
||||
}
|
||||
|
||||
// reduced variant only for load preview
|
||||
|
@ -193,23 +193,17 @@ object GameStarter {
|
||||
// and then all the other City-States in a random order! Because the sortedBy function is stable!
|
||||
availableCityStatesNames.addAll(ruleset.nations.filter { it.value.isCityState() }.keys
|
||||
.shuffled().sortedByDescending { it in civNamesWithStartingLocations })
|
||||
|
||||
val allMercantileResources = ruleset.tileResources.values.filter { it.unique == "Can only be created by Mercantile City-States" }.map { it.name }
|
||||
val unusedMercantileResources = Stack<String>()
|
||||
unusedMercantileResources.addAll(allMercantileResources.shuffled())
|
||||
|
||||
for (cityStateName in availableCityStatesNames.take(newGameParameters.numberOfCityStates)) {
|
||||
var addedCityStates = 0
|
||||
// Keep trying to add city states until we reach the target number.
|
||||
while (addedCityStates < newGameParameters.numberOfCityStates) {
|
||||
if (availableCityStatesNames.isEmpty()) // We ran out of city-states somehow
|
||||
break
|
||||
val cityStateName = availableCityStatesNames.pop()
|
||||
val civ = CivilizationInfo(cityStateName)
|
||||
civ.cityStatePersonality = CityStatePersonality.values().random()
|
||||
civ.cityStateResource = when {
|
||||
ruleset.nations[cityStateName]?.cityStateType != CityStateType.Mercantile -> null
|
||||
allMercantileResources.isEmpty() -> null
|
||||
unusedMercantileResources.empty() -> allMercantileResources.random() // When unused luxuries exhausted, random
|
||||
else -> unusedMercantileResources.pop() // First pick an unused luxury if possible
|
||||
}
|
||||
if (civ.initCityState(ruleset, newGameParameters.startingEra, availableCivNames)) { // true if successful init
|
||||
gameInfo.civilizations.add(civ)
|
||||
for (tech in startingTechs)
|
||||
civ.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
|
||||
addedCityStates++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ object NextTurnAutomation {
|
||||
val requestingCiv = civInfo.gameInfo.getCivilization(popupAlert.value)
|
||||
val diploManager = civInfo.getDiplomacyManager(requestingCiv)
|
||||
if (diploManager.relationshipLevel() > RelationshipLevel.Neutral
|
||||
&& !diploManager.otherCivDiplomacy().hasFlag(DiplomacyFlags.Denunceation)) {
|
||||
&& !diploManager.otherCivDiplomacy().hasFlag(DiplomacyFlags.Denunciation)) {
|
||||
diploManager.signDeclarationOfFriendship()
|
||||
requestingCiv.addNotification("We have signed a Declaration of Friendship with [${civInfo.civName}]!", NotificationIcon.Diplomacy, civInfo.civName)
|
||||
} else requestingCiv.addNotification("[${civInfo.civName}] has denied our Declaration of Friendship!", NotificationIcon.Diplomacy, civInfo.civName)
|
||||
@ -206,8 +206,9 @@ object NextTurnAutomation {
|
||||
}
|
||||
|
||||
// Bonus for luxury resources we can get from them
|
||||
value += cityState.detailedCivResources.count { it.resource.resourceType == ResourceType.Luxury
|
||||
&& it.resource !in civInfo.detailedCivResources.map { it.resource }
|
||||
value += cityState.detailedCivResources.count {
|
||||
it.resource.resourceType == ResourceType.Luxury
|
||||
&& it.resource !in civInfo.detailedCivResources.map { supply -> supply.resource }
|
||||
}
|
||||
|
||||
return value
|
||||
@ -340,8 +341,8 @@ object NextTurnAutomation {
|
||||
|
||||
private fun chooseBeliefs(civInfo: CivilizationInfo, beliefContainer: BeliefContainer): HashSet<Belief> {
|
||||
val chosenBeliefs = hashSetOf<Belief>()
|
||||
// The 'continues' should never be reached, but just in case I'd rather have AI have a
|
||||
// belief less than make the game crash. The 'continue's should only be reached whenever
|
||||
// The `continue`s should never be reached, but just in case I'd rather have the AI have a
|
||||
// belief less than make the game crash. The `continue`s should only be reached whenever
|
||||
// there are not enough beliefs to choose, but there should be, as otherwise we could
|
||||
// not have used a great prophet to found/enhance our religion.
|
||||
for (counter in 0 until beliefContainer.pantheonBeliefCount)
|
||||
@ -436,7 +437,7 @@ object NextTurnAutomation {
|
||||
it.isMajorCiv() && !it.isAtWarWith(civInfo)
|
||||
&& it.getDiplomacyManager(civInfo).relationshipLevel() > RelationshipLevel.Neutral
|
||||
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclarationOfFriendship)
|
||||
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.Denunceation)
|
||||
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.Denunciation)
|
||||
}
|
||||
.sortedByDescending { it.getDiplomacyManager(civInfo).relationshipLevel() }
|
||||
for (civ in civsThatWeCanDeclareFriendshipWith) {
|
||||
|
@ -6,6 +6,7 @@ import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.models.metadata.GameSpeed
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.getPlaceholderParameters
|
||||
import com.unciv.models.translations.getPlaceholderText
|
||||
@ -18,6 +19,51 @@ import kotlin.math.pow
|
||||
|
||||
/** Class containing city-state-specific functions */
|
||||
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
|
||||
|
||||
val startingTechs = ruleset.technologies.values.filter { it.uniques.contains("Starting tech") }
|
||||
for (tech in startingTechs)
|
||||
civInfo.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
|
||||
|
||||
val allMercantileResources = ruleset.tileResources.values.filter { it.unique == "Can only be created by Mercantile City-States" }.map { it.name }
|
||||
val allPossibleBonuses = HashSet<String>() // We look through these to determine what kind of city state we are
|
||||
for (era in ruleset.eras.values) {
|
||||
val allyBonuses = era.allyBonus[cityStateType.name]
|
||||
val friendBonuses = era.friendBonus[cityStateType.name]
|
||||
if (allyBonuses != null)
|
||||
allPossibleBonuses.addAll(allyBonuses)
|
||||
if (friendBonuses != null)
|
||||
allPossibleBonuses.addAll(friendBonuses)
|
||||
}
|
||||
|
||||
// CS Personality
|
||||
civInfo.cityStatePersonality = CityStatePersonality.values().random()
|
||||
|
||||
// Mercantile bonus resources
|
||||
if ("Provides a unique luxury" in allPossibleBonuses
|
||||
|| (allPossibleBonuses.isEmpty() && cityStateType == CityStateType.Mercantile)) { // Fallback for badly defined Eras.json
|
||||
civInfo.cityStateResource = allMercantileResources.random()
|
||||
}
|
||||
|
||||
// Unique unit for militaristic city-states
|
||||
if (allPossibleBonuses.any { it.getPlaceholderText() == "Provides military units every ≈[] turns" }
|
||||
|| (allPossibleBonuses.isEmpty() && cityStateType == CityStateType.Militaristic)) { // Fallback for badly defined Eras.json
|
||||
|
||||
val possibleUnits = ruleset.units.values.filter { it.requiredTech != null
|
||||
&& ruleset.eras[ruleset.technologies[it.requiredTech!!]!!.era()]!!.eraNumber > ruleset.eras[startingEra]!!.eraNumber // Not from the start era or before
|
||||
&& it.uniqueTo != null && it.uniqueTo in unusedMajorCivs // Must be from a major civ not in the game
|
||||
&& ruleset.unitTypes[it.unitType]!!.isLandUnit() && ( it.strength > 0 || it.rangedStrength > 0 ) } // Must be a land military unit
|
||||
if (possibleUnits.isNotEmpty())
|
||||
civInfo.cityStateUniqueUnit = possibleUnits.random().name
|
||||
}
|
||||
|
||||
// TODO: Return false if attempting to put a religious city-state in a game without religion
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** Gain a random great person from the city state */
|
||||
fun giveGreatPersonToPatron(receivingCiv: CivilizationInfo) {
|
||||
@ -37,7 +83,12 @@ class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||
fun giveMilitaryUnitToPatron(receivingCiv: CivilizationInfo) {
|
||||
val cities = NextTurnAutomation.getClosestCities(receivingCiv, civInfo)
|
||||
val city = cities.city1
|
||||
val militaryUnit = city.cityConstructions.getConstructableUnits()
|
||||
val uniqueUnit = civInfo.gameInfo.ruleSet.units[civInfo.cityStateUniqueUnit]
|
||||
// If the receiving civ has discovered the required tech and not the obsolete tech for our unique, always give them the unique
|
||||
val militaryUnit = if (uniqueUnit != null && receivingCiv.tech.isResearched(uniqueUnit.requiredTech!!)
|
||||
&& (uniqueUnit.obsoleteTech == null || !receivingCiv.tech.isResearched(uniqueUnit.obsoleteTech!!))) uniqueUnit
|
||||
// Otherwise pick at random
|
||||
else city.cityConstructions.getConstructableUnits()
|
||||
.filter { !it.isCivilian() && it.isLandUnit() && it.uniqueTo==null }
|
||||
.toList().random()
|
||||
// placing the unit may fail - in that case stay quiet
|
||||
|
@ -1,10 +1,11 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
enum class CityStateType(val color: String = "") {
|
||||
Cultured("#8b60ff"),
|
||||
Maritime("#38ff70"),
|
||||
Mercantile("#ffd800"),
|
||||
Militaristic("#ff0000")
|
||||
enum class CityStateType(val color: String = "", val icon: String) {
|
||||
Cultured("#8b60ff", "OtherIcons/Cultured"),
|
||||
Maritime("#38ff70", "OtherIcons/Maritime"),
|
||||
Mercantile("#ffd800", "OtherIcons/Mercantile"),
|
||||
Militaristic("#ff0000", "OtherIcons/Militaristic"),
|
||||
//Religious("#FFFFFF", "OtherIcons/Religious")
|
||||
}
|
||||
|
||||
enum class CityStatePersonality {
|
||||
|
@ -174,6 +174,7 @@ class CivilizationInfo {
|
||||
toReturn.naturalWonders.addAll(naturalWonders)
|
||||
toReturn.cityStatePersonality = cityStatePersonality
|
||||
toReturn.cityStateResource = cityStateResource
|
||||
toReturn.cityStateUniqueUnit = cityStateUniqueUnit
|
||||
toReturn.flagsCountdown.putAll(flagsCountdown)
|
||||
toReturn.temporaryUniques.addAll(temporaryUniques)
|
||||
toReturn.boughtConstructionsWithGloballyIncreasingPrice.putAll(boughtConstructionsWithGloballyIncreasingPrice)
|
||||
@ -208,6 +209,7 @@ class CivilizationInfo {
|
||||
val cityStateType: CityStateType get() = nation.cityStateType!!
|
||||
var cityStatePersonality: CityStatePersonality = CityStatePersonality.Neutral
|
||||
var cityStateResource: String? = null
|
||||
var cityStateUniqueUnit: String? = null // Unique unit for militaristic city state. Might still be null if there are no appropriate units
|
||||
fun isMajorCiv() = nation.isMajorCiv()
|
||||
fun isAlive(): Boolean = !isDefeated()
|
||||
fun hasEverBeenFriendWith(otherCiv: CivilizationInfo): Boolean = getDiplomacyManager(otherCiv).everBeenFriends()
|
||||
@ -894,6 +896,9 @@ class CivilizationInfo {
|
||||
}
|
||||
|
||||
//////////////////////// City State wrapper functions ////////////////////////
|
||||
|
||||
fun initCityState(ruleset: Ruleset, startingEra: String, unusedMajorCivs: Collection<String>)
|
||||
= cityStateFunctions.initCityState(ruleset, startingEra, unusedMajorCivs)
|
||||
/** Gain a random great person from the city state */
|
||||
private fun giveGreatPersonToPatron(receivingCiv: CivilizationInfo) {
|
||||
cityStateFunctions.giveGreatPersonToPatron(receivingCiv)
|
||||
|
@ -32,6 +32,7 @@ enum class DiplomacyFlags {
|
||||
DeclaredWar,
|
||||
DeclarationOfFriendship,
|
||||
ResearchAgreement,
|
||||
@Deprecated("Deprecated after 3.16.13", ReplaceWith("Denunciation"))
|
||||
Denunceation,
|
||||
BorderConflict,
|
||||
SettledCitiesNearUs,
|
||||
@ -43,7 +44,8 @@ enum class DiplomacyFlags {
|
||||
NotifiedAfraid,
|
||||
RecentlyPledgedProtection,
|
||||
RecentlyWithdrewProtection,
|
||||
AngerFreeIntrusion
|
||||
AngerFreeIntrusion,
|
||||
Denunciation
|
||||
}
|
||||
|
||||
enum class DiplomaticModifiers {
|
||||
@ -107,7 +109,7 @@ class DiplomacyManager() {
|
||||
get() = if (civInfo.isAtWarWith(otherCiv())) MINIMUM_INFLUENCE else field
|
||||
|
||||
/** Total of each turn Science during Research Agreement */
|
||||
var totalOfScienceDuringRA = 0
|
||||
private var totalOfScienceDuringRA = 0
|
||||
|
||||
fun clone(): DiplomacyManager {
|
||||
val toReturn = DiplomacyManager()
|
||||
@ -188,6 +190,7 @@ class DiplomacyManager() {
|
||||
return 0
|
||||
}
|
||||
|
||||
@Suppress("unused") //todo Finish original intent or remove
|
||||
fun matchesCityStateRelationshipFilter(filter: String): Boolean {
|
||||
val relationshipLevel = relationshipLevel()
|
||||
return when (filter) {
|
||||
@ -210,7 +213,7 @@ class DiplomacyManager() {
|
||||
}
|
||||
|
||||
// To be run from City-State DiplomacyManager, which holds the influence. Resting point for every major civ can be different.
|
||||
fun getCityStateInfluenceRestingPoint(): Float {
|
||||
private fun getCityStateInfluenceRestingPoint(): Float {
|
||||
var restingPoint = 0f
|
||||
|
||||
for (unique in otherCiv().getMatchingUniques("Resting point for Influence with City-States is increased by []"))
|
||||
@ -287,7 +290,7 @@ class DiplomacyManager() {
|
||||
return goldPerTurnForUs
|
||||
}
|
||||
|
||||
fun scienceFromResearchAgreement() {
|
||||
private fun scienceFromResearchAgreement() {
|
||||
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
|
||||
val scienceFromResearchAgreement = min(totalOfScienceDuringRA, otherCivDiplomacy().totalOfScienceDuringRA)
|
||||
civInfo.tech.scienceFromResearchAgreements += scienceFromResearchAgreement
|
||||
@ -538,6 +541,8 @@ class DiplomacyManager() {
|
||||
if (relationshipLevel() < RelationshipLevel.Friend) {
|
||||
if (hasFlag(DiplomacyFlags.ProvideMilitaryUnit)) removeFlag(DiplomacyFlags.ProvideMilitaryUnit)
|
||||
} else {
|
||||
val variance = listOf(-1, 0, 1).random()
|
||||
|
||||
val relevantBonuses =
|
||||
if (relationshipLevel() == RelationshipLevel.Friend)
|
||||
eraInfo.friendBonus[otherCiv().cityStateType.name]
|
||||
@ -546,19 +551,19 @@ class DiplomacyManager() {
|
||||
if (relevantBonuses == null && otherCiv().cityStateType == CityStateType.Militaristic) {
|
||||
// Deprecated, assume Civ V values for compatibility
|
||||
if (!hasFlag(DiplomacyFlags.ProvideMilitaryUnit) && relationshipLevel() == RelationshipLevel.Friend)
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, 20)
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, 20 + variance)
|
||||
|
||||
if ((!hasFlag(DiplomacyFlags.ProvideMilitaryUnit) || getFlag(DiplomacyFlags.ProvideMilitaryUnit) > 17)
|
||||
&& relationshipLevel() == RelationshipLevel.Ally)
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, 17)
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, 17 + variance)
|
||||
}
|
||||
if (relevantBonuses == null) return
|
||||
|
||||
for (bonus in relevantBonuses) {
|
||||
// Reset the countdown if it has ended, or if we have longer to go than the current maximum (can happen when going from friend to ally)
|
||||
if (bonus.getPlaceholderText() == "Provides military units every [] turns" &&
|
||||
if (bonus.getPlaceholderText() == "Provides military units every ≈[] turns" &&
|
||||
(!hasFlag(DiplomacyFlags.ProvideMilitaryUnit) || getFlag(DiplomacyFlags.ProvideMilitaryUnit) > bonus.getPlaceholderParameters()[0].toInt()))
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, bonus.getPlaceholderParameters()[0].toInt())
|
||||
setFlag(DiplomacyFlags.ProvideMilitaryUnit, bonus.getPlaceholderParameters()[0].toInt() + variance)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -696,16 +701,16 @@ class DiplomacyManager() {
|
||||
diplomaticModifiers[modifier.name] = amount
|
||||
}
|
||||
|
||||
fun getModifier(modifier: DiplomaticModifiers): Float {
|
||||
private fun getModifier(modifier: DiplomaticModifiers): Float {
|
||||
if (!hasModifier(modifier)) return 0f
|
||||
return diplomaticModifiers[modifier.name]!!
|
||||
}
|
||||
|
||||
fun removeModifier(modifier: DiplomaticModifiers) = diplomaticModifiers.remove(modifier.name)
|
||||
private fun removeModifier(modifier: DiplomaticModifiers) = diplomaticModifiers.remove(modifier.name)
|
||||
fun hasModifier(modifier: DiplomaticModifiers) = diplomaticModifiers.containsKey(modifier.name)
|
||||
|
||||
/** @param amount always positive, so you don't need to think about it */
|
||||
fun revertToZero(modifier: DiplomaticModifiers, amount: Float) {
|
||||
private fun revertToZero(modifier: DiplomaticModifiers, amount: Float) {
|
||||
if (!hasModifier(modifier)) return
|
||||
val currentAmount = getModifier(modifier)
|
||||
if (currentAmount > 0) addModifier(modifier, -amount)
|
||||
@ -728,7 +733,7 @@ class DiplomacyManager() {
|
||||
}
|
||||
}
|
||||
|
||||
fun setFriendshipBasedModifier() {
|
||||
private fun setFriendshipBasedModifier() {
|
||||
removeModifier(DiplomaticModifiers.DeclaredFriendshipWithOurAllies)
|
||||
removeModifier(DiplomaticModifiers.DeclaredFriendshipWithOurEnemies)
|
||||
for (thirdCiv in getCommonKnownCivs()
|
||||
@ -747,8 +752,8 @@ class DiplomacyManager() {
|
||||
fun denounce() {
|
||||
setModifier(DiplomaticModifiers.Denunciation, -35f)
|
||||
otherCivDiplomacy().setModifier(DiplomaticModifiers.Denunciation, -35f)
|
||||
setFlag(DiplomacyFlags.Denunceation, 30)
|
||||
otherCivDiplomacy().setFlag(DiplomacyFlags.Denunceation, 30)
|
||||
setFlag(DiplomacyFlags.Denunciation, 30)
|
||||
otherCivDiplomacy().setFlag(DiplomacyFlags.Denunciation, 30)
|
||||
|
||||
otherCiv().addNotification("[${civInfo.civName}] has denounced us!", NotificationIcon.Diplomacy, civInfo.civName)
|
||||
|
||||
|
@ -15,6 +15,7 @@ import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
import com.unciv.logic.civilization.CityStateType
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.ui.cityscreen.CityScreen
|
||||
import com.unciv.ui.trade.DiplomacyScreen
|
||||
@ -226,7 +227,8 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
|
||||
|
||||
val cityButtonText = city.name
|
||||
val label = cityButtonText.toLabel(secondaryColor)
|
||||
iconTable.add(label).padRight(20f).padLeft(20f) // sufficient horizontal padding
|
||||
val rightPadding = if (city.civInfo.isCityState()) 10f else 20f // CS needs less padding here as there will be an icon
|
||||
iconTable.add(label).padRight(rightPadding).padLeft(20f) // sufficient horizontal padding
|
||||
.fillY() // provide full-height clicking area
|
||||
label.toBack() // this is so the label is rendered right before the population group,
|
||||
// so we save the font texture and avoid another texture switch
|
||||
@ -237,6 +239,11 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
|
||||
iconTable.addActor(cityStrengthLabel) // We create this here to we can .toBack() it as well.
|
||||
cityStrengthLabel.toBack()
|
||||
|
||||
if (city.civInfo.isCityState()) {
|
||||
val cityStateImage = ImageGetter.getImage(city.civInfo.cityStateType.icon).apply { color = secondaryColor }
|
||||
iconTable.add(cityStateImage).size(20f).fillY()
|
||||
}
|
||||
|
||||
if (uncivGame.viewEntireMapForDebug || belongsToViewingCiv() || worldScreen.viewingCiv.isSpectator()) {
|
||||
val constructionGroup = getConstructionGroup(city.cityConstructions)
|
||||
iconTable.add(constructionGroup)
|
||||
|
@ -16,7 +16,6 @@ import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.logic.trade.TradeLogic
|
||||
import com.unciv.logic.trade.TradeOffer
|
||||
import com.unciv.logic.trade.TradeType
|
||||
import com.unciv.models.ruleset.Era
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Quest
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
@ -89,6 +88,17 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
}
|
||||
civIndicator.addActor(relationshipIcon)
|
||||
|
||||
if (civ.isCityState()) {
|
||||
val innerColor = civ.gameInfo.ruleSet.nations[civ.civName]!!.getInnerColor()
|
||||
val typeIcon = ImageGetter.getImage(civ.cityStateType.icon)
|
||||
.surroundWithCircle(size = 35f, color = innerColor).apply {
|
||||
actor.color = Color.BLACK
|
||||
}
|
||||
civIndicator.addActor(typeIcon)
|
||||
typeIcon.y = floor(civIndicator.height - typeIcon.height)
|
||||
typeIcon.x = floor(civIndicator.width - typeIcon.width)
|
||||
}
|
||||
|
||||
if (civ.isCityState() && civ.questManager.haveQuestsFor(viewingCiv)) {
|
||||
val questIcon = ImageGetter.getImage("OtherIcons/Quest")
|
||||
.surroundWithCircle(size = 30f, color = Color.GOLDENROD)
|
||||
@ -122,9 +132,9 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv)
|
||||
|
||||
val diplomacyTable = Table()
|
||||
diplomacyTable.defaults().pad(10f)
|
||||
diplomacyTable.defaults().pad(2.5f)
|
||||
|
||||
diplomacyTable.add(LeaderIntroTable(otherCiv)).row()
|
||||
diplomacyTable.add(LeaderIntroTable(otherCiv)).padBottom(15f).row()
|
||||
|
||||
diplomacyTable.add("{Type}: {${otherCiv.cityStateType}}".toLabel()).row()
|
||||
diplomacyTable.add("{Personality}: {${otherCiv.cityStatePersonality}}".toLabel()).row()
|
||||
@ -152,6 +162,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
}
|
||||
diplomacyTable.add(resourcesTable).row()
|
||||
}
|
||||
diplomacyTable.row().padTop(15f)
|
||||
|
||||
otherCiv.updateAllyCivForCityState()
|
||||
val ally = otherCiv.getAllyCiv()
|
||||
@ -176,6 +187,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
if (nextLevelString.isNotEmpty()) {
|
||||
diplomacyTable.add(nextLevelString.toLabel()).row()
|
||||
}
|
||||
diplomacyTable.row().padTop(15f)
|
||||
|
||||
val eraInfo = viewingCiv.getEra()
|
||||
|
||||
@ -205,6 +217,12 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
.apply { setAlignment(Align.center) }
|
||||
diplomacyTable.add(allyBonusLabel).row()
|
||||
|
||||
if (otherCiv.cityStateUniqueUnit != null) {
|
||||
val unitName = otherCiv.cityStateUniqueUnit
|
||||
val techName = viewingCiv.gameInfo.ruleSet.units[otherCiv.cityStateUniqueUnit]!!.requiredTech
|
||||
diplomacyTable.add("[${otherCiv.civName}] is able to provide [${unitName}] once [${techName}] is researched.".toLabel(fontSize = 18)).row()
|
||||
}
|
||||
|
||||
return diplomacyTable
|
||||
}
|
||||
|
||||
@ -582,7 +600,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
||||
diplomacyTable.add(researchAgreementButton).row()
|
||||
}
|
||||
|
||||
if (!diplomacyManager.hasFlag(DiplomacyFlags.Denunceation)
|
||||
if (!diplomacyManager.hasFlag(DiplomacyFlags.Denunciation)
|
||||
&& !diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship)
|
||||
) {
|
||||
val denounceButton = "Denounce ([30] turns)".toTextButton()
|
||||
|
@ -634,6 +634,10 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
* [Shield](https://thenounproject.com/search/?q=shield&i=813568) by Gregor Cresnar for Religious Strength
|
||||
* [skill sword flame](https://thenounproject.com/term/skill-sword-flame/2360212/) by Maxicons) for Remove Heresy
|
||||
* [Pencil](https://thenounproject.com/search/?q=pencil&i=4195852) by Muhamad Aldi Maulana for Enter Text Prompt Button / Pencil
|
||||
* [Parchment](https://thenounproject.com/term/parchment/1516378/) by hans draiman for Cultured City-States
|
||||
* [connection](https://thenounproject.com/term/connection/1365233/) by Popular for Mercantile City-States
|
||||
* [crossed sword](https://thenounproject.com/term/crossed-sword/2427559/) by ProSymbols for Militaristic City-States
|
||||
* [ship helm](https://thenounproject.com/term/ship-helm/2170591/) by Vectors Market for Maritime City-States
|
||||
|
||||
## Main menu
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user