Updated the culture victory so it now requires the Utopia Project to be built (#4060)

* Added the Utopia Project for the culture victory; AI will now build it

* Forgot to credit the icon

* Fixed a few minor issues

* Improved code quality; added translatable notifications

* Fixed mistakes; improved quality

* Changed a label

* Revert a small change which is no longer necessary

* Reverted the revert of a small change which is no longer necessary

* Made requsted changes
This commit is contained in:
Xander Lenstra 2021-06-14 13:48:22 +02:00 committed by GitHub
parent 07a43f3f1a
commit 1c21573a42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 79 additions and 19 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -732,45 +732,52 @@ University
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Walls Utopia Project
rotate: false rotate: false
xy: 1430, 920 xy: 1430, 920
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Walls of Babylon Walls
rotate: false rotate: false
xy: 614, 2 xy: 614, 2
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Wat Walls of Babylon
rotate: false rotate: false
xy: 716, 104 xy: 716, 104
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Water Mill Wat
rotate: false rotate: false
xy: 818, 206 xy: 818, 206
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Windmill Water Mill
rotate: false rotate: false
xy: 920, 308 xy: 920, 308
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Workshop Windmill
rotate: false rotate: false
xy: 1022, 410 xy: 1022, 410
size: 100, 100 size: 100, 100
orig: 100, 100 orig: 100, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
Workshop
rotate: false
xy: 1124, 512
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 248 KiB

View File

@ -1046,4 +1046,14 @@
"requiredTech": "Nanotechnology", "requiredTech": "Nanotechnology",
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"] "uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
} }
// All Era's
{
"name": "Utopia Project",
"cost": 1500,
"isNationalWonder": true,
"uniques": ["Hidden until [5] social policy branches have been completed", "Triggers a global alert upon build start",
"Triggers a Cultural Victory upon completion", "Hidden when cultural victory is disabled"]
}
] ]

View File

@ -404,6 +404,9 @@ Cannot provide unit upkeep for [unitName] - unit has been disbanded! =
[wonder] has been built in a faraway land = [wonder] has been built in a faraway land =
[civName] has completed [construction]! = [civName] has completed [construction]! =
An unknown civilization has completed [construction]! = An unknown civilization has completed [construction]! =
The city of [cityname] has started constructing [construction]! =
[civilization] has started constructing [construction]! =
An unknown civilization has started constructing [construction]! =
Work has started on [construction] = Work has started on [construction] =
[cityName] cannot continue work on [construction] = [cityName] cannot continue work on [construction] =
[cityName] has expanded its borders! = [cityName] has expanded its borders! =

View File

