diff --git a/android/ImagesToPackSeparately/BuildingIcons/Utopia Project.png b/android/ImagesToPackSeparately/BuildingIcons/Utopia Project.png new file mode 100644 index 0000000000..ef92427589 Binary files /dev/null and b/android/ImagesToPackSeparately/BuildingIcons/Utopia Project.png differ diff --git a/android/assets/BuildingIcons.atlas b/android/assets/BuildingIcons.atlas index b4cc2006e0..2492881bb8 100644 --- a/android/assets/BuildingIcons.atlas +++ b/android/assets/BuildingIcons.atlas @@ -732,45 +732,52 @@ University orig: 100, 100 offset: 0, 0 index: -1 -Walls +Utopia Project rotate: false xy: 1430, 920 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 -Walls of Babylon +Walls rotate: false xy: 614, 2 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 -Wat +Walls of Babylon rotate: false xy: 716, 104 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 -Water Mill +Wat rotate: false xy: 818, 206 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 -Windmill +Water Mill rotate: false xy: 920, 308 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 -Workshop +Windmill rotate: false xy: 1022, 410 size: 100, 100 orig: 100, 100 offset: 0, 0 index: -1 +Workshop + rotate: false + xy: 1124, 512 + size: 100, 100 + orig: 100, 100 + offset: 0, 0 + index: -1 diff --git a/android/assets/BuildingIcons.png b/android/assets/BuildingIcons.png index 68f9f03905..105b099743 100644 Binary files a/android/assets/BuildingIcons.png and b/android/assets/BuildingIcons.png differ diff --git a/android/assets/jsons/Civ V - Vanilla/Buildings.json b/android/assets/jsons/Civ V - Vanilla/Buildings.json index 9d4325675f..e153330e53 100644 --- a/android/assets/jsons/Civ V - Vanilla/Buildings.json +++ b/android/assets/jsons/Civ V - Vanilla/Buildings.json @@ -1046,4 +1046,14 @@ "requiredTech": "Nanotechnology", "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"] + } ] diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index e40188ef5d..c999a2bdd7 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -404,6 +404,9 @@ Cannot provide unit upkeep for [unitName] - unit has been disbanded! = [wonder] has been built in a faraway land = [civName] 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] = [cityName] cannot continue work on [construction] = [cityName] has expanded its borders! = diff --git a/core/src/com/unciv/logic/automation/ConstructionAutomation.kt b/core/src/com/unciv/logic/automation/ConstructionAutomation.kt index 0bfeff71b0..6a709e67a9 100644 --- a/core/src/com/unciv/logic/automation/ConstructionAutomation.kt +++ b/core/src/com/unciv/logic/automation/ConstructionAutomation.kt @@ -12,6 +12,7 @@ import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.VictoryType import com.unciv.models.stats.Stat import kotlin.math.min +import kotlin.math.roundToInt import kotlin.math.sqrt class ConstructionAutomation(val cityConstructions: CityConstructions){ @@ -155,8 +156,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){ .filter { it.isStatRelated(Stat.Culture) }.minByOrNull { it.cost } if (cultureBuilding != null) { var modifier = 0.5f - if(cityInfo.cityStats.currentCityStats.culture==0f) // It won't grow if we don't help it - modifier=0.8f + if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it + modifier = 0.8f if (preferredVictoryType == VictoryType.Cultural) modifier = 1.6f addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier) } @@ -166,8 +167,6 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){ val spaceshipPart = buildableNotWonders.firstOrNull { it.uniques.contains("Spaceship part") } if (spaceshipPart != null) { 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 addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier) } @@ -187,6 +186,11 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){ if (preferredVictoryType == VictoryType.Cultural && wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House")) 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 (allTechsAreResearched) return .5f if (preferredVictoryType == VictoryType.Scientific) return 1.5f diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index c9d9bb577e..f19c7844f3 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -231,7 +231,10 @@ class CityConstructions { fun turnsToConstruction(constructionName: String, useStoredProduction: Boolean = true): Int { 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 val cityStatsForConstruction: Stats @@ -304,6 +307,9 @@ class CityConstructions { validateInProgressConstructions() if (getConstruction(currentConstructionFromQueue) !is PerpetualConstruction) { + if (getWorkDone(currentConstructionFromQueue) == 0) { + constructionBegun(getConstruction(currentConstructionFromQueue)) + } addProductionPoints(cityStats.production.roundToInt() + productionOverflow) 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) { construction.postBuildEvent(this) if (construction.name in inProgressConstructions) @@ -386,7 +411,7 @@ class CityConstructions { cityInfo.civInfo.addNotification("[${construction.name}] has been built in [" + cityInfo.name + "]", 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) { // No need to notify ourself, since we already got the building notification anyway if (otherCiv == cityInfo.civInfo) continue diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 1294d30256..f6b6267fc1 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -168,6 +168,7 @@ class CivilizationInfo { fun isAlive(): Boolean = !isDefeated() fun hasEverBeenFriendWith(otherCiv: CivilizationInfo): Boolean = getDiplomacyManager(otherCiv).everBeenFriends() 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() } fun victoryType(): VictoryType { diff --git a/core/src/com/unciv/logic/civilization/VictoryManager.kt b/core/src/com/unciv/logic/civilization/VictoryManager.kt index 731b163287..9e20fdeb67 100644 --- a/core/src/com/unciv/logic/civilization/VictoryManager.kt +++ b/core/src/com/unciv/logic/civilization/VictoryManager.kt @@ -36,7 +36,7 @@ class VictoryManager { fun hasWonScientificVictory() = hasVictoryType(VictoryType.Scientific) && spaceshipPartsRemaining() == 0 fun hasWonCulturalVictory() = hasVictoryType(VictoryType.Cultural) - && civInfo.policies.adoptedPolicies.count { it.endsWith("Complete") } > 4 + && civInfo.hasUnique("Triggers a Cultural Victory upon completion") fun hasWonDominationVictory(): Boolean { return hasVictoryType(VictoryType.Domination) diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index e76b3c298e..8c7ee3a33d 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -352,6 +352,16 @@ class Building : NamedStats(), IConstruction { && 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 } + "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!!)) { @@ -383,7 +393,7 @@ class Building : NamedStats(), IConstruction { if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Scientific) && "Enables construction of Spaceship parts" in uniques) return "Can't construct spaceship parts if scientific victory is not enabled!" - + return "" } @@ -410,7 +420,6 @@ class Building : NamedStats(), IConstruction { } } - if (providesFreeBuilding != null && !cityConstructions.containsBuildingOrEquivalent(providesFreeBuilding!!)) { var buildingToAdd = providesFreeBuilding!! @@ -469,4 +478,4 @@ class Building : NamedStats(), IConstruction { resourceRequirements[unique.params[1]] = unique.params[0].toInt() return resourceRequirements } -} \ No newline at end of file +} diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index bfeb0b56ae..68aa84c592 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -198,7 +198,7 @@ class BaseUnit : INamed, IConstruction { 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") + civInfo.getMatchingUniques("New [] units start with [] Experience")) { if (unit.matchesFilter(unique.params[0])) @@ -206,7 +206,7 @@ class BaseUnit : INamed, IConstruction { } 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")) { val filter = unique.params[0] val promotion = unique.params[1] diff --git a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt index d5b205fa8e..0df25c1099 100644 --- a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt +++ b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt @@ -123,7 +123,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() { if (dominationVictoryEnabled) myVictoryStatusTable.add(conquestVictoryColumn()) myVictoryStatusTable.row() 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()) contentsTable.clear() diff --git a/docs/Credits.md b/docs/Credits.md index 380e8b5a7b..7fdfcc371d 100644 --- a/docs/Credits.md +++ b/docs/Credits.md @@ -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 * [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 +* [Illuminati](https://thenounproject.com/term/illuminati/1617812) by emilegraphics for the Utopia Project ## Social Policies