Added Diplomatic victory, United Nations, Globalization (#4619)

* Added globalization tech

* Added united nations and backbone for diplomatic victory

* You can now vote with the united nations every 20 * gameSpeed turns, and the results will be displayed the turn after

* You can now win with diplomacy

* AI will now vote in the united nations

* Added the amount of turns till the next diplomatic vote in the diplomacy overview

* Added translatable strings

* Implemented most of the requested changes (rest coming soon)

* Implemented further requested changes
This commit is contained in:
Xander Lenstra 2021-08-03 20:48:51 +02:00 committed by GitHub
parent bb5392db4a
commit 4d3195ec5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 371 additions and 115 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -746,59 +746,66 @@ Theatre
orig: 100, 100
offset: 0, 0
index: -1
University
United Nations
rotate: false
xy: 1516, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Utopia Project
University
rotate: false
xy: 1624, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Walls
Utopia Project
rotate: false
xy: 868, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Walls of Babylon
Walls
rotate: false
xy: 976, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Wat
Walls of Babylon
rotate: false
xy: 1084, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Water Mill
Wat
rotate: false
xy: 1192, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Windmill
Water Mill
rotate: false
xy: 1300, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Workshop
Windmill
rotate: false
xy: 1408, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Workshop
rotate: false
xy: 1516, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -1092,17 +1092,15 @@
"requiredTech": "Robotics"
},
// Column 16
/*
{
"name": "United Nations",
"isWonder": true,
"culture": 1,
"greatPersonPoints": {"Great Merchant": 2},
"uniques": ["Triggers voting for the Diplomatic Victory"],
"requiredTech": "Globalization",
"uniques": ["Triggers voting for the Diplomatic Victory", "Hidden when [Diplomatic] Victory is disabled", "Triggers a global alert upon completion"],
"quote": "'More than ever before in human history, we share a common destiny. We can master it only if we face it together. And that is why we have the United Nations.' - Kofi Annan"
"requiredTech": "Globalization", // todo doesn't exist yet!
},
*/
{
"name": "SS Engine",
"requiredResource": "Aluminum",
@ -1123,6 +1121,6 @@
"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"]
"Triggers a Cultural Victory upon completion", "Hidden when [Cultural] Victory is disabled"]
}
]

View File