@ -12,6 +12,7 @@ import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.VictoryType import com.unciv.models.ruleset.VictoryType
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sqrt import kotlin.math.sqrt
class ConstructionAutomation(val cityConstructions: CityConstructions){ class ConstructionAutomation(val cityConstructions: CityConstructions){
@ -155,8 +156,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
.filter { it.isStatRelated(Stat.Culture) }.minByOrNull { it.cost } .filter { it.isStatRelated(Stat.Culture) }.minByOrNull { it.cost }
if (cultureBuilding != null) { if (cultureBuilding != null) {
var modifier = 0.5f var modifier = 0.5f
if(cityInfo.cityStats.currentCityStats.culture==0f) // It won't grow if we don't help it if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
modifier=0.8f modifier = 0.8f
if (preferredVictoryType == VictoryType.Cultural) modifier = 1.6f if (preferredVictoryType == VictoryType.Cultural) modifier = 1.6f
addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier) addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier)
} }
@ -166,8 +167,6 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val spaceshipPart = buildableNotWonders.firstOrNull { it.uniques.contains("Spaceship part") } val spaceshipPart = buildableNotWonders.firstOrNull { it.uniques.contains("Spaceship part") }
if (spaceshipPart != null) { if (spaceshipPart != null) {
var modifier = 1.5f var modifier = 1.5f
if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
modifier = 0.8f
if (preferredVictoryType == VictoryType.Scientific) modifier = 2f if (preferredVictoryType == VictoryType.Scientific) modifier = 2f
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier) addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
} }
@ -187,6 +186,11 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
if (preferredVictoryType == VictoryType.Cultural if (preferredVictoryType == VictoryType.Cultural
&& wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House")) && wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House"))
return 3f return 3f
// Only start building if we are the city that would complete it the soonest
if (wonder.uniques.contains("Triggers a Cultural Victory upon completion") && cityInfo == civInfo.cities.minByOrNull {
it.cityConstructions.turnsToConstruction(wonder.name)
}!!)
return 10f
if (wonder.isStatRelated(Stat.Science)) { if (wonder.isStatRelated(Stat.Science)) {
if (allTechsAreResearched) return .5f if (allTechsAreResearched) return .5f
if (preferredVictoryType == VictoryType.Scientific) return 1.5f if (preferredVictoryType == VictoryType.Scientific) return 1.5f

View File

@ -231,7 +231,10 @@ class CityConstructions {
fun turnsToConstruction(constructionName: String, useStoredProduction: Boolean = true): Int { fun turnsToConstruction(constructionName: String, useStoredProduction: Boolean = true): Int {
val workLeft = getRemainingWork(constructionName, useStoredProduction) val workLeft = getRemainingWork(constructionName, useStoredProduction)
if (workLeft <= productionOverflow) // we might have done more work than actually necessary (if <0) - possible if circumstances cause buildings to be cheaper later if (workLeft < 0) // This most often happens when a production is more than finished in a multiplayer game while its not your turn
return 0 // So we finish it at the start of the next turn. This could technically also happen when we lower production costs during our turn,
// but distinguishing those two cases is difficult, and the second one is much rarer than the first
if (workLeft <= productionOverflow) // if we already have stored up enough production to finish it directly
return 1 // we'll finish this next turn return 1 // we'll finish this next turn
val cityStatsForConstruction: Stats val cityStatsForConstruction: Stats
@ -304,6 +307,9 @@ class CityConstructions {
validateInProgressConstructions() validateInProgressConstructions()
if (getConstruction(currentConstructionFromQueue) !is PerpetualConstruction) { if (getConstruction(currentConstructionFromQueue) !is PerpetualConstruction) {
if (getWorkDone(currentConstructionFromQueue) == 0) {
constructionBegun(getConstruction(currentConstructionFromQueue))
}
addProductionPoints(cityStats.production.roundToInt() + productionOverflow) addProductionPoints(cityStats.production.roundToInt() + productionOverflow)
productionOverflow = 0 productionOverflow = 0
} }
@ -362,6 +368,25 @@ class CityConstructions {
} }
} }
private fun constructionBegun(construction: IConstruction) {
if (construction !is Building) return;
if (construction.uniqueObjects.none { it.placeholderText == "Triggers a global alert upon build start" }) return
val buildingIcon = "BuildingIcons/${construction.name}"
for (otherCiv in cityInfo.civInfo.gameInfo.civilizations) {
if (otherCiv == cityInfo.civInfo) continue
when {
(otherCiv.exploredTiles.contains(cityInfo.location) && otherCiv != cityInfo.civInfo) ->
otherCiv.addNotification("The city of [${cityInfo.name}] has started constructing [${construction.name}]!",
cityInfo.location, NotificationIcon.Construction, buildingIcon)
(otherCiv.knows(cityInfo.civInfo)) ->
otherCiv.addNotification("[${cityInfo.civInfo.civName}] has started constructing [${construction.name}]!",
NotificationIcon.Construction, buildingIcon)
else -> otherCiv.addNotification("An unknown civilization has started constructing [${construction.name}]!",
NotificationIcon.Construction, buildingIcon)
}
}
}
private fun constructionComplete(construction: IConstruction) { private fun constructionComplete(construction: IConstruction) {
construction.postBuildEvent(this) construction.postBuildEvent(this)
if (construction.name in inProgressConstructions) if (construction.name in inProgressConstructions)
@ -386,7 +411,7 @@ class CityConstructions {
cityInfo.civInfo.addNotification("[${construction.name}] has been built in [" + cityInfo.name + "]", cityInfo.civInfo.addNotification("[${construction.name}] has been built in [" + cityInfo.name + "]",
cityInfo.location, NotificationIcon.Construction, icon) cityInfo.location, NotificationIcon.Construction, icon)
} }
if (construction is Building && "Triggers a global alert upon completion" in construction.uniques) { if (construction is Building && construction.uniqueObjects.any { it.placeholderText == "Triggers a global alert upon completion" } ) {
for (otherCiv in cityInfo.civInfo.gameInfo.civilizations) { for (otherCiv in cityInfo.civInfo.gameInfo.civilizations) {
// No need to notify ourself, since we already got the building notification anyway // No need to notify ourself, since we already got the building notification anyway
if (otherCiv == cityInfo.civInfo) continue if (otherCiv == cityInfo.civInfo) continue

View File

@ -168,6 +168,7 @@ class CivilizationInfo {
fun isAlive(): Boolean = !isDefeated() fun isAlive(): Boolean = !isDefeated()
fun hasEverBeenFriendWith(otherCiv: CivilizationInfo): Boolean = getDiplomacyManager(otherCiv).everBeenFriends() fun hasEverBeenFriendWith(otherCiv: CivilizationInfo): Boolean = getDiplomacyManager(otherCiv).everBeenFriends()
fun hasMetCivTerritory(otherCiv: CivilizationInfo): Boolean = otherCiv.getCivTerritory().any { it in exploredTiles } fun hasMetCivTerritory(otherCiv: CivilizationInfo): Boolean = otherCiv.getCivTerritory().any { it in exploredTiles }
fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { it.endsWith("Complete") }
private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() } private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
fun victoryType(): VictoryType { fun victoryType(): VictoryType {

View File

@ -36,7 +36,7 @@ class VictoryManager {
fun hasWonScientificVictory() = hasVictoryType(VictoryType.Scientific) && spaceshipPartsRemaining() == 0 fun hasWonScientificVictory() = hasVictoryType(VictoryType.Scientific) && spaceshipPartsRemaining() == 0
fun hasWonCulturalVictory() = hasVictoryType(VictoryType.Cultural) fun hasWonCulturalVictory() = hasVictoryType(VictoryType.Cultural)
&& civInfo.policies.adoptedPolicies.count { it.endsWith("Complete") } > 4 && civInfo.hasUnique("Triggers a Cultural Victory upon completion")
fun hasWonDominationVictory(): Boolean { fun hasWonDominationVictory(): Boolean {
return hasVictoryType(VictoryType.Domination) return hasVictoryType(VictoryType.Domination)

View File

@ -352,6 +352,16 @@ class Building : NamedStats(), IConstruction {
&& civInfo.cities.any { !it.isPuppet && !it.cityConstructions.containsBuildingOrEquivalent(unique.params[0]) }) && civInfo.cities.any { !it.isPuppet && !it.cityConstructions.containsBuildingOrEquivalent(unique.params[0]) })
return "Requires a [${civInfo.getEquivalentBuilding(unique.params[0])}] in all cities" // replace with civ-specific building for user return "Requires a [${civInfo.getEquivalentBuilding(unique.params[0])}] in all cities" // replace with civ-specific building for user
} }
"Hidden until [] social policy branches have been completed" -> {
if (construction.cityInfo.civInfo.getCompletedPolicyBranchesCount() < unique.params[0].toInt()) {
return "Should not be displayed"
}
}
"Hidden when cultural victory is disabled" -> {
if ( !civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Cultural)) {
return "Hidden when cultural victory is disabled"
}
}
} }
if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) { if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) {
@ -383,7 +393,7 @@ class Building : NamedStats(), IConstruction {
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Scientific) if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Scientific)
&& "Enables construction of Spaceship parts" in uniques) && "Enables construction of Spaceship parts" in uniques)
return "Can't construct spaceship parts if scientific victory is not enabled!" return "Can't construct spaceship parts if scientific victory is not enabled!"
return "" return ""
} }
@ -410,7 +420,6 @@ class Building : NamedStats(), IConstruction {
} }
} }
if (providesFreeBuilding != null && !cityConstructions.containsBuildingOrEquivalent(providesFreeBuilding!!)) { if (providesFreeBuilding != null && !cityConstructions.containsBuildingOrEquivalent(providesFreeBuilding!!)) {
var buildingToAdd = providesFreeBuilding!! var buildingToAdd = providesFreeBuilding!!
@ -469,4 +478,4 @@ class Building : NamedStats(), IConstruction {
resourceRequirements[unique.params[1]] = unique.params[0].toInt() resourceRequirements[unique.params[1]] = unique.params[0].toInt()
return resourceRequirements return resourceRequirements
} }
} }

