mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 06:16:37 -04:00
Improve completeness and consistency of Technology descriptions (#8860)
This commit is contained in:
parent
2f00aa2af1
commit
de12f671a5
@ -1,18 +1,11 @@
|
||||
package com.unciv.models.ruleset.tech
|
||||
|
||||
import com.unciv.GUI
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetObject
|
||||
import com.unciv.models.ruleset.RulesetStatsObject
|
||||
import com.unciv.models.ruleset.unique.UniqueFlag
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.Fonts
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.objectdescriptions.TechnologyDescriptions
|
||||
|
||||
class Technology: RulesetObject() {
|
||||
|
||||
@ -29,261 +22,14 @@ class Technology: RulesetObject() {
|
||||
fun isContinuallyResearchable() = hasUnique(UniqueType.ResearchableMultipleTimes)
|
||||
|
||||
|
||||
/**
|
||||
* Textual description used in TechPickerScreen and AlertPopup(AlertType.TechResearched)
|
||||
*/
|
||||
fun getDescription(ruleset: Ruleset): String {
|
||||
val lineList = ArrayList<String>() // more readable than StringBuilder, with same performance for our use-case
|
||||
for (unique in uniques) lineList += unique.tr()
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values) {
|
||||
for (unique in improvement.getMatchingUniques(UniqueType.Stats)) {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += "[${unique.params[0]}] from every [${improvement.name}]"
|
||||
}
|
||||
|
||||
for (unique in improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile)) {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += "[${unique.params[0]}] from every [${improvement.name}] on [${unique.params[1]}] tiles"
|
||||
}
|
||||
}
|
||||
|
||||
val viewingCiv = GUI.getViewingPlayer()
|
||||
val enabledUnits = getEnabledUnits(ruleset, viewingCiv)
|
||||
if (enabledUnits.any()) {
|
||||
lineList += "{Units enabled}: "
|
||||
for (unit in enabledUnits)
|
||||
lineList += " • " + unit.name.tr() + " (" + unit.getShortDescription() + ")"
|
||||
}
|
||||
|
||||
val enabledBuildings = getEnabledBuildings(ruleset, viewingCiv)
|
||||
|
||||
val regularBuildings = enabledBuildings.filter { !it.isAnyWonder() }
|
||||
if (regularBuildings.any()) {
|
||||
lineList += "{Buildings enabled}: "
|
||||
for (building in regularBuildings)
|
||||
lineList += " • " + building.name.tr() + " (" + building.getShortDescription() + ")"
|
||||
}
|
||||
|
||||
val wonders = enabledBuildings.filter { it.isAnyWonder() }
|
||||
if (wonders.any()) {
|
||||
lineList += "{Wonders enabled}: "
|
||||
for (wonder in wonders)
|
||||
lineList += " • " + wonder.name.tr() + " (" + wonder.getShortDescription() + ")"
|
||||
}
|
||||
|
||||
for (obj in getObsoletedObjects(ruleset, viewingCiv))
|
||||
lineList += "[${obj.name}] obsoleted"
|
||||
|
||||
for (resource in ruleset.tileResources.values.asSequence().filter { it.revealedBy == name }
|
||||
.map { it.name })
|
||||
lineList += "Reveals [$resource] on the map"
|
||||
|
||||
val tileImprovements = ruleset.tileImprovements.values.filter { it.techRequired == name }
|
||||
if (tileImprovements.isNotEmpty())
|
||||
lineList += "{Tile improvements enabled}: " + tileImprovements.joinToString { it.name.tr() }
|
||||
|
||||
return lineList.joinToString("\n") { it.tr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Sequence of [Building]s enabled by this Technology, filtered for [civInfo]'s uniques,
|
||||
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
|
||||
*/
|
||||
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring
|
||||
fun getEnabledBuildings(ruleset: Ruleset, civInfo: Civilization?) = getFilteredBuildings(ruleset, civInfo)
|
||||
{ it.requiredTech == name }
|
||||
|
||||
/**
|
||||
* Returns a Sequence of [Building]s obsoleted by this Technology, filtered for [civInfo]'s uniques,
|
||||
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
|
||||
*/
|
||||
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring
|
||||
|
||||
fun getObsoletedObjects(ruleset: Ruleset, civInfo: Civilization?): Sequence<RulesetStatsObject> =
|
||||
(getFilteredBuildings(ruleset, civInfo){true}
|
||||
+ ruleset.tileResources.values.asSequence()
|
||||
+ ruleset.tileImprovements.values.filter {
|
||||
it.uniqueTo == null || it.uniqueTo == civInfo?.civName
|
||||
}).filter { obj: RulesetStatsObject ->
|
||||
obj.getMatchingUniques(UniqueType.ObsoleteWith).any { it.params[0] == name }
|
||||
}
|
||||
|
||||
// Helper: common filtering for both getEnabledBuildings and getObsoletedBuildings, difference via predicate parameter
|
||||
private fun getFilteredBuildings(
|
||||
ruleset: Ruleset,
|
||||
civInfo: Civilization?,
|
||||
predicate: (Building)->Boolean
|
||||
): Sequence<Building> {
|
||||
val (nuclearWeaponsEnabled, religionEnabled) = getNukeAndReligionSwitches(civInfo)
|
||||
return ruleset.buildings.values.asSequence()
|
||||
.filter {
|
||||
predicate(it) // expected to be the most selective, thus tested first
|
||||
&& (it.uniqueTo == civInfo?.civName || it.uniqueTo == null && civInfo?.getEquivalentBuilding(it) == it)
|
||||
&& (nuclearWeaponsEnabled || !it.hasUnique(UniqueType.EnablesNuclearWeapons))
|
||||
&& (religionEnabled || !it.hasUnique(UniqueType.HiddenWithoutReligion))
|
||||
&& !it.hasUnique(UniqueType.HiddenFromCivilopedia)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNukeAndReligionSwitches(civInfo: Civilization?): Pair<Boolean, Boolean> {
|
||||
if (civInfo == null) return true to true
|
||||
return civInfo.gameInfo.run { gameParameters.nuclearWeaponsEnabled to isReligionEnabled() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Sequence of [BaseUnit]s enabled by this Technology, filtered for [civInfo]'s uniques,
|
||||
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
|
||||
*/
|
||||
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia"/HiddenFromCivilopedia unique this needs refactoring
|
||||
fun getEnabledUnits(ruleset: Ruleset, civInfo: Civilization?): Sequence<BaseUnit> {
|
||||
val (nuclearWeaponsEnabled, religionEnabled) = getNukeAndReligionSwitches(civInfo)
|
||||
return ruleset.units.values.asSequence()
|
||||
.filter {
|
||||
it.requiredTech == name
|
||||
&& (it.uniqueTo == civInfo?.civName || it.uniqueTo == null && civInfo?.getEquivalentUnit(it) == it)
|
||||
&& (nuclearWeaponsEnabled || !it.isNuclearWeapon())
|
||||
&& (religionEnabled || !it.hasUnique(UniqueType.HiddenWithoutReligion))
|
||||
&& !it.hasUnique(UniqueType.HiddenFromCivilopedia)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get improvements related to this tech by a unique */
|
||||
private fun getSeeAlsoObjects(ruleset: Ruleset) =
|
||||
// This is a band-aid to clarify the relation Offshore platform - Refrigeration. A generic variant
|
||||
// covering all mentions in uniques in all ruleset objects would be possible here but - overkill for now.
|
||||
ruleset.tileImprovements.values
|
||||
.asSequence()
|
||||
.filter { improvement ->
|
||||
improvement.uniqueObjects.any {
|
||||
unique -> unique.conditionals.any {
|
||||
(it.isOfType(UniqueType.ConditionalTech) || it.isOfType(UniqueType.ConditionalNoTech))
|
||||
&& it.params[0] == name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Civilization-specific description for TechPicker or AlertType.TechResearched */
|
||||
fun getDescription(viewingCiv: Civilization) =
|
||||
TechnologyDescriptions.getDescription(this, viewingCiv)
|
||||
|
||||
override fun makeLink() = "Technology/$name"
|
||||
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> {
|
||||
val lineList = ArrayList<FormattedLine>()
|
||||
|
||||
val eraColor = ruleset.eras[era()]?.getHexColor() ?: ""
|
||||
lineList += FormattedLine(era(), header = 3, color = eraColor)
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Cost}: $cost${Fonts.science}")
|
||||
|
||||
if (prerequisites.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
if (prerequisites.size == 1)
|
||||
prerequisites.first().let { lineList += FormattedLine("Required tech: [$it]", link = "Technology/$it") }
|
||||
else {
|
||||
lineList += FormattedLine("Requires all of the following:")
|
||||
prerequisites.forEach {
|
||||
lineList += FormattedLine(it, link = "Technology/$it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val leadsTo = ruleset.technologies.values.filter { name in it.prerequisites }
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uniques.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
uniqueObjects.forEach {
|
||||
if (!it.hasFlag(UniqueFlag.HiddenToUsers))
|
||||
lineList += FormattedLine(it)
|
||||
}
|
||||
}
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values)
|
||||
for (unique in improvement.uniqueObjects) {
|
||||
if (unique.isOfType(UniqueType.Stats)) {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += FormattedLine("[${unique.params[0]}] from every [${improvement.name}]",
|
||||
link = improvement.makeLink())
|
||||
} else if (unique.placeholderText == "[] on [] tiles") {
|
||||
val requiredTech = unique.conditionals.firstOrNull { it.isOfType(UniqueType.ConditionalTech) }?.params?.get(0)
|
||||
if (requiredTech != name) continue
|
||||
lineList += FormattedLine("[${unique.params[0]}] from every [${improvement.name}] on [${unique.params[1]}] tiles",
|
||||
link = improvement.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val viewingCiv = if (GUI.isWorldLoaded()) GUI.getViewingPlayer() else null
|
||||
val enabledUnits = getEnabledUnits(ruleset, viewingCiv)
|
||||
if (enabledUnits.any()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Units enabled}:")
|
||||
for (unit in enabledUnits)
|
||||
lineList += FormattedLine(unit.name.tr() + " (" + unit.getShortDescription() + ")", link = unit.makeLink())
|
||||
}
|
||||
|
||||
val enabledBuildings = getEnabledBuildings(ruleset, viewingCiv)
|
||||
.partition { it.isAnyWonder() }
|
||||
if (enabledBuildings.first.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Wonders enabled}:")
|
||||
for (wonder in enabledBuildings.first)
|
||||
lineList += FormattedLine(wonder.name.tr() + " (" + wonder.getShortDescription() + ")", link = wonder.makeLink())
|
||||
}
|
||||
if (enabledBuildings.second.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Buildings enabled}:")
|
||||
for (building in enabledBuildings.second)
|
||||
lineList += FormattedLine(building.name.tr() + " (" + building.getShortDescription() + ")", link = building.makeLink())
|
||||
}
|
||||
|
||||
val obsoletedObjects = getObsoletedObjects(ruleset, viewingCiv)
|
||||
if (obsoletedObjects.any()) {
|
||||
lineList += FormattedLine()
|
||||
obsoletedObjects.forEach {
|
||||
lineList += FormattedLine("[${it.name}] obsoleted", link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val revealedResources = ruleset.tileResources.values.asSequence().filter { it.revealedBy == name }
|
||||
if (revealedResources.any()) {
|
||||
lineList += FormattedLine()
|
||||
revealedResources.forEach {
|
||||
lineList += FormattedLine("Reveals [${it.name}] on the map", link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val tileImprovements = ruleset.tileImprovements.values.asSequence().filter { it.techRequired == name }
|
||||
if (tileImprovements.any()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Tile improvements enabled}:")
|
||||
tileImprovements.forEach {
|
||||
lineList += FormattedLine(it.name, link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val seeAlsoObjects = getSeeAlsoObjects(ruleset)
|
||||
if (seeAlsoObjects.any()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{See also}:")
|
||||
seeAlsoObjects.forEach {
|
||||
lineList += FormattedLine(it.name, link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
return lineList
|
||||
}
|
||||
override fun getCivilopediaTextLines(ruleset: Ruleset) =
|
||||
TechnologyDescriptions.getCivilopediaTextLines(this, ruleset)
|
||||
|
||||
fun matchesFilter(filter: String): Boolean {
|
||||
return when (filter) {
|
||||
|
@ -16,6 +16,7 @@ import com.unciv.ui.components.extensions.filterAndLogic
|
||||
import com.unciv.ui.components.extensions.getNeedMoreAmountString
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.objectdescriptions.BaseUnitDescriptions
|
||||
import kotlin.math.pow
|
||||
|
||||
// This is BaseUnit because Unit is already a base Kotlin class and to avoid mixing the two up
|
||||
|
@ -3,7 +3,7 @@ package com.unciv.models.ruleset.unit
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetObject
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unit.BaseUnitDescriptions.getUnitTypeCivilopediaTextLines
|
||||
import com.unciv.ui.objectdescriptions.BaseUnitDescriptions.getUnitTypeCivilopediaTextLines
|
||||
|
||||
|
||||
enum class UnitLayer { // The layer in which the unit moves
|
||||
|
@ -1,9 +1,12 @@
|
||||
package com.unciv.models.ruleset.unit
|
||||
package com.unciv.ui.objectdescriptions
|
||||
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.unique.UniqueFlag
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.ruleset.unit.UnitMovementType
|
||||
import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
@ -0,0 +1,370 @@
|
||||
package com.unciv.ui.objectdescriptions
|
||||
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetStatsObject
|
||||
import com.unciv.models.ruleset.tech.Technology
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.tile.TileResource
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueFlag
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.Fonts
|
||||
import com.unciv.ui.components.extensions.center
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.screens.civilopediascreen.ICivilopediaText
|
||||
import com.unciv.ui.screens.pickerscreens.TechButton
|
||||
|
||||
|
||||
object TechnologyDescriptions {
|
||||
//region Methods called from Technology
|
||||
/**
|
||||
* Textual description used in TechPickerScreen and AlertPopup(AlertType.TechResearched) -
|
||||
* Civilization always known and description tailored to
|
||||
*/
|
||||
fun getDescription(technology: Technology, viewingCiv: Civilization): String = technology.run {
|
||||
val ruleset = viewingCiv.gameInfo.ruleset
|
||||
val lineList = ArrayList<String>() // more readable than StringBuilder, with same performance for our use-case
|
||||
for (unique in uniques) lineList += unique
|
||||
|
||||
lineList.addAll(
|
||||
getAffectedImprovements(name, ruleset)
|
||||
.filter { it.improvement.uniqueTo == null || it.improvement.uniqueTo == viewingCiv.civName }
|
||||
.map { it.getText() }
|
||||
)
|
||||
|
||||
val enabledUnits = getEnabledUnits(name, ruleset, viewingCiv)
|
||||
if (enabledUnits.any()) {
|
||||
lineList += "{Units enabled}: "
|
||||
for (unit in enabledUnits)
|
||||
lineList += " • " + unit.name.tr() + " (" + unit.getShortDescription() + ")"
|
||||
}
|
||||
|
||||
val (wonders, regularBuildings) = getEnabledBuildings(name, ruleset, viewingCiv)
|
||||
.partition { it.isAnyWonder() }
|
||||
|
||||
if (regularBuildings.isNotEmpty()) {
|
||||
lineList += "{Buildings enabled}: "
|
||||
for (building in regularBuildings)
|
||||
lineList += " • " + building.name.tr() + " (" + building.getShortDescription() + ")"
|
||||
}
|
||||
|
||||
if (wonders.isNotEmpty()) {
|
||||
lineList += "{Wonders enabled}: "
|
||||
for (wonder in wonders)
|
||||
lineList += " • " + wonder.name.tr() + " (" + wonder.getShortDescription() + ")"
|
||||
}
|
||||
|
||||
for (obj in getObsoletedObjects(name, ruleset, viewingCiv))
|
||||
lineList += "[${obj.name}] obsoleted"
|
||||
|
||||
val resourcesRevealed = ruleset.tileResources.values.asSequence()
|
||||
.filter { it.revealedBy == name }
|
||||
.map { it.name }
|
||||
for (resource in resourcesRevealed)
|
||||
lineList += "Reveals [$resource] on the map"
|
||||
|
||||
val tileImprovements = ruleset.tileImprovements.values.asSequence()
|
||||
.filter { it.techRequired == name }
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == viewingCiv.civName }
|
||||
.toList()
|
||||
if (tileImprovements.isNotEmpty())
|
||||
lineList += "{Tile improvements enabled}: " + tileImprovements.joinToString { it.name.tr() }
|
||||
|
||||
return lineList.joinToString("\n") { it.tr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets icons to display on a [TechButton] - all should be also described in [getDescription]
|
||||
*/
|
||||
fun getTechEnabledIcons(tech: Technology, viewingCiv: Civilization, techIconSize: Float) = sequence {
|
||||
val ruleset = viewingCiv.gameInfo.ruleset
|
||||
val techName = tech.name
|
||||
val civName = viewingCiv.civName
|
||||
|
||||
for (unit in getEnabledUnits(techName, ruleset, viewingCiv)) {
|
||||
yield(ImageGetter.getConstructionPortrait(unit.name, techIconSize))
|
||||
}
|
||||
|
||||
for (building in getEnabledBuildings(techName, ruleset, viewingCiv)) {
|
||||
yield(ImageGetter.getConstructionPortrait(building.name, techIconSize))
|
||||
}
|
||||
|
||||
yieldAll(
|
||||
getObsoletedObjects(techName, ruleset, viewingCiv)
|
||||
.mapNotNull { it.getObsoletedIcon(techIconSize) }
|
||||
)
|
||||
|
||||
for (resource in ruleset.tileResources.values.filter { it.revealedBy == techName }) {
|
||||
yield(ImageGetter.getResourcePortrait(resource.name, techIconSize))
|
||||
}
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values.asSequence()
|
||||
.filter { it.techRequired == techName }
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == civName }
|
||||
) {
|
||||
yield(ImageGetter.getImprovementPortrait(improvement.name, techIconSize))
|
||||
}
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values.asSequence()
|
||||
.filter { it.uniqueObjects.any { u -> u.allParams.contains(techName) } }
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == civName }
|
||||
) {
|
||||
yield(ImageGetter.getUniquePortrait(improvement.name, techIconSize))
|
||||
}
|
||||
|
||||
for (unique in tech.uniqueObjects) {
|
||||
yield(
|
||||
when {
|
||||
unique.isOfType(UniqueType.EnablesCivWideStatProduction) ->
|
||||
ImageGetter.getConstructionPortrait(unique.params[0], techIconSize)
|
||||
else ->
|
||||
ImageGetter.getUniquePortrait(unique.text, techIconSize)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of [ICivilopediaText.getCivilopediaTextLines]
|
||||
*/
|
||||
fun getCivilopediaTextLines(technology: Technology, ruleset: Ruleset): List<FormattedLine> = technology.run {
|
||||
val lineList = ArrayList<FormattedLine>()
|
||||
|
||||
val eraColor = ruleset.eras[era()]?.getHexColor() ?: ""
|
||||
lineList += FormattedLine(era(), header = 3, color = eraColor)
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Cost}: $cost${Fonts.science}")
|
||||
|
||||
if (prerequisites.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
if (prerequisites.size == 1)
|
||||
prerequisites.first().let { lineList += FormattedLine("Required tech: [$it]", link = "Technology/$it") }
|
||||
else {
|
||||
lineList += FormattedLine("Requires all of the following:")
|
||||
prerequisites.forEach {
|
||||
lineList += FormattedLine(it, link = "Technology/$it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val leadsTo = ruleset.technologies.values.filter { name in it.prerequisites }
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uniques.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
uniqueObjects.forEach {
|
||||
if (!it.hasFlag(UniqueFlag.HiddenToUsers))
|
||||
lineList += FormattedLine(it)
|
||||
}
|
||||
}
|
||||
|
||||
val affectedImprovements = getAffectedImprovements(name, ruleset)
|
||||
if (affectedImprovements.any()) {
|
||||
lineList += FormattedLine()
|
||||
for (entry in affectedImprovements) {
|
||||
lineList += FormattedLine(entry.getText(), link = entry.improvement.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val enabledUnits = getEnabledUnits(name, ruleset, null)
|
||||
if (enabledUnits.any()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Units enabled}:")
|
||||
for (unit in enabledUnits)
|
||||
lineList += FormattedLine(unit.name.tr() + " (" + unit.getShortDescription() + ")", link = unit.makeLink())
|
||||
}
|
||||
|
||||
val (wonders, regularBuildings) = getEnabledBuildings(name, ruleset, null)
|
||||
.partition { it.isAnyWonder() }
|
||||
|
||||
if (wonders.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Wonders enabled}:")
|
||||
for (wonder in wonders)
|
||||
lineList += FormattedLine(wonder.name.tr() + " (" + wonder.getShortDescription() + ")", link = wonder.makeLink())
|
||||
}
|
||||
|
||||
if (regularBuildings.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Buildings enabled}:")
|
||||
for (building in regularBuildings)
|
||||
lineList += FormattedLine(building.name.tr() + " (" + building.getShortDescription() + ")", link = building.makeLink())
|
||||
}
|
||||
|
||||
val obsoletedObjects = getObsoletedObjects(name, ruleset, null).toList()
|
||||
if (obsoletedObjects.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
obsoletedObjects.forEach {
|
||||
lineList += FormattedLine("[${it.name}] obsoleted", link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val revealedResources = ruleset.tileResources.values.asSequence().filter { it.revealedBy == name }
|
||||
if (revealedResources.any()) {
|
||||
lineList += FormattedLine()
|
||||
revealedResources.forEach {
|
||||
lineList += FormattedLine("Reveals [${it.name}] on the map", link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val tileImprovements = ruleset.tileImprovements.values.asSequence().filter { it.techRequired == name }
|
||||
if (tileImprovements.any()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{Tile improvements enabled}:")
|
||||
tileImprovements.forEach {
|
||||
lineList += FormattedLine(it.name, link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
val seeAlsoObjects = getSeeAlsoObjects(ruleset)
|
||||
if (seeAlsoObjects.isNotEmpty()) {
|
||||
lineList += FormattedLine()
|
||||
lineList += FormattedLine("{See also}:")
|
||||
seeAlsoObjects.forEach {
|
||||
lineList += FormattedLine(it.name, link = it.makeLink())
|
||||
}
|
||||
}
|
||||
|
||||
return lineList
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Helpers
|
||||
|
||||
/**
|
||||
* Returns a Sequence of [Building]s enabled by this Technology, filtered for [civInfo]'s uniques,
|
||||
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
|
||||
*/
|
||||
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring
|
||||
private fun getEnabledBuildings(techName: String, ruleset: Ruleset, civInfo: Civilization?) =
|
||||
getFilteredBuildings(ruleset, civInfo) { it.requiredTech == techName }
|
||||
|
||||
/**
|
||||
* Returns a Sequence of [RulesetStatsObject]s obsoleted by this Technology, filtered for [civInfo]'s uniques,
|
||||
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
|
||||
*/
|
||||
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring
|
||||
private fun getObsoletedObjects(techName: String, ruleset: Ruleset, civInfo: Civilization?): Sequence<RulesetStatsObject> =
|
||||
(
|
||||
getFilteredBuildings(ruleset, civInfo) { true }
|
||||
+ ruleset.tileResources.values.asSequence()
|
||||
+ ruleset.tileImprovements.values.filter {
|
||||
it.uniqueTo == null || it.uniqueTo == civInfo?.civName
|
||||
}
|
||||
).filter { obj: RulesetStatsObject ->
|
||||
obj.getMatchingUniques(UniqueType.ObsoleteWith).any { it.params[0] == techName }
|
||||
}
|
||||
|
||||
/** Readability - for the 'obsoleted' in [getTechEnabledIcons] */
|
||||
private fun RulesetStatsObject.getObsoletedIcon(techIconSize: Float) =
|
||||
when (this) {
|
||||
is Building -> ImageGetter.getConstructionPortrait(name, techIconSize)
|
||||
is TileResource -> ImageGetter.getResourcePortrait(name, techIconSize)
|
||||
is TileImprovement -> ImageGetter.getImprovementPortrait(name, techIconSize)
|
||||
else -> null
|
||||
}?.also {
|
||||
val closeImage = ImageGetter.getRedCross(techIconSize / 2, 1f)
|
||||
closeImage.center(it)
|
||||
it.addActor(closeImage)
|
||||
}
|
||||
|
||||
/** Common filtering for both [getEnabledBuildings] and [getObsoletedObjects], difference via predicate parameter */
|
||||
private fun getFilteredBuildings(
|
||||
ruleset: Ruleset,
|
||||
civInfo: Civilization?,
|
||||
predicate: (Building) -> Boolean
|
||||
): Sequence<Building> {
|
||||
val (nuclearWeaponsEnabled, religionEnabled) = getNukeAndReligionSwitches(civInfo)
|
||||
return ruleset.buildings.values.asSequence()
|
||||
.filter {
|
||||
predicate(it) // expected to be the most selective, thus tested first
|
||||
&& (it.uniqueTo == civInfo?.civName || it.uniqueTo == null && civInfo?.getEquivalentBuilding(it) == it)
|
||||
&& (nuclearWeaponsEnabled || !it.hasUnique(UniqueType.EnablesNuclearWeapons))
|
||||
&& (religionEnabled || !it.hasUnique(UniqueType.HiddenWithoutReligion))
|
||||
&& !it.hasUnique(UniqueType.HiddenFromCivilopedia)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNukeAndReligionSwitches(civInfo: Civilization?): Pair<Boolean, Boolean> {
|
||||
if (civInfo == null) return true to true
|
||||
return civInfo.gameInfo.run { gameParameters.nuclearWeaponsEnabled to isReligionEnabled() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Sequence of [BaseUnit]s enabled by this Technology, filtered for [civInfo]'s uniques,
|
||||
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia.
|
||||
*/
|
||||
// Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia"/HiddenFromCivilopedia unique this needs refactoring
|
||||
private fun getEnabledUnits(techName: String, ruleset: Ruleset, civInfo: Civilization?): Sequence<BaseUnit> {
|
||||
val (nuclearWeaponsEnabled, religionEnabled) = getNukeAndReligionSwitches(civInfo)
|
||||
return ruleset.units.values.asSequence()
|
||||
.filter {
|
||||
it.requiredTech == techName
|
||||
&& (it.uniqueTo == civInfo?.civName || it.uniqueTo == null && civInfo?.getEquivalentUnit(it) == it)
|
||||
&& (nuclearWeaponsEnabled || !it.isNuclearWeapon())
|
||||
&& (religionEnabled || !it.hasUnique(UniqueType.HiddenWithoutReligion))
|
||||
&& !it.hasUnique(UniqueType.HiddenFromCivilopedia)
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests whether a Unique means bonus Stats enabled by [techName] */
|
||||
private fun Unique.isImprovementStatsEnabledByTech(techName: String) =
|
||||
(isOfType(UniqueType.Stats) || isOfType(UniqueType.ImprovementStatsOnTile)) &&
|
||||
conditionals.any {
|
||||
it.isOfType(UniqueType.ConditionalTech) && it.params[0] == techName
|
||||
}
|
||||
|
||||
/** Tests whether a Unique Conditional is enabling or disabling its parent by a tech */
|
||||
private fun Unique.isTechConditional() =
|
||||
isOfType(UniqueType.ConditionalTech) ||
|
||||
isOfType(UniqueType.ConditionalNoTech)
|
||||
|
||||
/** Tests whether a Unique is enabled or disabled by [techName] */
|
||||
private fun Unique.isRelatedToTech(techName: String) =
|
||||
conditionals.any {
|
||||
it.isTechConditional() && it.params[0] == techName
|
||||
}
|
||||
|
||||
/** Used by [getAffectedImprovements] only */
|
||||
private data class ImprovementAndUnique(val improvement: TileImprovement, val unique: Unique) {
|
||||
fun getText() = "[${unique.params[0]}] from every [${improvement.name}]" +
|
||||
(if (unique.isOfType(UniqueType.Stats)) "" else " on [${unique.params[1]}] tiles")
|
||||
}
|
||||
|
||||
/** Yields Improvements with bonus Stats enabled by [techName] including the Unique doing it */
|
||||
private fun getAffectedImprovements(techName: String, ruleset: Ruleset): Sequence<ImprovementAndUnique> =
|
||||
ruleset.tileImprovements.values.asSequence()
|
||||
.flatMap { improvement ->
|
||||
improvement.uniqueObjects.asSequence()
|
||||
.filter { it.isImprovementStatsEnabledByTech(techName) }
|
||||
.map { ImprovementAndUnique(improvement, it) }
|
||||
}
|
||||
|
||||
/** Get other objects related to this tech by a unique,
|
||||
* e.g. Petra/Archaeology or Work Boats/Astronomy
|
||||
* but not including the Improvement Stats increases already listed */
|
||||
private fun Technology.getSeeAlsoObjects(ruleset: Ruleset) =
|
||||
ruleset.allRulesetObjects()
|
||||
.filter { iRulesetObject ->
|
||||
iRulesetObject.uniqueObjects.any {
|
||||
it.isRelatedToTech(name) && !it.isImprovementStatsEnabledByTech(name)
|
||||
}
|
||||
}.toList()
|
||||
|
||||
//endregion
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
package com.unciv.ui.screens.pickerscreens
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Group
|
||||
import com.badlogic.gdx.scenes.scene2d.Touchable
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.logic.civilization.managers.TechManager
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.tile.TileResource
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.objectdescriptions.TechnologyDescriptions
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.components.extensions.addBorder
|
||||
@ -21,29 +17,38 @@ import com.unciv.ui.components.extensions.darken
|
||||
import com.unciv.ui.components.extensions.setFontSize
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
|
||||
class TechButton(techName:String, private val techManager: TechManager, isWorldScreen: Boolean = true) : Table(
|
||||
BaseScreen.skin) {
|
||||
val text = "".toLabel().apply {
|
||||
class TechButton(
|
||||
techName: String,
|
||||
private val techManager: TechManager,
|
||||
isWorldScreen: Boolean = true
|
||||
) : Table(BaseScreen.skin) {
|
||||
|
||||
internal val text = "".toLabel().apply {
|
||||
wrap = false
|
||||
setFontSize(14)
|
||||
setAlignment(Align.left)
|
||||
setEllipsis(true)
|
||||
}
|
||||
val turns = "".toLabel().apply {
|
||||
|
||||
internal val turns = "".toLabel().apply {
|
||||
setFontSize(14)
|
||||
setAlignment(Align.right)
|
||||
}
|
||||
var bg = Image(BaseScreen.skinStrings.getUiBackground("TechPickerScreen/TechButton", BaseScreen.skinStrings.roundedEdgeRectangleMidShape))
|
||||
|
||||
private val backgroundImage: Image // Table.background is the border
|
||||
|
||||
init {
|
||||
touchable = Touchable.enabled
|
||||
background = BaseScreen.skinStrings.getUiBackground("TechPickerScreen/TechButton", BaseScreen.skinStrings.roundedEdgeRectangleMidShape,
|
||||
tintColor = Color.WHITE.cpy().darken(0.3f))
|
||||
|
||||
bg.toBack()
|
||||
addActor(bg)
|
||||
val path = "TechPickerScreen/TechButton"
|
||||
val default = BaseScreen.skinStrings.roundedEdgeRectangleMidShape
|
||||
backgroundImage = Image(BaseScreen.skinStrings.getUiBackground(path, default))
|
||||
background = BaseScreen.skinStrings.getUiBackground(path, default, Color.WHITE.darken(0.3f))
|
||||
|
||||
pad(0f).padBottom(5f).padTop(5f).padLeft(5f).padRight(0f)
|
||||
backgroundImage.toBack()
|
||||
addActor(backgroundImage)
|
||||
|
||||
pad(5f, 5f, 5f, 0f)
|
||||
|
||||
add(ImageGetter.getTechIconPortrait(techName, 46f))
|
||||
.padRight(5f).padLeft(2f).left()
|
||||
@ -73,15 +78,15 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
|
||||
add(rightSide).expandX().left()
|
||||
pack()
|
||||
|
||||
bg.setSize(width-3f, height-3f)
|
||||
bg.align = Align.center
|
||||
bg.center(this)
|
||||
backgroundImage.setSize(width - 3f, height - 3f)
|
||||
backgroundImage.align = Align.center
|
||||
backgroundImage.center(this)
|
||||
|
||||
pack()
|
||||
}
|
||||
|
||||
fun setButtonColor(color: Color) {
|
||||
bg.color = color
|
||||
backgroundImage.color = color
|
||||
pack()
|
||||
}
|
||||
|
||||
@ -92,74 +97,15 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
|
||||
BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
|
||||
tintColor = Color.BLACK.cpy().apply { a = 0.7f }
|
||||
)
|
||||
techEnabledIcons.pad(0f).padLeft(10f).padTop(2f).padBottom(2f)
|
||||
techEnabledIcons.pad(2f, 10f, 2f, 0f)
|
||||
techEnabledIcons.defaults().padRight(5f)
|
||||
val techIconSize = 30f
|
||||
|
||||
val civName = techManager.civInfo.civName
|
||||
val ruleset = techManager.civInfo.gameInfo.ruleset
|
||||
val civ = techManager.civInfo
|
||||
val tech = civ.gameInfo.ruleset.technologies[techName]!!
|
||||
|
||||
val tech = ruleset.technologies[techName]!!
|
||||
|
||||
val icons = ArrayList<Group>()
|
||||
|
||||
for (unit in tech.getEnabledUnits(ruleset, techManager.civInfo)) {
|
||||
icons.add(ImageGetter.getConstructionPortrait(unit.name, techIconSize))
|
||||
}
|
||||
|
||||
for (building in tech.getEnabledBuildings(ruleset, techManager.civInfo)) {
|
||||
icons.add(ImageGetter.getConstructionPortrait(building.name, techIconSize))
|
||||
}
|
||||
|
||||
for (obj in tech.getObsoletedObjects(ruleset, techManager.civInfo)) {
|
||||
val obsoletedIcon = when (obj) {
|
||||
is Building -> ImageGetter.getConstructionPortrait(obj.name, techIconSize)
|
||||
is TileResource -> ImageGetter.getResourcePortrait(obj.name, techIconSize)
|
||||
is TileImprovement -> ImageGetter.getImprovementPortrait(obj.name, techIconSize)
|
||||
else -> continue
|
||||
}.also {
|
||||
val closeImage = ImageGetter.getRedCross(techIconSize / 2, 1f)
|
||||
closeImage.center(it)
|
||||
it.addActor(closeImage)
|
||||
}
|
||||
icons.add(obsoletedIcon)
|
||||
}
|
||||
|
||||
for (resource in ruleset.tileResources.values.filter { it.revealedBy == techName }) {
|
||||
icons.add(ImageGetter.getResourcePortrait(resource.name, techIconSize))
|
||||
}
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values.asSequence()
|
||||
.filter { it.techRequired == techName }
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == civName }
|
||||
) {
|
||||
icons.add(ImageGetter.getImprovementPortrait(improvement.name, techIconSize))
|
||||
}
|
||||
|
||||
for (improvement in ruleset.tileImprovements.values.asSequence()
|
||||
.filter { it.uniqueObjects.any { u -> u.allParams.contains(techName) } }
|
||||
.filter { it.uniqueTo == null || it.uniqueTo == civName }
|
||||
) {
|
||||
icons.add(ImageGetter.getUniquePortrait(improvement.name, techIconSize))
|
||||
}
|
||||
|
||||
for (unique in tech.uniques) {
|
||||
icons.add(
|
||||
when (unique) {
|
||||
UniqueType.EnablesCivWideStatProduction.text.replace("civWideStat", "Gold" )
|
||||
-> ImageGetter.getConstructionPortrait("Gold", techIconSize)
|
||||
UniqueType.EnablesCivWideStatProduction.text.replace("civWideStat", "Science" )
|
||||
-> ImageGetter.getConstructionPortrait("Science", techIconSize)
|
||||
else -> ImageGetter.getUniquePortrait(unique, techIconSize)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
for (i in 0..4) {
|
||||
val icon = icons.getOrNull(i)
|
||||
if (icon != null)
|
||||
techEnabledIcons.add(icon)
|
||||
}
|
||||
TechnologyDescriptions.getTechEnabledIcons(tech, civ, techIconSize = 30f)
|
||||
.take(5)
|
||||
.forEach { techEnabledIcons.add(it) }
|
||||
|
||||
rightSide.add(techEnabledIcons)
|
||||
.colspan(2)
|
||||
|
@ -9,7 +9,6 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.GUI
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.managers.TechManager
|
||||
import com.unciv.models.UncivSound
|
||||
@ -370,7 +369,7 @@ class TechPickerScreen(
|
||||
|
||||
val previousSelectedTech = selectedTech
|
||||
selectedTech = tech
|
||||
descriptionLabel.setText(tech?.getDescription(civInfo.gameInfo.ruleset))
|
||||
descriptionLabel.setText(tech?.getDescription(civInfo))
|
||||
|
||||
if (!switchFromWorldScreen)
|
||||
return
|
||||
|
@ -229,7 +229,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
|
||||
val centerTable = Table()
|
||||
centerTable.add(tech.quote.toLabel().apply { wrap = true }).width(worldScreen.stage.width / 3)
|
||||
centerTable.add(ImageGetter.getTechIconPortrait(tech.name, 100f)).pad(20f)
|
||||
val descriptionScroll = ScrollPane(tech.getDescription(gameBasics).toLabel().apply { wrap = true })
|
||||
val descriptionScroll = ScrollPane(tech.getDescription(worldScreen.viewingCiv).toLabel().apply { wrap = true })
|
||||
centerTable.add(descriptionScroll).width(worldScreen.stage.width / 3).maxHeight(worldScreen.stage.height / 2)
|
||||
add(centerTable).row()
|
||||
add(getCloseButton(Constants.close))
|
||||
|
Loading…
x
Reference in New Issue
Block a user