@ -606,6 +606,12 @@
"buildingCost": 750,
"wonderCost": 1250,
"techs": [
{
"name": "Globalization",
"row": 2,
"prerequisites": ["Telecommunications"],
"quote": "'The new electronic interdependence recreates the world in the image of a global village.' - Marshall McLuhan"
},
{
"name": "Particle Physics",
"row": 3,
@ -642,7 +648,7 @@
{
"name": "Future Tech",
"row": 5,
"prerequisites": ["Particle Physics", "Nuclear Fusion", "Nanotechnology", "Stealth"],
"prerequisites": ["Globalization","Particle Physics", "Nuclear Fusion", "Nanotechnology", "Stealth"],
"uniques": ["Who knows what the future holds?", "Can be continually researched"],
"quote": "'I think we agree, the past is over.' - George W. Bush"
}

View File

@ -787,6 +787,7 @@ Treasury deficit =
Science victory =
Cultural victory =
Conquest victory =
Diplomatic victory =
Complete all the spaceship parts\n to win! =
Complete 5 policy branches\n to win! =
Complete 5 policy branches and build\n the Utopia Project to win! =
@ -794,12 +795,14 @@ Destroy all enemies\n to win! =
You have won a scientific victory! =
You have won a cultural victory! =
You have won a domination victory! =
You have won a diplomatic victory! =
You have won! =
You have achieved victory through the awesome power of your Culture. Your civilization's greatness - the magnificence of its monuments and the power of its artists - have astounded the world! Poets will honor you as long as beauty brings gladness to a weary heart. =
The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph! =
You have achieved victory through mastery of Science! You have conquered the mysteries of nature and led your people on a voyage to a brave new world! Your triumph will be remembered as long as the stars burn in the night sky! =
Your civilization stands above all others! The exploits of your people shall be remembered until the end of civilizaton itself! =
You have been defeated. Your civilization has been overwhelmed by its many foes. But your people do not despair, for they know that one day you shall return - and lead them forward to victory! =
You have triumphed over your foes through the art of diplomacy! Your cunning and wisdom have earned you great friends - and divided and sown confusion among your enemies! Forever will you be remembered as the leader who brought peace to this weary world! =
One more turn...! =
Built Apollo Program =
Destroy [civName] =
@ -809,6 +812,16 @@ Rankings =
Spaceship parts remaining =
Branches completed =
Undefeated civs =
# The \n here means: put a newline (enter) here. If this is omitted, the sidebox in the diplomacy overview will become _really_ wide.
# Feel free to replace it with a space and put it somewhere else in your translation
Turns until the next\ndiplomacy victory vote: [$turnsTillNextDiplomaticVote] =
Choose a civ to vote for =
Choose who should become the world leader and win a diplomatic victory! =
Voted for =
Vote for [civilizationName] =
Continue =
Abstained =
Vote for World Leader =
# Capturing a city

View File

@ -43,6 +43,9 @@ class GameInfo {
var currentPlayer = ""
var gameId = UUID.randomUUID().toString() // random string
// Maps a civ to the civ they voted for
var diplomaticVictoryVotesCast = HashMap<String, String>()
/**Keep track of a custom location this game was saved to _or_ loaded from
*
* Note this was used as silent autosave destination, but it was decided (#3898) to
@ -71,6 +74,7 @@ class GameInfo {
toReturn.difficulty = difficulty
toReturn.gameParameters = gameParameters
toReturn.gameId = gameId
toReturn.diplomaticVictoryVotesCast.putAll(diplomaticVictoryVotesCast)
toReturn.oneMoreTurnMode = oneMoreTurnMode
toReturn.customSaveLocation = customSaveLocation
return toReturn

View File

@ -50,6 +50,7 @@ object NextTurnAutomation {
automateUnits(civInfo)
reassignWorkedTiles(civInfo)
trainSettler(civInfo)
tryVoteForDiplomaticVictory(civInfo)
}
@ -208,6 +209,7 @@ object NextTurnAutomation {
VictoryType.Cultural -> listOf("Piety", "Freedom", "Tradition", "Commerce", "Patronage")
VictoryType.Scientific -> listOf("Rationalism", "Commerce", "Liberty", "Order", "Patronage")
VictoryType.Domination -> listOf("Autocracy", "Honor", "Liberty", "Rationalism", "Commerce")
VictoryType.Diplomatic -> listOf("Patronage", "Commerce", "Rationalism", "Freedom", "Tradition")
VictoryType.Neutral -> listOf()
}
val policiesByPreference = adoptablePolicies
@ -464,7 +466,7 @@ object NextTurnAutomation {
.filterNot { it == civInfo || it.isBarbarian() || it.cities.isEmpty() }
.filter { !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedPeace) }
// Don't allow AIs to offer peace to city states allied with their enemies
.filterNot { it.isCityState() && it.getAllyCiv() != "" && civInfo.isAtWarWith(civInfo.gameInfo.getCivilization(it.getAllyCiv())) }
.filterNot { it.isCityState() && it.getAllyCiv() != null && civInfo.isAtWarWith(civInfo.gameInfo.getCivilization(it.getAllyCiv()!!)) }
for (enemy in enemiesCiv) {
val motivationToAttack = motivationToAttack(civInfo, enemy)
@ -559,6 +561,27 @@ object NextTurnAutomation {
}
}
// Technically, this function should also check for civs that have liberated one or more cities
// Hoewever, that can be added in another update, this PR is large enough as it is.
private fun tryVoteForDiplomaticVictory(civInfo: CivilizationInfo) {
if (!civInfo.mayVoteForDiplomaticVictory()) return
val chosenCiv: String? = if (civInfo.isMajorCiv()) {
val knownMajorCivs = civInfo.getKnownCivs().filter { it.isMajorCiv() }
val highestOpinion = knownMajorCivs
.maxOfOrNull {
civInfo.getDiplomacyManager(it).opinionOfOtherCiv()
}
if (highestOpinion == null) null
else knownMajorCivs.filter { civInfo.getDiplomacyManager(it).opinionOfOtherCiv() == highestOpinion}.random().civName
} else {
civInfo.getAllyCiv()
}
civInfo.diplomaticVoteForCiv(chosenCiv)
}
private fun issueRequests(civInfo: CivilizationInfo) {
for (otherCiv in civInfo.getKnownCivs().filter { it.isMajorCiv() && !civInfo.isAtWarWith(it) }) {

View File

@ -89,13 +89,13 @@ class CivilizationInfo {
var diplomacy = HashMap<String, DiplomacyManager>()
var notifications = ArrayList<Notification>()
val popupAlerts = ArrayList<PopupAlert>()
private var allyCivName = ""
private var allyCivName: String? = null
var naturalWonders = ArrayList<String>()
/** for trades here, ourOffers is the current civ's offers, and theirOffers is what the requesting civ offers */
val tradeRequests = ArrayList<TradeRequest>()
/** See DiplomacyManager.flagsCountdown to why not eEnum */
/** See DiplomacyManager.flagsCountdown for why this does not map Enums to ints */
private var flagsCountdown = HashMap<String, Int>()
/** Arraylist instead of HashMap as there might be doubles
* Pairs of Uniques and the amount of turns they are still active
@ -540,7 +540,7 @@ class CivilizationInfo {
updateViewableTiles() // adds explored tiles so that the units will be able to perform automated actions better
transients().updateCitiesConnectedToCapital()
turnStartFlags()
startTurnFlags()
for (city in cities) city.startTurn()
for (unit in getCivUnits()) unit.startTurn()
@ -606,14 +606,11 @@ class CivilizationInfo {
updateHasActiveGreatWall()
}
private fun turnStartFlags() {
// This function may be too abstracted for what it currently does (only managing a single flag)
// But eh, it works.
private fun startTurnFlags() {
for (flag in flagsCountdown.keys.toList()) {
if (flag == CivFlags.cityStateGreatPersonGift.name) {
val cityStateAllies =
getKnownCivs().filter { it.isCityState() && it.getAllyCiv() == civName }
// the "ignoreCase = true" is to catch 'cityStateGreatPersonGift' instead of 'CityStateGreatPersonGift' being in old save files
if (flag == CivFlags.CityStateGreatPersonGift.name || flag.equals(CivFlags.CityStateGreatPersonGift.name, ignoreCase = true)) {
val cityStateAllies = getKnownCivs().filter { it.isCityState() && it.getAllyCiv() == civName }
if (cityStateAllies.any()) flagsCountdown[flag] = flagsCountdown[flag]!! - 1
@ -630,10 +627,50 @@ class CivilizationInfo {
if (flagsCountdown[flag]!! > 0)
flagsCountdown[flag] = flagsCountdown[flag]!! - 1
if (flagsCountdown[flag]!! != 0) continue
when (flag) {
CivFlags.TurnsTillNextDiplomaticVote.name -> addFlag(CivFlags.ShowDiplomaticVotingResults.name, 1)
CivFlags.ShouldResetDiplomaticVotes.name -> {
gameInfo.diplomaticVictoryVotesCast.clear()
removeFlag(CivFlags.ShouldResetDiplomaticVotes.name)
removeFlag(CivFlags.ShowDiplomaticVotingResults.name)
}
CivFlags.ShowDiplomaticVotingResults.name -> {
if (gameInfo.civilizations.any { it.victoryManager.hasWon() } )
// We have either already done this calculation, or it doesn't matter anymore,
// so don't waste resources doing it
continue
addFlag(CivFlags.ShouldResetDiplomaticVotes.name, 1)
}
}
}
}
fun addFlag(flag: String, count: Int) { flagsCountdown[flag] = count }
fun removeFlag(flag: String) { flagsCountdown.remove(flag) }
fun getTurnsBetweenDiplomaticVotings() = (15 * gameInfo.gameParameters.gameSpeed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
fun mayVoteForDiplomaticVictory() =
getTurnsTillNextDiplomaticVote() == 0
&& civName !in gameInfo.diplomaticVictoryVotesCast.keys
fun diplomaticVoteForCiv(chosenCivName: String?) {
if (chosenCivName != null) gameInfo.diplomaticVictoryVotesCast[civName] = chosenCivName
addFlag(CivFlags.TurnsTillNextDiplomaticVote.name, getTurnsBetweenDiplomaticVotings())
}
fun shouldShowDiplomaticVotingResults() =
flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0
// Yes, this is the same function as above, but with a different use case so it has a different name.
fun shouldCheckForDiplomaticVictory() =
flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0
/** Modify gold by a given amount making sure it does neither overflow nor underflow.
* @param delta the amount to add (can be negative)
@ -717,7 +754,6 @@ class CivilizationInfo {
}
fun destroy() {
val destructionText = if (isMajorCiv()) "The civilization of [$civName] has been destroyed!"
else "The City-State of [$civName] has been destroyed!"
@ -831,7 +867,7 @@ class CivilizationInfo {
}
fun updateAllyCivForCityState() {
var newAllyName = ""
var newAllyName: String? = null
if (!isCityState()) return
val maxInfluence = diplomacy
.filter { !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() }
@ -848,7 +884,7 @@ class CivilizationInfo {
// This means that it will NOT HAVE a capital at that time, so if we run getCapital we'll get a crash!
val capitalLocation = if (cities.isNotEmpty()) getCapital().location else null
if (newAllyName != "") {
if (newAllyName != null) {
val newAllyCiv = gameInfo.getCivilization(newAllyName)
val text = "We have allied with [${civName}]."
if (capitalLocation != null) newAllyCiv.addNotification(text, capitalLocation, civName, NotificationIcon.Diplomacy)
@ -856,7 +892,7 @@ class CivilizationInfo {
newAllyCiv.updateViewableTiles()
newAllyCiv.updateDetailedCivResources()
}
if (oldAllyName != "") {
if (oldAllyName != null) {
val oldAllyCiv = gameInfo.getCivilization(oldAllyName)
val text = "We have lost alliance with [${civName}]."
if (capitalLocation != null) oldAllyCiv.addNotification(text, capitalLocation, civName, NotificationIcon.Diplomacy)
@ -879,5 +915,8 @@ class CivilizationInfoPreview {
}
enum class CivFlags {
cityStateGreatPersonGift
CityStateGreatPersonGift,
TurnsTillNextDiplomaticVote,
ShowDiplomaticVotingResults,
ShouldResetDiplomaticVotes,
}

View File

@ -9,6 +9,7 @@ class VictoryManager {
var requiredSpaceshipParts = Counter<String>()
var currentsSpaceshipParts = Counter<String>()
var hasWonDiplomaticVictory = false
init {
requiredSpaceshipParts.add("SS Booster", 3)
@ -31,6 +32,36 @@ class VictoryManager {
fun spaceshipPartsRemaining() = requiredSpaceshipParts.values.sum() - currentsSpaceshipParts.values.sum()
fun calculateDiplomaticVotingResults(votesCast: HashMap<String, String>): Counter<String> {
val results = Counter<String>()
for (castVote in votesCast) {
results.add(castVote.value, 1)
}
return results
}
fun votesNeededForDiplomaticVictory(): Int {
val civCount = civInfo.gameInfo.civilizations.count { !it.isDefeated() }
// CvGame.cpp::DoUpdateDiploVictory() in the source code of the original
return (
if (civCount > 28) 0.35 * civCount
else (67 - 1.1 * civCount) / 100 * civCount
).toInt()
}
fun hasEnoughVotesForDiplomaticVictory(): Boolean {
val results = calculateDiplomaticVotingResults(civInfo.gameInfo.diplomaticVictoryVotesCast)
val bestCiv = results.maxByOrNull { it.value }
if (bestCiv == null) return false
// If we don't have the highest score, we have not won anyway
if (bestCiv.key != civInfo.civName) return false
// If there's a tie, we haven't won either
return (results.none { it != bestCiv && it.value == bestCiv.value })
}
private fun hasVictoryType(victoryType: VictoryType) = civInfo.gameInfo.gameParameters.victoryTypes.contains(victoryType)
fun hasWonScientificVictory() = hasVictoryType(VictoryType.Scientific) && spaceshipPartsRemaining() == 0
@ -43,11 +74,16 @@ class VictoryManager {
&& civInfo.gameInfo.civilizations.all { it == civInfo || it.isDefeated() || !it.isMajorCiv() }
}
fun hasWonDiplomaticVictory() = hasVictoryType(VictoryType.Diplomatic)
&& civInfo.shouldCheckForDiplomaticVictory()
&& hasEnoughVotesForDiplomaticVictory()
fun hasWonVictoryType(): VictoryType? {
if (!civInfo.isMajorCiv()) return null
if (hasWonDominationVictory()) return VictoryType.Domination
if (hasWonScientificVictory()) return VictoryType.Scientific
if (hasWonCulturalVictory()) return VictoryType.Cultural
if (hasWonDiplomaticVictory()) return VictoryType.Diplomatic
if (civInfo.hasUnique("Triggers victory")) return VictoryType.Neutral
return null
}

View File

@ -23,7 +23,8 @@ class GameParameters { // Default values are the default new game
var nuclearWeaponsEnabled = true
var religionEnabled = false
var victoryTypes: ArrayList<VictoryType> = arrayListOf(VictoryType.Cultural, VictoryType.Domination, VictoryType.Scientific) // By default, all victory types
// By default, all victory types except Diplomacy as it is quite new
var victoryTypes: ArrayList<VictoryType> = arrayListOf(VictoryType.Cultural, VictoryType.Domination, VictoryType.Scientific)
var startingEra = "Ancient Era"
var isOnlineMultiplayer = false

View File

@ -494,11 +494,18 @@ class Building : NamedStats(), IConstruction, ICivilopediaText {
return "Should not be displayed"
}
}
"Hidden when [] Victory is disabled" -> {
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0]))) {
return unique.text
}
}
// Deprecated since 3.15.14
"Hidden when cultural victory is disabled" -> {
if ( !civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Cultural)) {
return "Hidden when cultural victory is disabled"
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.Cultural)) {
return unique.text
}
}
//
}
if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) {

View File

@ -17,8 +17,9 @@ import com.unciv.ui.utils.colorFromRGB
enum class VictoryType {
Neutral,
Cultural,
Diplomatic,
Domination,
Scientific
Scientific,
}
class Nation : INamed, ICivilopediaText {

View File

@ -110,7 +110,7 @@ object UniqueTriggerActivation {
}
}
"Allied City-States will occasionally gift Great People" ->
civInfo.addFlag(CivFlags.cityStateGreatPersonGift.name, civInfo.turnsForGreatPersonFromCityState() / 2)
civInfo.addFlag(CivFlags.CityStateGreatPersonGift.name, civInfo.turnsForGreatPersonFromCityState() / 2)
// The mechanics for granting great people are wonky, but basically the following happens:
// Based on the game speed, a timer with some amount of turns is set, 40 on regular speed
// Every turn, 1 is subtracted from this timer, as long as you have at least 1 city state ally
@ -126,6 +126,10 @@ object UniqueTriggerActivation {
// Note that the way this is implemented now, this unique does NOT stack
// I could parametrize the [Allied], but eh.
"Triggers voting for the Diplomatic Victory" ->
for (civ in civInfo.gameInfo.civilizations)
if (!civ.isBarbarian() && !civ.isSpectator())
civ.addFlag(CivFlags.TurnsTillNextDiplomaticVote.name, civInfo.getTurnsBetweenDiplomaticVotings())
}
}
}

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.*
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.Unique
import com.unciv.models.ruleset.VictoryType
import com.unciv.models.stats.INamed
import com.unciv.models.translations.tr
@ -172,12 +173,9 @@ class CivilopediaScreen(
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
val hideReligionItems = !game.gameInfo.hasReligionEnabled()
val noCulturalVictory = VictoryType.Cultural !in game.gameInfo.gameParameters.victoryTypes
categoryToEntries[CivilopediaCategories.Building] = ruleset.buildings.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques
&& !(hideReligionItems && "Hidden when religion is disabled" in it.uniques)
&& !(noCulturalVictory && "Hidden when cultural victory is disabled" in it.uniques)
.filter { shouldBeDisplayed(it.uniqueObjects)
&& !it.isAnyWonder() }
.map {
CivilopediaEntry(
@ -188,9 +186,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Wonder] = ruleset.buildings.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques
&& !(hideReligionItems && "Hidden when religion is disabled" in it.uniques)
&& !(noCulturalVictory && "Hidden when cultural victory is disabled" in it.uniques)
.filter { shouldBeDisplayed(it.uniqueObjects)
&& it.isAnyWonder() }
.map {
CivilopediaEntry(
@ -210,6 +206,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Terrain] = ruleset.terrains.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
@ -219,6 +216,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Improvement] = ruleset.tileImprovements.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
@ -228,9 +226,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Unit] = ruleset.units.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques
&& !(hideReligionItems && "Hidden when religion is disabled" in it.uniques)
}
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
@ -240,7 +236,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Nation] = ruleset.nations.values
.filter { it.isMajorCiv() }
.filter { shouldBeDisplayed(it.uniqueObjects) && it.isMajorCiv() }
.map {
CivilopediaEntry(
it.name,
@ -250,6 +246,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Technology] = ruleset.technologies.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
@ -259,6 +256,7 @@ class CivilopediaScreen(
)
}
categoryToEntries[CivilopediaCategories.Promotion] = ruleset.unitPromotions.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
@ -377,6 +375,21 @@ class CivilopediaScreen(
return SimpleCivilopediaText(lines, true)
}
private fun shouldBeDisplayed(uniqueObjects: List<Unique>): Boolean {
val uniques = uniqueObjects.map { it.placeholderText }
val hideReligionItems = !game.gameInfo.hasReligionEnabled()
val noCulturalVictory = VictoryType.Cultural !in game.gameInfo.gameParameters.victoryTypes
return "Will not be displayed in Civilopedia" !in uniques
&& !(hideReligionItems && "Hidden when religion is disabled" in uniques)
&& !(uniqueObjects.filter { unique -> unique.placeholderText == "Hidden when [] Victory is disabled"}.any {
unique -> !game.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0] ))
})
// Deprecated since 3.15.14
&& !(noCulturalVictory && "Hidden when cultural victory is disabled" in uniques)
//
}
override fun resize(width: Int, height: Int) {
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
game.setScreen(CivilopediaScreen(game.worldScreen.gameInfo.ruleSet, currentCategory, currentEntry))

View File

@ -72,6 +72,9 @@ class DiplomacyOverviewTable (
civTable.background = ImageGetter.getBackground(Color.BLACK)
civTable.add("[${relevantCivs.size}] Civilizations in the game".toLabel()).pad(5f).colspan(2).row()
civTable.add(titleTable).colspan(2).row()
val turnsTillNextDiplomaticVote = viewingPlayer.getTurnsTillNextDiplomaticVote()
if (turnsTillNextDiplomaticVote != null)
civTable.add("Turns until the next\ndiplomacy victory vote: [$turnsTillNextDiplomaticVote]".toLabel()).center().pad(5f).colspan(2).row()
civTable.addSeparator()
civTable.add("Known and alive ([${playerKnowsAndUndefeatedCivs.size - 1}])".toLabel())
.pad(5f).colspan(2).row()

View File

@ -0,0 +1,42 @@
package com.unciv.ui.pickerscreens
import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.UncivSound
import com.unciv.models.translations.tr
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toLabel
class DiplomaticVotePickerScreen(private val votingCiv: CivilizationInfo) : PickerScreen() {
private var chosenCiv: String? = null
init {
setDefaultCloseAction()
rightSideButton.setText("Choose a civ to vote for".tr())
descriptionLabel.setText("Choose who should become the world leader and win a diplomatic victory!".tr())
val choosableCivs = votingCiv.gameInfo.civilizations.filter { it.isMajorCiv() && it != votingCiv && !it.isDefeated() }
for (civ in choosableCivs)
{
val button = Button(skin)
button.add(ImageGetter.getNationIndicator(civ.nation, 30f)).pad(10f)
button.add(civ.civName.toLabel()).pad(10f)
button.pack()
button.onClick {
chosenCiv = civ.civName
pick("Vote for [${civ.civName}]".tr())
}
topTable.add(button).pad(10f).row()
}
rightSideButton.onClick(UncivSound.Chimes) {
votingCiv.diplomaticVoteForCiv(chosenCiv!!)
UncivGame.Current.setWorldScreen()
}
}
}

View File

@ -0,0 +1,55 @@
package com.unciv.ui.pickerscreens
import com.unciv.UncivGame
import com.unciv.logic.civilization.CivFlags
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.UncivSound
import com.unciv.models.translations.tr
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.enable
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toLabel
class DiplomaticVoteResultScreen(val votesCast: HashMap<String, String>, val viewingCiv: CivilizationInfo) : PickerScreen() {
val gameInfo = viewingCiv.gameInfo
init {
closeButton.remove()
addVote(viewingCiv.civName)
for (civ in gameInfo.civilizations.filter { it.isMajorCiv() && it != viewingCiv })
addVote(civ.civName)
for (civ in gameInfo.civilizations.filter { it.isCityState() })
addVote(civ.civName)
rightSideButton.onClick(UncivSound.Click) {
viewingCiv.addFlag(CivFlags.ShowDiplomaticVotingResults.name, -1)
UncivGame.Current.setWorldScreen()
}
rightSideButton.enable()
rightSideButton.setText("Continue".tr())
}
private fun addVote(civName: String) {
val civ = gameInfo.civilizations.firstOrNull { it.civName == civName }
if (civ == null || civ.isDefeated()) return
topTable.add(ImageGetter.getNationIndicator(civ.nation, 30f)).pad(10f)
topTable.add(civName.toLabel()).pad(20f)
if (civName !in votesCast.keys) {
topTable.add("Abstained".toLabel()).row()
return
}
val votedCiv = gameInfo.civilizations.firstOrNull { it.civName == votesCast[civName] }!!
if (votedCiv.isDefeated()) {
topTable.add("Abstained".toLabel()).row()
return
}
topTable.add("Voted for".toLabel()).pad(20f)
topTable.add(ImageGetter.getNationIndicator(votedCiv.nation, 30f)).pad(10f)
topTable.add(votedCiv.civName.toLabel()).row()
}
}

View File

@ -115,7 +115,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
otherCiv.updateAllyCivForCityState()
val ally = otherCiv.getAllyCiv()
if (ally != "") {
if (ally != null) {
val allyString = "{Ally}: {$ally} {Influence}: ".tr() +
otherCiv.getDiplomacyManager(ally).influence.toString()
diplomacyTable.add(allyString.toLabel()).row()

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.badlogic.gdx.utils.Align
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.ruleset.VictoryType
import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.models.translations.tr
import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.NewGameScreen
@ -54,24 +55,14 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
val playerVictoryType = playerCivInfo.victoryManager.hasWonVictoryType()
if (playerVictoryType != null) {
someoneHasWon = true
when (playerVictoryType) {
VictoryType.Cultural -> wonOrLost("You have won a cultural victory!")
VictoryType.Domination -> wonOrLost("You have won a domination victory!")
VictoryType.Scientific -> wonOrLost("You have won a scientific victory!")
VictoryType.Neutral -> wonOrLost("You have won!")
}
wonOrLost("You have won a [${playerVictoryType.name}] Victory!")
}
for (civ in gameInfo.civilizations.filter { it.isMajorCiv() && it != playerCivInfo }) {
val civVictoryType = civ.victoryManager.hasWonVictoryType()
if (civVictoryType != null) {
someoneHasWon = true
val winningCivName = civ.civName
when (civVictoryType) {
VictoryType.Cultural -> wonOrLost("[$winningCivName] has won a cultural victory!")
VictoryType.Domination -> wonOrLost("[$winningCivName] has won a domination victory!")
VictoryType.Scientific -> wonOrLost("[$winningCivName] has won a scientific victory!")
VictoryType.Neutral -> wonOrLost("[$winningCivName] has won!")
}
wonOrLost("[$winningCivName] has won a [${civVictoryType.name}] Victory!")
}
}
@ -86,11 +77,14 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
private fun wonOrLost(description: String) {
val endGameMessage = when (description) {
"You have won a cultural victory!" -> "You have achieved victory through the awesome power of your Culture. Your civilization's greatness - the magnificence of its monuments and the power of its artists - have astounded the world! Poets will honor you as long as beauty brings gladness to a weary heart."
"You have won a domination victory!" -> "The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph!"
"You have won a scientific victory!" -> "You have achieved victory through mastery of Science! You have conquered the mysteries of nature and led your people on a voyage to a brave new world! Your triumph will be remembered as long as the stars burn in the night sky!"
"You have won!" -> "Your civilization stands above all others! The exploits of your people shall be remembered until the end of civilizaton itself!"
val endGameMessage = when (description.getPlaceholderParameters()[0]) {
// Taking the 0th element is a dirty hack to differentiate between the cases
// "you have won" and "someone else has won", but it works, so I don't question it
VictoryType.Cultural.name -> "You have achieved victory through the awesome power of your Culture. Your civilization's greatness - the magnificence of its monuments and the power of its artists - have astounded the world! Poets will honor you as long as beauty brings gladness to a weary heart."
VictoryType.Domination.name -> "The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph!"
VictoryType.Scientific.name -> "You have achieved victory through mastery of Science! You have conquered the mysteries of nature and led your people on a voyage to a brave new world! Your triumph will be remembered as long as the stars burn in the night sky!"
VictoryType.Diplomatic.name -> "You have triumphed over your foes through the art of diplomacy! Your cunning and wisdom have earned you great friends - and divided and sown confusion among your enemies! Forever will you be remembered as the leader who brought peace to this weary world!"
VictoryType.Neutral.name -> "Your civilization stands above all others! The exploits of your people shall be remembered until the end of civilizaton itself!"
else -> "You have been defeated. Your civilization has been overwhelmed by its many foes. But your people do not despair, for they know that one day you shall return - and lead them forward to victory!"
}

View File

@ -400,6 +400,8 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
when {
!gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.civilizations.any { it.victoryManager.hasWon() }) ->
game.setScreen(VictoryScreen(this))
viewingCiv.shouldShowDiplomaticVotingResults() ->
UncivGame.Current.setScreen(DiplomaticVoteResultScreen(gameInfo.diplomaticVictoryVotesCast, viewingCiv))
viewingCiv.greatPeople.freeGreatPeople > 0 -> game.setScreen(GreatPersonPickerScreen(viewingCiv))
viewingCiv.popupAlerts.any() -> AlertPopup(this, viewingCiv.popupAlerts.first()).open()
viewingCiv.tradeRequests.isNotEmpty() -> TradePopup(this).open()
@ -701,6 +703,10 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
NextTurnAction("Found Religion", Color.WHITE) {
game.setScreen(FoundReligionPickerScreen(viewingCiv, gameInfo))
}
viewingCiv.mayVoteForDiplomaticVictory() ->
NextTurnAction("Vote for World Leader", Color.RED) {
game.setScreen(DiplomaticVotePickerScreen(viewingCiv))
}
else ->
NextTurnAction("${Fonts.turn}{Next turn}", Color.WHITE) {

View File

@ -265,6 +265,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Harbor](https://thenounproject.com/term/harbor/225583/) By Rflor for Seaport
* [Mansion](https://www.flaticon.com/free-icon/mansion_509903#term=persian&page=1&position=19) by Freepik for Satrap's Court
* [Bullets](https://thenounproject.com/term/bullets/810156/) By Aldric Rodriguez for Arsenal
* [St. Petersburg](https://thenounproject.com/search/?q=kremlin&i=1569704) By Carpe Diem for Kremlin
### Industrial Era
@ -281,30 +282,30 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Hangar](https://thenounproject.com/search/?q=hangar&i=1705288) By Rflor for Military Base
* [Eiffel Tower](https://thenounproject.com/term/eiffel-tower/1907757/) By Felipe Alvarado
* [Statue of Liberty](https://thenounproject.com/search/?q=statue%20of%20liberty&i=1801199) By 1516
* [Microscope](https://thenounproject.com/term/microscope/1452362/) By Arafat Uddin for Research Lab
* [Christ the redeemer](https://thenounproject.com/term/christ-the-redeemer/56112/) By Stefan Spieler for Cristo Redentor
* [St. Petersburg](https://thenounproject.com/search/?q=kremlin&i=1569704) By Carpe Diem for Kremlin
* [Neuschwanstein](https://thenounproject.com/search/?q=Neuschwanstein&i=2107683) By Vectors Market
* [Big Ben](https://thenounproject.com/search/?q=Big%20Ben&i=443690) By Ben Davis, RO
### Information Era
### Atomic Era
* [Chemistry](https://thenounproject.com/term/chemistry/175847/) By Creative Stall for Medical Lab
* [Microscope](https://thenounproject.com/term/microscope/1452362/) By Arafat Uddin for Research Lab
* [Water dam](https://thenounproject.com/term/water-dam/1002726/) By Symbolon for Hydro Plant
* [Pentagon](https://thenounproject.com/search/?q=the%20pentagon&i=1788323) By Maxim Kulikov
* [Solar panel](https://thenounproject.com/term/solar-panel/1131/) By Modik for Solar Plant
* [Opera House Sydney](https://thenounproject.com/term/opera-house-sydney/1626283/) By Pham Duy Phuong Hung for Sydney Opera House
* [Water dam](https://thenounproject.com/term/water-dam/1002726/) By Symbolon for Hydro Plant
* [Manhattan Project](https://thenounproject.com/search/?q=Nuclear%20Bomb&i=2041074) By corpus delicti, GR
* [Spaceship](https://thenounproject.com/term/spaceship/1444621/) By Dinosoft Labs for Apollo Program
* [Build](https://thenounproject.com/term/build/1156478/) By Michael G Brown for Spaceship Factory
* [Nuclear Plant](https://thenounproject.com/term/nuclear-plant/1132340/) By Andrejs Kirma
* [Pentagon](https://thenounproject.com/search/?q=the%20pentagon&i=1788323) By Maxim Kulikov
* [Spaceship](https://thenounproject.com/term/spaceship/1444621/) By Dinosoft Labs for Apollo Program
### Future Era
* [Hubble Telescope](https://thenounproject.com/search/?q=hubble%20space&i=445502) By Scott Lewis for Hubble Space Telescope
### Information Era
* [CN Tower Toronto](https://thenounproject.com/search/?q=cn%20tower&i=807678) By mikicon for CN tower
* [War Shelter](https://www.flaticon.com/free-icon/war-shelter_978661) by [Frepik](www.freepik.com) for Bomb Shelter
* [Missile](https://thenounproject.com/term/missile/799922/) By ProSymbols for SS Booster
* [Rocket](https://thenounproject.com/term/rocket/937173/) By BomSymbols for SS Cockpit
* [Hubble Telescope](https://thenounproject.com/search/?q=hubble%20space&i=445502) By Scott Lewis for Hubble Space Telescope
* [Build](https://thenounproject.com/term/build/1156478/) By Michael G Brown for Spaceship Factory
* [United Nations](https://thenounproject.com/search/?q=united+nations&i=3104698) by Imam for United Nations
* [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
@ -462,28 +463,31 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Microphone](https://thenounproject.com/term/microphone/470266/) By Viktor Vorobyev for Mass Media
* [Flight](https://thenounproject.com/term/flight/1014306/) By Genius Icons
* [Train](https://thenounproject.com/term/train/651644/) By Federico Panzano for Railroad
* [Refridgerator](https://thenounproject.com/search/?q=refridgerator&i=1188873) By b farias, CL
* [Fridge](https://thenounproject.com/search/?q=refridgerator&i=1188873) By b farias for Refrigeration
### Atomic
* [Pill](https://thenounproject.com/term/pill/780458/) By Alex Arseneau for Pharmaceuticals
* [Satellite Dish](https://thenounproject.com/search/?q=satellite%20dish&i=2054441) By Vectors Market for Radar
* [Ecology](https://thenounproject.com/term/ecology/1970666/) By ProSymbols
* [Nuclear Reactor](https://thenounproject.com/term/nuclear-reactor/426463/) By Jeremie Sommet for Nuclear Fission
* [Rocket](https://thenounproject.com/term/rocket/1743642/) By kareemov for Rocketry
* [Computer](https://thenounproject.com/term/computer/1967529/) By Shastry for Computers
### Information
* [Pill](https://thenounproject.com/term/pill/780458/) By Alex Arseneau for Pharmaceuticals
* [Computer](https://thenounproject.com/term/computer/1967529/) By Shastry for Computers
* [telecommunications](https://thenounproject.com/search/?q=telecommunications&i=3191260) by Wichai Wi for Telcommunications
* [Tactics](https://thenounproject.com/search/?q=tactics&i=2290123) By Grafix Point for Mobile Tactics
* [Laser](https://thenounproject.com/search/?q=laser&i=232249) by Andrew Doane for Lasers
* [Satellite Dish](https://thenounproject.com/search/?q=satellite%20dish&i=2054441) By Vectors Market for Radar
* [Nuclear Reactor](https://thenounproject.com/term/nuclear-reactor/426463/) By Jeremie Sommet for Nuclear Fission
* [Ecology](https://thenounproject.com/term/ecology/1970666/) By ProSymbols
* [Robotic Arm](https://thenounproject.com/term/robotic-arm/1970874/) By Karl Gilbert for Robotics
* [Rocket](https://thenounproject.com/term/rocket/1743642/) By kareemov for Rocketry
* [Rocket](https://thenounproject.com/term/rocket/3999811) Kusdarti for Advanced Ballistics
* [Satellite](https://thenounproject.com/term/satellite/1466641/) By Ben Davis for Satellites
* [Robotic Arm](https://thenounproject.com/term/robotic-arm/1970874/) By Karl Gilbert for Robotics
* [Laser](https://thenounproject.com/search/?q=laser&i=232249) by Andrew Doane for Lasers
* [global](https://thenounproject.com/search/?q=globalization&i=4073147) by Rank Sol for Globilization
* [Atom](https://thenounproject.com/term/atom/1586852/) By Kelsey Armstrong for Particle Physics
* [Nanoparticles](https://thenounproject.com/term/nanoparticles/822286/) By Gyan Lakhwani for Nanotechnology
* [Thermonuclear fusion](https://thenounproject.com/search/?q=fusion&i=3292735) by Olena Panasovska, UA for Nuclear Fusion
* [Electronics](https://thenounproject.com/search/?q=Electronics&i=1565843) By Cuby Design
* [Radar](https://thenounproject.com/term/radar/1546196/) By CINDYFLA, ID for Stealth
### Future
* [Nanoparticles](https://thenounproject.com/term/nanoparticles/822286/) By Gyan Lakhwani for Nanotechnology
* [Satellite](https://thenounproject.com/term/satellite/1466641/) By Ben Davis for Satellites
* [Electronics](https://thenounproject.com/search/?q=Electronics&i=1565843) By Cuby Design
* [Atom](https://thenounproject.com/term/atom/1586852/) By Kelsey Armstrong for Particle Physics
* [Thermonuclear fusion](https://thenounproject.com/search/?q=fusion&i=3292735) by Olena Panasovska, UA for Nuclear Fusion
* [telecommunications](https://thenounproject.com/search/?q=telecommunications&i=3191260) by Wichai Wi for Telcommunications
* [Radar](https://thenounproject.com/term/radar/1546196/) By CINDYFLA, ID for Stealth
* [Information Technology](https://thenounproject.com/term/information-technology/1927668/) By Vectors Markeet for Future Tech
## Terrain