mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-26 21:35:14 -04:00
Moddable victories (#6161)
* Added a moddable (but not yet functional) file for victories * Spaceship parts are units now * Fixed tests * Added milestone objects * Made 'our status' table in VictoryScreen dependend on file * Updated VictoryManager to use the new Milestone system * Fixed bug where in vanilla too many spaceship parts could beb uild * Whoops * Updated global victory table to use the jsons * Updated the new game screen to show the new victory types Also started with the deprecation of VictorType * Did some translation stuff, also finally fixed the tests * Removed VictoryType and reworked AI to use Milestones instead * Add some checks for the victory file; tested that custom victories work Also moves some code to a better spot and fixes compilation errors * Fixed some things I thought about while falling asleep Most notably: built -> build; fixed spaceship part construction priority; removed more code for the old system * Fixed translation issues on the victory screen
This commit is contained in:
parent
382707a6a5
commit
34105efdda
42
android/assets/jsons/Civ V - Gods & Kings/VictoryTypes.json
Normal file
42
android/assets/jsons/Civ V - Gods & Kings/VictoryTypes.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Scientific",
|
||||||
|
"victoryScreenHeader": "Complete all the spaceship parts\nto win!",
|
||||||
|
"milestones": [
|
||||||
|
"Build [Apollo Program]", "Add all [spaceship parts] in capital"
|
||||||
|
],
|
||||||
|
"requiredSpaceshipParts": [
|
||||||
|
"SS Engine", "SS Stasis Chamber", "SS Cockpit", "SS Booster", "SS Booster", "SS Booster"
|
||||||
|
],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cultural",
|
||||||
|
"victoryScreenHeader": "Complete 5 policy branches and\nbuild the Utopia Project to win!",
|
||||||
|
"milestones": ["Complete [5] Policy branches", "Build [Utopia Project]"],
|
||||||
|
"victoryString": "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.",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Domination",
|
||||||
|
"victoryScreenHeader": "Destroy all enemies\nto win!",
|
||||||
|
"milestones": ["Destroy all players"],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Diplomatic",
|
||||||
|
"hiddenInVictoryScreen": true,
|
||||||
|
"milestones": ["[United Nations] build globally", "Win diplomatic vote"],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Time",
|
||||||
|
"hiddenInVictoryScreen": true,
|
||||||
|
"milestones": ["Have highest score after max turns"],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
}
|
||||||
|
]
|
@ -1356,7 +1356,7 @@
|
|||||||
"cost": 500,
|
"cost": 500,
|
||||||
"requiredTech": "Robotics",
|
"requiredTech": "Robotics",
|
||||||
"requiredResource": "Aluminum",
|
"requiredResource": "Aluminum",
|
||||||
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital"]
|
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital", "Limited to [3] per Civilization"]
|
||||||
// costs 1500 in BNW
|
// costs 1500 in BNW
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1366,7 +1366,7 @@
|
|||||||
"cost": 750,
|
"cost": 750,
|
||||||
"requiredTech": "Satellites",
|
"requiredTech": "Satellites",
|
||||||
"requiredResource": "Aluminum",
|
"requiredResource": "Aluminum",
|
||||||
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital"]
|
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital", "Limited to [1] per Civilization"]
|
||||||
// costs 1500 in BNW
|
// costs 1500 in BNW
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1376,7 +1376,7 @@
|
|||||||
"cost": 750,
|
"cost": 750,
|
||||||
"requiredTech": "Particle Physics",
|
"requiredTech": "Particle Physics",
|
||||||
"requiredResource": "Aluminum",
|
"requiredResource": "Aluminum",
|
||||||
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital"]
|
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital", "Limited to [1] per Civilization"]
|
||||||
// costs 1500 in BNW
|
// costs 1500 in BNW
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1386,7 +1386,7 @@
|
|||||||
"cost": 750,
|
"cost": 750,
|
||||||
"requiredTech": "Nanotechnology",
|
"requiredTech": "Nanotechnology",
|
||||||
"requiredResource": "Aluminum",
|
"requiredResource": "Aluminum",
|
||||||
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital"]
|
"uniques": ["Spaceship part", "Cannot be purchased", "Only available <if [Apollo Program] is constructed>", "Uncapturable", "Can be added to [The Spaceship] in the Capital", "Limited to [1] per Civilization"]
|
||||||
// costs 1500 in BNW
|
// costs 1500 in BNW
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
42
android/assets/jsons/Civ V - Vanilla/VictoryTypes.json
Normal file
42
android/assets/jsons/Civ V - Vanilla/VictoryTypes.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Scientific",
|
||||||
|
"victoryScreenHeader": "Complete all the spaceship parts\nto win!",
|
||||||
|
"milestones": [
|
||||||
|
"Build [Apollo Program]", "Add all [spaceship parts] in capital"
|
||||||
|
],
|
||||||
|
"requiredSpaceshipParts": [
|
||||||
|
"SS Engine", "SS Stasis Chamber", "SS Cockpit", "SS Booster", "SS Booster", "SS Booster"
|
||||||
|
],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cultural",
|
||||||
|
"victoryScreenHeader": "Complete 5 policy branches and\nbuild the Utopia Project to win!",
|
||||||
|
"milestones": ["Complete [5] Policy branches", "Build [Utopia Project]"],
|
||||||
|
"victoryString": "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.",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Domination",
|
||||||
|
"victoryScreenHeader": "Destroy all enemies\nto win!",
|
||||||
|
"milestones": ["Destroy all players"],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Diplomatic",
|
||||||
|
"hiddenInVictoryScreen": true,
|
||||||
|
"milestones": ["[United Nations] build globally", "Win diplomatic vote"],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Time",
|
||||||
|
"hiddenInVictoryScreen": true,
|
||||||
|
"milestones": ["Have highest score after max turns"],
|
||||||
|
"victoryString": "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!",
|
||||||
|
"defeatString": "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!"
|
||||||
|
}
|
||||||
|
]
|
@ -415,6 +415,8 @@ Starting Era =
|
|||||||
It looks like we can't make a map with the parameters you requested! =
|
It looks like we can't make a map with the parameters you requested! =
|
||||||
Maybe you put too many players into too small a map? =
|
Maybe you put too many players into too small a map? =
|
||||||
No human players selected! =
|
No human players selected! =
|
||||||
|
Invalid Player ID! =
|
||||||
|
No victory conditions were selected! =
|
||||||
Mods: =
|
Mods: =
|
||||||
Extension mods: =
|
Extension mods: =
|
||||||
Base ruleset: =
|
Base ruleset: =
|
||||||
@ -1078,31 +1080,22 @@ Number of your cities\ndemanding this resource for\n'We Love The King Day' =
|
|||||||
|
|
||||||
# Victory
|
# Victory
|
||||||
|
|
||||||
Science victory =
|
[victoryType] Victory =
|
||||||
Cultural victory =
|
Built [building] =
|
||||||
Conquest victory =
|
Add all spaceship parts in capital =
|
||||||
Diplomatic victory =
|
Destroy all players =
|
||||||
Complete all the spaceship parts\n to win! =
|
Capture all capitals =
|
||||||
Complete 5 policy branches\n to win! =
|
Complete [amount] Policy branche =
|
||||||
Complete 5 policy branches and build\n the Utopia Project to win! =
|
|
||||||
Destroy all enemies\n to win! =
|
|
||||||
You have won a [victoryType] Victory! =
|
You have won a [victoryType] 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. =
|
[civilization] has won a [victoryType] 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 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 civilization itself! =
|
Your civilization stands above all others! The exploits of your people shall be remembered until the end of civilization 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 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! =
|
|
||||||
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! =
|
|
||||||
One more turn...! =
|
One more turn...! =
|
||||||
Built Apollo Program =
|
|
||||||
Destroy [civName] =
|
Destroy [civName] =
|
||||||
|
Capture [cityName] =
|
||||||
Our status =
|
Our status =
|
||||||
Global status =
|
Global status =
|
||||||
Rankings =
|
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.
|
# 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 between other words in your translation
|
# Feel free to replace it with a space and put it between other words in your translation
|
||||||
Turns until the next\ndiplomacy victory vote: [amount] =
|
Turns until the next\ndiplomacy victory vote: [amount] =
|
||||||
|
@ -47,9 +47,11 @@ object Constants {
|
|||||||
const val citadel = "Citadel"
|
const val citadel = "Citadel"
|
||||||
|
|
||||||
const val futureTech = "Future Tech"
|
const val futureTech = "Future Tech"
|
||||||
// Easter egg name. Hopefully is to hopefully avoid conflicts when later players can name their own religions.
|
// Easter egg name. Is to avoid conflicts when players name their own religions.
|
||||||
// This religion name should never be displayed.
|
// This religion name should never be displayed.
|
||||||
const val noReligionName = "The religion of TheLegend27"
|
const val noReligionName = "The religion of TheLegend27"
|
||||||
|
|
||||||
|
const val neutralVictoryType = "Neutral"
|
||||||
|
|
||||||
const val cancelImprovementOrder = "Cancel improvement order"
|
const val cancelImprovementOrder = "Cancel improvement order"
|
||||||
const val tutorialPopupNamePrefix = "Tutorial: "
|
const val tutorialPopupNamePrefix = "Tutorial: "
|
||||||
|
@ -12,7 +12,7 @@ class JsonParser {
|
|||||||
fun <T> getFromJson(tClass: Class<T>, filePath: String): T = getFromJson(tClass, Gdx.files.internal(filePath))
|
fun <T> getFromJson(tClass: Class<T>, filePath: String): T = getFromJson(tClass, Gdx.files.internal(filePath))
|
||||||
|
|
||||||
fun <T> getFromJson(tClass: Class<T>, file: FileHandle): T {
|
fun <T> getFromJson(tClass: Class<T>, file: FileHandle): T {
|
||||||
try{
|
try {
|
||||||
val jsonText = file.readString(Charsets.UTF_8.name())
|
val jsonText = file.readString(Charsets.UTF_8.name())
|
||||||
return json.fromJson(tClass, jsonText)
|
return json.fromJson(tClass, jsonText)
|
||||||
} catch (exception:Exception){
|
} catch (exception:Exception){
|
||||||
|
@ -37,6 +37,8 @@ class GameInfo {
|
|||||||
|
|
||||||
// Maps a civ to the civ they voted for
|
// Maps a civ to the civ they voted for
|
||||||
var diplomaticVictoryVotesCast = HashMap<String, String>()
|
var diplomaticVictoryVotesCast = HashMap<String, String>()
|
||||||
|
// Set to false whenever the results still need te be processed
|
||||||
|
var diplomaticVictoryVotesProcessed = false
|
||||||
|
|
||||||
/**Keep track of a custom location this game was saved to _or_ loaded from
|
/**Keep track of a custom location this game was saved to _or_ loaded from
|
||||||
*
|
*
|
||||||
@ -205,7 +207,6 @@ class GameInfo {
|
|||||||
currentPlayerIndex = (currentPlayerIndex + 1) % civilizations.size
|
currentPlayerIndex = (currentPlayerIndex + 1) % civilizations.size
|
||||||
if (currentPlayerIndex == 0) {
|
if (currentPlayerIndex == 0) {
|
||||||
turns++
|
turns++
|
||||||
checkForTimeVictory()
|
|
||||||
}
|
}
|
||||||
thisPlayer = civilizations[currentPlayerIndex]
|
thisPlayer = civilizations[currentPlayerIndex]
|
||||||
thisPlayer.startTurn()
|
thisPlayer.startTurn()
|
||||||
@ -266,7 +267,7 @@ class GameInfo {
|
|||||||
&& (!it.militaryUnit!!.isInvisible(thisPlayer) || viewableInvisibleTiles.contains(it.position)))
|
&& (!it.militaryUnit!!.isInvisible(thisPlayer) || viewableInvisibleTiles.contains(it.position)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// enemy units ON our territory
|
// enemy units IN our territory
|
||||||
addEnemyUnitNotification(thisPlayer,
|
addEnemyUnitNotification(thisPlayer,
|
||||||
enemyUnitsCloseToTerritory.filter { it.getOwner() == thisPlayer },
|
enemyUnitsCloseToTerritory.filter { it.getOwner() == thisPlayer },
|
||||||
"in"
|
"in"
|
||||||
@ -283,16 +284,17 @@ class GameInfo {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getEnabledVictories() = ruleSet.victories.filter { !it.value.hiddenInVictoryScreen && gameParameters.victoryTypes.contains(it.key) }
|
||||||
|
|
||||||
private fun checkForTimeVictory() {
|
fun processDiplomaticVictory() {
|
||||||
if (turns != gameParameters.maxTurns || !gameParameters.victoryTypes.contains(VictoryType.Time)) return
|
if (diplomaticVictoryVotesProcessed) return
|
||||||
|
for (civInfo in civilizations) {
|
||||||
val winningCiv = civilizations
|
if (civInfo.victoryManager.hasEnoughVotesForDiplomaticVictory()) {
|
||||||
.filter { it.isMajorCiv() && !it.isSpectator() && !it.isBarbarian() }
|
civInfo.victoryManager.hasEverWonDiplomaticVote = true
|
||||||
.maxByOrNull { it.calculateTotalScore() }
|
}
|
||||||
?: return // Are there no civs left?
|
}
|
||||||
|
diplomaticVictoryVotesProcessed = true
|
||||||
winningCiv.victoryManager.hasWonTimeVictory = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addEnemyUnitNotification(thisPlayer: CivilizationInfo, tiles: List<TileInfo>, inOrNear: String) {
|
private fun addEnemyUnitNotification(thisPlayer: CivilizationInfo, tiles: List<TileInfo>, inOrNear: String) {
|
||||||
@ -450,8 +452,10 @@ class GameInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spaceResources.clear()
|
||||||
spaceResources.addAll(ruleSet.buildings.values.filter { it.hasUnique(UniqueType.SpaceshipPart) }
|
spaceResources.addAll(ruleSet.buildings.values.filter { it.hasUnique(UniqueType.SpaceshipPart) }
|
||||||
.flatMap { it.getResourceRequirements().keys } )
|
.flatMap { it.getResourceRequirements().keys } )
|
||||||
|
spaceResources.addAll(ruleSet.victories.values.flatMap { it.requiredSpaceshipParts })
|
||||||
|
|
||||||
barbarians.setTransients(this)
|
barbarians.setTransients(this)
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ import com.unciv.logic.map.MapUnit
|
|||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
import com.unciv.logic.map.TileMap
|
import com.unciv.logic.map.TileMap
|
||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
import com.unciv.models.ruleset.VictoryType
|
import com.unciv.models.ruleset.MilestoneType
|
||||||
|
import com.unciv.models.ruleset.ThingToFocus
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.ruleset.unit.BaseUnit
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
@ -41,7 +42,7 @@ object Automation {
|
|||||||
|
|
||||||
rank += stats.production
|
rank += stats.production
|
||||||
rank += stats.science
|
rank += stats.science
|
||||||
if (city.tiles.size < 12 || city.civInfo.victoryType() == VictoryType.Cultural) {
|
if (city.tiles.size < 12 || city.civInfo.wantsToFocusOn(ThingToFocus.Culture)) {
|
||||||
rank += stats.culture
|
rank += stats.culture
|
||||||
} else rank += stats.culture / 2
|
} else rank += stats.culture / 2
|
||||||
}
|
}
|
||||||
@ -175,7 +176,7 @@ object Automation {
|
|||||||
return true
|
return true
|
||||||
|
|
||||||
// Spaceships are always allowed
|
// Spaceships are always allowed
|
||||||
if (construction.hasUnique(UniqueType.SpaceshipPart))
|
if (construction.name in civInfo.gameInfo.spaceResources)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
val requiredResources = construction.getResourceRequirements()
|
val requiredResources = construction.getResourceRequirements()
|
||||||
@ -237,7 +238,7 @@ object Automation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getReservedSpaceResourceAmount(civInfo: CivilizationInfo): Int {
|
fun getReservedSpaceResourceAmount(civInfo: CivilizationInfo): Int {
|
||||||
return if (civInfo.nation.preferredVictoryType == VictoryType.Scientific) 3 else 2
|
return if (civInfo.wantsToFocusOn(ThingToFocus.Science)) 3 else 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fun threatAssessment(assessor: CivilizationInfo, assessed: CivilizationInfo): ThreatLevel {
|
fun threatAssessment(assessor: CivilizationInfo, assessed: CivilizationInfo): ThreatLevel {
|
||||||
|
@ -5,7 +5,7 @@ import com.unciv.logic.civilization.CivilizationInfo
|
|||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
import com.unciv.models.ruleset.Belief
|
import com.unciv.models.ruleset.Belief
|
||||||
import com.unciv.models.ruleset.BeliefType
|
import com.unciv.models.ruleset.BeliefType
|
||||||
import com.unciv.models.ruleset.VictoryType
|
import com.unciv.models.ruleset.ThingToFocus
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -133,7 +133,7 @@ object ChooseBeliefsAutomation {
|
|||||||
score += modifier * when (unique.placeholderText) {
|
score += modifier * when (unique.placeholderText) {
|
||||||
"Earn []% of [] unit's [] as [] when killed within 4 tiles of a city following this religion" ->
|
"Earn []% of [] unit's [] as [] when killed within 4 tiles of a city following this religion" ->
|
||||||
unique.params[0].toFloat() * 4f *
|
unique.params[0].toFloat() * 4f *
|
||||||
if (civInfo.victoryType() == VictoryType.Domination) 2f
|
if (civInfo.wantsToFocusOn(ThingToFocus.Military)) 2f
|
||||||
else 1f
|
else 1f
|
||||||
"May buy [] buildings for [] [] []", "May buy [] units for [] [] []" ->
|
"May buy [] buildings for [] [] []", "May buy [] units for [] [] []" ->
|
||||||
if (civInfo.religionManager.religion != null
|
if (civInfo.religionManager.religion != null
|
||||||
@ -152,7 +152,7 @@ object ChooseBeliefsAutomation {
|
|||||||
// what happens over there
|
// what happens over there
|
||||||
else civInfo.statsForNextTurn[Stat.valueOf(unique.params[1])] * 10f / civInfo.getEra().baseUnitBuyCost
|
else civInfo.statsForNextTurn[Stat.valueOf(unique.params[1])] * 10f / civInfo.getEra().baseUnitBuyCost
|
||||||
UniqueType.BuyUnitsByProductionCost.placeholderText ->
|
UniqueType.BuyUnitsByProductionCost.placeholderText ->
|
||||||
15f * if (civInfo.victoryType() == VictoryType.Domination) 2f else 1f
|
15f * if (civInfo.wantsToFocusOn(ThingToFocus.Military)) 2f else 1f
|
||||||
"when a city adopts this religion for the first time (modified by game speed)" -> // Modified by personality
|
"when a city adopts this religion for the first time (modified by game speed)" -> // Modified by personality
|
||||||
unique.stats.values.sum() * 10f
|
unique.stats.values.sum() * 10f
|
||||||
"When spreading religion to a city, gain [] times the amount of followers of other religions as []" ->
|
"When spreading religion to a city, gain [] times the amount of followers of other religions as []" ->
|
||||||
|
@ -8,7 +8,8 @@ import com.unciv.logic.civilization.NotificationIcon
|
|||||||
import com.unciv.logic.civilization.PlayerType
|
import com.unciv.logic.civilization.PlayerType
|
||||||
import com.unciv.logic.map.BFS
|
import com.unciv.logic.map.BFS
|
||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
import com.unciv.models.ruleset.VictoryType
|
import com.unciv.models.ruleset.MilestoneType
|
||||||
|
import com.unciv.models.ruleset.ThingToFocus
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.ruleset.unit.BaseUnit
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
@ -36,8 +37,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
val allTechsAreResearched = civInfo.tech.getNumberOfTechsResearched() >= civInfo.gameInfo.ruleSet.technologies.size
|
val allTechsAreResearched = civInfo.tech.getNumberOfTechsResearched() >= civInfo.gameInfo.ruleSet.technologies.size
|
||||||
|
|
||||||
val isAtWar = civInfo.isAtWar()
|
val isAtWar = civInfo.isAtWar()
|
||||||
val preferredVictoryType = civInfo.victoryType()
|
private val buildingsForVictory = civInfo.gameInfo.getEnabledVictories().values
|
||||||
|
.mapNotNull { civInfo.victoryManager.getNextMilestone(it.name) }
|
||||||
|
.filter { it.type == MilestoneType.BuiltBuilding || it.type == MilestoneType.BuildingBuiltGlobally }
|
||||||
|
.map { it.params[0] }
|
||||||
|
|
||||||
|
private val spaceshipParts = civInfo.gameInfo.spaceResources
|
||||||
|
|
||||||
|
|
||||||
private val averageProduction = civInfo.cities.map { it.cityStats.currentCityStats.production }.average()
|
private val averageProduction = civInfo.cities.map { it.cityStats.currentCityStats.production }.average()
|
||||||
val cityIsOverAverageProduction = cityInfo.cityStats.currentCityStats.production >= averageProduction
|
val cityIsOverAverageProduction = cityInfo.cityStats.currentCityStats.production >= averageProduction
|
||||||
|
|
||||||
@ -45,9 +52,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
|
|
||||||
private val faithConstruction = arrayListOf<BaseUnit>()
|
private val faithConstruction = arrayListOf<BaseUnit>()
|
||||||
|
|
||||||
data class ConstructionChoice(val choice:String, var choiceModifier:Float,val remainingWork:Int)
|
data class ConstructionChoice(val choice: String, var choiceModifier: Float, val remainingWork: Int)
|
||||||
|
|
||||||
fun addChoice(choices:ArrayList<ConstructionChoice>, choice:String, choiceModifier: Float){
|
private fun addChoice(choices: ArrayList<ConstructionChoice>, choice: String, choiceModifier: Float){
|
||||||
choices.add(ConstructionChoice(choice,choiceModifier,cityConstructions.getRemainingWork(choice)))
|
choices.add(ConstructionChoice(choice,choiceModifier,cityConstructions.getRemainingWork(choice)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +132,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
val unitsToCitiesRatio = cities.toFloat() / (militaryUnits + 1)
|
val unitsToCitiesRatio = cities.toFloat() / (militaryUnits + 1)
|
||||||
// most buildings and civ units contribute the the civ's growth, military units are anti-growth
|
// most buildings and civ units contribute the the civ's growth, military units are anti-growth
|
||||||
var modifier = sqrt(unitsToCitiesRatio) / 2
|
var modifier = sqrt(unitsToCitiesRatio) / 2
|
||||||
if (preferredVictoryType == VictoryType.Domination) modifier *= 3
|
if (civInfo.wantsToFocusOn(ThingToFocus.Military)) modifier *= 3
|
||||||
else if (isAtWar) modifier *= unitsToCitiesRatio * 2
|
else if (isAtWar) modifier *= unitsToCitiesRatio * 2
|
||||||
|
|
||||||
if (Automation.afraidOfBarbarians(civInfo)) modifier = 2f // military units are pro-growth if pressured by barbs
|
if (Automation.afraidOfBarbarians(civInfo)) modifier = 2f // military units are pro-growth if pressured by barbs
|
||||||
@ -193,16 +200,15 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
var modifier = 0.5f
|
var modifier = 0.5f
|
||||||
if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
||||||
modifier = 0.8f
|
modifier = 0.8f
|
||||||
if (preferredVictoryType == VictoryType.Cultural) modifier = 1.6f
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture)) modifier = 1.6f
|
||||||
addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSpaceshipPartChoice() {
|
private fun addSpaceshipPartChoice() {
|
||||||
val spaceshipPart = (buildableNotWonders + buildableUnits).firstOrNull { it.hasUnique(UniqueType.SpaceshipPart) }
|
val spaceshipPart = (buildableNotWonders + buildableUnits).firstOrNull { it.name in spaceshipParts }
|
||||||
if (spaceshipPart != null) {
|
if (spaceshipPart != null) {
|
||||||
var modifier = 1.5f
|
val modifier = 2f
|
||||||
if (preferredVictoryType == VictoryType.Scientific) modifier = 2f
|
|
||||||
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
|
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,23 +223,27 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getWonderPriority(wonder: Building): Float {
|
private fun getWonderPriority(wonder: Building): Float {
|
||||||
if (wonder.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts))
|
// Only start building if we are the city that would complete it the soonest
|
||||||
return 2f
|
if (wonder.hasUnique(UniqueType.TriggersCulturalVictory)
|
||||||
if (preferredVictoryType == VictoryType.Cultural
|
&& cityInfo == civInfo.cities.minByOrNull {
|
||||||
|
it.cityConstructions.turnsToConstruction(wonder.name)
|
||||||
|
}!!
|
||||||
|
) {
|
||||||
|
return 10f
|
||||||
|
}
|
||||||
|
if (wonder.name in buildingsForVictory)
|
||||||
|
return 5f
|
||||||
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture)
|
||||||
|
// TODO: Moddability
|
||||||
&& wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House"))
|
&& wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House"))
|
||||||
return 3f
|
return 3f
|
||||||
// Only start building if we are the city that would complete it the soonest
|
|
||||||
if (wonder.hasUnique(UniqueType.TriggersCulturalVictory) && cityInfo == civInfo.cities.minByOrNull {
|
|
||||||
it.cityConstructions.turnsToConstruction(wonder.name)
|
|
||||||
}!!)
|
|
||||||
return 10f
|
|
||||||
if (wonder.isStatRelated(Stat.Science)) {
|
if (wonder.isStatRelated(Stat.Science)) {
|
||||||
if (allTechsAreResearched) return .5f
|
if (allTechsAreResearched) return .5f
|
||||||
if (preferredVictoryType == VictoryType.Scientific) return 1.5f
|
if (civInfo.wantsToFocusOn(ThingToFocus.Science)) return 1.5f
|
||||||
else return 1.3f
|
else return 1.3f
|
||||||
}
|
}
|
||||||
if (wonder.name == "Manhattan Project") {
|
if (wonder.name == "Manhattan Project") {
|
||||||
if (preferredVictoryType == VictoryType.Domination) return 2f
|
if (civInfo.wantsToFocusOn(ThingToFocus.Military)) return 2f
|
||||||
else return 1.3f
|
else return 1.3f
|
||||||
}
|
}
|
||||||
if (wonder.isStatRelated(Stat.Happiness)) return 1.2f
|
if (wonder.isStatRelated(Stat.Happiness)) return 1.2f
|
||||||
@ -259,10 +269,10 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
val unitTrainingBuilding = buildableNotWonders.asSequence()
|
val unitTrainingBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.hasUnique(UniqueType.UnitStartingExperience)
|
.filter { it.hasUnique(UniqueType.UnitStartingExperience)
|
||||||
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
||||||
if (unitTrainingBuilding != null && (preferredVictoryType != VictoryType.Cultural || isAtWar)) {
|
if (unitTrainingBuilding != null && (!civInfo.wantsToFocusOn(ThingToFocus.Culture) || isAtWar)) {
|
||||||
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
||||||
if (isAtWar) modifier *= 2
|
if (isAtWar) modifier *= 2
|
||||||
if (preferredVictoryType == VictoryType.Domination)
|
if (civInfo.wantsToFocusOn(ThingToFocus.Military))
|
||||||
modifier *= 1.3f
|
modifier *= 1.3f
|
||||||
addChoice(relativeCostEffectiveness, unitTrainingBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, unitTrainingBuilding.name, modifier)
|
||||||
}
|
}
|
||||||
@ -272,7 +282,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
val defensiveBuilding = buildableNotWonders.asSequence()
|
val defensiveBuilding = buildableNotWonders.asSequence()
|
||||||
.filter { it.cityStrength > 0
|
.filter { it.cityStrength > 0
|
||||||
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
||||||
if (defensiveBuilding != null && (isAtWar || preferredVictoryType != VictoryType.Cultural)) {
|
if (defensiveBuilding != null && (isAtWar || !civInfo.wantsToFocusOn(ThingToFocus.Culture))) {
|
||||||
var modifier = 0.2f
|
var modifier = 0.2f
|
||||||
if (isAtWar) modifier = 0.5f
|
if (isAtWar) modifier = 0.5f
|
||||||
|
|
||||||
@ -308,7 +318,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost }
|
||||||
if (scienceBuilding != null) {
|
if (scienceBuilding != null) {
|
||||||
var modifier = 1.1f
|
var modifier = 1.1f
|
||||||
if (preferredVictoryType == VictoryType.Scientific)
|
if (civInfo.wantsToFocusOn(ThingToFocus.Science))
|
||||||
modifier *= 1.4f
|
modifier *= 1.4f
|
||||||
addChoice(relativeCostEffectiveness, scienceBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, scienceBuilding.name, modifier)
|
||||||
}
|
}
|
||||||
@ -352,7 +362,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
// these 4 if conditions are used to determine if an AI should buy units to spread religion, or spend faith to buy things like new military units or new buildings.
|
// these 4 if conditions are used to determine if an AI should buy units to spread religion, or spend faith to buy things like new military units or new buildings.
|
||||||
// currently this AI can only buy inquisitors and missionaries with faith
|
// currently this AI can only buy inquisitors and missionaries with faith
|
||||||
// this system will have to be reengineered to support buying other stuff with faith
|
// this system will have to be reengineered to support buying other stuff with faith
|
||||||
if (preferredVictoryType == VictoryType.Domination) return
|
if (civInfo.wantsToFocusOn(ThingToFocus.Military)) return
|
||||||
if (civInfo.religionManager.religion?.name == null) return
|
if (civInfo.religionManager.religion?.name == null) return
|
||||||
if (cityInfo.religion.getMajorityReligion()?.name != civInfo.religionManager.religion?.name)
|
if (cityInfo.religion.getMajorityReligion()?.name != civInfo.religionManager.religion?.name)
|
||||||
return // you don't want to build units of opposing religions.
|
return // you don't want to build units of opposing religions.
|
||||||
@ -372,7 +382,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
|||||||
&& it.canBePurchasedWithStat(cityInfo, Stat.Faith) }
|
&& it.canBePurchasedWithStat(cityInfo, Stat.Faith) }
|
||||||
|
|
||||||
|
|
||||||
if (preferredVictoryType == VictoryType.Cultural) modifier += 1
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture)) modifier += 1
|
||||||
if (isAtWar) modifier -= 0.5f
|
if (isAtWar) modifier -= 0.5f
|
||||||
|
|
||||||
val citiesNotFollowingOurReligion = civInfo.cities.asSequence()
|
val citiesNotFollowingOurReligion = civInfo.cities.asSequence()
|
||||||
|
@ -260,7 +260,7 @@ object NextTurnAutomation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (civInfo.victoryType() == VictoryType.Cultural) {
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture)) {
|
||||||
for (cityState in civInfo.getKnownCivs()
|
for (cityState in civInfo.getKnownCivs()
|
||||||
.filter { it.isCityState() && it.cityStateType == CityStateType.Cultured }) {
|
.filter { it.isCityState() && it.cityStateType == CityStateType.Cultured }) {
|
||||||
val diploManager = cityState.getDiplomacyManager(civInfo)
|
val diploManager = cityState.getDiplomacyManager(civInfo)
|
||||||
@ -298,20 +298,20 @@ object NextTurnAutomation {
|
|||||||
if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty())
|
if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty())
|
||||||
return value
|
return value
|
||||||
|
|
||||||
if (civInfo.victoryType() == VictoryType.Cultural && cityState.canGiveStat(Stat.Culture)) {
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture) && cityState.canGiveStat(Stat.Culture)) {
|
||||||
value += 10
|
value += 10
|
||||||
}
|
}
|
||||||
else if (civInfo.victoryType() == VictoryType.Scientific && cityState.canGiveStat(Stat.Science)) {
|
else if (civInfo.wantsToFocusOn(ThingToFocus.Science) && cityState.canGiveStat(Stat.Science)) {
|
||||||
// In case someone mods this in
|
// In case someone mods this in
|
||||||
value += 10
|
value += 10
|
||||||
}
|
}
|
||||||
else if (civInfo.victoryType() == VictoryType.Domination) {
|
else if (civInfo.wantsToFocusOn(ThingToFocus.Military)) {
|
||||||
// Don't ally close city-states, conquer them instead
|
// Don't ally close city-states, conquer them instead
|
||||||
val distance = getMinDistanceBetweenCities(civInfo, cityState)
|
val distance = getMinDistanceBetweenCities(civInfo, cityState)
|
||||||
if (distance < 20)
|
if (distance < 20)
|
||||||
value -= (20 - distance) / 4
|
value -= (20 - distance) / 4
|
||||||
}
|
}
|
||||||
else if (civInfo.victoryType() == VictoryType.Diplomatic) {
|
else if (civInfo.wantsToFocusOn(ThingToFocus.CityStates)) {
|
||||||
value += 5 // Generally be friendly
|
value += 5 // Generally be friendly
|
||||||
}
|
}
|
||||||
if (civInfo.gold < 100) {
|
if (civInfo.gold < 100) {
|
||||||
@ -658,7 +658,7 @@ object NextTurnAutomation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun declareWar(civInfo: CivilizationInfo) {
|
private fun declareWar(civInfo: CivilizationInfo) {
|
||||||
if (civInfo.victoryType() == VictoryType.Cultural) return
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture)) return
|
||||||
if (civInfo.cities.isEmpty() || civInfo.diplomacy.isEmpty()) return
|
if (civInfo.cities.isEmpty() || civInfo.diplomacy.isEmpty()) return
|
||||||
if (civInfo.isAtWar() || civInfo.getHappiness() <= 0) return
|
if (civInfo.isAtWar() || civInfo.getHappiness() <= 0) return
|
||||||
|
|
||||||
@ -848,7 +848,7 @@ object NextTurnAutomation {
|
|||||||
private fun trainSettler(civInfo: CivilizationInfo) {
|
private fun trainSettler(civInfo: CivilizationInfo) {
|
||||||
if (civInfo.isCityState()) return
|
if (civInfo.isCityState()) return
|
||||||
if (civInfo.isAtWar()) return // don't train settlers when you could be training troops.
|
if (civInfo.isAtWar()) return // don't train settlers when you could be training troops.
|
||||||
if (civInfo.victoryType() == VictoryType.Cultural && civInfo.cities.size > 3) return
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture) && civInfo.cities.size > 3) return
|
||||||
if (civInfo.cities.none() || civInfo.getHappiness() <= civInfo.cities.size + 5) return
|
if (civInfo.cities.none() || civInfo.getHappiness() <= civInfo.cities.size + 5) return
|
||||||
|
|
||||||
val settlerUnits = civInfo.gameInfo.ruleSet.units.values
|
val settlerUnits = civInfo.gameInfo.ruleSet.units.values
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.unciv.logic.city
|
package com.unciv.logic.city
|
||||||
|
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.models.ruleset.IHasUniques
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.INamed
|
import com.unciv.models.stats.INamed
|
||||||
@ -20,6 +20,7 @@ interface IConstruction : INamed {
|
|||||||
|
|
||||||
interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
|
||||||
val hurryCostModifier: Int
|
val hurryCostModifier: Int
|
||||||
|
var requiredTech: String?
|
||||||
|
|
||||||
fun getProductionCost(civInfo: CivilizationInfo): Int
|
fun getProductionCost(civInfo: CivilizationInfo): Int
|
||||||
fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int?
|
fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int?
|
||||||
@ -111,14 +112,14 @@ class RejectionReasons: HashSet<RejectionReasonInstance>() {
|
|||||||
RejectionReason.RequiresTech,
|
RejectionReason.RequiresTech,
|
||||||
RejectionReason.RequiresPolicy,
|
RejectionReason.RequiresPolicy,
|
||||||
RejectionReason.MorePolicyBranches,
|
RejectionReason.MorePolicyBranches,
|
||||||
RejectionReason.RequiresBuildingInSomeCity
|
RejectionReason.RequiresBuildingInSomeCity,
|
||||||
)
|
)
|
||||||
private val reasonsToDefinitivelyRemoveFromQueue = hashSetOf(
|
private val reasonsToDefinitivelyRemoveFromQueue = hashSetOf(
|
||||||
RejectionReason.Obsoleted,
|
RejectionReason.Obsoleted,
|
||||||
RejectionReason.WonderAlreadyBuilt,
|
RejectionReason.WonderAlreadyBuilt,
|
||||||
RejectionReason.NationalWonderAlreadyBuilt,
|
RejectionReason.NationalWonderAlreadyBuilt,
|
||||||
RejectionReason.CannotBeBuiltWith,
|
RejectionReason.CannotBeBuiltWith,
|
||||||
RejectionReason.ReachedBuildCap
|
RejectionReason.MaxNumberBuildable,
|
||||||
)
|
)
|
||||||
private val orderOfErrorMessages = listOf(
|
private val orderOfErrorMessages = listOf(
|
||||||
RejectionReason.WonderBeingBuiltElsewhere,
|
RejectionReason.WonderBeingBuiltElsewhere,
|
||||||
@ -130,7 +131,7 @@ class RejectionReasons: HashSet<RejectionReasonInstance>() {
|
|||||||
RejectionReason.ConsumesResources,
|
RejectionReason.ConsumesResources,
|
||||||
RejectionReason.CanOnlyBePurchased,
|
RejectionReason.CanOnlyBePurchased,
|
||||||
RejectionReason.MaxNumberBuildable,
|
RejectionReason.MaxNumberBuildable,
|
||||||
RejectionReason.NoPlaceToPutUnit
|
RejectionReason.NoPlaceToPutUnit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +153,7 @@ enum class RejectionReason(val shouldShow: Boolean, val errorMessage: String) {
|
|||||||
MustOwnTile(false, "Must own a specific tile close by"),
|
MustOwnTile(false, "Must own a specific tile close by"),
|
||||||
WaterUnitsInCoastalCities(false, "May only built water units in coastal cities"),
|
WaterUnitsInCoastalCities(false, "May only built water units in coastal cities"),
|
||||||
CanOnlyBeBuiltInSpecificCities(false, "Can only be built in specific cities"),
|
CanOnlyBeBuiltInSpecificCities(false, "Can only be built in specific cities"),
|
||||||
MaxNumberBuildable(true, "Maximum number have been built or are being constructed"),
|
MaxNumberBuildable(false, "Maximum number have been built or are being constructed"),
|
||||||
|
|
||||||
UniqueToOtherNation(false, "Unique to another nation"),
|
UniqueToOtherNation(false, "Unique to another nation"),
|
||||||
ReplacedByOurUnique(false, "Our unique replaces this"),
|
ReplacedByOurUnique(false, "Our unique replaces this"),
|
||||||
@ -179,8 +180,6 @@ enum class RejectionReason(val shouldShow: Boolean, val errorMessage: String) {
|
|||||||
CityStateNationalWonder(false, "No National Wonders for city-states"),
|
CityStateNationalWonder(false, "No National Wonders for city-states"),
|
||||||
WonderDisabledEra(false, "This Wonder is disabled when starting in this era"),
|
WonderDisabledEra(false, "This Wonder is disabled when starting in this era"),
|
||||||
|
|
||||||
ReachedBuildCap(false, "Don't need to build any more of these!"),
|
|
||||||
|
|
||||||
ConsumesResources(true, "Consumes resources which you are lacking"),
|
ConsumesResources(true, "Consumes resources which you are lacking"),
|
||||||
|
|
||||||
PopulationRequirement(true, "Requires more population"),
|
PopulationRequirement(true, "Requires more population"),
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.unciv.logic.civilization
|
package com.unciv.logic.civilization
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.GameInfo
|
import com.unciv.logic.GameInfo
|
||||||
import com.unciv.logic.UncivShowableException
|
import com.unciv.logic.UncivShowableException
|
||||||
import com.unciv.logic.automation.NextTurnAutomation
|
import com.unciv.logic.automation.NextTurnAutomation
|
||||||
import com.unciv.logic.automation.WorkerAutomation
|
import com.unciv.logic.automation.WorkerAutomation
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.city.IConstruction
|
|
||||||
import com.unciv.logic.civilization.RuinsManager.RuinsManager
|
import com.unciv.logic.civilization.RuinsManager.RuinsManager
|
||||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||||
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
|
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
|
||||||
@ -119,6 +119,9 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
val lastEraResourceUsedForUnit = HashMap<String, Int>()
|
val lastEraResourceUsedForUnit = HashMap<String, Int>()
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var thingsToFocusOnForVictory = setOf<ThingToFocus>()
|
||||||
|
|
||||||
var playerType = PlayerType.AI
|
var playerType = PlayerType.AI
|
||||||
|
|
||||||
@ -322,21 +325,33 @@ class CivilizationInfo {
|
|||||||
fun isAlive(): Boolean = !isDefeated()
|
fun isAlive(): Boolean = !isDefeated()
|
||||||
|
|
||||||
@Suppress("unused") //TODO remove if future use unlikely, including DiplomacyFlags.EverBeenFriends and 2 DiplomacyManager methods - see #3183
|
@Suppress("unused") //TODO remove if future use unlikely, including DiplomacyFlags.EverBeenFriends and 2 DiplomacyManager methods - see #3183
|
||||||
|
// I'm willing to call this deprecated after so long
|
||||||
fun hasEverBeenFriendWith(otherCiv: CivilizationInfo): Boolean = getDiplomacyManager(otherCiv).everBeenFriends()
|
fun hasEverBeenFriendWith(otherCiv: CivilizationInfo): Boolean = getDiplomacyManager(otherCiv).everBeenFriends()
|
||||||
|
|
||||||
fun hasMetCivTerritory(otherCiv: CivilizationInfo): Boolean = otherCiv.getCivTerritory().any { it in exploredTiles }
|
fun hasMetCivTerritory(otherCiv: CivilizationInfo): Boolean = otherCiv.getCivTerritory().any { it in exploredTiles }
|
||||||
fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) }
|
fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) }
|
||||||
|
fun originalMajorCapitalsOwned(): Int = cities.count { it.isOriginalCapital && it.foundingCiv != "" && gameInfo.getCivilization(it.foundingCiv).isMajorCiv() }
|
||||||
private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
||||||
|
|
||||||
fun victoryType(): VictoryType {
|
fun getPreferredVictoryType(): String {
|
||||||
val victoryTypes = gameInfo.gameParameters.victoryTypes
|
val victoryTypes = gameInfo.gameParameters.victoryTypes
|
||||||
if (victoryTypes.size == 1)
|
if (victoryTypes.size == 1)
|
||||||
return victoryTypes.first() // That is the most relevant one
|
return victoryTypes.first() // That is the most relevant one
|
||||||
val victoryType = nation.preferredVictoryType
|
val victoryType = nation.preferredVictoryType
|
||||||
return if (victoryType in victoryTypes) victoryType
|
return if (victoryType in gameInfo.ruleSet.victories) victoryType
|
||||||
else VictoryType.Neutral
|
else Constants.neutralVictoryType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPreferredVictoryTypeObject(): Victory? {
|
||||||
|
val preferredVictoryType = getPreferredVictoryType()
|
||||||
|
return if (preferredVictoryType == Constants.neutralVictoryType) null
|
||||||
|
else gameInfo.ruleSet.victories[getPreferredVictoryType()]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun wantsToFocusOn(thingToFocusOn: ThingToFocus): Boolean {
|
||||||
|
return thingsToFocusOnForVictory.contains(thingToFocusOn)
|
||||||
|
}
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private val civInfoStats = CivInfoStats(this)
|
private val civInfoStats = CivInfoStats(this)
|
||||||
fun stats() = civInfoStats
|
fun stats() = civInfoStats
|
||||||
@ -751,6 +766,8 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
victoryManager.civInfo = this
|
victoryManager.civInfo = this
|
||||||
|
|
||||||
|
thingsToFocusOnForVictory = getPreferredVictoryTypeObject()?.getThingsToFocus(this) ?: setOf()
|
||||||
|
|
||||||
for (cityInfo in cities) {
|
for (cityInfo in cities) {
|
||||||
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
||||||
@ -928,16 +945,17 @@ class CivilizationInfo {
|
|||||||
private fun handleDiplomaticVictoryFlags() {
|
private fun handleDiplomaticVictoryFlags() {
|
||||||
if (flagsCountdown[CivFlags.ShouldResetDiplomaticVotes.name] == 0) {
|
if (flagsCountdown[CivFlags.ShouldResetDiplomaticVotes.name] == 0) {
|
||||||
gameInfo.diplomaticVictoryVotesCast.clear()
|
gameInfo.diplomaticVictoryVotesCast.clear()
|
||||||
removeFlag(CivFlags.ShouldResetDiplomaticVotes.name)
|
|
||||||
removeFlag(CivFlags.ShowDiplomaticVotingResults.name)
|
removeFlag(CivFlags.ShowDiplomaticVotingResults.name)
|
||||||
|
removeFlag(CivFlags.ShouldResetDiplomaticVotes.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0) {
|
if (flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0) {
|
||||||
|
gameInfo.processDiplomaticVictory()
|
||||||
if (gameInfo.civilizations.any { it.victoryManager.hasWon() } ) {
|
if (gameInfo.civilizations.any { it.victoryManager.hasWon() } ) {
|
||||||
removeFlag(CivFlags.TurnsTillNextDiplomaticVote.name)
|
removeFlag(CivFlags.TurnsTillNextDiplomaticVote.name)
|
||||||
} else {
|
} else {
|
||||||
addFlag(CivFlags.ShouldResetDiplomaticVotes.name, 1)
|
addFlag(CivFlags.ShouldResetDiplomaticVotes.name, 1)
|
||||||
addFlag(CivFlags.TurnsTillNextDiplomaticVote.name, getTurnsBetweenDiplomaticVotings())
|
addFlag(CivFlags.TurnsTillNextDiplomaticVote.name, getTurnsBetweenDiplomaticVotes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +968,7 @@ class CivilizationInfo {
|
|||||||
fun removeFlag(flag: String) = flagsCountdown.remove(flag)
|
fun removeFlag(flag: String) = flagsCountdown.remove(flag)
|
||||||
fun hasFlag(flag: String) = flagsCountdown.contains(flag)
|
fun hasFlag(flag: String) = flagsCountdown.contains(flag)
|
||||||
|
|
||||||
fun getTurnsBetweenDiplomaticVotings() = (15 * gameInfo.gameParameters.gameSpeed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files
|
fun getTurnsBetweenDiplomaticVotes() = (15 * gameInfo.gameParameters.gameSpeed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files
|
||||||
|
|
||||||
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
|
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
|
||||||
|
|
||||||
@ -975,8 +993,8 @@ class CivilizationInfo {
|
|||||||
// to the user and thus the flag is set at -1/
|
// to the user and thus the flag is set at -1/
|
||||||
fun shouldCheckForDiplomaticVictory() =
|
fun shouldCheckForDiplomaticVictory() =
|
||||||
(flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0
|
(flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0
|
||||||
|| flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == -1)
|
|| flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == -1)
|
||||||
&& gameInfo.civilizations.any { it.isMajorCiv() && !it.isDefeated() && it != this }
|
&& gameInfo.civilizations.any { it.isMajorCiv() && !it.isDefeated() && it != this }
|
||||||
|
|
||||||
private fun updateRevolts() {
|
private fun updateRevolts() {
|
||||||
if (gameInfo.civilizations.none { it.isBarbarian() }) {
|
if (gameInfo.civilizations.none { it.isBarbarian() }) {
|
||||||
|
@ -38,7 +38,7 @@ class PolicyManager {
|
|||||||
get() {
|
get() {
|
||||||
val value = HashMap<PolicyBranch, Int>()
|
val value = HashMap<PolicyBranch, Int>()
|
||||||
for (branch in branches) {
|
for (branch in branches) {
|
||||||
value[branch] = branch.priorities[civInfo.victoryType().name] ?: 0
|
value[branch] = branch.priorities[civInfo.getPreferredVictoryType()] ?: 0
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,27 @@
|
|||||||
package com.unciv.logic.civilization
|
package com.unciv.logic.civilization
|
||||||
|
|
||||||
|
import com.unciv.Constants
|
||||||
import com.unciv.models.Counter
|
import com.unciv.models.Counter
|
||||||
import com.unciv.models.ruleset.VictoryType
|
import com.unciv.models.ruleset.Milestone
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
|
|
||||||
class VictoryManager {
|
class VictoryManager {
|
||||||
@Transient
|
@Transient
|
||||||
lateinit var civInfo: CivilizationInfo
|
lateinit var civInfo: CivilizationInfo
|
||||||
|
|
||||||
var requiredSpaceshipParts = Counter<String>()
|
// There is very likely a typo in this name (currents), but as its saved in save files,
|
||||||
|
// fixing it is non-trivial
|
||||||
var currentsSpaceshipParts = Counter<String>()
|
var currentsSpaceshipParts = Counter<String>()
|
||||||
var hasWonTimeVictory = false
|
var hasEverWonDiplomaticVote = false
|
||||||
|
|
||||||
init {
|
|
||||||
requiredSpaceshipParts.add("SS Booster", 3)
|
|
||||||
requiredSpaceshipParts.add("SS Cockpit", 1)
|
|
||||||
requiredSpaceshipParts.add("SS Engine", 1)
|
|
||||||
requiredSpaceshipParts.add("SS Stasis Chamber", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clone(): VictoryManager {
|
fun clone(): VictoryManager {
|
||||||
val toReturn = VictoryManager()
|
val toReturn = VictoryManager()
|
||||||
toReturn.currentsSpaceshipParts.putAll(currentsSpaceshipParts)
|
toReturn.currentsSpaceshipParts.putAll(currentsSpaceshipParts)
|
||||||
|
toReturn.hasEverWonDiplomaticVote = hasEverWonDiplomaticVote
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unconstructedSpaceshipParts(): Counter<String> {
|
private fun calculateDiplomaticVotingResults(votesCast: HashMap<String, String>): Counter<String> {
|
||||||
val counter = requiredSpaceshipParts.clone()
|
|
||||||
counter.remove(currentsSpaceshipParts)
|
|
||||||
return counter
|
|
||||||
}
|
|
||||||
|
|
||||||
fun spaceshipPartsRemaining() = requiredSpaceshipParts.values.sum() - currentsSpaceshipParts.values.sum()
|
|
||||||
|
|
||||||
fun calculateDiplomaticVotingResults(votesCast: HashMap<String, String>): Counter<String> {
|
|
||||||
val results = Counter<String>()
|
val results = Counter<String>()
|
||||||
for (castVote in votesCast) {
|
for (castVote in votesCast) {
|
||||||
results.add(castVote.value, 1)
|
results.add(castVote.value, 1)
|
||||||
@ -41,7 +29,7 @@ class VictoryManager {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
fun votesNeededForDiplomaticVictory(): Int {
|
private fun votesNeededForDiplomaticVictory(): Int {
|
||||||
val civCount = civInfo.gameInfo.civilizations.count { !it.isDefeated() }
|
val civCount = civInfo.gameInfo.civilizations.count { !it.isDefeated() }
|
||||||
|
|
||||||
// CvGame.cpp::DoUpdateDiploVictory() in the source code of the original
|
// CvGame.cpp::DoUpdateDiploVictory() in the source code of the original
|
||||||
@ -54,44 +42,46 @@ class VictoryManager {
|
|||||||
fun hasEnoughVotesForDiplomaticVictory(): Boolean {
|
fun hasEnoughVotesForDiplomaticVictory(): Boolean {
|
||||||
val results = calculateDiplomaticVotingResults(civInfo.gameInfo.diplomaticVictoryVotesCast)
|
val results = calculateDiplomaticVotingResults(civInfo.gameInfo.diplomaticVictoryVotesCast)
|
||||||
val bestCiv = results.maxByOrNull { it.value } ?: return false
|
val bestCiv = results.maxByOrNull { it.value } ?: return false
|
||||||
|
|
||||||
// If we don't have the highest score, we have not won anyway
|
// If we don't have the highest score, we have not won anyway
|
||||||
if (bestCiv.key != civInfo.civName) return false
|
if (bestCiv.key != civInfo.civName) return false
|
||||||
|
|
||||||
|
// If we don't have enough votes, we haven't won
|
||||||
if (bestCiv.value < votesNeededForDiplomaticVictory()) return false
|
if (bestCiv.value < votesNeededForDiplomaticVictory()) return false
|
||||||
|
|
||||||
// If there's a tie, we haven't won either
|
// If there's a tie, we haven't won either
|
||||||
return (results.none { it != bestCiv && it.value == bestCiv.value })
|
return (results.none { it != bestCiv && it.value == bestCiv.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasVictoryType(victoryType: VictoryType) = civInfo.gameInfo.gameParameters.victoryTypes.contains(victoryType)
|
fun getVictoryTypeAchieved(): String? {
|
||||||
|
|
||||||
fun hasWonScientificVictory() = hasVictoryType(VictoryType.Scientific)
|
|
||||||
&& spaceshipPartsRemaining() == 0
|
|
||||||
|
|
||||||
fun hasWonCulturalVictory() = hasVictoryType(VictoryType.Cultural)
|
|
||||||
&& civInfo.hasUnique(UniqueType.TriggersCulturalVictory)
|
|
||||||
|
|
||||||
fun hasWonDominationVictory()= hasVictoryType(VictoryType.Domination)
|
|
||||||
&& civInfo.gameInfo.civilizations.all { it == civInfo || it.isDefeated() || !it.isMajorCiv() }
|
|
||||||
|
|
||||||
fun hasWonDiplomaticVictory() = hasVictoryType(VictoryType.Diplomatic)
|
|
||||||
&& civInfo.shouldCheckForDiplomaticVictory()
|
|
||||||
&& hasEnoughVotesForDiplomaticVictory()
|
|
||||||
|
|
||||||
fun hasWonTimeVictory() = hasVictoryType(VictoryType.Time)
|
|
||||||
&& hasWonTimeVictory
|
|
||||||
|
|
||||||
fun hasWonVictoryType(): VictoryType? {
|
|
||||||
if (!civInfo.isMajorCiv()) return null
|
if (!civInfo.isMajorCiv()) return null
|
||||||
if (hasWonTimeVictory()) return VictoryType.Time
|
for (victoryName in civInfo.gameInfo.ruleSet.victories.keys.filter { it != Constants.neutralVictoryType}) {
|
||||||
if (hasWonDominationVictory()) return VictoryType.Domination
|
if (getNextMilestone(victoryName) == null)
|
||||||
if (hasWonScientificVictory()) return VictoryType.Scientific
|
return victoryName
|
||||||
if (hasWonCulturalVictory()) return VictoryType.Cultural
|
}
|
||||||
if (hasWonDiplomaticVictory()) return VictoryType.Diplomatic
|
if (civInfo.hasUnique(UniqueType.TriggersVictory))
|
||||||
if (civInfo.hasUnique(UniqueType.TriggersVictory)) return VictoryType.Neutral
|
return Constants.neutralVictoryType
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getNextMilestone(victory: String): Milestone? {
|
||||||
|
for (milestone in civInfo.gameInfo.ruleSet.victories[victory]!!.milestoneObjects) {
|
||||||
|
if (!milestone.hasBeenCompletedBy(civInfo))
|
||||||
|
return milestone
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun amountMilestonesCompleted(victory: String): Int {
|
||||||
|
var completed = 0
|
||||||
|
for (milestone in civInfo.gameInfo.ruleSet.victories[victory]!!.milestoneObjects) {
|
||||||
|
if (milestone.hasBeenCompletedBy(civInfo))
|
||||||
|
++completed
|
||||||
|
else
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return completed
|
||||||
|
}
|
||||||
|
|
||||||
fun hasWon() = hasWonVictoryType() != null
|
fun hasWon() = getVictoryTypeAchieved() != null
|
||||||
}
|
}
|
@ -34,6 +34,10 @@ open class Counter<K> : LinkedHashMap<K, Int>() {
|
|||||||
for (key in keys) newCounter[key] = this[key]!! * amount
|
for (key in keys) newCounter[key] = this[key]!! * amount
|
||||||
return newCounter
|
return newCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sumValues(): Int {
|
||||||
|
return this.map { it.value }.sum()
|
||||||
|
}
|
||||||
|
|
||||||
override fun clone(): Counter<K> {
|
override fun clone(): Counter<K> {
|
||||||
val newCounter = Counter<K>()
|
val newCounter = Counter<K>()
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package com.unciv.models.metadata
|
package com.unciv.models.metadata
|
||||||
|
|
||||||
import com.unciv.Constants
|
|
||||||
import com.unciv.logic.civilization.PlayerType
|
import com.unciv.logic.civilization.PlayerType
|
||||||
import com.unciv.models.ruleset.Ruleset
|
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
|
|
||||||
enum class BaseRuleset(val fullName:String){
|
enum class BaseRuleset(val fullName:String){
|
||||||
Civ_V_Vanilla("Civ V - Vanilla"),
|
Civ_V_Vanilla("Civ V - Vanilla"),
|
||||||
@ -26,9 +22,8 @@ class GameParameters { // Default values are the default new game
|
|||||||
var godMode = false
|
var godMode = false
|
||||||
var nuclearWeaponsEnabled = true
|
var nuclearWeaponsEnabled = true
|
||||||
var religionEnabled = false
|
var religionEnabled = false
|
||||||
|
|
||||||
// By default, all victory types except Diplomacy and time as they are quite new
|
var victoryTypes: ArrayList<String> = arrayListOf()
|
||||||
var victoryTypes: ArrayList<VictoryType> = arrayListOf(VictoryType.Cultural, VictoryType.Domination, VictoryType.Scientific)
|
|
||||||
var startingEra = "Ancient era"
|
var startingEra = "Ancient era"
|
||||||
|
|
||||||
var isOnlineMultiplayer = false
|
var isOnlineMultiplayer = false
|
||||||
@ -70,9 +65,7 @@ class GameParameters { // Default values are the default new game
|
|||||||
if (!nuclearWeaponsEnabled) yield("No nukes")
|
if (!nuclearWeaponsEnabled) yield("No nukes")
|
||||||
if (religionEnabled) yield("Religion")
|
if (religionEnabled) yield("Religion")
|
||||||
if (godMode) yield("God mode")
|
if (godMode) yield("God mode")
|
||||||
for (victoryType in VictoryType.values()) {
|
yield("Enabled Victories: " + victoryTypes.joinToString())
|
||||||
if (victoryType !in victoryTypes) yield("No $victoryType Victory")
|
|
||||||
}
|
|
||||||
yield(baseRuleset)
|
yield(baseRuleset)
|
||||||
yield(if (mods.isEmpty()) "no mods" else mods.joinToString(",", "mods=(", ")", 6) )
|
yield(if (mods.isEmpty()) "no mods" else mods.joinToString(",", "mods=(", ")", 6) )
|
||||||
}.joinToString(prefix = "(", postfix = ")")
|
}.joinToString(prefix = "(", postfix = ")")
|
||||||
|
@ -18,7 +18,7 @@ import kotlin.math.pow
|
|||||||
|
|
||||||
class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||||
|
|
||||||
var requiredTech: String? = null
|
override var requiredTech: String? = null
|
||||||
|
|
||||||
var cost: Int = 0
|
var cost: Int = 0
|
||||||
var maintenance = 0
|
var maintenance = 0
|
||||||
@ -535,11 +535,10 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To be replaced with `Only available <after [Apollo Project] has been build>`
|
||||||
UniqueType.SpaceshipPart -> {
|
UniqueType.SpaceshipPart -> {
|
||||||
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts))
|
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts))
|
||||||
rejectionReasons.add(RejectionReason.RequiresBuildingInSomeCity.toInstance("Apollo project not built!"))
|
rejectionReasons.add(RejectionReason.RequiresBuildingInSomeCity.toInstance("Apollo project not built!"))
|
||||||
if (civInfo.victoryManager.unconstructedSpaceshipParts()[name] == 0)
|
|
||||||
rejectionReasons.add(RejectionReason.ReachedBuildCap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueType.RequiresAnotherBuilding -> {
|
UniqueType.RequiresAnotherBuilding -> {
|
||||||
@ -590,7 +589,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UniqueType.HiddenWithoutVictoryType -> {
|
UniqueType.HiddenWithoutVictoryType -> {
|
||||||
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0])))
|
if (!civInfo.gameInfo.gameParameters.victoryTypes.contains(unique.params[0]))
|
||||||
rejectionReasons.add(RejectionReason.HiddenWithoutVictory.toInstance(unique.text))
|
rejectionReasons.add(RejectionReason.HiddenWithoutVictory.toInstance(unique.text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,7 +669,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
|||||||
override fun postBuildEvent(cityConstructions: CityConstructions, boughtWith: Stat?): Boolean {
|
override fun postBuildEvent(cityConstructions: CityConstructions, boughtWith: Stat?): Boolean {
|
||||||
val civInfo = cityConstructions.cityInfo.civInfo
|
val civInfo = cityConstructions.cityInfo.civInfo
|
||||||
|
|
||||||
if (hasUnique(UniqueType.SpaceshipPart)) {
|
if (civInfo.gameInfo.spaceResources.contains(name)) {
|
||||||
civInfo.victoryManager.currentsSpaceshipParts.add(name, 1)
|
civInfo.victoryManager.currentsSpaceshipParts.add(name, 1)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ package com.unciv.models.ruleset
|
|||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.unciv.logic.civilization.CityStateType
|
import com.unciv.logic.civilization.CityStateType
|
||||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||||
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||||
import com.unciv.models.stats.INamed
|
|
||||||
import com.unciv.ui.utils.colorFromRGB
|
import com.unciv.ui.utils.colorFromRGB
|
||||||
|
|
||||||
class Era : RulesetObject(), IHasUniques {
|
class Era : RulesetObject(), IHasUniques {
|
||||||
|
@ -14,15 +14,6 @@ import com.unciv.ui.civilopedia.FormattedLine
|
|||||||
import com.unciv.ui.utils.Fonts
|
import com.unciv.ui.utils.Fonts
|
||||||
import com.unciv.ui.utils.colorFromRGB
|
import com.unciv.ui.utils.colorFromRGB
|
||||||
|
|
||||||
enum class VictoryType {
|
|
||||||
Neutral,
|
|
||||||
Cultural,
|
|
||||||
Diplomatic,
|
|
||||||
Domination,
|
|
||||||
Scientific,
|
|
||||||
Time,
|
|
||||||
}
|
|
||||||
|
|
||||||
class Nation : RulesetObject() {
|
class Nation : RulesetObject() {
|
||||||
var leaderName = ""
|
var leaderName = ""
|
||||||
fun getLeaderDisplayName() = if (isCityState()) name
|
fun getLeaderDisplayName() = if (isCityState()) name
|
||||||
@ -30,7 +21,7 @@ class Nation : RulesetObject() {
|
|||||||
|
|
||||||
val style = ""
|
val style = ""
|
||||||
var cityStateType: CityStateType? = null
|
var cityStateType: CityStateType? = null
|
||||||
var preferredVictoryType: VictoryType = VictoryType.Neutral
|
var preferredVictoryType: String = Constants.neutralVictoryType
|
||||||
var declaringWar = ""
|
var declaringWar = ""
|
||||||
var attacked = ""
|
var attacked = ""
|
||||||
var defeated = ""
|
var defeated = ""
|
||||||
|
@ -16,6 +16,7 @@ import com.unciv.models.ruleset.tile.Terrain
|
|||||||
import com.unciv.models.ruleset.tile.TerrainType
|
import com.unciv.models.ruleset.tile.TerrainType
|
||||||
import com.unciv.models.ruleset.tile.TileImprovement
|
import com.unciv.models.ruleset.tile.TileImprovement
|
||||||
import com.unciv.models.ruleset.tile.TileResource
|
import com.unciv.models.ruleset.tile.TileResource
|
||||||
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
@ -92,6 +93,7 @@ class Ruleset {
|
|||||||
val units = LinkedHashMap<String, BaseUnit>()
|
val units = LinkedHashMap<String, BaseUnit>()
|
||||||
val unitPromotions = LinkedHashMap<String, Promotion>()
|
val unitPromotions = LinkedHashMap<String, Promotion>()
|
||||||
val unitTypes = LinkedHashMap<String, UnitType>()
|
val unitTypes = LinkedHashMap<String, UnitType>()
|
||||||
|
var victories = LinkedHashMap<String, Victory>()
|
||||||
|
|
||||||
val mods = LinkedHashSet<String>()
|
val mods = LinkedHashSet<String>()
|
||||||
var modOptions = ModOptions()
|
var modOptions = ModOptions()
|
||||||
@ -132,6 +134,7 @@ class Ruleset {
|
|||||||
unitPromotions.putAll(ruleset.unitPromotions)
|
unitPromotions.putAll(ruleset.unitPromotions)
|
||||||
units.putAll(ruleset.units)
|
units.putAll(ruleset.units)
|
||||||
unitTypes.putAll(ruleset.unitTypes)
|
unitTypes.putAll(ruleset.unitTypes)
|
||||||
|
victories.putAll(ruleset.victories)
|
||||||
for (unitToRemove in ruleset.modOptions.unitsToRemove) units.remove(unitToRemove)
|
for (unitToRemove in ruleset.modOptions.unitsToRemove) units.remove(unitToRemove)
|
||||||
modOptions.uniques.addAll(ruleset.modOptions.uniques)
|
modOptions.uniques.addAll(ruleset.modOptions.uniques)
|
||||||
modOptions.constants.merge(ruleset.modOptions.constants)
|
modOptions.constants.merge(ruleset.modOptions.constants)
|
||||||
@ -159,6 +162,7 @@ class Ruleset {
|
|||||||
unitPromotions.clear()
|
unitPromotions.clear()
|
||||||
units.clear()
|
units.clear()
|
||||||
unitTypes.clear()
|
unitTypes.clear()
|
||||||
|
victories.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allRulesetObjects(): Sequence<IRulesetObject> =
|
fun allRulesetObjects(): Sequence<IRulesetObject> =
|
||||||
@ -181,6 +185,7 @@ class Ruleset {
|
|||||||
unitPromotions.values.asSequence() +
|
unitPromotions.values.asSequence() +
|
||||||
units.values.asSequence() +
|
units.values.asSequence() +
|
||||||
unitTypes.values.asSequence()
|
unitTypes.values.asSequence()
|
||||||
|
// Victories is only INamed
|
||||||
fun allIHasUniques(): Sequence<IHasUniques> =
|
fun allIHasUniques(): Sequence<IHasUniques> =
|
||||||
allRulesetObjects() + sequenceOf(modOptions)
|
allRulesetObjects() + sequenceOf(modOptions)
|
||||||
|
|
||||||
@ -258,7 +263,7 @@ class Ruleset {
|
|||||||
// Setup this branch
|
// Setup this branch
|
||||||
branch.requires = ArrayList()
|
branch.requires = ArrayList()
|
||||||
branch.branch = branch
|
branch.branch = branch
|
||||||
for (victoryType in VictoryType.values()) {
|
for (victoryType in victories.values) {
|
||||||
if (victoryType.name !in branch.priorities.keys) {
|
if (victoryType.name !in branch.priorities.keys) {
|
||||||
branch.priorities[victoryType.name] = 0
|
branch.priorities[victoryType.name] = 0
|
||||||
}
|
}
|
||||||
@ -306,6 +311,33 @@ class Ruleset {
|
|||||||
if (globalUniquesFile.exists()) {
|
if (globalUniquesFile.exists()) {
|
||||||
globalUniques = jsonParser.getFromJson(GlobalUniques::class.java, globalUniquesFile)
|
globalUniques = jsonParser.getFromJson(GlobalUniques::class.java, globalUniquesFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val victoryTypesFiles = folderHandle.child("VictoryTypes.json")
|
||||||
|
if (victoryTypesFiles.exists()) {
|
||||||
|
victories += createHashmap(jsonParser.getFromJson(Array<Victory>::class.java, victoryTypesFiles))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Add objects that might not be present in base ruleset mods, but are required
|
||||||
|
if (modOptions.isBaseRuleset) {
|
||||||
|
// This one should be temporary
|
||||||
|
if (unitTypes.isEmpty()) {
|
||||||
|
unitTypes.putAll(RulesetCache.getVanillaRuleset().unitTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should be permanent
|
||||||
|
if (ruinRewards.isEmpty()) {
|
||||||
|
ruinRewards.putAll(RulesetCache.getVanillaRuleset().ruinRewards)
|
||||||
|
}
|
||||||
|
if (globalUniques.uniques.isEmpty()) {
|
||||||
|
globalUniques = RulesetCache.getVanillaRuleset().globalUniques
|
||||||
|
}
|
||||||
|
// If we have no victories, add all the default victories
|
||||||
|
if (victories.isEmpty()) {
|
||||||
|
victories.putAll(RulesetCache.getVanillaRuleset().victories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val gameBasicsLoadTime = System.currentTimeMillis() - gameBasicsStartTime
|
val gameBasicsLoadTime = System.currentTimeMillis() - gameBasicsStartTime
|
||||||
if (printOutput) println("Loading ruleset - " + gameBasicsLoadTime + "ms")
|
if (printOutput) println("Loading ruleset - " + gameBasicsLoadTime + "ms")
|
||||||
@ -354,7 +386,7 @@ class Ruleset {
|
|||||||
forOptionsPopup: Boolean
|
forOptionsPopup: Boolean
|
||||||
) {
|
) {
|
||||||
val name = if (uniqueContainer is INamed) uniqueContainer.name else ""
|
val name = if (uniqueContainer is INamed) uniqueContainer.name else ""
|
||||||
|
|
||||||
for (unique in uniqueContainer.uniqueObjects) {
|
for (unique in uniqueContainer.uniqueObjects) {
|
||||||
val errors = checkUnique(
|
val errors = checkUnique(
|
||||||
unique,
|
unique,
|
||||||
@ -771,6 +803,18 @@ class Ruleset {
|
|||||||
for (unitType in unitTypes.values) {
|
for (unitType in unitTypes.values) {
|
||||||
checkUniques(unitType, lines, rulesetSpecific, forOptionsPopup)
|
checkUniques(unitType, lines, rulesetSpecific, forOptionsPopup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (victoryType in victories.values) {
|
||||||
|
for (requiredUnit in victoryType.requiredSpaceshipParts)
|
||||||
|
if (!units.contains(requiredUnit))
|
||||||
|
lines.add("Victory type ${victoryType.name} requires adding the non-existant unit $requiredUnit to the capital to win!", RulesetErrorSeverity.Warning)
|
||||||
|
for (milestone in victoryType.milestoneObjects)
|
||||||
|
if (milestone.type == null)
|
||||||
|
lines.add("Victory type ${victoryType.name} has milestone ${milestone.uniqueDescription} that is of an unknown type!", RulesetErrorSeverity.Error)
|
||||||
|
for (victory in victories.values)
|
||||||
|
if (victory.name != victoryType.name && victory.milestones == victoryType.milestones)
|
||||||
|
lines.add("Victory types ${victoryType.name} and ${victory.name} have the same requirements!", RulesetErrorSeverity.Warning)
|
||||||
|
}
|
||||||
|
|
||||||
for (difficulty in difficulties.values) {
|
for (difficulty in difficulties.values) {
|
||||||
for (unitName in difficulty.aiCityStateBonusStartingUnits + difficulty.aiMajorCivBonusStartingUnits + difficulty.playerBonusStartingUnits)
|
for (unitName in difficulty.aiCityStateBonusStartingUnits + difficulty.aiMajorCivBonusStartingUnits + difficulty.playerBonusStartingUnits)
|
||||||
@ -878,7 +922,6 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
|||||||
this[optionalBaseRuleset]!!
|
this[optionalBaseRuleset]!!
|
||||||
else getVanillaRuleset()
|
else getVanillaRuleset()
|
||||||
|
|
||||||
|
|
||||||
val loadedMods = mods.asSequence()
|
val loadedMods = mods.asSequence()
|
||||||
.filter { containsKey(it) }
|
.filter { containsKey(it) }
|
||||||
.map { this[it]!! }
|
.map { this[it]!! }
|
||||||
@ -896,19 +939,9 @@ object RulesetCache : HashMap<String,Ruleset>() {
|
|||||||
}
|
}
|
||||||
newRuleset.updateBuildingCosts() // only after we've added all the mods can we calculate the building costs
|
newRuleset.updateBuildingCosts() // only after we've added all the mods can we calculate the building costs
|
||||||
|
|
||||||
// This one should be temporary
|
println(optionalBaseRuleset)
|
||||||
if (newRuleset.unitTypes.isEmpty()) {
|
println(newRuleset.victories)
|
||||||
newRuleset.unitTypes.putAll(getVanillaRuleset().unitTypes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// These should be permanent
|
|
||||||
if (newRuleset.ruinRewards.isEmpty()) {
|
|
||||||
newRuleset.ruinRewards.putAll(getVanillaRuleset().ruinRewards)
|
|
||||||
}
|
|
||||||
if (newRuleset.globalUniques.uniques.isEmpty()) {
|
|
||||||
newRuleset.globalUniques = getVanillaRuleset().globalUniques
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRuleset
|
return newRuleset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package com.unciv.models.ruleset
|
package com.unciv.models.ruleset
|
||||||
|
|
||||||
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
import com.unciv.models.ruleset.unique.Unique
|
||||||
import com.unciv.models.stats.INamed
|
import com.unciv.models.stats.INamed
|
||||||
import com.unciv.models.stats.NamedStats
|
import com.unciv.models.stats.NamedStats
|
||||||
import com.unciv.ui.civilopedia.FormattedLine
|
import com.unciv.ui.civilopedia.FormattedLine
|
||||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||||
|
|
||||||
interface IRulesetObject:INamed, IHasUniques, ICivilopediaText
|
interface IRulesetObject: INamed, IHasUniques, ICivilopediaText
|
||||||
|
|
||||||
abstract class RulesetObject: IRulesetObject {
|
abstract class RulesetObject: IRulesetObject {
|
||||||
override var name = ""
|
override var name = ""
|
||||||
|
240
core/src/com/unciv/models/ruleset/Victory.kt
Normal file
240
core/src/com/unciv/models/ruleset/Victory.kt
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
package com.unciv.models.ruleset
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
|
import com.unciv.Constants
|
||||||
|
import com.unciv.models.stats.INamed
|
||||||
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
|
import com.unciv.models.Counter
|
||||||
|
import com.unciv.models.translations.getPlaceholderParameters
|
||||||
|
import com.unciv.models.translations.getPlaceholderText
|
||||||
|
import com.unciv.ui.utils.toTextButton
|
||||||
|
|
||||||
|
|
||||||
|
enum class MilestoneType(val text: String) {
|
||||||
|
BuiltBuilding("Build [building]"),
|
||||||
|
BuildingBuiltGlobally("[building] build globally"),
|
||||||
|
AddedSSPartsInCapital("Add all [comment] in capital"),
|
||||||
|
DestroyAllPlayers("Destroy all players"),
|
||||||
|
CaptureAllCapitals("Capture all capitals"),
|
||||||
|
CompletePolicyBranches("Complete [amount] Policy branches"),
|
||||||
|
WinDiplomaticVote("Win diplomatic vote"),
|
||||||
|
ScoreAfterTimeOut("Have highest score after max turns"),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class CompletionStatus {
|
||||||
|
Completed,
|
||||||
|
Partially,
|
||||||
|
Incomplete
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ThingToFocus {
|
||||||
|
Production,
|
||||||
|
Gold,
|
||||||
|
Science,
|
||||||
|
Culture,
|
||||||
|
Military,
|
||||||
|
CityStates,
|
||||||
|
Score,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Victory : INamed {
|
||||||
|
override var name = ""
|
||||||
|
val victoryScreenHeader = "Do things to win!"
|
||||||
|
val hiddenInVictoryScreen = false
|
||||||
|
// Things to do to win
|
||||||
|
// Needs to be ordered, as the milestones are supposed to be obtained in a specific order
|
||||||
|
val milestones = ArrayList<String>()
|
||||||
|
val milestoneObjects by lazy { milestones.map { Milestone(it, this) }}
|
||||||
|
val requiredSpaceshipParts = ArrayList<String>()
|
||||||
|
|
||||||
|
val requiredSpaceshipPartsAsCounter by lazy {
|
||||||
|
val parts = Counter<String>()
|
||||||
|
for (spaceshipPart in requiredSpaceshipParts)
|
||||||
|
parts.add(spaceshipPart, 1)
|
||||||
|
parts
|
||||||
|
}
|
||||||
|
|
||||||
|
val victoryString = "Your civilization stands above all others! The exploits of your people shall be remembered until the end of civilization itself!"
|
||||||
|
val defeatString = "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!"
|
||||||
|
|
||||||
|
fun enablesMaxTurns(): Boolean = milestoneObjects.any { it.type == MilestoneType.ScoreAfterTimeOut }
|
||||||
|
fun getThingsToFocus(civInfo: CivilizationInfo): Set<ThingToFocus> = milestoneObjects
|
||||||
|
.filter { !it.hasBeenCompletedBy(civInfo) }
|
||||||
|
.map { it.getThingToFocus(civInfo) }
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Milestone(val uniqueDescription: String, private val accompaniedVictory: Victory) {
|
||||||
|
|
||||||
|
val type: MilestoneType? = MilestoneType.values().firstOrNull { uniqueDescription.getPlaceholderText() == it.text.getPlaceholderText() }
|
||||||
|
val params by lazy { uniqueDescription.getPlaceholderParameters() }
|
||||||
|
|
||||||
|
fun getIncompleteSpaceshipParts(civInfo: CivilizationInfo): Counter<String> {
|
||||||
|
val incompleteSpaceshipParts = accompaniedVictory.requiredSpaceshipPartsAsCounter.clone()
|
||||||
|
incompleteSpaceshipParts.remove(civInfo.victoryManager.currentsSpaceshipParts)
|
||||||
|
return incompleteSpaceshipParts
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasBeenCompletedBy(civInfo: CivilizationInfo): Boolean {
|
||||||
|
return when (type!!) {
|
||||||
|
MilestoneType.BuiltBuilding ->
|
||||||
|
civInfo.cities.any { it.cityConstructions.builtBuildings.contains(params[0])}
|
||||||
|
MilestoneType.AddedSSPartsInCapital -> {
|
||||||
|
getIncompleteSpaceshipParts(civInfo).isEmpty()
|
||||||
|
}
|
||||||
|
MilestoneType.DestroyAllPlayers ->
|
||||||
|
civInfo.gameInfo.getAliveMajorCivs() == listOf(civInfo)
|
||||||
|
MilestoneType.CaptureAllCapitals ->
|
||||||
|
civInfo.originalMajorCapitalsOwned() == civInfo.gameInfo.civilizations.count { it.isMajorCiv() }
|
||||||
|
MilestoneType.CompletePolicyBranches ->
|
||||||
|
civInfo.policies.completedBranches.size >= params[0].toInt()
|
||||||
|
MilestoneType.BuildingBuiltGlobally -> civInfo.gameInfo.getCities().any {
|
||||||
|
it.cityConstructions.builtBuildings.contains(params[0])
|
||||||
|
}
|
||||||
|
MilestoneType.WinDiplomaticVote -> civInfo.victoryManager.hasEverWonDiplomaticVote
|
||||||
|
MilestoneType.ScoreAfterTimeOut -> {
|
||||||
|
civInfo.gameInfo.turns >= civInfo.gameInfo.gameParameters.maxTurns
|
||||||
|
&& civInfo == civInfo.gameInfo.civilizations.maxByOrNull { it.calculateTotalScore() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMilestoneButton(text: String, achieved: Boolean): TextButton {
|
||||||
|
val textButton = text.toTextButton()
|
||||||
|
if (achieved) textButton.color = Color.GREEN
|
||||||
|
else textButton.color = Color.GRAY
|
||||||
|
return textButton
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVictoryScreenButtonHeaderText(completed: Boolean, civInfo: CivilizationInfo): String {
|
||||||
|
return when (type!!) {
|
||||||
|
MilestoneType.BuildingBuiltGlobally, MilestoneType.WinDiplomaticVote,
|
||||||
|
MilestoneType.ScoreAfterTimeOut, MilestoneType.BuiltBuilding ->
|
||||||
|
uniqueDescription
|
||||||
|
MilestoneType.CompletePolicyBranches -> {
|
||||||
|
val amountToDo = params[0]
|
||||||
|
val amountDone =
|
||||||
|
if (completed) amountToDo
|
||||||
|
else civInfo.getCompletedPolicyBranchesCount()
|
||||||
|
"{$uniqueDescription} ($amountDone/$amountToDo)"
|
||||||
|
}
|
||||||
|
MilestoneType.CaptureAllCapitals -> {
|
||||||
|
val amountToDo = civInfo.gameInfo.civilizations.count { it.isMajorCiv() }
|
||||||
|
val amountDone =
|
||||||
|
if (completed) amountToDo
|
||||||
|
else civInfo.originalMajorCapitalsOwned()
|
||||||
|
"{$uniqueDescription} ($amountDone/$amountToDo)"
|
||||||
|
}
|
||||||
|
MilestoneType.DestroyAllPlayers -> {
|
||||||
|
val amountToDo = civInfo.gameInfo.civilizations.count { it.isMajorCiv() } - 1 // Don't count yourself
|
||||||
|
val amountDone =
|
||||||
|
if (completed) amountToDo
|
||||||
|
else amountToDo - (civInfo.gameInfo.getAliveMajorCivs().filter { it != civInfo }.count())
|
||||||
|
"{$uniqueDescription} ($amountDone/$amountToDo)"
|
||||||
|
}
|
||||||
|
MilestoneType.AddedSSPartsInCapital -> {
|
||||||
|
val completeSpaceshipParts = civInfo.victoryManager.currentsSpaceshipParts
|
||||||
|
val incompleteSpaceshipParts = accompaniedVictory.requiredSpaceshipPartsAsCounter.clone()
|
||||||
|
val amountToDo = incompleteSpaceshipParts.sumValues()
|
||||||
|
incompleteSpaceshipParts.remove(completeSpaceshipParts)
|
||||||
|
|
||||||
|
val amountDone = amountToDo - incompleteSpaceshipParts.sumValues()
|
||||||
|
|
||||||
|
"{$uniqueDescription} ($amountDone/$amountToDo)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVictoryScreenButtons(completionStatus: CompletionStatus, civInfo: CivilizationInfo): List<TextButton> {
|
||||||
|
val headerButton = getMilestoneButton(
|
||||||
|
getVictoryScreenButtonHeaderText(completionStatus == CompletionStatus.Completed, civInfo),
|
||||||
|
completionStatus == CompletionStatus.Completed
|
||||||
|
)
|
||||||
|
if (completionStatus == CompletionStatus.Completed || completionStatus == CompletionStatus.Incomplete) {
|
||||||
|
// When done or not working on this milestone, only show the header button
|
||||||
|
return listOf(headerButton)
|
||||||
|
}
|
||||||
|
// Otherwise, append the partial buttons of each step
|
||||||
|
val buttons = mutableListOf(headerButton)
|
||||||
|
when (type) {
|
||||||
|
// No extra buttons necessary
|
||||||
|
MilestoneType.BuiltBuilding, MilestoneType.BuildingBuiltGlobally,
|
||||||
|
MilestoneType.ScoreAfterTimeOut, MilestoneType.WinDiplomaticVote -> {}
|
||||||
|
MilestoneType.AddedSSPartsInCapital -> {
|
||||||
|
val completedSpaceshipParts = civInfo.victoryManager.currentsSpaceshipParts
|
||||||
|
val incompleteSpaceshipParts = getIncompleteSpaceshipParts(civInfo)
|
||||||
|
|
||||||
|
for (part in completedSpaceshipParts) {
|
||||||
|
repeat(part.value) {
|
||||||
|
buttons.add(getMilestoneButton(part.key, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (part in incompleteSpaceshipParts) {
|
||||||
|
repeat(part.value) {
|
||||||
|
buttons.add(getMilestoneButton(part.key, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MilestoneType.DestroyAllPlayers -> {
|
||||||
|
for (civ in civInfo.gameInfo.civilizations.filter { it != civInfo && it.isMajorCiv() && !it.isAlive() }) {
|
||||||
|
buttons.add(getMilestoneButton("Destroy [${civ.civName}]", true))
|
||||||
|
}
|
||||||
|
for (civ in civInfo.gameInfo.getAliveMajorCivs().filter { it != civInfo}) {
|
||||||
|
buttons.add(getMilestoneButton("Destroy [${civ.civName}]", false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MilestoneType.CaptureAllCapitals -> {
|
||||||
|
for (city in civInfo.gameInfo.getAliveMajorCivs().mapNotNull {
|
||||||
|
civ -> civ.cities.firstOrNull { it.isOriginalCapital && it.foundingCiv == civ.civName }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
buttons.add(getMilestoneButton("Capture [${city.name}]", false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MilestoneType.CompletePolicyBranches -> {
|
||||||
|
for (branch in civInfo.gameInfo.ruleSet.policyBranches.values) {
|
||||||
|
val finisher = branch.policies.last().name
|
||||||
|
buttons.add(getMilestoneButton(finisher, civInfo.policies.isAdopted(finisher)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getThingToFocus(civInfo: CivilizationInfo): ThingToFocus {
|
||||||
|
val ruleset = civInfo.gameInfo.ruleSet
|
||||||
|
return when (type!!) {
|
||||||
|
MilestoneType.BuiltBuilding -> {
|
||||||
|
val building = ruleset.buildings[params[0]]!!
|
||||||
|
if (building.requiredTech != null && !civInfo.tech.isResearched(building.requiredTech!!)) ThingToFocus.Science
|
||||||
|
// if (building.hasUnique(UniqueType.Unbuildable)) Stat.Gold // Temporary, should be replaced with whatever is required to buy
|
||||||
|
ThingToFocus.Production
|
||||||
|
}
|
||||||
|
MilestoneType.BuildingBuiltGlobally -> {
|
||||||
|
val building = ruleset.buildings[params[0]]!!
|
||||||
|
if (building.requiredTech != null && !civInfo.tech.isResearched(building.requiredTech!!)) ThingToFocus.Science
|
||||||
|
// if (building.hasUnique(UniqueType.Unbuildable)) ThingToFocus.Gold
|
||||||
|
ThingToFocus.Production
|
||||||
|
}
|
||||||
|
MilestoneType.AddedSSPartsInCapital -> {
|
||||||
|
val constructions =
|
||||||
|
getIncompleteSpaceshipParts(civInfo).keys.map {
|
||||||
|
if (it in ruleset.buildings)
|
||||||
|
ruleset.buildings[it]!!
|
||||||
|
else ruleset.units[it]!!
|
||||||
|
}
|
||||||
|
if (constructions.any { it.requiredTech != null && !civInfo.tech.isResearched(it.requiredTech!!) } ) ThingToFocus.Science
|
||||||
|
// if (constructions.any { it.hasUnique(UniqueType.Unbuildable) } ) Stat.Gold
|
||||||
|
ThingToFocus.Production
|
||||||
|
}
|
||||||
|
MilestoneType.DestroyAllPlayers, MilestoneType.CaptureAllCapitals -> ThingToFocus.Military
|
||||||
|
MilestoneType.CompletePolicyBranches -> ThingToFocus.Culture
|
||||||
|
MilestoneType.WinDiplomaticVote -> ThingToFocus.CityStates
|
||||||
|
MilestoneType.ScoreAfterTimeOut -> ThingToFocus.Score
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,4 @@
|
|||||||
package com.unciv.models.ruleset
|
package com.unciv.models.ruleset.unique
|
||||||
|
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common interface for all 'ruleset objects' that have Uniques, like BaseUnit, Nation, etc.
|
* Common interface for all 'ruleset objects' that have Uniques, like BaseUnit, Nation, etc.
|
@ -5,7 +5,6 @@ import com.unciv.models.metadata.BaseRuleset
|
|||||||
import com.unciv.models.ruleset.BeliefType
|
import com.unciv.models.ruleset.BeliefType
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.translations.TranslationFileWriter // for Kdoc only
|
import com.unciv.models.translations.TranslationFileWriter // for Kdoc only
|
||||||
@ -429,7 +428,7 @@ enum class UniqueParameterType(
|
|||||||
parameterText: String,
|
parameterText: String,
|
||||||
ruleset: Ruleset
|
ruleset: Ruleset
|
||||||
): UniqueType.UniqueComplianceErrorSeverity? {
|
): UniqueType.UniqueComplianceErrorSeverity? {
|
||||||
return if (parameterText in VictoryType.values().map { it.name }) null
|
return if (parameterText in ruleset.victories) null
|
||||||
else UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant
|
else UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6,8 +6,8 @@ import com.unciv.logic.city.CityInfo
|
|||||||
import com.unciv.logic.civilization.*
|
import com.unciv.logic.civilization.*
|
||||||
import com.unciv.logic.map.MapUnit
|
import com.unciv.logic.map.MapUnit
|
||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
|
import com.unciv.models.ruleset.ThingToFocus
|
||||||
import com.unciv.models.ruleset.unique.UniqueType.*
|
import com.unciv.models.ruleset.unique.UniqueType.*
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.translations.fillPlaceholders
|
import com.unciv.models.translations.fillPlaceholders
|
||||||
import com.unciv.models.translations.hasPlaceholderParameters
|
import com.unciv.models.translations.hasPlaceholderParameters
|
||||||
@ -144,14 +144,12 @@ object UniqueTriggerActivation {
|
|||||||
if (greatPeople.isEmpty()) return false
|
if (greatPeople.isEmpty()) return false
|
||||||
var greatPerson = greatPeople.random()
|
var greatPerson = greatPeople.random()
|
||||||
|
|
||||||
val preferredVictoryType = civInfo.victoryType()
|
if (civInfo.wantsToFocusOn(ThingToFocus.Culture)) {
|
||||||
|
|
||||||
if (preferredVictoryType == VictoryType.Cultural) {
|
|
||||||
val culturalGP =
|
val culturalGP =
|
||||||
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
|
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
|
||||||
if (culturalGP != null) greatPerson = culturalGP
|
if (culturalGP != null) greatPerson = culturalGP
|
||||||
}
|
}
|
||||||
if (preferredVictoryType == VictoryType.Scientific) {
|
if (civInfo.wantsToFocusOn(ThingToFocus.Science)) {
|
||||||
val scientificGP =
|
val scientificGP =
|
||||||
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
|
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
|
||||||
if (scientificGP != null) greatPerson = scientificGP
|
if (scientificGP != null) greatPerson = scientificGP
|
||||||
@ -459,7 +457,7 @@ object UniqueTriggerActivation {
|
|||||||
if (!civ.isBarbarian() && !civ.isSpectator())
|
if (!civ.isBarbarian() && !civ.isSpectator())
|
||||||
civ.addFlag(
|
civ.addFlag(
|
||||||
CivFlags.TurnsTillNextDiplomaticVote.name,
|
CivFlags.TurnsTillNextDiplomaticVote.name,
|
||||||
civInfo.getTurnsBetweenDiplomaticVotings()
|
civInfo.getTurnsBetweenDiplomaticVotes()
|
||||||
)
|
)
|
||||||
if (notification != null)
|
if (notification != null)
|
||||||
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
civInfo.addNotification(notification, NotificationIcon.Diplomacy)
|
||||||
|
@ -496,7 +496,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
|||||||
CannotBeBarbarian("Never appears as a Barbarian unit", UniqueTarget.Unit, flags = UniqueFlag.setOfHiddenToUsers),
|
CannotBeBarbarian("Never appears as a Barbarian unit", UniqueTarget.Unit, flags = UniqueFlag.setOfHiddenToUsers),
|
||||||
|
|
||||||
ReligiousUnit("Religious Unit", UniqueTarget.Unit),
|
ReligiousUnit("Religious Unit", UniqueTarget.Unit),
|
||||||
SpaceshipPart("Spaceship part", UniqueTarget.Unit, UniqueTarget.Building), // Usage for buildings is deprecated
|
SpaceshipPart("Spaceship part", UniqueTarget.Unit, UniqueTarget.Building), // Should be deprecated in the near future
|
||||||
AddInCapital("Can be added to [comment] in the Capital", UniqueTarget.Unit),
|
AddInCapital("Can be added to [comment] in the Capital", UniqueTarget.Unit),
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
|||||||
var interceptRange = 0
|
var interceptRange = 0
|
||||||
lateinit var unitType: String
|
lateinit var unitType: String
|
||||||
fun getType() = ruleset.unitTypes[unitType]!!
|
fun getType() = ruleset.unitTypes[unitType]!!
|
||||||
var requiredTech: String? = null
|
override var requiredTech: String? = null
|
||||||
private var requiredResource: String? = null
|
private var requiredResource: String? = null
|
||||||
|
|
||||||
override fun getUniqueTarget() = UniqueTarget.Unit
|
override fun getUniqueTarget() = UniqueTarget.Unit
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package com.unciv.models.ruleset.unit
|
package com.unciv.models.ruleset.unit
|
||||||
|
|
||||||
import com.unciv.models.ruleset.IHasUniques
|
|
||||||
import com.unciv.models.ruleset.RulesetObject
|
import com.unciv.models.ruleset.RulesetObject
|
||||||
import com.unciv.models.ruleset.unique.Unique
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||||
import com.unciv.models.stats.INamed
|
|
||||||
|
|
||||||
|
|
||||||
enum class UnitLayer { // The layer in which the unit moves
|
enum class UnitLayer { // The layer in which the unit moves
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.unciv.models.simulation
|
package com.unciv.models.simulation
|
||||||
|
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.GameInfo
|
import com.unciv.logic.GameInfo
|
||||||
import com.unciv.logic.GameStarter
|
import com.unciv.logic.GameStarter
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
import com.unciv.models.metadata.GameSetupInfo
|
import com.unciv.models.metadata.GameSetupInfo
|
||||||
import com.unciv.ui.crashhandling.crashHandlingThread
|
import com.unciv.ui.crashhandling.crashHandlingThread
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
@ -23,7 +23,7 @@ class Simulation(
|
|||||||
private var endTime: Long = 0
|
private var endTime: Long = 0
|
||||||
var steps = ArrayList<SimulationStep>()
|
var steps = ArrayList<SimulationStep>()
|
||||||
var winRate = mutableMapOf<String, MutableInt>()
|
var winRate = mutableMapOf<String, MutableInt>()
|
||||||
private var winRateByVictory = HashMap<String, MutableMap<VictoryType, MutableInt>>()
|
private var winRateByVictory = HashMap<String, MutableMap<String, MutableInt>>()
|
||||||
private var avgSpeed = 0f
|
private var avgSpeed = 0f
|
||||||
private var avgDuration: Duration = Duration.ZERO
|
private var avgDuration: Duration = Duration.ZERO
|
||||||
private var totalTurns = 0
|
private var totalTurns = 0
|
||||||
@ -35,7 +35,7 @@ class Simulation(
|
|||||||
for (civ in civilizations) {
|
for (civ in civilizations) {
|
||||||
this.winRate[civ] = MutableInt(0)
|
this.winRate[civ] = MutableInt(0)
|
||||||
winRateByVictory[civ] = mutableMapOf()
|
winRateByVictory[civ] = mutableMapOf()
|
||||||
for (victory in VictoryType.values())
|
for (victory in UncivGame.Current.gameInfo.ruleSet.victories.keys)
|
||||||
winRateByVictory[civ]!![victory] = MutableInt(0)
|
winRateByVictory[civ]!![victory] = MutableInt(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ class Simulation(
|
|||||||
outString += "\n$civ:\n"
|
outString += "\n$civ:\n"
|
||||||
val wins = winRate[civ]!!.value * 100 / max(steps.size, 1)
|
val wins = winRate[civ]!!.value * 100 / max(steps.size, 1)
|
||||||
outString += "$wins% total win rate \n"
|
outString += "$wins% total win rate \n"
|
||||||
for (victory in VictoryType.values()) {
|
for (victory in UncivGame.Current.gameInfo.ruleSet.victories.keys) {
|
||||||
val winsVictory = winRateByVictory[civ]!![victory]!!.value * 100 / max(winRate[civ]!!.value, 1)
|
val winsVictory = winRateByVictory[civ]!![victory]!!.value * 100 / max(winRate[civ]!!.value, 1)
|
||||||
outString += "$victory: $winsVictory% "
|
outString += "$victory: $winsVictory% "
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import com.unciv.logic.GameInfo
|
|||||||
|
|
||||||
class SimulationStep (gameInfo: GameInfo) {
|
class SimulationStep (gameInfo: GameInfo) {
|
||||||
var turns = gameInfo.turns
|
var turns = gameInfo.turns
|
||||||
var victoryType = gameInfo.currentPlayerCiv.victoryManager.hasWonVictoryType()
|
var victoryType = gameInfo.currentPlayerCiv.victoryManager.getVictoryTypeAchieved()
|
||||||
var winner: String? = null
|
var winner: String? = null
|
||||||
val currentPlayer = gameInfo.currentPlayer
|
val currentPlayer = gameInfo.currentPlayer
|
||||||
// val durationString: String = formatDuration(Duration.ofMillis(System.currentTimeMillis() - startTime))
|
// val durationString: String = formatDuration(Duration.ofMillis(System.currentTimeMillis() - startTime))
|
||||||
|
@ -377,7 +377,9 @@ object TranslationFileWriter {
|
|||||||
when {
|
when {
|
||||||
// Promotion names are not uniques but since we did the "[unitName] ability"
|
// Promotion names are not uniques but since we did the "[unitName] ability"
|
||||||
// they need the "parameters" treatment too
|
// they need the "parameters" treatment too
|
||||||
(field.name == "uniques" || field.name == "promotions") && (fieldValue is java.util.AbstractCollection<*>) ->
|
// Same for victory milestones
|
||||||
|
(field.name == "uniques" || field.name == "promotions" || field.name == "milestones")
|
||||||
|
&& (fieldValue is java.util.AbstractCollection<*>) ->
|
||||||
for (item in fieldValue)
|
for (item in fieldValue)
|
||||||
if (item is String) submitString(item, Unique(item)) else serializeElement(item!!)
|
if (item is String) submitString(item, Unique(item)) else serializeElement(item!!)
|
||||||
fieldValue is java.util.AbstractCollection<*> ->
|
fieldValue is java.util.AbstractCollection<*> ->
|
||||||
@ -456,6 +458,7 @@ object TranslationFileWriter {
|
|||||||
"UnitPromotions" -> emptyArray<Promotion>().javaClass
|
"UnitPromotions" -> emptyArray<Promotion>().javaClass
|
||||||
"Units" -> emptyArray<BaseUnit>().javaClass
|
"Units" -> emptyArray<BaseUnit>().javaClass
|
||||||
"UnitTypes" -> emptyArray<UnitType>().javaClass
|
"UnitTypes" -> emptyArray<UnitType>().javaClass
|
||||||
|
"VictoryTypes" -> emptyArray<Victory>().javaClass
|
||||||
else -> this.javaClass // dummy value
|
else -> this.javaClass // dummy value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -322,6 +322,10 @@ fun String.tr(): String {
|
|||||||
return fullyTranslatedString
|
return fullyTranslatedString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contains('{')) { // Translating partial sentences
|
||||||
|
return curlyBraceRegex.replace(this) { it.groups[1]!!.value.tr() }
|
||||||
|
}
|
||||||
|
|
||||||
// There might still be optimization potential here!
|
// There might still be optimization potential here!
|
||||||
if (contains('[')) { // Placeholders!
|
if (contains('[')) { // Placeholders!
|
||||||
/**
|
/**
|
||||||
@ -371,9 +375,6 @@ fun String.tr(): String {
|
|||||||
return languageSpecificPlaceholder // every component is already translated
|
return languageSpecificPlaceholder // every component is already translated
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contains('{')) { // sentence
|
|
||||||
return curlyBraceRegex.replace(this) { it.groups[1]!!.value.tr() }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Stats.isStats(this)) return Stats.parse(this).toString()
|
if (Stats.isStats(this)) return Stats.parse(this).toString()
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.*
|
|||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.models.ruleset.*
|
import com.unciv.models.ruleset.*
|
||||||
|
import com.unciv.models.ruleset.unique.IHasUniques
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.INamed
|
import com.unciv.models.stats.INamed
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
@ -177,13 +178,13 @@ class CivilopediaScreen(
|
|||||||
val religionEnabled = if (game.isGameInfoInitialized()) game.gameInfo.isReligionEnabled()
|
val religionEnabled = if (game.isGameInfoInitialized()) game.gameInfo.isReligionEnabled()
|
||||||
else ruleset.beliefs.isNotEmpty()
|
else ruleset.beliefs.isNotEmpty()
|
||||||
val victoryTypes = if (game.isGameInfoInitialized()) game.gameInfo.gameParameters.victoryTypes
|
val victoryTypes = if (game.isGameInfoInitialized()) game.gameInfo.gameParameters.victoryTypes
|
||||||
else VictoryType.values().toList()
|
else listOf()
|
||||||
|
|
||||||
fun shouldBeDisplayed(obj: IHasUniques): Boolean {
|
fun shouldBeDisplayed(obj: IHasUniques): Boolean {
|
||||||
return when {
|
return when {
|
||||||
obj.hasUnique(UniqueType.HiddenFromCivilopedia) -> false
|
obj.hasUnique(UniqueType.HiddenFromCivilopedia) -> false
|
||||||
(!religionEnabled && obj.hasUnique(UniqueType.HiddenWithoutReligion)) -> false
|
(!religionEnabled && obj.hasUnique(UniqueType.HiddenWithoutReligion)) -> false
|
||||||
obj.getMatchingUniques(UniqueType.HiddenWithoutVictoryType).any { !victoryTypes.contains(VictoryType.valueOf(it.params[0])) } -> false
|
obj.getMatchingUniques(UniqueType.HiddenWithoutVictoryType).any { !victoryTypes.contains(it.params[0]) } -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import com.unciv.UncivGame
|
|||||||
import com.unciv.logic.civilization.CityStateType
|
import com.unciv.logic.civilization.CityStateType
|
||||||
import com.unciv.models.metadata.GameSpeed
|
import com.unciv.models.metadata.GameSpeed
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.audio.MusicMood
|
import com.unciv.ui.audio.MusicMood
|
||||||
@ -135,7 +134,8 @@ class GameOptionsTable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Table.addMaxTurnsSlider(): UncivSlider? {
|
private fun Table.addMaxTurnsSlider(): UncivSlider? {
|
||||||
if (!gameParameters.victoryTypes.contains(VictoryType.Time)) return null
|
if (gameParameters.victoryTypes.none { ruleset.victories[it]?.enablesMaxTurns() == true })
|
||||||
|
return null
|
||||||
|
|
||||||
add("{Max Turns}:".toLabel()).left().expandX()
|
add("{Max Turns}:".toLabel()).left().expandX()
|
||||||
val slider = UncivSlider(250f, 1500f, 50f) {
|
val slider = UncivSlider(250f, 1500f, 50f) {
|
||||||
@ -229,25 +229,23 @@ class GameOptionsTable(
|
|||||||
add("{Victory Conditions}:".toLabel()).colspan(2).row()
|
add("{Victory Conditions}:".toLabel()).colspan(2).row()
|
||||||
|
|
||||||
// Create a checkbox for each VictoryType existing
|
// Create a checkbox for each VictoryType existing
|
||||||
var i = 0
|
|
||||||
val victoryConditionsTable = Table().apply { defaults().pad(5f) }
|
val victoryConditionsTable = Table().apply { defaults().pad(5f) }
|
||||||
for (victoryType in VictoryType.values()) {
|
for ((i, victoryType) in ruleset.victories.values.withIndex()) {
|
||||||
if (victoryType == VictoryType.Neutral) continue
|
val victoryCheckbox = victoryType.name.toCheckBox(gameParameters.victoryTypes.contains(victoryType.name)) {
|
||||||
val victoryCheckbox = victoryType.name.toCheckBox(gameParameters.victoryTypes.contains(victoryType)) {
|
|
||||||
// If the checkbox is checked, adds the victoryTypes else remove it
|
// If the checkbox is checked, adds the victoryTypes else remove it
|
||||||
if (it) {
|
if (it) {
|
||||||
gameParameters.victoryTypes.add(victoryType)
|
gameParameters.victoryTypes.add(victoryType.name)
|
||||||
} else {
|
} else {
|
||||||
gameParameters.victoryTypes.remove(victoryType)
|
gameParameters.victoryTypes.remove(victoryType.name)
|
||||||
}
|
}
|
||||||
// show or hide the max turns select box
|
// show or hide the max turns select box
|
||||||
if (victoryType == VictoryType.Time)
|
if (victoryType.enablesMaxTurns())
|
||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
victoryCheckbox.name = victoryType.name
|
victoryCheckbox.name = victoryType.name
|
||||||
victoryCheckbox.isDisabled = locked
|
victoryCheckbox.isDisabled = locked
|
||||||
victoryConditionsTable.add(victoryCheckbox).left()
|
victoryConditionsTable.add(victoryCheckbox).left()
|
||||||
if (++i % 2 == 0) victoryConditionsTable.row()
|
if ((i + 1) % 2 == 0) victoryConditionsTable.row()
|
||||||
}
|
}
|
||||||
add(victoryConditionsTable).colspan(2).row()
|
add(victoryConditionsTable).colspan(2).row()
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@ class NewGameScreen(
|
|||||||
init {
|
init {
|
||||||
updateRuleset() // must come before playerPickerTable so mod nations from fromSettings
|
updateRuleset() // must come before playerPickerTable so mod nations from fromSettings
|
||||||
// Has to be initialized before the mapOptionsTable, since the mapOptionsTable refers to it on init
|
// Has to be initialized before the mapOptionsTable, since the mapOptionsTable refers to it on init
|
||||||
|
|
||||||
|
if (gameSetupInfo.gameParameters.victoryTypes.isEmpty())
|
||||||
|
gameSetupInfo.gameParameters.victoryTypes.addAll(ruleset.victories.keys)
|
||||||
|
|
||||||
playerPickerTable = PlayerPickerTable(
|
playerPickerTable = PlayerPickerTable(
|
||||||
this, gameSetupInfo.gameParameters,
|
this, gameSetupInfo.gameParameters,
|
||||||
if (isNarrowerThan4to3()) stage.width - 20f else 0f
|
if (isNarrowerThan4to3()) stage.width - 20f else 0f
|
||||||
@ -92,6 +96,14 @@ class NewGameScreen(
|
|||||||
noHumanPlayersPopup.open()
|
noHumanPlayersPopup.open()
|
||||||
return@onClick
|
return@onClick
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gameSetupInfo.gameParameters.victoryTypes.isEmpty()) {
|
||||||
|
val noVictoryTypesPopup = Popup(this)
|
||||||
|
noVictoryTypesPopup.addGoodSizedLabel("No victory conditions were selected!".tr()).row()
|
||||||
|
noVictoryTypesPopup.addCloseButton()
|
||||||
|
noVictoryTypesPopup.open()
|
||||||
|
return@onClick
|
||||||
|
}
|
||||||
|
|
||||||
Gdx.input.inputProcessor = null // remove input processing - nothing will be clicked!
|
Gdx.input.inputProcessor = null // remove input processing - nothing will be clicked!
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import com.unciv.logic.map.TileInfo
|
|||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
import com.unciv.models.ruleset.Era
|
import com.unciv.models.ruleset.Era
|
||||||
import com.unciv.models.ruleset.QuestName
|
import com.unciv.models.ruleset.QuestName
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.civilopedia.CivilopediaCategories
|
import com.unciv.ui.civilopedia.CivilopediaCategories
|
||||||
@ -108,7 +107,7 @@ class WonderOverviewTab(
|
|||||||
wonder.name in startingObsolete -> false
|
wonder.name in startingObsolete -> false
|
||||||
wonder.getMatchingUniques(UniqueType.HiddenWithoutVictoryType)
|
wonder.getMatchingUniques(UniqueType.HiddenWithoutVictoryType)
|
||||||
.any { unique ->
|
.any { unique ->
|
||||||
!gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0]))
|
!gameInfo.gameParameters.victoryTypes.contains(unique.params[0])
|
||||||
} -> false
|
} -> false
|
||||||
else -> wonderEra <= viewerEra
|
else -> wonderEra <= viewerEra
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,12 @@ package com.unciv.ui.victoryscreen
|
|||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.models.ruleset.Policy
|
|
||||||
import com.unciv.models.ruleset.VictoryType
|
|
||||||
import com.unciv.models.translations.getPlaceholderParameters
|
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.models.metadata.GameSetupInfo
|
import com.unciv.models.metadata.GameSetupInfo
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.CompletionStatus
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
import com.unciv.ui.newgamescreen.NewGameScreen
|
import com.unciv.ui.newgamescreen.NewGameScreen
|
||||||
import com.unciv.ui.pickerscreens.PickerScreen
|
import com.unciv.ui.pickerscreens.PickerScreen
|
||||||
@ -22,10 +18,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
|||||||
|
|
||||||
val gameInfo = worldScreen.gameInfo
|
val gameInfo = worldScreen.gameInfo
|
||||||
private val playerCivInfo = worldScreen.viewingCiv
|
private val playerCivInfo = worldScreen.viewingCiv
|
||||||
val victoryTypes = gameInfo.gameParameters.victoryTypes
|
val enabledVictoryTypes = gameInfo.gameParameters.victoryTypes
|
||||||
private val scientificVictoryEnabled = victoryTypes.contains(VictoryType.Scientific)
|
|
||||||
private val culturalVictoryEnabled = victoryTypes.contains(VictoryType.Cultural)
|
|
||||||
private val dominationVictoryEnabled = victoryTypes.contains(VictoryType.Domination)
|
|
||||||
|
|
||||||
private val contentsTable = Table()
|
private val contentsTable = Table()
|
||||||
|
|
||||||
@ -35,7 +28,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
|||||||
stage.addActor(difficultyLabel)
|
stage.addActor(difficultyLabel)
|
||||||
|
|
||||||
val tabsTable = Table().apply { defaults().pad(10f) }
|
val tabsTable = Table().apply { defaults().pad(10f) }
|
||||||
val setMyVictoryButton = "Our status".toTextButton().onClick { setMyVictoryTable() }
|
val setMyVictoryButton = "Our status".toTextButton().onClick { setOurVictoryTable() }
|
||||||
if (!playerCivInfo.isSpectator()) tabsTable.add(setMyVictoryButton)
|
if (!playerCivInfo.isSpectator()) tabsTable.add(setMyVictoryButton)
|
||||||
val setGlobalVictoryButton = "Global status".toTextButton().onClick { setGlobalVictoryTable() }
|
val setGlobalVictoryButton = "Global status".toTextButton().onClick { setGlobalVictoryTable() }
|
||||||
tabsTable.add(setGlobalVictoryButton)
|
tabsTable.add(setGlobalVictoryButton)
|
||||||
@ -48,28 +41,27 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
|||||||
if (playerCivInfo.isSpectator())
|
if (playerCivInfo.isSpectator())
|
||||||
setGlobalVictoryTable()
|
setGlobalVictoryTable()
|
||||||
else
|
else
|
||||||
setMyVictoryTable()
|
setOurVictoryTable()
|
||||||
|
|
||||||
rightSideButton.isVisible = false
|
rightSideButton.isVisible = false
|
||||||
|
|
||||||
var someoneHasWon = false
|
var someoneHasWon = false
|
||||||
|
|
||||||
val playerVictoryType = playerCivInfo.victoryManager.hasWonVictoryType()
|
val playerVictoryType = playerCivInfo.victoryManager.getVictoryTypeAchieved()
|
||||||
if (playerVictoryType != null) {
|
if (playerVictoryType != null) {
|
||||||
someoneHasWon = true
|
someoneHasWon = true
|
||||||
wonOrLost("You have won a [${playerVictoryType.name}] Victory!")
|
wonOrLost("You have won a [$playerVictoryType] Victory!", playerVictoryType, true)
|
||||||
}
|
}
|
||||||
for (civ in gameInfo.civilizations.filter { it.isMajorCiv() && it != playerCivInfo }) {
|
for (civ in gameInfo.civilizations.filter { it.isMajorCiv() && it != playerCivInfo }) {
|
||||||
val civVictoryType = civ.victoryManager.hasWonVictoryType()
|
val civVictoryType = civ.victoryManager.getVictoryTypeAchieved()
|
||||||
if (civVictoryType != null) {
|
if (civVictoryType != null) {
|
||||||
someoneHasWon = true
|
someoneHasWon = true
|
||||||
val winningCivName = civ.civName
|
wonOrLost("[${civ.civName}] has won a [$civVictoryType] Victory!", civVictoryType, false)
|
||||||
wonOrLost("[$winningCivName] has won a [${civVictoryType.name}] Victory!")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerCivInfo.isDefeated()) {
|
if (playerCivInfo.isDefeated()) {
|
||||||
wonOrLost("")
|
wonOrLost("", null, false)
|
||||||
} else if (!someoneHasWon) {
|
} else if (!someoneHasWon) {
|
||||||
setDefaultCloseAction()
|
setDefaultCloseAction()
|
||||||
onBackButtonClicked { game.setWorldScreen() }
|
onBackButtonClicked { game.setWorldScreen() }
|
||||||
@ -77,18 +69,15 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun wonOrLost(description: String) {
|
private fun wonOrLost(description: String, victoryType: String?, hasWon: Boolean) {
|
||||||
// description will be empty when the player loses - no parameters - so this will be when(null) and end up in the else branch:
|
val endGameMessage =
|
||||||
val endGameMessage = when (description.getPlaceholderParameters().firstOrNull()) {
|
when {
|
||||||
VictoryType.Time.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!"
|
hasWon && (victoryType == null || victoryType !in gameInfo.ruleSet.victories) -> "Your civilization stands above all others! The exploits of your people shall be remembered until the end of civilization itself!"
|
||||||
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 == null || victoryType !in gameInfo.ruleSet.victories -> "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!"
|
||||||
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!"
|
hasWon -> playerCivInfo.gameInfo.ruleSet.victories[victoryType]!!.victoryString
|
||||||
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!"
|
else -> playerCivInfo.gameInfo.ruleSet.victories[victoryType]!!.defeatString
|
||||||
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 civilization 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!"
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptionLabel.setText(description.tr() + "\n" + endGameMessage.tr())
|
descriptionLabel.setText(description.tr() + "\n" + endGameMessage.tr())
|
||||||
|
|
||||||
rightSideButton.setText("Start new game".tr())
|
rightSideButton.setText("Start new game".tr())
|
||||||
@ -107,134 +96,81 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setOurVictoryTable() {
|
||||||
private fun setMyVictoryTable() {
|
val ourVictoryStatusTable = Table()
|
||||||
val myVictoryStatusTable = Table()
|
ourVictoryStatusTable.defaults().pad(10f)
|
||||||
myVictoryStatusTable.defaults().pad(10f)
|
val victoriesToShow = gameInfo.getEnabledVictories()
|
||||||
if (scientificVictoryEnabled) myVictoryStatusTable.add("Science victory".toLabel())
|
|
||||||
if (culturalVictoryEnabled) myVictoryStatusTable.add("Cultural victory".toLabel())
|
for (victory in victoriesToShow) {
|
||||||
if (dominationVictoryEnabled) myVictoryStatusTable.add("Conquest victory".toLabel())
|
ourVictoryStatusTable.add("[${victory.key}] Victory".toLabel())
|
||||||
myVictoryStatusTable.row()
|
|
||||||
if (scientificVictoryEnabled) myVictoryStatusTable.add(scienceVictoryColumn())
|
|
||||||
if (culturalVictoryEnabled) myVictoryStatusTable.add(culturalVictoryColumn())
|
|
||||||
if (dominationVictoryEnabled) myVictoryStatusTable.add(conquestVictoryColumn())
|
|
||||||
myVictoryStatusTable.row()
|
|
||||||
if (scientificVictoryEnabled) myVictoryStatusTable.add("Complete all the spaceship parts\n to win!".toLabel())
|
|
||||||
if (culturalVictoryEnabled) myVictoryStatusTable.add("Complete 5 policy branches and build\n the Utopia Project to win!".toLabel())
|
|
||||||
if (dominationVictoryEnabled) myVictoryStatusTable.add("Destroy all enemies\n to win!".toLabel())
|
|
||||||
|
|
||||||
contentsTable.clear()
|
|
||||||
contentsTable.add(myVictoryStatusTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scienceVictoryColumn(): Table {
|
|
||||||
val t = Table()
|
|
||||||
t.defaults().pad(5f)
|
|
||||||
t.add(getMilestone("Built Apollo Program",
|
|
||||||
playerCivInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts))).row()
|
|
||||||
|
|
||||||
val victoryManager = playerCivInfo.victoryManager
|
|
||||||
|
|
||||||
for (key in victoryManager.requiredSpaceshipParts.keys)
|
|
||||||
for (i in 0 until victoryManager.requiredSpaceshipParts[key]!!)
|
|
||||||
t.add(getMilestone(key, victoryManager.currentsSpaceshipParts[key]!! > i)).row() //(key, builtSpaceshipParts)
|
|
||||||
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun culturalVictoryColumn(): Table {
|
|
||||||
val t = Table()
|
|
||||||
t.defaults().pad(5f)
|
|
||||||
for (branch in playerCivInfo.gameInfo.ruleSet.policyBranches.values) {
|
|
||||||
val finisher = branch.policies.last().name
|
|
||||||
t.add(getMilestone(finisher, playerCivInfo.policies.isAdopted(finisher))).row()
|
|
||||||
}
|
}
|
||||||
return t
|
ourVictoryStatusTable.row()
|
||||||
|
|
||||||
|
for (victory in victoriesToShow) {
|
||||||
|
ourVictoryStatusTable.add(getOurVictoryColumn(victory.key))
|
||||||
|
}
|
||||||
|
ourVictoryStatusTable.row()
|
||||||
|
|
||||||
|
for (victory in victoriesToShow) {
|
||||||
|
ourVictoryStatusTable.add(victory.value.victoryScreenHeader.toLabel())
|
||||||
|
}
|
||||||
|
|
||||||
|
contentsTable.clear()
|
||||||
|
contentsTable.add(ourVictoryStatusTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun conquestVictoryColumn(): Table {
|
private fun getOurVictoryColumn(victory: String): Table {
|
||||||
|
val victoryObject = gameInfo.ruleSet.victories[victory]!!
|
||||||
val table = Table()
|
val table = Table()
|
||||||
table.defaults().pad(5f)
|
table.defaults().pad(5f)
|
||||||
for (civ in playerCivInfo.gameInfo.civilizations) {
|
var firstIncomplete: Boolean = true
|
||||||
if (civ.isCurrentPlayer() || !civ.isMajorCiv()) continue
|
for (milestone in victoryObject.milestoneObjects) {
|
||||||
val civName =
|
val completionStatus =
|
||||||
if (playerCivInfo.diplomacy.containsKey(civ.civName)) civ.civName
|
when {
|
||||||
else Constants.unknownNationName
|
milestone.hasBeenCompletedBy(playerCivInfo) -> CompletionStatus.Completed
|
||||||
table.add(getMilestone("Destroy [$civName]", civ.isDefeated())).row()
|
firstIncomplete -> {
|
||||||
|
firstIncomplete = false
|
||||||
|
CompletionStatus.Partially
|
||||||
|
}
|
||||||
|
else -> CompletionStatus.Incomplete
|
||||||
|
}
|
||||||
|
for (button in milestone.getVictoryScreenButtons(completionStatus, playerCivInfo)) {
|
||||||
|
table.add(button).row()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMilestone(text: String, achieved: Boolean): TextButton {
|
|
||||||
val textButton = text.toTextButton()
|
|
||||||
if (achieved) textButton.color = Color.GREEN
|
|
||||||
else textButton.color = Color.GRAY
|
|
||||||
return textButton
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun setGlobalVictoryTable() {
|
private fun setGlobalVictoryTable() {
|
||||||
val majorCivs = gameInfo.civilizations.filter { it.isMajorCiv() }
|
val majorCivs = gameInfo.civilizations.filter { it.isMajorCiv() }
|
||||||
val globalVictoryTable = Table().apply { defaults().pad(10f) }
|
val globalVictoryTable = Table().apply { defaults().pad(10f) }
|
||||||
|
val victoriesToShow = gameInfo.ruleSet.victories.filter { !it.value.hiddenInVictoryScreen && enabledVictoryTypes.contains(it.key) }
|
||||||
if (scientificVictoryEnabled) globalVictoryTable.add(getGlobalScientificVictoryColumn(majorCivs))
|
|
||||||
if (culturalVictoryEnabled) globalVictoryTable.add(getGlobalCulturalVictoryColumn(majorCivs))
|
for (victory in victoriesToShow) {
|
||||||
if (dominationVictoryEnabled) globalVictoryTable.add(getGlobalDominationVictoryColumn(majorCivs))
|
globalVictoryTable.add(getGlobalVictoryColumn(majorCivs, victory.key))
|
||||||
|
}
|
||||||
|
|
||||||
contentsTable.clear()
|
contentsTable.clear()
|
||||||
contentsTable.add(globalVictoryTable)
|
contentsTable.add(globalVictoryTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getGlobalVictoryColumn(majorCivs: List<CivilizationInfo>, victory: String): Table {
|
||||||
|
val victoryColumn = Table().apply { defaults().pad(10f) }
|
||||||
|
|
||||||
|
victoryColumn.add("[$victory] Victory".toLabel()).row()
|
||||||
|
victoryColumn.addSeparator()
|
||||||
|
|
||||||
private fun getGlobalDominationVictoryColumn(majorCivs: List<CivilizationInfo>): Table {
|
for (civ in majorCivs.filter { !it.isDefeated() }.sortedByDescending { it.victoryManager.amountMilestonesCompleted(victory) }) {
|
||||||
val dominationVictoryColumn = Table().apply { defaults().pad(10f) }
|
val buttonText = civ.victoryManager.getNextMilestone(victory)?.getVictoryScreenButtonHeaderText(false, civ) ?: "Done!"
|
||||||
|
victoryColumn.add(getCivGroup(civ, "\n" + buttonText.tr(), playerCivInfo)).fillX().row()
|
||||||
dominationVictoryColumn.add("Undefeated civs".toLabel()).row()
|
|
||||||
dominationVictoryColumn.addSeparator()
|
|
||||||
|
|
||||||
for (civ in majorCivs.filter { !it.isDefeated() })
|
|
||||||
dominationVictoryColumn.add(getCivGroup(civ, "", playerCivInfo)).fillX().row()
|
|
||||||
|
|
||||||
for (civ in majorCivs.filter { it.isDefeated() })
|
|
||||||
dominationVictoryColumn.add(getCivGroup(civ, "", playerCivInfo)).fillX().row()
|
|
||||||
|
|
||||||
return dominationVictoryColumn
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getGlobalCulturalVictoryColumn(majorCivs: List<CivilizationInfo>): Table {
|
|
||||||
val policyVictoryColumn = Table().apply { defaults().pad(10f) }
|
|
||||||
policyVictoryColumn.add("Branches completed".toLabel()).row()
|
|
||||||
policyVictoryColumn.addSeparator()
|
|
||||||
|
|
||||||
data class CivToBranchesCompleted(val civ: CivilizationInfo, val branchesCompleted: Int)
|
|
||||||
|
|
||||||
val civsToBranchesCompleted = majorCivs.map {
|
|
||||||
CivToBranchesCompleted(it, it.policies.adoptedPolicies.count { pol -> Policy.isBranchCompleteByName(pol) })
|
|
||||||
}.sortedByDescending { it.branchesCompleted }
|
|
||||||
|
|
||||||
for (entry in civsToBranchesCompleted) {
|
|
||||||
val civToBranchesHaveCompleted = getCivGroup(entry.civ, " - " + entry.branchesCompleted, playerCivInfo)
|
|
||||||
policyVictoryColumn.add(civToBranchesHaveCompleted).fillX().row()
|
|
||||||
}
|
|
||||||
return policyVictoryColumn
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getGlobalScientificVictoryColumn(majorCivs: List<CivilizationInfo>): Table {
|
|
||||||
val scientificVictoryColumn = Table().apply { defaults().pad(10f) }
|
|
||||||
scientificVictoryColumn.add("Spaceship parts remaining".toLabel()).row()
|
|
||||||
scientificVictoryColumn.addSeparator()
|
|
||||||
|
|
||||||
data class civToSpaceshipPartsRemaining(val civ: CivilizationInfo, val partsRemaining: Int)
|
|
||||||
|
|
||||||
val civsToPartsRemaining = majorCivs.map {
|
|
||||||
civToSpaceshipPartsRemaining(it,
|
|
||||||
it.victoryManager.spaceshipPartsRemaining())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entry in civsToPartsRemaining) {
|
for (civ in majorCivs.filter { it.isDefeated() }.sortedByDescending { it.victoryManager.amountMilestonesCompleted(victory) }) {
|
||||||
val civToPartsBeRemaining = (getCivGroup(entry.civ, " - " + entry.partsRemaining, playerCivInfo))
|
val buttonText = civ.victoryManager.getNextMilestone(victory)?.getVictoryScreenButtonHeaderText(false, civ) ?: "Done!"
|
||||||
scientificVictoryColumn.add(civToPartsBeRemaining).fillX().row()
|
victoryColumn.add(getCivGroup(civ, "\n" + buttonText.tr(), playerCivInfo)).fillX().row()
|
||||||
}
|
}
|
||||||
return scientificVictoryColumn
|
|
||||||
|
return victoryColumn
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setCivRankingsTable() {
|
private fun setCivRankingsTable() {
|
||||||
@ -257,36 +193,38 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
|
|||||||
contentsTable.add(civRankingsTable)
|
contentsTable.add(civRankingsTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private fun getCivGroup(civ: CivilizationInfo, afterCivNameText: String, currentPlayer: CivilizationInfo): Table {
|
||||||
fun getCivGroup(civ: CivilizationInfo, afterCivNameText:String, currentPlayer:CivilizationInfo): Table {
|
val civGroup = Table()
|
||||||
val civGroup = Table()
|
|
||||||
|
|
||||||
var labelText = civ.civName.tr()+afterCivNameText
|
var labelText = "{${civ.civName.tr()}}{${afterCivNameText.tr()}}"
|
||||||
var labelColor = Color.WHITE
|
var labelColor = Color.WHITE
|
||||||
val backgroundColor: Color
|
val backgroundColor: Color
|
||||||
|
|
||||||
if (civ.isDefeated()) {
|
if (civ.isDefeated()) {
|
||||||
civGroup.add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
|
civGroup.add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
|
||||||
backgroundColor = Color.LIGHT_GRAY
|
backgroundColor = Color.LIGHT_GRAY
|
||||||
labelColor = Color.BLACK
|
labelColor = Color.BLACK
|
||||||
} else if (currentPlayer == civ // || game.viewEntireMapForDebug
|
} else if (currentPlayer == civ // || game.viewEntireMapForDebug
|
||||||
|| currentPlayer.knows(civ) || currentPlayer.isDefeated() || currentPlayer.victoryManager.hasWon()) {
|
|| currentPlayer.knows(civ)
|
||||||
civGroup.add(ImageGetter.getNationIndicator(civ.nation, 30f))
|
|| currentPlayer.isDefeated()
|
||||||
backgroundColor = civ.nation.getOuterColor()
|
|| currentPlayer.victoryManager.hasWon()
|
||||||
labelColor = civ.nation.getInnerColor()
|
) {
|
||||||
} else {
|
civGroup.add(ImageGetter.getNationIndicator(civ.nation, 30f))
|
||||||
civGroup.add(ImageGetter.getRandomNationIndicator(30f))
|
backgroundColor = civ.nation.getOuterColor()
|
||||||
backgroundColor = Color.DARK_GRAY
|
labelColor = civ.nation.getInnerColor()
|
||||||
labelText = Constants.unknownNationName
|
} else {
|
||||||
}
|
civGroup.add(ImageGetter.getRandomNationIndicator(30f))
|
||||||
|
backgroundColor = Color.DARK_GRAY
|
||||||
civGroup.background = ImageGetter.getRoundedEdgeRectangle(backgroundColor)
|
labelText = Constants.unknownNationName
|
||||||
val label = labelText.toLabel(labelColor)
|
|
||||||
label.setAlignment(Align.center)
|
|
||||||
|
|
||||||
civGroup.add(label).padLeft(10f)
|
|
||||||
civGroup.pack()
|
|
||||||
return civGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
civGroup.background = ImageGetter.getRoundedEdgeRectangle(backgroundColor)
|
||||||
|
val label = labelText.toLabel(labelColor)
|
||||||
|
label.setAlignment(Align.center)
|
||||||
|
|
||||||
|
civGroup.add(label).padLeft(10f)
|
||||||
|
civGroup.pack()
|
||||||
|
return civGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user