diff --git a/android/Images/TechIcons/Globalization.png b/android/Images/TechIcons/Globalization.png new file mode 100644 index 0000000000..6bc66aa3c6 Binary files /dev/null and b/android/Images/TechIcons/Globalization.png differ diff --git a/android/ImagesToPackSeparately/BuildingIcons/United Nations.png b/android/ImagesToPackSeparately/BuildingIcons/United Nations.png new file mode 100644 index 0000000000..3c1a1e445a Binary files /dev/null and b/android/ImagesToPackSeparately/BuildingIcons/United Nations.png differ diff --git a/android/assets/BuildingIcons.atlas b/android/assets/BuildingIcons.atlas index 3759cfa340..75223d23ae 100644 --- a/android/assets/BuildingIcons.atlas +++ b/android/assets/BuildingIcons.atlas @@ -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 diff --git a/android/assets/BuildingIcons.png b/android/assets/BuildingIcons.png index ce53469ed9..03eee2d0d4 100644 Binary files a/android/assets/BuildingIcons.png and b/android/assets/BuildingIcons.png differ diff --git a/android/assets/game.png b/android/assets/game.png index 1dfcec196b..23343fb416 100644 Binary files a/android/assets/game.png and b/android/assets/game.png differ diff --git a/android/assets/jsons/Civ V - Vanilla/Buildings.json b/android/assets/jsons/Civ V - Vanilla/Buildings.json index 722be5be0e..82997bbccc 100644 --- a/android/assets/jsons/Civ V - Vanilla/Buildings.json +++ b/android/assets/jsons/Civ V - Vanilla/Buildings.json @@ -22,7 +22,7 @@ }, // Column 1 { - "name": "Granary", + "name": "Granary", "food": 2, "maintenance": 1, "hurryCostModifier": 25, @@ -121,7 +121,7 @@ "hurryCostModifier": 25, "maintenance": 1, "percentStatBonus": {"food": 15}, - "requiredTech": "The Wheel" + "requiredTech": "The Wheel" }, { "name": "Walls", @@ -185,7 +185,7 @@ "quote": "'He spoke, the son of Kronos, and nodded his head with the dark brows, and the immortally anointed hair of the great god swept from his divine head, and all Olympos was shaken' - The Iliad" // "Requires [Honor]" in BNW }, - + // Classical Era // Column 3 @@ -210,11 +210,11 @@ "quote": "'They that go down to the sea in ships, that do business in great waters; these see the works of the Lord, and his wonders in the deep.' - The Bible, Psalms 107:23-24" }, { - "name": "Stable", + "name": "Stable", "maintenance": 1, "requiredNearbyImprovedResources": ["Horses","Sheep","Cattle"], "hurryCostModifier": 25, - "uniques": ["+[15]% Production when constructing [Mounted] units [in this city]", + "uniques": ["+[15]% Production when constructing [Mounted] units [in this city]", "[+1 Production] from [Cattle] tiles [in this city]", "[+1 Production] from [Horses] tiles [in this city]", "[+1 Production] from [Sheep] tiles [in this city]"], @@ -248,7 +248,7 @@ "quote": "'I think that if ever a mortal heard the word of God it would be in a garden at the cool of the day.' - F. Frankfort Moore" }, { - "name": "Colosseum", + "name": "Colosseum", "maintenance": 1, "happiness": 2, "hurryCostModifier": 25, @@ -320,7 +320,7 @@ // stats, so it's a mish-mash of vanilla and G&K. It should be changed when faith is // implemented. - // Further edit: Temples are now implemented. However, due to Religion still being in + // Further edit: Temples are now implemented. However, due to Religion still being in // its alpha stages, these buildings are still the culture variant. When religion is finally // fully released, this building should be replaced with amphitheatres in existing save files, // and then later the G&K faith version can be added @@ -344,7 +344,7 @@ // stats, so it's a mish-mash of vanilla and G&K. It should be changed when faith is // implemented. - // Further edit: Temples are now implemented. However, due to Religion still being in + // Further edit: Temples are now implemented. However, due to Religion still being in // its alpha stages, these buildings are still the culture variant. When religion is finally // fully released, this building should be replaced with amphitheatres in existing save files, // and then later the G&K faith version can be added @@ -431,7 +431,7 @@ "requiredTech": "Iron Working", "quote": "'Why man, he doth bestride the narrow world like a colossus, and we petty men walk under his huge legs, and peep about to find ourselves dishonorable graves.' - William Shakespeare, Julius Caesar" }, - + // Medieval Era // Column 5 @@ -489,7 +489,7 @@ "quote": "'Few romances can ever surpass that of the granite citadel on top of the beetling precipices of Machu Picchu, the crown of Inca Land.' - Hiram Bingham" }, { - "name": "Workshop", + "name": "Workshop", "maintenance": 2, "production": 2, "specialistSlots": {"Engineer": 1}, @@ -631,7 +631,7 @@ "uniques": ["New [Military] units start with [15] Experience [in this city]"] "requiredTech": "Steel" }, - + // Renaissance Era // Column 7 @@ -674,7 +674,7 @@ { "name": "Satrap's Court", "replaces": "Bank", - "uniqueTo": "Persia", + "uniqueTo": "Persia", "gold": 2, "specialistSlots": {"Merchant": 1}, "happiness": 2, @@ -686,7 +686,7 @@ { "name": "Hanse", "replaces": "Bank", - "uniqueTo": "Germany", + "uniqueTo": "Germany", "gold": 2, "specialistSlots": {"Merchant": 1}, "hurryCostModifier": 15, @@ -717,7 +717,7 @@ "name": "Leaning Tower of Pisa", "culture": 1, "isWonder": true, - "greatPersonPoints": {"Great Artist": 1}, + "greatPersonPoints": {"Great Artist": 1}, "uniques": ["[+25]% great person generation [in all cities]", "Free Great Person"], "requiredTech": "Printing Press", "quote": "'Don't clap too hard - it's a very old building.' - John Osbourne" @@ -769,7 +769,7 @@ "uniques": ["Free [Great Scientist] appears","Science gained from research agreements [+50]%"], "requiredTech": "Architecture", "quote": "'Things always seem fairer when we look back at them, and it is out of that inaccessible tower of the past that Longing leans and beckons.' - James Russell Lowell" - }, + }, { "name": "Windmill", "production": 2, @@ -797,7 +797,7 @@ "requiredTech": "Metallurgy", "quote": "'The Law is a fortress on a hill that armies cannot take or floods wash away.' - The Prophet Muhammed" }, - + // Industrial Era // Column 9 @@ -896,7 +896,7 @@ "requiredTech": "Electricity" }, */ - + // Modern Era // Column 11 @@ -1003,7 +1003,7 @@ // Column 14 { "name": "Solar Plant", - "production": 5, + "production": 5, "percentStatBonus": {"production": 15}, "requiredBuilding": "Factory", "maintenance": 3, @@ -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"], - "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! + "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" }, - */ { "name": "SS Engine", "requiredResource": "Aluminum", @@ -1117,12 +1115,12 @@ }, // All Eras - + { "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"] + "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/Civ V - Vanilla/Techs.json b/android/assets/jsons/Civ V - Vanilla/Techs.json index 978991054a..f488afd5bf 100644 --- a/android/assets/jsons/Civ V - Vanilla/Techs.json +++ b/android/assets/jsons/Civ V - Vanilla/Techs.json @@ -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" } diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 8b95351b12..fbeeeb3ddc 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -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 diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index f22e89c277..a0f42aed14 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -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() + /**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 @@ -454,4 +458,4 @@ class GameInfoPreview { var gameId = "" var currentPlayer = "" fun getCivilization(civName: String) = civilizations.first { it.civName == civName } -} +} \ No newline at end of file diff --git a/core/src/com/unciv/logic/automation/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/NextTurnAutomation.kt index 756789bacb..fcf47cdb5c 100644 --- a/core/src/com/unciv/logic/automation/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/NextTurnAutomation.kt @@ -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) }) { diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 6f2293a2dd..f6802c6542 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -89,13 +89,13 @@ class CivilizationInfo { var diplomacy = HashMap() var notifications = ArrayList() val popupAlerts = ArrayList() - private var allyCivName = "" + private var allyCivName: String? = null var naturalWonders = ArrayList() /** for trades here, ourOffers is the current civ's offers, and theirOffers is what the requesting civ offers */ val tradeRequests = ArrayList() - /** See DiplomacyManager.flagsCountdown to why not eEnum */ + /** See DiplomacyManager.flagsCountdown for why this does not map Enums to ints */ private var flagsCountdown = HashMap() /** 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 @@ -629,12 +626,52 @@ 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, } diff --git a/core/src/com/unciv/logic/civilization/VictoryManager.kt b/core/src/com/unciv/logic/civilization/VictoryManager.kt index 9e20fdeb67..4b8dcc5463 100644 --- a/core/src/com/unciv/logic/civilization/VictoryManager.kt +++ b/core/src/com/unciv/logic/civilization/VictoryManager.kt @@ -9,6 +9,7 @@ class VictoryManager { var requiredSpaceshipParts = Counter() var currentsSpaceshipParts = Counter() + 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): Counter { + val results = Counter() + 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 @@ -42,12 +73,17 @@ class VictoryManager { return hasVictoryType(VictoryType.Domination) && 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 } diff --git a/core/src/com/unciv/models/metadata/GameParameters.kt b/core/src/com/unciv/models/metadata/GameParameters.kt index 4d3448f0b6..cc5aef0cae 100644 --- a/core/src/com/unciv/models/metadata/GameParameters.kt +++ b/core/src/com/unciv/models/metadata/GameParameters.kt @@ -23,7 +23,8 @@ class GameParameters { // Default values are the default new game var nuclearWeaponsEnabled = true var religionEnabled = false - var victoryTypes: ArrayList = 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 = arrayListOf(VictoryType.Cultural, VictoryType.Domination, VictoryType.Scientific) var startingEra = "Ancient Era" var isOnlineMultiplayer = false diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 0ce5b1086f..c375e99701 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -494,11 +494,18 @@ class Building : NamedStats(), IConstruction, ICivilopediaText { 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" + "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 unique.text + } + } + // } if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) { diff --git a/core/src/com/unciv/models/ruleset/Nation.kt b/core/src/com/unciv/models/ruleset/Nation.kt index 42bca3810f..b5a253fd82 100644 --- a/core/src/com/unciv/models/ruleset/Nation.kt +++ b/core/src/com/unciv/models/ruleset/Nation.kt @@ -17,8 +17,9 @@ import com.unciv.ui.utils.colorFromRGB enum class VictoryType { Neutral, Cultural, + Diplomatic, Domination, - Scientific + Scientific, } class Nation : INamed, ICivilopediaText { diff --git a/core/src/com/unciv/models/ruleset/Unique.kt b/core/src/com/unciv/models/ruleset/Unique.kt index 72ed1efcb3..f8c58c983f 100644 --- a/core/src/com/unciv/models/ruleset/Unique.kt +++ b/core/src/com/unciv/models/ruleset/Unique.kt @@ -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()) } } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt index 3c1c40cb66..a0f875bf05 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt @@ -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, @@ -376,6 +374,21 @@ class CivilopediaScreen( } return SimpleCivilopediaText(lines, true) } + + private fun shouldBeDisplayed(uniqueObjects: List): 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) { diff --git a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt index f84a1d6416..73d1b09ea2 100644 --- a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt @@ -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() diff --git a/core/src/com/unciv/ui/pickerscreens/DiplomaticVotePickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/DiplomaticVotePickerScreen.kt new file mode 100644 index 0000000000..cc0da3816a --- /dev/null +++ b/core/src/com/unciv/ui/pickerscreens/DiplomaticVotePickerScreen.kt @@ -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() + } + + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/pickerscreens/DiplomaticVoteResultScreen.kt b/core/src/com/unciv/ui/pickerscreens/DiplomaticVoteResultScreen.kt new file mode 100644 index 0000000000..51b2851478 --- /dev/null +++ b/core/src/com/unciv/ui/pickerscreens/DiplomaticVoteResultScreen.kt @@ -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, 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() + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index 87b409f4f3..cf92dee887 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -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() diff --git a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt index dc0e45e40a..7adb2b768c 100644 --- a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt +++ b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt @@ -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!") } } @@ -85,12 +76,15 @@ 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!" } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 79249f7b70..af97bbf333 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -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) { diff --git a/docs/Credits.md b/docs/Credits.md index 09d765060c..c0d17f650b 100644 --- a/docs/Credits.md +++ b/docs/Credits.md @@ -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 @@ -461,29 +462,32 @@ Unless otherwise specified, all the following are from [the Noun Project](https: * [Plastic](https://thenounproject.com/term/plastic/478826/) By Yu luck * [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 +* [Train](https://thenounproject.com/term/train/651644/) By Federico Panzano for Railroad +* [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