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

@ -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"]
}
]

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
@ -454,4 +458,4 @@ class GameInfoPreview {
var gameId = ""
var currentPlayer = ""
fun getCivilization(civName: String) = civilizations.first { it.civName == civName }
}
}

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
@ -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,
}

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
@ -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
}

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 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!!)) {

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,
@ -376,6 +374,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) {

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!")
}
}
@ -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!"
}

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
@ -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