Civilopedia Phase X (#5003)

* Civilopedia Phase X - Show Policies

* Civilopedia Phase X - Show City States

* Civilopedia - Loop-driven init and Cleanup

* Civilopedia - City States

* Civilopedia Phase X - Remove spurious comments
This commit is contained in:
SomeTroglodyte 2021-08-27 15:24:23 +02:00 committed by GitHub
parent 30e5ac3665
commit bf2ee91b67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 333 additions and 283 deletions

View File

@ -161,7 +161,6 @@ Declare Protection of [cityStateName]? =
Build [improvementName] on [resourceName] (200 Gold) = Build [improvementName] on [resourceName] (200 Gold) =
Gift Improvement = Gift Improvement =
Cultured = Cultured =
Maritime = Maritime =
Mercantile = Mercantile =
@ -175,9 +174,12 @@ Personality =
Influence = Influence =
Reach 30 for friendship. = Reach 30 for friendship. =
Reach highest influence above 60 for alliance. = Reach highest influence above 60 for alliance. =
When Friends: =
When Allies: =
The unique luxury is one of: =
# Trades # Trades
Trade = Trade =
Offer trade = Offer trade =
Retract offer = Retract offer =
@ -989,9 +991,10 @@ Adopt free policy =
Unlocked at = Unlocked at =
Gain 2 free technologies = Gain 2 free technologies =
All policies adopted = All policies adopted =
Policy branch: [branchName] =
# Religions
# Religions
Choose an Icon and name for your Religion = Choose an Icon and name for your Religion =
Choose a [beliefType] belief! = Choose a [beliefType] belief! =
Found [religionName] = Found [religionName] =

View File

@ -1,10 +1,10 @@
package com.unciv.logic.civilization package com.unciv.logic.civilization
enum class CityStateType { enum class CityStateType(val color: String = "") {
Cultured, Cultured("#8b60ff"),
Maritime, Maritime("#38ff70"),
Mercantile, Mercantile("#ffd800"),
Militaristic Militaristic("#ff0000")
} }
enum class CityStatePersonality { enum class CityStatePersonality {

View File

@ -2,6 +2,7 @@ package com.unciv.logic.civilization
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.metadata.BASE_GAME_DURATION_TURNS import com.unciv.models.metadata.BASE_GAME_DURATION_TURNS
import com.unciv.models.ruleset.Policy
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.stats.StatMap import com.unciv.models.stats.StatMap
@ -221,7 +222,7 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
if (civInfo.hasUnique("Provides 1 happiness per 2 additional social policies adopted")) { if (civInfo.hasUnique("Provides 1 happiness per 2 additional social policies adopted")) {
if (!statMap.containsKey("Policies")) statMap["Policies"] = 0f if (!statMap.containsKey("Policies")) statMap["Policies"] = 0f
statMap["Policies"] = statMap["Policies"]!! + statMap["Policies"] = statMap["Policies"]!! +
civInfo.policies.getAdoptedPolicies().count { !it.endsWith("Complete") } / 2 civInfo.policies.getAdoptedPolicies().count { !Policy.isBranchCompleteByName(it) } / 2
} }
var happinessPerNaturalWonder = 1f var happinessPerNaturalWonder = 1f

View File

@ -204,7 +204,7 @@ class CivilizationInfo {
fun isAlive(): Boolean = !isDefeated() fun isAlive(): Boolean = !isDefeated()
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 { it.endsWith("Complete") } fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) }
private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() } private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
fun victoryType(): VictoryType { fun victoryType(): VictoryType {
@ -490,7 +490,7 @@ class CivilizationInfo {
RankingType.Force -> units.sumBy { it.baseUnit.strength } RankingType.Force -> units.sumBy { it.baseUnit.strength }
RankingType.Happiness -> getHappiness() RankingType.Happiness -> getHappiness()
RankingType.Technologies -> tech.researchedTechnologies.size RankingType.Technologies -> tech.researchedTechnologies.size
RankingType.Culture -> policies.adoptedPolicies.count { !it.endsWith("Complete") } RankingType.Culture -> policies.adoptedPolicies.count { !Policy.isBranchCompleteByName(it) }
} }
} }
@ -520,7 +520,7 @@ class CivilizationInfo {
policies.civInfo = this policies.civInfo = this
if (policies.adoptedPolicies.size > 0 && policies.numberOfAdoptedPolicies == 0) if (policies.adoptedPolicies.size > 0 && policies.numberOfAdoptedPolicies == 0)
policies.numberOfAdoptedPolicies = policies.adoptedPolicies.count { !it.endsWith("Complete") } policies.numberOfAdoptedPolicies = policies.adoptedPolicies.count { !Policy.isBranchCompleteByName(it) }
policies.setTransients() policies.setTransients()
questManager.civInfo = this questManager.civInfo = this

View File

@ -2,6 +2,7 @@ package com.unciv.logic.civilization
import com.unciv.logic.map.MapSize import com.unciv.logic.map.MapSize
import com.unciv.models.ruleset.Policy import com.unciv.models.ruleset.Policy
import com.unciv.models.ruleset.Policy.PolicyBranchType
import com.unciv.models.ruleset.UniqueMap import com.unciv.models.ruleset.UniqueMap
import com.unciv.models.ruleset.UniqueTriggerActivation import com.unciv.models.ruleset.UniqueTriggerActivation
import com.unciv.models.translations.equalsPlaceholderText import com.unciv.models.translations.equalsPlaceholderText
@ -124,7 +125,7 @@ class PolicyManager {
*/ */
fun isAdoptable(policy: Policy, checkEra: Boolean = true): Boolean { fun isAdoptable(policy: Policy, checkEra: Boolean = true): Boolean {
if (isAdopted(policy.name)) return false if (isAdopted(policy.name)) return false
if (policy.name.endsWith("Complete")) return false if (policy.policyBranchType == PolicyBranchType.BranchComplete) return false
if (!getAdoptedPolicies().containsAll(policy.requires!!)) return false if (!getAdoptedPolicies().containsAll(policy.requires!!)) return false
if (checkEra && civInfo.gameInfo.ruleSet.getEraNumber(policy.branch.era) > civInfo.getEraNumber()) return false if (checkEra && civInfo.gameInfo.ruleSet.getEraNumber(policy.branch.era) > civInfo.getEraNumber()) return false
if (policy.uniqueObjects.any { it.placeholderText == "Incompatible with []" && adoptedPolicies.contains(it.params[0]) }) return false if (policy.uniqueObjects.any { it.placeholderText == "Incompatible with []" && adoptedPolicies.contains(it.params[0]) }) return false

View File

@ -2,8 +2,10 @@ package com.unciv.models.ruleset
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.models.stats.INamed import com.unciv.models.stats.INamed
import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.FormattedLine import com.unciv.ui.civilopedia.FormattedLine
import com.unciv.ui.civilopedia.ICivilopediaText import com.unciv.ui.civilopedia.ICivilopediaText
import java.text.Collator
import java.util.ArrayList import java.util.ArrayList
class Belief : INamed, ICivilopediaText, IHasUniques { class Belief : INamed, ICivilopediaText, IHasUniques {
@ -15,12 +17,16 @@ class Belief : INamed, ICivilopediaText, IHasUniques {
override var civilopediaText = listOf<FormattedLine>() override var civilopediaText = listOf<FormattedLine>()
override fun makeLink() = "Belief/$name" override fun makeLink() = "Belief/$name"
override fun getCivilopediaTextHeader() = FormattedLine(name, icon = makeLink(), header = 2, color = if (type == BeliefType.None) "#e34a2b" else "")
override fun replacesCivilopediaDescription() = true override fun replacesCivilopediaDescription() = true
override fun hasCivilopediaTextLines() = true override fun hasCivilopediaTextLines() = true
override fun getSortGroup(ruleset: Ruleset) = type.ordinal
override fun getIconName() = if (type == BeliefType.None) "Religion" else type.name
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> { override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
val textList = ArrayList<FormattedLine>() val textList = ArrayList<FormattedLine>()
textList += FormattedLine("{Type}: $type", color=type.color ) if (type != BeliefType.None)
textList += FormattedLine("{Type}: $type", color = type.color )
uniqueObjects.forEach { uniqueObjects.forEach {
textList += FormattedLine(it) textList += FormattedLine(it)
} }
@ -49,7 +55,17 @@ class Belief : INamed, ICivilopediaText, IHasUniques {
val matchingBeliefs = getBeliefsMatching(name, ruleset) val matchingBeliefs = getBeliefsMatching(name, ruleset)
if (matchingBeliefs.none()) return@sequence if (matchingBeliefs.none()) return@sequence
if (withSeeAlso) { yield(FormattedLine()); yield(FormattedLine("{See also}:")) } if (withSeeAlso) { yield(FormattedLine()); yield(FormattedLine("{See also}:")) }
yieldAll(matchingBeliefs.map { FormattedLine(it.name, link=it.makeLink(), indent = 1) }) yieldAll(matchingBeliefs.map { FormattedLine(it.name, link = it.makeLink(), indent = 1) })
}
fun getCivilopediaReligionEntry(ruleset: Ruleset) = Belief().apply {
name = "Religions"
val lines = ArrayList<FormattedLine>()
lines += FormattedLine(separator = true)
ruleset.religions.sortedWith(compareBy(Collator.getInstance(), { it.tr() })).forEach {
lines += FormattedLine(it, icon = "Belief/$it")
}
civilopediaText = lines
} }
} }
} }

View File

@ -31,9 +31,27 @@ class Era : INamed {
repeat(startingMilitaryUnitCount) {startingUnits.add(startingMilitaryUnit)} repeat(startingMilitaryUnitCount) {startingUnits.add(startingMilitaryUnit)}
return startingUnits return startingUnits
} }
fun getColor(): Color { fun getColor(): Color {
if (iconRGB == null) return Color.WHITE.cpy() if (iconRGB == null) return Color.WHITE.cpy()
return colorFromRGB(iconRGB!![0], iconRGB!![1], iconRGB!![2]) return colorFromRGB(iconRGB!![0], iconRGB!![1], iconRGB!![2])
} }
fun getHexColor() = "#" + getColor().toString().substring(0,6)
companion object {
// User for CS bonuses in case the Eras file is missing (legacy mods)
fun getLegacyCityStateBonusEra(eraNumber: Int) = Era().apply {
val cultureBonus = if(eraNumber in 0..1) 3 else if (eraNumber in 2..3) 6 else 13
val happinessBonus = if(eraNumber in 0..1) 2 else 3
friendBonus[CityStateType.Militaristic.name] = arrayListOf("Provides military units every [20] turns")
friendBonus[CityStateType.Cultured.name] = arrayListOf("Provides [$cultureBonus] [Culture] per turn")
friendBonus[CityStateType.Mercantile.name] = arrayListOf("Provides [$happinessBonus] Happiness")
friendBonus[CityStateType.Maritime.name] = arrayListOf("Provides [2] [Food] [in capital]")
allyBonus[CityStateType.Militaristic.name] = arrayListOf("Provides military units every [17] turns")
allyBonus[CityStateType.Cultured.name] = arrayListOf("Provides [${cultureBonus*2}] [Culture] per turn")
allyBonus[CityStateType.Mercantile.name] = arrayListOf("Provides [$happinessBonus] Happiness", "Provides a unique luxury")
allyBonus[CityStateType.Maritime.name] = arrayListOf("Provides [2] [Food] [in capital]", "Provides [1] [Food] [in all cities]")
}
}
} }

