mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-31 15:19:29 +07:00
Civilopedia phase 8 - Nations and Promotions (#4657)
* Civilopedia phase 8 - Nations and Promotions * Civilopedia phase 8 - Nations and Promotions - AS insubordination * Civilopedia phase 8 - Nations and Promotions - OR template * Civilopedia phase 8 - Nations and Promotions - proofread * Civilopedia phase 8 - Nations and Promotions - revert OR
This commit is contained in:
@ -575,7 +575,7 @@ open class TileInfo {
|
||||
}
|
||||
|
||||
fun toMarkup(viewingCiv: CivilizationInfo?): ArrayList<FormattedLine> {
|
||||
val lineList = ArrayList<FormattedLine>() // more readable than StringBuilder, with same performance for our use-case
|
||||
val lineList = ArrayList<FormattedLine>()
|
||||
val isViewableToPlayer = viewingCiv == null || UncivGame.Current.viewEntireMapForDebug
|
||||
|| viewingCiv.viewableTiles.contains(this)
|
||||
|
||||
|
@ -3,8 +3,14 @@
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.civilization.CityStateType
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.translations.Translations
|
||||
import com.unciv.models.translations.squareBraceRegex
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.CivilopediaText
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import com.unciv.ui.utils.colorFromRGB
|
||||
|
||||
@ -15,7 +21,7 @@ enum class VictoryType {
|
||||
Scientific
|
||||
}
|
||||
|
||||
class Nation : INamed {
|
||||
class Nation : INamed, ICivilopediaText {
|
||||
override lateinit var name: String
|
||||
|
||||
var leaderName = ""
|
||||
@ -49,6 +55,8 @@ class Nation : INamed {
|
||||
var adjective = ArrayList<String>()
|
||||
*/
|
||||
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
@Transient
|
||||
private lateinit var outerColorObject: Color
|
||||
fun getOuterColor(): Color = outerColorObject
|
||||
@ -88,14 +96,10 @@ class Nation : INamed {
|
||||
lateinit var cities: ArrayList<String>
|
||||
|
||||
|
||||
fun getUniqueString(ruleset: Ruleset, forPickerScreen: Boolean = true): String {
|
||||
/** Used only by NewGame Nation picker */
|
||||
fun getUniqueString(ruleset: Ruleset): String {
|
||||
val textList = ArrayList<String>()
|
||||
|
||||
if (leaderName.isNotEmpty() && !forPickerScreen) {
|
||||
textList += getLeaderDisplayName().tr()
|
||||
textList += ""
|
||||
}
|
||||
|
||||
if (uniqueName != "") textList += uniqueName.tr() + ":"
|
||||
if (uniqueText != "") {
|
||||
textList += " " + uniqueText.tr()
|
||||
@ -190,4 +194,152 @@ class Nation : INamed {
|
||||
textList += " " + unique.tr()
|
||||
}
|
||||
}
|
||||
|
||||
override fun makeLink() = "Nation/$name"
|
||||
override fun replacesCivilopediaDescription() = true
|
||||
override fun hasCivilopediaTextLines() = true
|
||||
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
|
||||
val textList = ArrayList<FormattedLine>()
|
||||
|
||||
if (leaderName.isNotEmpty()) {
|
||||
textList += FormattedLine(extraImage = "LeaderIcons/$leaderName", imageSize = 200f)
|
||||
textList += FormattedLine(getLeaderDisplayName(), centered = true, header = 3)
|
||||
textList += FormattedLine()
|
||||
}
|
||||
|
||||
if (uniqueName != "")
|
||||
textList += FormattedLine("{$uniqueName}:", header = 4)
|
||||
if (uniqueText != "") {
|
||||
textList += FormattedLine(uniqueText, indent = 1)
|
||||
} else {
|
||||
uniqueObjects.forEach {
|
||||
textList += FormattedLine(it)
|
||||
}
|
||||
textList += FormattedLine()
|
||||
}
|
||||
|
||||
if (startBias.isNotEmpty()) {
|
||||
startBias.withIndex().forEach {
|
||||
// can be "Avoid []"
|
||||
val link = if ('[' !in it.value) it.value
|
||||
else squareBraceRegex.find(it.value)!!.groups[1]!!.value
|
||||
textList += FormattedLine(
|
||||
(if (it.index == 0) "[Start bias:] " else "") + it.value.tr(), // extra tr because tr cannot nest {[]}
|
||||
link = "Terrain/$link",
|
||||
indent = if (it.index == 0) 0 else 1)
|
||||
}
|
||||
textList += FormattedLine()
|
||||
}
|
||||
addUniqueBuildingsText(textList, ruleset)
|
||||
addUniqueUnitsText(textList, ruleset)
|
||||
addUniqueImprovementsText(textList, ruleset)
|
||||
|
||||
return textList
|
||||
}
|
||||
|
||||
@JvmName("addUniqueBuildingsText1") // These overloads are too similar - but I hope to remove the other one soon
|
||||
private fun addUniqueBuildingsText(textList: ArrayList<FormattedLine>, ruleset: Ruleset) {
|
||||
for (building in ruleset.buildings.values) {
|
||||
if (building.uniqueTo != name || "Will not be displayed in Civilopedia" in building.uniques) continue
|
||||
textList += FormattedLine("{${building.name}} -", link=building.makeLink())
|
||||
if (building.replaces != null && ruleset.buildings.containsKey(building.replaces!!)) {
|
||||
val originalBuilding = ruleset.buildings[building.replaces!!]!!
|
||||
textList += FormattedLine("Replaces [${originalBuilding.name}]", link=originalBuilding.makeLink(), indent=1)
|
||||
|
||||
val originalBuildingStatMap = originalBuilding.toHashMap()
|
||||
for (stat in building.toHashMap())
|
||||
if (stat.value != originalBuildingStatMap[stat.key])
|
||||
textList += FormattedLine(
|
||||
stat.key.toString().tr() + " " +
|
||||
"[${stat.value.toInt()}] vs [${originalBuildingStatMap[stat.key]!!.toInt()}]".tr(),
|
||||
indent=1)
|
||||
|
||||
for (unique in building.uniques.filter { it !in originalBuilding.uniques })
|
||||
textList += FormattedLine(unique, indent=1)
|
||||
if (building.maintenance != originalBuilding.maintenance)
|
||||
textList += FormattedLine("{Maintenance} ".tr() + "[${building.maintenance}] vs [${originalBuilding.maintenance}]".tr(), indent=1)
|
||||
if (building.cost != originalBuilding.cost)
|
||||
textList += FormattedLine("{Cost} ".tr() + "[${building.cost}] vs [${originalBuilding.cost}]".tr(), indent=1)
|
||||
if (building.cityStrength != originalBuilding.cityStrength)
|
||||
textList += FormattedLine("{City strength} ".tr() + "[${building.cityStrength}] vs [${originalBuilding.cityStrength}]".tr(), indent=1)
|
||||
if (building.cityHealth != originalBuilding.cityHealth)
|
||||
textList += FormattedLine("{City health} ".tr() + "[${building.cityHealth}] vs [${originalBuilding.cityHealth}]".tr(), indent=1)
|
||||
textList += FormattedLine()
|
||||
} else if (building.replaces != null) {
|
||||
textList += FormattedLine("Replaces [${building.replaces}], which is not found in the ruleset!", indent=1)
|
||||
} else {
|
||||
textList += FormattedLine(building.getShortDescription(ruleset), indent=1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("addUniqueUnitsText1")
|
||||
private fun addUniqueUnitsText(textList: ArrayList<FormattedLine>, ruleset: Ruleset) {
|
||||
for (unit in ruleset.units.values) {
|
||||
if (unit.uniqueTo != name || "Will not be displayed in Civilopedia" in unit.uniques) continue
|
||||
textList += FormattedLine("{${unit.name}} -", link="Unit/${unit.name}")
|
||||
if (unit.replaces != null && ruleset.units.containsKey(unit.replaces!!)) {
|
||||
val originalUnit = ruleset.units[unit.replaces!!]!!
|
||||
textList += FormattedLine("Replaces [${originalUnit.name}]", link="Unit/${originalUnit.name}", indent=1)
|
||||
if (unit.cost != originalUnit.cost)
|
||||
textList += FormattedLine("{Cost} ".tr() + "[${unit.cost}] vs [${originalUnit.cost}]".tr(), indent=1)
|
||||
if (unit.strength != originalUnit.strength)
|
||||
textList += FormattedLine("${Fonts.strength} " + "[${unit.strength}] vs [${originalUnit.strength}]".tr(), indent=1)
|
||||
if (unit.rangedStrength != originalUnit.rangedStrength)
|
||||
textList += FormattedLine("${Fonts.rangedStrength} " + "[${unit.rangedStrength}] vs [${originalUnit.rangedStrength}]".tr(), indent=1)
|
||||
if (unit.range != originalUnit.range)
|
||||
textList += FormattedLine("${Fonts.range} " + "[${unit.range}] vs [${originalUnit.range}]".tr(), indent=1)
|
||||
if (unit.movement != originalUnit.movement)
|
||||
textList += FormattedLine("${Fonts.movement} " + "[${unit.movement}] vs [${originalUnit.movement}]".tr(), indent=1)
|
||||
for (resource in originalUnit.getResourceRequirements().keys)
|
||||
if (!unit.getResourceRequirements().containsKey(resource)) {
|
||||
textList += FormattedLine("[$resource] not required", link="Resource/$resource", indent=1)
|
||||
}
|
||||
// This does not use the auto-linking FormattedLine(Unique) for two reasons:
|
||||
// would look a little chaotic as unit uniques unlike most uniques are a HashSet and thus do not preserve order
|
||||
// No .copy() factory on FormattedLine and no FormattedLine(Unique, all other val's) constructor either
|
||||
for (unique in unit.uniques.filterNot { it in originalUnit.uniques })
|
||||
textList += FormattedLine(unique, indent=1)
|
||||
for (unique in originalUnit.uniques.filterNot { it in unit.uniques })
|
||||
textList += FormattedLine("Lost ability".tr() + " (" + "vs [${originalUnit.name}]".tr() + "): " +
|
||||
unique.tr(), indent=1)
|
||||
for (promotion in unit.promotions.filter { it !in originalUnit.promotions }) {
|
||||
val effect = ruleset.unitPromotions[promotion]!!.effect
|
||||
// "{$promotion} ({$effect})" won't work as effect may contain [] and tr() does not support that kind of nesting
|
||||
textList += FormattedLine(
|
||||
if (effect.isEmpty()) promotion
|
||||
else "${promotion.tr()} (${effect.tr()})",
|
||||
link = "Promotion/$promotion", indent = 1 )
|
||||
}
|
||||
} else if (unit.replaces != null) {
|
||||
textList += FormattedLine("Replaces [${unit.replaces}], which is not found in the ruleset!", indent=1)
|
||||
} else {
|
||||
textList += unit.getCivilopediaTextLines(ruleset).map {
|
||||
FormattedLine(it.text, link=it.link, indent = it.indent + 1, color=it.color)
|
||||
}
|
||||
}
|
||||
|
||||
textList += FormattedLine()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("addUniqueImprovementsText1")
|
||||
private fun addUniqueImprovementsText(textList: ArrayList<FormattedLine>, ruleset: Ruleset) {
|
||||
for (improvement in ruleset.tileImprovements.values) {
|
||||
if (improvement.uniqueTo != name ) continue
|
||||
|
||||
textList += FormattedLine(improvement.name, link="Improvement/${improvement.name}")
|
||||
textList += FormattedLine(improvement.clone().toString(), indent=1) // = (improvement as Stats).toString minus import plus copy overhead
|
||||
if (improvement.terrainsCanBeBuiltOn.isNotEmpty()) {
|
||||
improvement.terrainsCanBeBuiltOn.withIndex().forEach {
|
||||
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)
|
||||
}
|
||||
}
|
||||
for (unique in improvement.uniques)
|
||||
textList += FormattedLine(unique, indent=1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ class BaseUnit : INamed, IConstruction, CivilopediaText() {
|
||||
stats += "$rangedStrength${Fonts.rangedStrength}"
|
||||
stats += "$range${Fonts.range}"
|
||||
}
|
||||
if (movement != 0) stats += "$movement${Fonts.movement}"
|
||||
if (movement != 0 && !movesLikeAirUnits()) stats += "$movement${Fonts.movement}"
|
||||
if (cost != 0) stats += "{Cost}: $cost"
|
||||
if (stats.isNotEmpty())
|
||||
textList += FormattedLine(stats.joinToString(", "))
|
||||
|
@ -4,45 +4,136 @@ import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.Unique
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
|
||||
class Promotion : INamed{
|
||||
|
||||
class Promotion : INamed, ICivilopediaText {
|
||||
override lateinit var name: String
|
||||
var prerequisites = listOf<String>()
|
||||
var effect = ""
|
||||
var unitTypes = listOf<String>() // The json parser wouldn't agree to deserialize this as a list of UnitTypes. =(
|
||||
|
||||
var uniques = ArrayList<String>()
|
||||
val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } + Unique(effect) }
|
||||
private fun uniquesWithEffect() = sequence {
|
||||
if (effect.isNotEmpty()) yield(effect)
|
||||
yieldAll(uniques)
|
||||
}
|
||||
val uniqueObjects: List<Unique> by lazy { uniquesWithEffect().map { Unique(it) }.toList() }
|
||||
|
||||
fun getDescription(promotionsForUnitType: Collection<Promotion>, forCivilopedia:Boolean=false, ruleSet:Ruleset? = null):String {
|
||||
// we translate it before it goes in to get uniques like "vs units in rough terrain" and after to get "vs city
|
||||
val stringBuilder = StringBuilder()
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
for (unique in uniques + effect) {
|
||||
stringBuilder.appendLine(unique.tr())
|
||||
|
||||
/** Used to describe a Promotion on the PromotionPickerScreen */
|
||||
fun getDescription(promotionsForUnitType: Collection<Promotion>):String {
|
||||
val textList = ArrayList<String>()
|
||||
|
||||
for (unique in uniquesWithEffect()) {
|
||||
textList += unique.tr()
|
||||
}
|
||||
|
||||
if(prerequisites.isNotEmpty()) {
|
||||
if (prerequisites.isNotEmpty()) {
|
||||
val prerequisitesString: ArrayList<String> = arrayListOf()
|
||||
for (i in prerequisites.filter { promotionsForUnitType.any { promotion -> promotion.name == it } }) {
|
||||
prerequisitesString.add(i.tr())
|
||||
}
|
||||
stringBuilder.appendLine("{Requires}: ".tr() + prerequisitesString.joinToString(" OR ".tr()))
|
||||
textList += "{Requires}: ".tr() + prerequisitesString.joinToString(" OR ".tr())
|
||||
}
|
||||
if(forCivilopedia){
|
||||
if (unitTypes.isNotEmpty()) {
|
||||
val unitTypesString = unitTypes.joinToString(", ") { it.tr() }
|
||||
stringBuilder.appendLine("Available for [$unitTypesString]".tr())
|
||||
}
|
||||
return textList.joinToString("\n")
|
||||
}
|
||||
|
||||
if (ruleSet!=null) {
|
||||
val freeforUnits = ruleSet.units.filter { it.value.promotions.contains(name) }
|
||||
if (freeforUnits.isNotEmpty()) {
|
||||
val freeforString = freeforUnits.map { it.value.name }.joinToString(", ") { it.tr() }
|
||||
stringBuilder.appendLine("Free for [$freeforString]".tr())
|
||||
override fun makeLink() = "Promotion/$name"
|
||||
override fun hasCivilopediaTextLines() = true
|
||||
override fun replacesCivilopediaDescription() = true
|
||||
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
|
||||
val textList = ArrayList<FormattedLine>()
|
||||
|
||||
for (unique in uniqueObjects) {
|
||||
textList += FormattedLine(unique)
|
||||
}
|
||||
|
||||
val filteredPrerequisites = prerequisites.mapNotNull {
|
||||
ruleset.unitPromotions[it]
|
||||
}
|
||||
if (filteredPrerequisites.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
if (filteredPrerequisites.size == 1) {
|
||||
filteredPrerequisites[0].let {
|
||||
textList += FormattedLine("Requires [${it.name}]", link = it.makeLink())
|
||||
}
|
||||
} else {
|
||||
textList += FormattedLine("Requires at least one of the following:")
|
||||
filteredPrerequisites.forEach {
|
||||
textList += FormattedLine(it.name, link = it.makeLink())
|
||||
}
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString()
|
||||
|
||||
if (unitTypes.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
// This separates the linkable (corresponding to a BaseUnit name) unitFilter entries
|
||||
// from the others - `first` collects those for which the predicate is `true`.
|
||||
val types = unitTypes.partition { it in ruleset.units }
|
||||
if (unitTypes.size == 1) {
|
||||
if (types.first.isNotEmpty())
|
||||
types.first.first().let {
|
||||
textList += FormattedLine("Available for [${it.tr()}]", link = "Unit/$it")
|
||||
}
|
||||
else
|
||||
textList += FormattedLine("Available for [${types.second.first().tr()}]")
|
||||
} else {
|
||||
textList += FormattedLine("Available for:")
|
||||
types.first.forEach {
|
||||
textList += FormattedLine(it, indent = 1, link = "Unit/$it")
|
||||
}
|
||||
types.second.forEach {
|
||||
textList += FormattedLine(it, indent = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val freeForUnits = ruleset.units.filter { it.value.promotions.contains(name) }.map { it.key }
|
||||
if (freeForUnits.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
if (freeForUnits.size == 1) {
|
||||
freeForUnits[0].let {
|
||||
textList += FormattedLine("Free for [$it]", link = "Unit/$it")
|
||||
}
|
||||
} else {
|
||||
textList += FormattedLine("Free for:")
|
||||
freeForUnits.forEach {
|
||||
textList += FormattedLine(it, link = "Unit/$it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val grantors = ruleset.buildings.values.filter {
|
||||
building -> building.uniqueObjects.any {
|
||||
it.placeholderText == "All newly-trained [] units [] receive the [] promotion"
|
||||
&& it.params[2] == name
|
||||
}
|
||||
} + ruleset.terrains.values.filter {
|
||||
// once that unique is parameterized, this will be the efficient order of checks
|
||||
terrain -> terrain.uniques.any {
|
||||
it == "Grants Rejuvenation (all healing effects doubled) to adjacent military land units for the rest of the game"
|
||||
&& name == "Rejuvenation"
|
||||
}
|
||||
}
|
||||
if (grantors.isNotEmpty()) {
|
||||
textList += FormattedLine()
|
||||
if (grantors.size == 1) {
|
||||
grantors[0].let {
|
||||
textList += FormattedLine("Granted by [${it.name}]", link = it.makeLink())
|
||||
}
|
||||
} else {
|
||||
textList += FormattedLine("Granted by:")
|
||||
grantors.forEach {
|
||||
textList += FormattedLine(it.name, link = it.makeLink(), indent = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return textList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ class CivilopediaScreen(
|
||||
.map {
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getUniqueString(ruleset, false),
|
||||
"",
|
||||
CivilopediaCategories.Nation.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
@ -260,7 +260,7 @@ class CivilopediaScreen(
|
||||
.map {
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(ruleset.unitPromotions.values, true, ruleset),
|
||||
"",
|
||||
CivilopediaCategories.Promotion.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
|
@ -138,6 +138,8 @@ class FormattedLine (
|
||||
const val iconPad = 5f
|
||||
/** Padding distance per [indent] level */
|
||||
const val indentPad = 30f
|
||||
/** Where indent==1 will be, measured as icon count */
|
||||
const val indentOneAtNumIcons = 3
|
||||
|
||||
// Helper for constructor(Unique)
|
||||
private fun getUniqueLink(unique: Unique): String {
|
||||
@ -259,7 +261,9 @@ class FormattedLine (
|
||||
centered -> -usedWidth
|
||||
indent == 0 && iconCount == 0 -> 0f
|
||||
indent == 0 -> iconPad
|
||||
else -> (indent-1) * indentPad + 3 * minIconSize + 4 * iconPad - usedWidth
|
||||
noLinkImages -> indent * indentPad - usedWidth
|
||||
else -> (indent-1) * indentPad +
|
||||
indentOneAtNumIcons * (minIconSize + iconPad) + iconPad - usedWidth
|
||||
}
|
||||
val label = if (fontSize == defaultSize && labelColor == defaultColor) textToDisplay.toLabel()
|
||||
else textToDisplay.toLabel(labelColor,fontSize)
|
||||
|
Reference in New Issue
Block a user