View File

@ -198,7 +198,7 @@ class BaseUnit : INamed, IConstruction {
var XP = cityConstructions.getBuiltBuildings().sumBy { it.xpForNewUnits } var XP = cityConstructions.getBuiltBuildings().sumBy { it.xpForNewUnits }
for (unique in cityConstructions.cityInfo.cityConstructions.builtBuildingUniqueMap for (unique in cityConstructions.builtBuildingUniqueMap
.getUniques("New [] units start with [] Experience in this city") .getUniques("New [] units start with [] Experience in this city")
+ civInfo.getMatchingUniques("New [] units start with [] Experience")) { + civInfo.getMatchingUniques("New [] units start with [] Experience")) {
if (unit.matchesFilter(unique.params[0])) if (unit.matchesFilter(unique.params[0]))
@ -206,7 +206,7 @@ class BaseUnit : INamed, IConstruction {
} }
unit.promotions.XP = XP unit.promotions.XP = XP
for (unique in cityConstructions.cityInfo.cityConstructions.builtBuildingUniqueMap for (unique in cityConstructions.builtBuildingUniqueMap
.getUniques("All newly-trained [] units in this city receive the [] promotion")) { .getUniques("All newly-trained [] units in this city receive the [] promotion")) {
val filter = unique.params[0] val filter = unique.params[0]
val promotion = unique.params[1] val promotion = unique.params[1]

View File

@ -123,7 +123,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
if (dominationVictoryEnabled) myVictoryStatusTable.add(conquestVictoryColumn()) if (dominationVictoryEnabled) myVictoryStatusTable.add(conquestVictoryColumn())
myVictoryStatusTable.row() myVictoryStatusTable.row()
if (scientificVictoryEnabled) myVictoryStatusTable.add("Complete all the spaceship parts\n to win!".toLabel()) if (scientificVictoryEnabled) myVictoryStatusTable.add("Complete all the spaceship parts\n to win!".toLabel())
if (culturalVictoryEnabled) myVictoryStatusTable.add("Complete 5 policy branches\n to win!".toLabel()) if (culturalVictoryEnabled) myVictoryStatusTable.add("Complete 5 policy branches and build\n the Utopia Project to win!".toLabel())
if (dominationVictoryEnabled) myVictoryStatusTable.add("Destroy all enemies\n to win!".toLabel()) if (dominationVictoryEnabled) myVictoryStatusTable.add("Destroy all enemies\n to win!".toLabel())
contentsTable.clear() contentsTable.clear()

View File

@ -284,6 +284,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Rocket](https://thenounproject.com/term/rocket/937173/) By BomSymbols for SS Cockpit * [Rocket](https://thenounproject.com/term/rocket/937173/) By BomSymbols for SS Cockpit
* [Engine](https://thenounproject.com/term/engine/1877958/) By Andre for SS Engine * [Engine](https://thenounproject.com/term/engine/1877958/) By Andre for SS Engine
* [Chamber](https://thenounproject.com/term/chamber/1242689/) By IYIKON for SS Stasis Chamber * [Chamber](https://thenounproject.com/term/chamber/1242689/) By IYIKON for SS Stasis Chamber
* [Illuminati](https://thenounproject.com/term/illuminati/1617812) by emilegraphics for the Utopia Project
## Social Policies ## Social Policies