View File

@ -2,6 +2,7 @@
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.civilization.CityStateType import com.unciv.logic.civilization.CityStateType
import com.unciv.models.stats.INamed import com.unciv.models.stats.INamed
import com.unciv.models.translations.squareBraceRegex import com.unciv.models.translations.squareBraceRegex
@ -195,8 +196,15 @@ class Nation : INamed, ICivilopediaText, IHasUniques {
override fun makeLink() = "Nation/$name" override fun makeLink() = "Nation/$name"
override fun replacesCivilopediaDescription() = true override fun replacesCivilopediaDescription() = true
override fun hasCivilopediaTextLines() = true override fun hasCivilopediaTextLines() = true
override fun getSortGroup(ruleset: Ruleset) = when {
isCityState() -> 1
isBarbarian() -> 9
else -> 0
}
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> { override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
if (isCityState()) return getCityStateInfo(ruleset)
val textList = ArrayList<FormattedLine>() val textList = ArrayList<FormattedLine>()
if (leaderName.isNotEmpty()) { if (leaderName.isNotEmpty()) {
@ -235,6 +243,50 @@ class Nation : INamed, ICivilopediaText, IHasUniques {
return textList return textList
} }
private fun getCityStateInfo(ruleset: Ruleset): List<FormattedLine> {
val textList = ArrayList<FormattedLine>()
textList += FormattedLine("Type: [$cityStateType]", header = 4, color = cityStateType!!.color)
val viewingCiv = UncivGame.Current.gameInfo.currentPlayerCiv
val era = viewingCiv.getEraObject() ?: Era.getLegacyCityStateBonusEra(viewingCiv.getEraNumber())
var showResources = false
val friendBonus = era.friendBonus[cityStateType!!.name]
if (friendBonus != null && friendBonus.isNotEmpty()) {
textList += FormattedLine()
textList += FormattedLine("When Friends: ")
friendBonus.forEach {
textList += FormattedLine(Unique(it), indent = 1)
if (it == "Provides a unique luxury") showResources = true
}
}
val allyBonus = era.allyBonus[cityStateType!!.name]
if (allyBonus != null && allyBonus.isNotEmpty()) {
textList += FormattedLine()
textList += FormattedLine("When Allies: ")
allyBonus.forEach {
textList += FormattedLine(Unique(it), indent = 1)
if (it == "Provides a unique luxury") showResources = true
}
}
if (showResources) {
val allMercantileResources = ruleset.tileResources.values
.filter { it.unique == "Can only be created by Mercantile City-States" }
if (allMercantileResources.isNotEmpty()) {
textList += FormattedLine()
textList += FormattedLine("The unique luxury is one of:")
allMercantileResources.forEach {
textList += FormattedLine(it.name, it.makeLink(), indent = 1)
}
}
}
// personality is not a nation property, it gets assigned to the civ randomly
return textList
}
@JvmName("addUniqueBuildingsText1") // These overloads are too similar - but I hope to remove the other one soon @JvmName("addUniqueBuildingsText1") // These overloads are too similar - but I hope to remove the other one soon
private fun addUniqueBuildingsText(textList: ArrayList<FormattedLine>, ruleset: Ruleset) { private fun addUniqueBuildingsText(textList: ArrayList<FormattedLine>, ruleset: Ruleset) {
for (building in ruleset.buildings.values) { for (building in ruleset.buildings.values) {
@ -305,10 +357,10 @@ class Nation : INamed, ICivilopediaText, IHasUniques {
link = "Promotion/$promotion", indent = 1 ) link = "Promotion/$promotion", indent = 1 )
} }
} else if (unit.replaces != null) { } else if (unit.replaces != null) {
textList += FormattedLine("Replaces [${unit.replaces}], which is not found in the ruleset!", indent=1) textList += FormattedLine("Replaces [${unit.replaces}], which is not found in the ruleset!", indent = 1)
} else { } else {
textList += unit.getCivilopediaTextLines(ruleset).map { textList += unit.getCivilopediaTextLines(ruleset).map {
FormattedLine(it.text, link=it.link, indent = it.indent + 1, color=it.color) FormattedLine(it.text, link = it.link, indent = it.indent + 1, color = it.color)
} }
} }
@ -321,16 +373,16 @@ class Nation : INamed, ICivilopediaText, IHasUniques {
for (improvement in ruleset.tileImprovements.values) { for (improvement in ruleset.tileImprovements.values) {
if (improvement.uniqueTo != name ) continue if (improvement.uniqueTo != name ) continue
textList += FormattedLine(improvement.name, link="Improvement/${improvement.name}") textList += FormattedLine(improvement.name, link = "Improvement/${improvement.name}")
textList += FormattedLine(improvement.clone().toString(), indent=1) // = (improvement as Stats).toString minus import plus copy overhead textList += FormattedLine(improvement.clone().toString(), indent = 1) // = (improvement as Stats).toString minus import plus copy overhead
if (improvement.terrainsCanBeBuiltOn.isNotEmpty()) { if (improvement.terrainsCanBeBuiltOn.isNotEmpty()) {
improvement.terrainsCanBeBuiltOn.withIndex().forEach { improvement.terrainsCanBeBuiltOn.withIndex().forEach {
textList += FormattedLine(if (it.index == 0) "{Can be built on} {${it.value}}" else "or [${it.value}]", textList += FormattedLine(if (it.index == 0) "{Can be built on} {${it.value}}" else "or [${it.value}]",
link="Terrain/${it.value}", indent=if (it.index == 0) 1 else 2) link = "Terrain/${it.value}", indent = if (it.index == 0) 1 else 2)
} }
} }
for (unique in improvement.uniques) for (unique in improvement.uniques)
textList += FormattedLine(unique, indent=1) textList += FormattedLine(unique, indent = 1)
} }
} }

View File

@ -1,8 +1,11 @@
package com.unciv.models.ruleset package com.unciv.models.ruleset
import com.unciv.models.stats.INamed import com.unciv.models.stats.INamed
import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.FormattedLine
import com.unciv.ui.civilopedia.ICivilopediaText
open class Policy : INamed, IHasUniques { open class Policy : INamed, IHasUniques, ICivilopediaText {
lateinit var branch: PolicyBranch // not in json - added in gameBasics lateinit var branch: PolicyBranch // not in json - added in gameBasics
override lateinit var name: String override lateinit var name: String
@ -12,6 +15,94 @@ open class Policy : INamed, IHasUniques {
var column: Int = 0 var column: Int = 0
var requires: ArrayList<String>? = null var requires: ArrayList<String>? = null
override var civilopediaText = listOf<FormattedLine>()
/** Indicates whether a [Policy] is a [PolicyBranch] starting policy, a normal one, or the branch completion */
enum class PolicyBranchType {BranchStart, Member, BranchComplete}
/** Indicates whether this [Policy] is a [PolicyBranch] starting policy, a normal one, or the branch completion */
val policyBranchType: PolicyBranchType by lazy { when {
this is PolicyBranch -> PolicyBranchType.BranchStart
isBranchCompleteByName(name) -> PolicyBranchType.BranchComplete
else -> PolicyBranchType.Member
} }
companion object {
const val branchCompleteSuffix = " Complete"
/** Some tests to count policies by completion or not use only the String collection without instantiating them.
* To keep the hardcoding in one place, this is public and should be used instead of duplicating it.
*/
fun isBranchCompleteByName(name: String) = name.endsWith(branchCompleteSuffix)
}
override fun toString() = name override fun toString() = name
/** Used in PolicyPickerScreen to display Policy properties */
fun getDescription(): String {
val policyText = ArrayList<String>()
policyText += name
policyText += uniques
if (policyBranchType != PolicyBranchType.BranchComplete) {
policyText += if (requires!!.isNotEmpty())
"Requires [" + requires!!.joinToString { it.tr() } + "]"
else
"{Unlocked at} {${branch.era}}"
}
return policyText.joinToString("\n") { it.tr() }
}
override fun makeLink() = "Policy/$name"
override fun replacesCivilopediaDescription() = true
override fun hasCivilopediaTextLines() = true
override fun getSortGroup(ruleset: Ruleset) =
ruleset.getEraNumber(branch.era) * 10000 +
ruleset.policyBranches.keys.indexOf(branch.name) * 100 +
policyBranchType.ordinal
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
val lineList = ArrayList<FormattedLine>()
lineList += if (this is PolicyBranch) {
val eraColor = ruleset.eras[era]?.getHexColor() ?: ""
FormattedLine("{Unlocked at} {${branch.era}}", header = 4, color = eraColor)
} else {
FormattedLine("Policy branch: [${branch.name}]", link = branch.makeLink())
}
if (policyBranchType != PolicyBranchType.BranchComplete && requires != null && requires!!.isNotEmpty()) {
lineList += FormattedLine()
if (requires!!.size == 1)
requires!!.first().let { lineList += FormattedLine("Requires: [$it]", link = "Policy/$it") }
else {
lineList += FormattedLine("Requires all of the following:")
requires!!.forEach {
lineList += FormattedLine(it, link = "Policy/$it")
}
}
}
val leadsTo = ruleset.policies.values.filter {
it.requires != null && name in it.requires!!
&& it.policyBranchType != PolicyBranchType.BranchComplete
}
if (leadsTo.isNotEmpty()) {
lineList += FormattedLine()
if (leadsTo.size == 1)
leadsTo.first().let { lineList += FormattedLine("Leads to [${it.name}]", link = it.makeLink()) }
else {
lineList += FormattedLine("Leads to:")
leadsTo.forEach {
lineList += FormattedLine(it.name, link = it.makeLink(), indent = 1)
}
}
}
if (uniques.isNotEmpty()) {
lineList += FormattedLine()
for (unique in uniqueObjects) lineList += FormattedLine(unique)
}
return lineList
}
} }

View File

@ -4,12 +4,12 @@ import com.unciv.models.stats.INamed
import com.unciv.ui.civilopedia.FormattedLine import com.unciv.ui.civilopedia.FormattedLine
import com.unciv.ui.civilopedia.ICivilopediaText import com.unciv.ui.civilopedia.ICivilopediaText
class RuinReward : INamed, ICivilopediaText { class RuinReward : INamed, ICivilopediaText, IHasUniques {
override lateinit var name: String // Displayed in Civilopedia! override lateinit var name: String // Displayed in Civilopedia!
val notification: String = "" val notification: String = ""
val uniques: List<String> = listOf() override var uniques = ArrayList<String>()
@delegate:Transient // Defense in depth against mad modders @delegate:Transient // Defense in depth against mad modders
val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } } override val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } }
val excludedDifficulties: List<String> = listOf() val excludedDifficulties: List<String> = listOf()
val weight: Int = 1 val weight: Int = 1
val color: String = "" // For Civilopedia val color: String = "" // For Civilopedia

View File

@ -206,7 +206,7 @@ class Ruleset {
if (policy.requires == null) policy.requires = arrayListOf(branch.name) if (policy.requires == null) policy.requires = arrayListOf(branch.name)
policies[policy.name] = policy policies[policy.name] = policy
} }
branch.policies.last().name = branch.name + " Complete" branch.policies.last().name = branch.name + Policy.branchCompleteSuffix
} }
} }

View File

@ -150,7 +150,8 @@ class Technology: INamed, ICivilopediaText, IHasUniques {
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> { override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
val lineList = ArrayList<FormattedLine>() val lineList = ArrayList<FormattedLine>()
lineList += FormattedLine(era(), header = 3, color = "#8080ff") val eraColor = ruleset.eras[era()]?.getHexColor() ?: ""
lineList += FormattedLine(era(), header = 3, color = eraColor)
lineList += FormattedLine() lineList += FormattedLine()
lineList += FormattedLine("{Cost}: $cost${Fonts.science}") lineList += FormattedLine("{Cost}: $cost${Fonts.science}")

View File

@ -127,21 +127,12 @@ class Terrain : NamedStats(), ICivilopediaText {
if (defenceBonus != 0f) if (defenceBonus != 0f)
textList += FormattedLine("{Defence bonus}: ${(defenceBonus * 100).toInt()}%") textList += FormattedLine("{Defence bonus}: ${(defenceBonus * 100).toInt()}%")
val seeAlso = ( val seeAlso = (ruleset.buildings.values.asSequence() + ruleset.units.values.asSequence())
//todo: Could vastly be simplified using upcoming INonPerpetualConstruction .filter {
ruleset.buildings.values.asSequence() construction -> construction.uniqueObjects.any {
.filter { unique -> unique.params.any { it == name }
building -> building.uniqueObjects.any {
unique -> unique.params.any { it == name }
}
} +
ruleset.units.values.asSequence()
.filter {
unit -> unit.uniqueObjects.any {
unique -> unique.params.any { it == name }
}
} }
).map { FormattedLine(it.name, it.makeLink(), indent=1) } + }.map { FormattedLine(it.name, it.makeLink(), indent=1) } +
Belief.getCivilopediaTextMatching(name, ruleset, false) Belief.getCivilopediaTextMatching(name, ruleset, false)
if (seeAlso.any()) { if (seeAlso.any()) {
textList += FormattedLine() textList += FormattedLine()

View File

@ -3,7 +3,6 @@ package com.unciv.models.ruleset.tile
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.RuinsManager.RuinsManager
import com.unciv.models.ruleset.Belief import com.unciv.models.ruleset.Belief
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
import com.unciv.models.ruleset.IHasUniques import com.unciv.models.ruleset.IHasUniques

View File

@ -200,10 +200,10 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
var productionCost = cost.toFloat() var productionCost = cost.toFloat()
if (civInfo.isCityState()) if (civInfo.isCityState())
productionCost *= 1.5f productionCost *= 1.5f
if (civInfo.isPlayerCivilization()) productionCost *= if (civInfo.isPlayerCivilization())
productionCost *= civInfo.getDifficulty().unitCostModifier civInfo.getDifficulty().unitCostModifier
else else
productionCost *= civInfo.gameInfo.getDifficulty().aiUnitCostModifier civInfo.gameInfo.getDifficulty().aiUnitCostModifier
productionCost *= civInfo.gameInfo.gameParameters.gameSpeed.modifier productionCost *= civInfo.gameInfo.gameParameters.gameSpeed.modifier
return productionCost.toInt() return productionCost.toInt()
} }
@ -298,8 +298,8 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
for ((resource, amount) in getResourceRequirements()) for ((resource, amount) in getResourceRequirements())
if (civInfo.getCivResourcesByName()[resource]!! < amount) { if (civInfo.getCivResourcesByName()[resource]!! < amount) {
if (amount == 1) return "Consumes 1 [$resource]" // Again, to preserve existing translations return if (amount == 1) "Consumes 1 [$resource]" // Again, to preserve existing translations
else return "Consumes [$amount] [$resource]" else "Consumes [$amount] [$resource]"
} }
if (uniques.contains(Constants.settlerUnique) && civInfo.isCityState()) return "No settler for city-states" if (uniques.contains(Constants.settlerUnique) && civInfo.isCityState()) return "No settler for city-states"
@ -322,7 +322,7 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
override fun postBuildEvent(cityConstructions: CityConstructions, wasBought: Boolean): Boolean { override fun postBuildEvent(cityConstructions: CityConstructions, wasBought: Boolean): Boolean {
val civInfo = cityConstructions.cityInfo.civInfo val civInfo = cityConstructions.cityInfo.civInfo
val unit = civInfo.placeUnitNearTile(cityConstructions.cityInfo.location, name) val unit = civInfo.placeUnitNearTile(cityConstructions.cityInfo.location, name)
if (unit == null) return false // couldn't place the unit, so there's actually no unit =( ?: return false // couldn't place the unit, so there's actually no unit =(
//movement penalty //movement penalty
if (wasBought && !civInfo.gameInfo.gameParameters.godMode && !unit.hasUnique("Can move immediately once bought")) if (wasBought && !civInfo.gameInfo.gameParameters.godMode && !unit.hasUnique("Can move immediately once bought"))

View File

@ -16,7 +16,7 @@ enum class Stat(
Culture(NotificationIcon.Culture, UncivSound.Paper, Fonts.culture), Culture(NotificationIcon.Culture, UncivSound.Paper, Fonts.culture),
Happiness(NotificationIcon.Happiness, UncivSound.Click, Fonts.happiness), Happiness(NotificationIcon.Happiness, UncivSound.Click, Fonts.happiness),
Faith(NotificationIcon.Faith, UncivSound.Choir, Fonts.faith); Faith(NotificationIcon.Faith, UncivSound.Choir, Fonts.faith);
companion object { companion object {
val statsUsableToBuy = listOf(Gold, Food, Science, Culture, Faith) val statsUsableToBuy = listOf(Gold, Food, Science, Culture, Faith)
} }

View File

@ -17,6 +17,7 @@ import java.io.File
/** Encapsulates the knowledge on how to get an icon for each of the Civilopedia categories */ /** Encapsulates the knowledge on how to get an icon for each of the Civilopedia categories */
object CivilopediaImageGetters { object CivilopediaImageGetters {
private const val policyIconFolder = "PolicyIcons" private const val policyIconFolder = "PolicyIcons"
private const val policyInnerSize = 0.25f
// Todo: potential synergy with map editor // Todo: potential synergy with map editor
fun terrainImage(terrain: Terrain, ruleset: Ruleset, imageSize: Float): Actor { fun terrainImage(terrain: Terrain, ruleset: Ruleset, imageSize: Float): Actor {
@ -48,7 +49,7 @@ object CivilopediaImageGetters {
val construction = { name: String, size: Float -> val construction = { name: String, size: Float ->
ImageGetter.getConstructionImage(name) ImageGetter.getConstructionImage(name)
.surroundWithCircle(size, color = Color.WHITE) .surroundWithCircle(size)
} }
val improvement = { name: String, size: Float -> val improvement = { name: String, size: Float ->
ImageGetter.getImprovementIcon(name, size) ImageGetter.getImprovementIcon(name, size)
@ -59,8 +60,16 @@ object CivilopediaImageGetters {
else ImageGetter.getNationIndicator(nation, size) else ImageGetter.getNationIndicator(nation, size)
} }
val policy = { name: String, size: Float -> val policy = { name: String, size: Float ->
ImageGetter.getImage(policyIconFolder + File.separator + name) // policy branch start and complete have no icons but are linked -> nonexistence must be passed down
.apply { setSize(size,size) } val fileName = policyIconFolder + File.separator + name
if (ImageGetter.imageExists(fileName))
ImageGetter.getImage(fileName)
.apply {
setSize(size * policyInnerSize,size * policyInnerSize)
color = Color.BROWN
}
.surroundWithCircle(size)
else null
} }
val resource = { name: String, size: Float -> val resource = { name: String, size: Float ->
ImageGetter.getResourceImage(name, size) ImageGetter.getResourceImage(name, size)
@ -97,6 +106,7 @@ object CivilopediaImageGetters {
/** Enum used as keys for Civilopedia "pages" (categories). /** Enum used as keys for Civilopedia "pages" (categories).
* *
* Note names are singular on purpose - a "link" allows both key and label * Note names are singular on purpose - a "link" allows both key and label
* Order of values determines ordering of the categories in the Civilopedia top bar
* *
* @param label Translatable caption for the Civilopedia button * @param label Translatable caption for the Civilopedia button
*/ */
@ -114,10 +124,10 @@ enum class CivilopediaCategories (
Nation ("Nations", false, CivilopediaImageGetters.nation ), Nation ("Nations", false, CivilopediaImageGetters.nation ),
Technology ("Technologies", false, CivilopediaImageGetters.technology ), Technology ("Technologies", false, CivilopediaImageGetters.technology ),
Promotion ("Promotions", false, CivilopediaImageGetters.promotion ), Promotion ("Promotions", false, CivilopediaImageGetters.promotion ),
Policy ("Policies", true, CivilopediaImageGetters.policy ), Policy ("Policies", false, CivilopediaImageGetters.policy ),
Belief("Religions and Beliefs", false, CivilopediaImageGetters.belief ),
Tutorial ("Tutorials", false, null ), Tutorial ("Tutorials", false, null ),
Difficulty ("Difficulty levels", false, null ), Difficulty ("Difficulty levels", false, null ),
Belief("Religions and Beliefs", false, CivilopediaImageGetters.belief)
; ;
companion object { companion object {

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.* 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.Belief
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.Unique import com.unciv.models.ruleset.Unique
import com.unciv.models.ruleset.VictoryType import com.unciv.models.ruleset.VictoryType
@ -77,7 +78,7 @@ class CivilopediaScreen(
/** Select a specified category /** Select a specified category
* @param name Category name or label * @param name Category name or label
*/ */
fun selectCategory(name: String) { private fun selectCategory(name: String) {
val category = CivilopediaCategories.fromLink(name) val category = CivilopediaCategories.fromLink(name)
?: return // silently ignore unknown category names in links ?: return // silently ignore unknown category names in links
selectCategory(category) selectCategory(category)
@ -86,7 +87,7 @@ class CivilopediaScreen(
/** Select a specified category - unselects entry, rebuilds left side buttons. /** Select a specified category - unselects entry, rebuilds left side buttons.
* @param category Category key * @param category Category key
*/ */
fun selectCategory(category: CivilopediaCategories) { private fun selectCategory(category: CivilopediaCategories) {
currentCategory = category currentCategory = category
entrySelectTable.clear() entrySelectTable.clear()
entryIndex.clear() entryIndex.clear()
@ -173,139 +174,53 @@ class CivilopediaScreen(
onBackButtonClicked { UncivGame.Current.setWorldScreen() } onBackButtonClicked { UncivGame.Current.setWorldScreen() }
val hideReligionItems = !game.gameInfo.hasReligionEnabled() val hideReligionItems = !game.gameInfo.hasReligionEnabled()
val noCulturalVictory = VictoryType.Cultural !in game.gameInfo.gameParameters.victoryTypes
categoryToEntries[CivilopediaCategories.Building] = ruleset.buildings.values fun shouldBeDisplayed(uniqueObjects: List<Unique>): Boolean {
.filter { shouldBeDisplayed(it.uniqueObjects) val uniques = uniqueObjects.map { it.placeholderText }
&& !it.isAnyWonder() }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Building.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Wonder] = ruleset.buildings.values
.filter { shouldBeDisplayed(it.uniqueObjects)
&& it.isAnyWonder() }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Wonder.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Resource] = ruleset.tileResources.values
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Resource.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Terrain] = ruleset.terrains.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Terrain.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Improvement] = ruleset.tileImprovements.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Improvement.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Unit] = ruleset.units.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Unit.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Nation] = ruleset.nations.values
.filter { shouldBeDisplayed(it.uniqueObjects) && it.isMajorCiv() }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Nation.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Technology] = ruleset.technologies.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Technology.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Promotion] = ruleset.unitPromotions.values
.filter { shouldBeDisplayed(it.uniqueObjects) }
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Promotion.getImage?.invoke(it.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
)
}
categoryToEntries[CivilopediaCategories.Tutorial] = tutorialController.getCivilopediaTutorials() return Constants.hideFromCivilopediaUnique !in uniques
.map { && !(hideReligionItems && Constants.hiddenWithoutReligionUnique in uniques)
CivilopediaEntry( && !(uniqueObjects.filter { unique -> unique.placeholderText == "Hidden when [] Victory is disabled"}.any {
it.name, unique -> !game.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0] ))
"", })
// CivilopediaCategories.Tutorial.getImage?.invoke(it.name, imageSize) // Deprecated since 3.15.14
flavour = it && !(noCulturalVictory && "Hidden when cultural victory is disabled" in uniques)
) //
} }
categoryToEntries[CivilopediaCategories.Difficulty] = ruleset.difficulties.values fun getCategoryIterator(category: CivilopediaCategories): Collection<ICivilopediaText> =
.map { when (category) {
CivilopediaEntry( CivilopediaCategories.Building -> ruleset.buildings.values.filter { !it.isAnyWonder() }
it.name, CivilopediaCategories.Wonder -> ruleset.buildings.values.filter { it.isAnyWonder() }
"", CivilopediaCategories.Resource -> ruleset.tileResources.values
// CivilopediaCategories.Difficulty.getImage?.invoke(it.name, imageSize) CivilopediaCategories.Terrain -> ruleset.terrains.values
flavour = (it as? ICivilopediaText) CivilopediaCategories.Improvement -> ruleset.tileImprovements.values
) CivilopediaCategories.Unit -> ruleset.units.values
} CivilopediaCategories.Nation -> ruleset.nations.values.filter { !it.isSpectator() }
CivilopediaCategories.Technology -> ruleset.technologies.values
CivilopediaCategories.Promotion -> ruleset.unitPromotions.values
CivilopediaCategories.Policy -> ruleset.policies.values
CivilopediaCategories.Tutorial -> tutorialController.getCivilopediaTutorials()
CivilopediaCategories.Difficulty -> ruleset.difficulties.values
CivilopediaCategories.Belief -> (ruleset.beliefs.values.asSequence() +
Belief.getCivilopediaReligionEntry(ruleset)).toList()
}
for (loopCategory in CivilopediaCategories.values()) {
if (loopCategory.hide) continue
if (hideReligionItems && loopCategory == CivilopediaCategories.Belief) continue
categoryToEntries[loopCategory] =
getCategoryIterator(loopCategory)
.filter { it.getUniquesAsObjects()?.let { uniques -> shouldBeDisplayed(uniques) } ?: true }
.map { CivilopediaEntry(
(it as INamed).name, "",
loopCategory.getImage?.invoke(it.getIconName(), imageSize),
it.takeUnless { ct -> ct.isCivilopediaTextEmpty() },
sortBy = it.getSortGroup(ruleset)
) }
}
if (!hideReligionItems)
categoryToEntries[CivilopediaCategories.Belief] = (
ruleset.beliefs.values.asSequence()
.map {
CivilopediaEntry(
it.name,
"",
CivilopediaCategories.Belief.getImage?.invoke(it.type.name, imageSize),
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() },
sortBy = it.type.ordinal
)
} + CivilopediaEntry(
"Religions",
"",
CivilopediaCategories.Belief.getImage?.invoke("Religion", imageSize),
getReligionText(),
sortBy = -1
)
).toList()
val buttonTable = Table() val buttonTable = Table()
buttonTable.pad(15f) buttonTable.pad(15f)
buttonTable.defaults().pad(10f) buttonTable.defaults().pad(10f)
@ -364,31 +279,6 @@ class CivilopediaScreen(
selectEntry(link, noScrollAnimation = true) selectEntry(link, noScrollAnimation = true)
} }
private fun getReligionText(): ICivilopediaText {
val lines = ArrayList<FormattedLine>()
lines += FormattedLine("Religions", header=2, color="#e34a2b")
lines += FormattedLine(separator=true)
ruleset.religions.sortedWith(compareBy(Collator.getInstance(), { it.tr() })).forEach {
lines += FormattedLine(it, icon="Belief/$it")
}
return SimpleCivilopediaText(lines, true)
}
private fun shouldBeDisplayed(uniqueObjects: List<Unique>): Boolean {
val uniques = uniqueObjects.map { it.placeholderText }
val hideReligionItems = !game.gameInfo.hasReligionEnabled()
val noCulturalVictory = VictoryType.Cultural !in game.gameInfo.gameParameters.victoryTypes
return Constants.hideFromCivilopediaUnique !in uniques
&& !(hideReligionItems && Constants.hiddenWithoutReligionUnique in uniques)
&& !(uniqueObjects.filter { unique -> unique.placeholderText == "Hidden when [] Victory is disabled"}.any {
unique -> !game.gameInfo.gameParameters.victoryTypes.contains(VictoryType.valueOf(unique.params[0] ))
})
// Deprecated since 3.15.14
&& !(noCulturalVictory && "Hidden when cultural victory is disabled" in uniques)
//
}
override fun resize(width: Int, height: Int) { override fun resize(width: Int, height: Int) {
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) { if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
game.setScreen(CivilopediaScreen(game.worldScreen.gameInfo.ruleSet, currentCategory, currentEntry)) game.setScreen(CivilopediaScreen(game.worldScreen.gameInfo.ruleSet, currentCategory, currentEntry))

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.models.metadata.BaseRuleset import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.ruleset.IHasUniques
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.Unique import com.unciv.models.ruleset.Unique
@ -141,7 +142,7 @@ class FormattedLine (
/** Padding distance per [indent] level */ /** Padding distance per [indent] level */
const val indentPad = 30f const val indentPad = 30f
/** Where indent==1 will be, measured as icon count */ /** Where indent==1 will be, measured as icon count */
const val indentOneAtNumIcons = 3 const val indentOneAtNumIcons = 2
private var rulesetCachedInNameMap: Ruleset? = null private var rulesetCachedInNameMap: Ruleset? = null
// Cache to quickly match Categories to names. Takes a few ms to build on a slower desktop and will use just a few 10k bytes. // Cache to quickly match Categories to names. Takes a few ms to build on a slower desktop and will use just a few 10k bytes.
@ -378,11 +379,6 @@ object MarkupRenderer {
} }
} }
/** Storage class for interface [ICivilopediaText] for use as base class */
@Deprecated("As of 3.16.1, use ICivilopediaText directly please")
abstract class CivilopediaText : ICivilopediaText {
override var civilopediaText = listOf<FormattedLine>()
}
/** Storage class for instantiation of the simplest form containing only the lines collection */ /** Storage class for instantiation of the simplest form containing only the lines collection */
open class SimpleCivilopediaText( open class SimpleCivilopediaText(
override var civilopediaText: List<FormattedLine>, override var civilopediaText: List<FormattedLine>,
@ -401,7 +397,7 @@ open class SimpleCivilopediaText(
/** Addon common to most ruleset game objects managing civilopedia display /** Addon common to most ruleset game objects managing civilopedia display
* *
* ### Usage: * ### Usage:
* 1. Let [Ruleset] object implement this (e.g. by inheriting class [CivilopediaText] or adding var [civilopediaText] itself) * 1. Let [Ruleset] object implement this (by inheriting and implementing class [ICivilopediaText])
* 2. Add `"civilopediaText": ["",],` in the json for these objects * 2. Add `"civilopediaText": ["",],` in the json for these objects
* 3. Optionally override [getCivilopediaTextHeader] to supply a different header line * 3. Optionally override [getCivilopediaTextHeader] to supply a different header line
* 4. Optionally override [getCivilopediaTextLines] to supply automatic stuff like tech prerequisites, uniques, etc. * 4. Optionally override [getCivilopediaTextLines] to supply automatic stuff like tech prerequisites, uniques, etc.
@ -484,4 +480,20 @@ interface ICivilopediaText {
/** Create the correct string for a Civilopedia link */ /** Create the correct string for a Civilopedia link */
fun makeLink(): String fun makeLink(): String
/** This just marshals access to the uniques so they can be queried as part of the ICivilopediaText interface.
* Used exclusively by CivilopediaScreen, named to avoid JVM signature confusion
* (a getUniqueObjects exists in IHasUniques and most civilopedia objects will implement that interface)
*/
fun getUniquesAsObjects() = (this as? IHasUniques)?.uniqueObjects
/** Overrides alphabetical sorting in Civilopedia
* @param ruleset The current ruleset in case the function needs to do lookups
*/
fun getSortGroup(ruleset: Ruleset): Int = 0
/** Overrides Icon used for Civilopedia entry list (where you select the instance)
* This will still be passed to the category-specific image getter.
*/
fun getIconName() = if (this is INamed) name else ""
} }

View File

@ -9,6 +9,7 @@ import com.unciv.models.Tutorial
import com.unciv.models.UncivSound import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Policy import com.unciv.models.ruleset.Policy
import com.unciv.models.ruleset.PolicyBranch import com.unciv.models.ruleset.PolicyBranch
import com.unciv.models.ruleset.Policy.PolicyBranchType
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
import com.unciv.ui.worldscreen.WorldScreen import com.unciv.ui.worldscreen.WorldScreen
@ -110,7 +111,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
|| worldScreen.viewingCiv.isSpectator() // viewingCiv var points to selectedCiv in case of spectator || worldScreen.viewingCiv.isSpectator() // viewingCiv var points to selectedCiv in case of spectator
|| viewingCiv.isDefeated() || viewingCiv.isDefeated()
|| viewingCiv.policies.isAdopted(policy.name) || viewingCiv.policies.isAdopted(policy.name)
|| policy.name.endsWith("Complete") || policy.policyBranchType == PolicyBranchType.BranchComplete
|| !viewingCiv.policies.isAdoptable(policy) || !viewingCiv.policies.isAdoptable(policy)
|| !viewingCiv.policies.canAdoptPolicy()) { || !viewingCiv.policies.canAdoptPolicy()) {
rightSideButton.disable() rightSideButton.disable()
@ -123,17 +124,8 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
game.setScreen(PolicyPickerScreen(worldScreen)) game.setScreen(PolicyPickerScreen(worldScreen))
} }
pickedPolicy = policy pickedPolicy = policy
val policyText = mutableListOf<String>()
policyText += policy.name descriptionLabel.setText(policy.getDescription())
policyText += policy.uniques
if (!policy.name.endsWith("Complete")) {
if (policy.requires!!.isNotEmpty())
policyText += "Requires [" + policy.requires!!.joinToString { it.tr() } + "]"
else
policyText += "{Unlocked at} {" + policy.branch.era + "}"
}
descriptionLabel.setText(policyText.joinToString("\n") { it.tr() })
} }
/** /**
@ -151,7 +143,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
var currentColumn = 1 var currentColumn = 1
val branchTable = Table() val branchTable = Table()
for (policy in branch.policies) { for (policy in branch.policies) {
if (policy.name.endsWith("Complete")) continue if (policy.policyBranchType == PolicyBranchType.BranchComplete) continue
if (policy.row > currentRow) { if (policy.row > currentRow) {
branchTable.row().pad(2.5f) branchTable.row().pad(2.5f)
currentRow++ currentRow++

View File

@ -16,6 +16,7 @@ import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.trade.TradeLogic import com.unciv.logic.trade.TradeLogic
import com.unciv.logic.trade.TradeOffer import com.unciv.logic.trade.TradeOffer
import com.unciv.logic.trade.TradeType import com.unciv.logic.trade.TradeType
import com.unciv.models.ruleset.Era
import com.unciv.models.ruleset.ModOptionsConstants import com.unciv.models.ruleset.ModOptionsConstants
import com.unciv.models.ruleset.Quest import com.unciv.models.ruleset.Quest
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
@ -158,67 +159,38 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
else -> "Reach highest influence above 60 for alliance." else -> "Reach highest influence above 60 for alliance."
} }
diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row() diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row()
if (nextLevelString != "") { if (nextLevelString.isNotEmpty()) {
diplomacyTable.add(nextLevelString.toLabel()).row() diplomacyTable.add(nextLevelString.toLabel()).row()
} }
var friendBonusText = "When Friends: ".tr() val eraInfo = viewingCiv.getEraObject() ?: Era.getLegacyCityStateBonusEra(viewingCiv.getEraNumber())
val eraInfo = viewingCiv.getEraObject()
val friendBonuses =
if (eraInfo == null) null
else eraInfo.friendBonus[otherCiv.cityStateType.name]
friendBonusText +=
if (friendBonuses != null) {
friendBonuses.joinToString(separator = ", ") { it.tr() }
} else {
// Deprecated, assume Civ V values for compatibility
val cultureBonus = if(viewingCiv.getEraNumber() in 0..1) "3" else if (viewingCiv.getEraNumber() in 2..3) "6" else "13"
val happinessBonus = if(viewingCiv.getEraNumber() in 0..1) "2" else "3"
when (otherCiv.cityStateType) {
CityStateType.Militaristic -> "Provides military units every [20] turns".tr()
CityStateType.Cultured -> ("Provides [" + cultureBonus + "] [Culture] per turn").tr()
CityStateType.Mercantile -> ("Provides [" + happinessBonus + "] Happiness").tr()
CityStateType.Maritime -> "Provides [2] [Food] [in capital]".tr()
}
}
var friendBonusText = "{When Friends:} ".tr()
val friendBonuses = eraInfo.friendBonus[otherCiv.cityStateType.name]
friendBonusText += friendBonuses?.joinToString(separator = ", ") { it.tr() } ?: ""
var allyBonusText = "When Allies: " var allyBonusText = "{When Allies:} ".tr()
val allyBonuses = val allyBonuses = eraInfo.allyBonus[otherCiv.cityStateType.name]
if (eraInfo == null) null allyBonusText += allyBonuses?.joinToString(separator = ", ") { it.tr() } ?: ""
else eraInfo.allyBonus[otherCiv.cityStateType.name]
if (allyBonuses != null) {
allyBonusText += allyBonuses.joinToString(separator = ", ") { it.tr() }
} else {
// Deprecated, assume Civ V values for compatibility
val cultureBonus = if(viewingCiv.getEraNumber() in 0..1) "6" else if (viewingCiv.getEraNumber() in 2..3) "12" else "26"
val happinessBonus = if(viewingCiv.getEraNumber() in 0..1) "2" else "3"
allyBonusText += when (otherCiv.cityStateType) {
CityStateType.Militaristic -> "Provides military units every [20] turns".tr()
CityStateType.Cultured -> ("Provides [" + cultureBonus + "] [Culture] per turn").tr()
CityStateType.Mercantile -> ("Provides [" + happinessBonus + "] Happiness").tr() + ", " + "Provides a unique luxury".tr()
CityStateType.Maritime -> "Provides [2] [Food] [in capital]".tr() + ", " + "Provides [1] [Food] [in all cities]".tr()
}
}
val friendBonusLabelColor: Color val relationLevel = otherCivDiplomacyManager.relationshipLevel()
if (otherCivDiplomacyManager.relationshipLevel() >= RelationshipLevel.Friend) { if (relationLevel >= RelationshipLevel.Friend) {
friendBonusLabelColor = Color.GREEN
// RelationshipChange = Ally -> Friend or Friend -> Favorable // RelationshipChange = Ally -> Friend or Friend -> Favorable
val turnsToRelationshipChange = otherCivDiplomacyManager.getTurnsToRelationshipChange() val turnsToRelationshipChange = otherCivDiplomacyManager.getTurnsToRelationshipChange()
diplomacyTable.add("Relationship changes in another [$turnsToRelationshipChange] turns".toLabel()) diplomacyTable.add("Relationship changes in another [$turnsToRelationshipChange] turns".toLabel())
.row() .row()
} else }
friendBonusLabelColor = Color.GRAY
val friendBonusLabelColor = if (relationLevel >= RelationshipLevel.Friend) Color.GREEN else Color.GRAY
val friendBonusLabel = friendBonusText.toLabel(friendBonusLabelColor) val friendBonusLabel = friendBonusText.toLabel(friendBonusLabelColor)
.apply { setAlignment(Align.center) } .apply { setAlignment(Align.center) }
diplomacyTable.add(friendBonusLabel).row() diplomacyTable.add(friendBonusLabel).row()
val allyBonusLabelColor = if (otherCivDiplomacyManager.relationshipLevel() == RelationshipLevel.Ally) Color.GREEN else Color.GRAY
val allyBonusLabelColor = if (relationLevel == RelationshipLevel.Ally) Color.GREEN else Color.GRAY
val allyBonusLabel = allyBonusText.toLabel(allyBonusLabelColor) val allyBonusLabel = allyBonusText.toLabel(allyBonusLabelColor)
.apply { setAlignment(Align.center) } .apply { setAlignment(Align.center) }
diplomacyTable.add(allyBonusLabel).row() diplomacyTable.add(allyBonusLabel).row()
return diplomacyTable return diplomacyTable
} }

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
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.ruleset.VictoryType
import com.unciv.models.translations.getPlaceholderParameters import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
@ -202,11 +203,11 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() {
policyVictoryColumn.add("Branches completed".toLabel()).row() policyVictoryColumn.add("Branches completed".toLabel()).row()
policyVictoryColumn.addSeparator() policyVictoryColumn.addSeparator()
data class civToBranchesCompleted(val civ: CivilizationInfo, val branchesCompleted: Int) data class CivToBranchesCompleted(val civ: CivilizationInfo, val branchesCompleted: Int)
val civsToBranchesCompleted = val civsToBranchesCompleted = majorCivs.map {
majorCivs.map { civToBranchesCompleted(it, it.policies.adoptedPolicies.count { pol -> pol.endsWith("Complete") }) } CivToBranchesCompleted(it, it.policies.adoptedPolicies.count { pol -> Policy.isBranchCompleteByName(pol) })
.sortedByDescending { it.branchesCompleted } }.sortedByDescending { it.branchesCompleted }
for (entry in civsToBranchesCompleted) { for (entry in civsToBranchesCompleted) {
val civToBranchesHaveCompleted = EmpireOverviewScreen.getCivGroup(entry.civ, " - " + entry.branchesCompleted, playerCivInfo) val civToBranchesHaveCompleted = EmpireOverviewScreen.getCivGroup(entry.civ, " - " + entry.branchesCompleted, playerCivInfo)