diff --git a/core/src/com/unciv/logic/city/IConstruction.kt b/core/src/com/unciv/logic/city/IConstruction.kt index bdb42bdb7f..5b1e77e24d 100644 --- a/core/src/com/unciv/logic/city/IConstruction.kt +++ b/core/src/com/unciv/logic/city/IConstruction.kt @@ -1,6 +1,7 @@ package com.unciv.logic.city import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.models.ruleset.IHasUniques import com.unciv.models.ruleset.Unique import com.unciv.models.stats.INamed import com.unciv.models.stats.Stat @@ -15,10 +16,8 @@ interface IConstruction : INamed { fun getResourceRequirements(): HashMap } -interface INonPerpetualConstruction : IConstruction, INamed { +interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques { val hurryCostModifier: Int - val uniqueObjects: List - val uniques: List fun getProductionCost(civInfo: CivilizationInfo): Int fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int? diff --git a/core/src/com/unciv/models/ruleset/Belief.kt b/core/src/com/unciv/models/ruleset/Belief.kt index a214dfa905..1156a17e12 100644 --- a/core/src/com/unciv/models/ruleset/Belief.kt +++ b/core/src/com/unciv/models/ruleset/Belief.kt @@ -6,11 +6,11 @@ import com.unciv.ui.civilopedia.FormattedLine import com.unciv.ui.civilopedia.ICivilopediaText import java.util.ArrayList -class Belief : INamed, ICivilopediaText { +class Belief : INamed, ICivilopediaText, IHasUniques { override var name: String = "" var type: BeliefType = BeliefType.None - var uniques = ArrayList() - val uniqueObjects: List by lazy { uniques.map { Unique(it) } } + override var uniques = ArrayList() + override val uniqueObjects: List by lazy { uniques.map { Unique(it) } } override var civilopediaText = listOf() diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index de83ee32c9..22bf8d3bac 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -69,11 +69,12 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText { @Deprecated("As of 3.15.16 - replaced with 'Provides a free [buildingName] [cityFilter]'") var providesFreeBuilding: String? = null override var uniques = ArrayList() - var replacementTextForUniques = "" override val uniqueObjects: List by lazy { uniques.map { Unique(it) } } + var replacementTextForUniques = "" override var civilopediaText = listOf() + fun getShortDescription(ruleset: Ruleset): String { // should fit in one line val infoList = mutableListOf() val str = getStats(null).toString() diff --git a/core/src/com/unciv/models/ruleset/IHasUniques.kt b/core/src/com/unciv/models/ruleset/IHasUniques.kt new file mode 100644 index 0000000000..f2564cbb07 --- /dev/null +++ b/core/src/com/unciv/models/ruleset/IHasUniques.kt @@ -0,0 +1,9 @@ +package com.unciv.models.ruleset + +/** + * Common interface for all 'ruleset objects' that have Uniques, like BaseUnit, Nation, etc. + */ +interface IHasUniques { + var uniques: ArrayList // Can not be a hashset as that would remove doubles + val uniqueObjects: List +} diff --git a/core/src/com/unciv/models/ruleset/Nation.kt b/core/src/com/unciv/models/ruleset/Nation.kt index df664c9ce4..759a039914 100644 --- a/core/src/com/unciv/models/ruleset/Nation.kt +++ b/core/src/com/unciv/models/ruleset/Nation.kt @@ -3,12 +3,9 @@ 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 @@ -22,7 +19,7 @@ enum class VictoryType { Scientific, } -class Nation : INamed, ICivilopediaText { +class Nation : INamed, ICivilopediaText, IHasUniques { override lateinit var name: String var leaderName = "" @@ -43,8 +40,8 @@ class Nation : INamed, ICivilopediaText { lateinit var outerColor: List var uniqueName = "" - var uniques = HashSet() - val uniqueObjects: List by lazy { uniques.map { Unique(it) } } + override var uniques = ArrayList() + override val uniqueObjects: List by lazy { uniques.map { Unique(it) } } var uniqueText = "" var innerColor: List? = null var startBias = ArrayList() diff --git a/core/src/com/unciv/models/ruleset/Policy.kt b/core/src/com/unciv/models/ruleset/Policy.kt index db37b719c1..3498723e79 100644 --- a/core/src/com/unciv/models/ruleset/Policy.kt +++ b/core/src/com/unciv/models/ruleset/Policy.kt @@ -2,12 +2,12 @@ package com.unciv.models.ruleset import com.unciv.models.stats.INamed -open class Policy : INamed { +open class Policy : INamed, IHasUniques { lateinit var branch: PolicyBranch // not in json - added in gameBasics override lateinit var name: String - var uniques: ArrayList = ArrayList() - val uniqueObjects:List by lazy { uniques.map { Unique(it) } } + override var uniques: ArrayList = ArrayList() + override val uniqueObjects: List by lazy { uniques.map { Unique(it) } } var row: Int = 0 var column: Int = 0 var requires: ArrayList? = null diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index 3f9be944e0..00c903fac5 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -41,7 +41,7 @@ class ModOptions { var modSize = 0 val maxXPfromBarbarians = 30 - var uniques = HashSet() + var uniques = HashSet() // No reason for now to use IHasUniques here, in that case needs to change to ArrayList } class Ruleset { diff --git a/core/src/com/unciv/models/ruleset/tech/Technology.kt b/core/src/com/unciv/models/ruleset/tech/Technology.kt index 1bf11d2ece..e570535284 100644 --- a/core/src/com/unciv/models/ruleset/tech/Technology.kt +++ b/core/src/com/unciv/models/ruleset/tech/Technology.kt @@ -3,6 +3,7 @@ package com.unciv.models.ruleset.tech import com.unciv.UncivGame import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.ruleset.Building +import com.unciv.models.ruleset.IHasUniques import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Unique import com.unciv.models.translations.tr @@ -13,14 +14,14 @@ import com.unciv.ui.civilopedia.FormattedLine import com.unciv.ui.utils.Fonts import java.util.* -class Technology: INamed, ICivilopediaText { +class Technology: INamed, ICivilopediaText, IHasUniques { override lateinit var name: String var cost: Int = 0 var prerequisites = HashSet() - var uniques = ArrayList() - val uniqueObjects: List by lazy { uniques.map { Unique(it) } } + override var uniques = ArrayList() + override val uniqueObjects: List by lazy { uniques.map { Unique(it) } } var column: TechColumn? = null // The column that this tech is in the tech tree var row: Int = 0 diff --git a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt index 4c354843a9..d612dc18bd 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt @@ -3,6 +3,7 @@ package com.unciv.models.ruleset.tile import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.ruleset.Belief import com.unciv.logic.map.RoadStatus +import com.unciv.models.ruleset.IHasUniques import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Unique import com.unciv.models.stats.NamedStats @@ -12,13 +13,13 @@ import com.unciv.ui.civilopedia.ICivilopediaText import java.util.* import kotlin.math.roundToInt -class TileImprovement : NamedStats(), ICivilopediaText { +class TileImprovement : NamedStats(), ICivilopediaText, IHasUniques { var terrainsCanBeBuiltOn: Collection = ArrayList() var techRequired: String? = null var uniqueTo:String? = null - var uniques = ArrayList() - val uniqueObjects:List by lazy { uniques.map { Unique(it) } } + override var uniques = ArrayList() + override val uniqueObjects: List by lazy { uniques.map { Unique(it) } } val shortcutKey: Char? = null val turnsToBuild: Int = 0 // This is the base cost. diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index d0926c51e3..f156a01c7b 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -12,20 +12,19 @@ import com.unciv.models.ruleset.Unique import com.unciv.models.stats.INamed import com.unciv.models.stats.Stat 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 java.util.* import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.collections.HashSet -import kotlin.math.pow // This is BaseUnit because Unit is already a base Kotlin class and to avoid mixing the two up /** This is the basic info of the units, as specified in Units.json, in contrast to MapUnit, which is a specific unit of a certain type that appears on the map */ -class BaseUnit : INamed, INonPerpetualConstruction, CivilopediaText() { +class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText { override lateinit var name: String var cost: Int = 0 diff --git a/core/src/com/unciv/models/ruleset/unit/Promotion.kt b/core/src/com/unciv/models/ruleset/unit/Promotion.kt index 22e8530caf..8c97f8b62c 100644 --- a/core/src/com/unciv/models/ruleset/unit/Promotion.kt +++ b/core/src/com/unciv/models/ruleset/unit/Promotion.kt @@ -1,5 +1,6 @@ package com.unciv.models.ruleset.unit +import com.unciv.models.ruleset.IHasUniques import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Unique import com.unciv.models.stats.INamed @@ -8,18 +9,18 @@ import com.unciv.ui.civilopedia.FormattedLine import com.unciv.ui.civilopedia.ICivilopediaText -class Promotion : INamed, ICivilopediaText { +class Promotion : INamed, ICivilopediaText, IHasUniques { override lateinit var name: String var prerequisites = listOf() var effect = "" var unitTypes = listOf() // The json parser wouldn't agree to deserialize this as a list of UnitTypes. =( - var uniques = ArrayList() + override var uniques = ArrayList() fun uniquesWithEffect() = sequence { if (effect.isNotEmpty()) yield(effect) yieldAll(uniques) } - val uniqueObjects: List by lazy { uniquesWithEffect().map { Unique(it) }.toList() } + override val uniqueObjects: List by lazy { uniquesWithEffect().map { Unique(it) }.toList() } override var civilopediaText = listOf() diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt index a0f875bf05..0872e3729c 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt @@ -31,7 +31,7 @@ class CivilopediaScreen( * @param name From [Ruleset] object [INamed.name] * @param description Multiline text * @param image Icon for button - * @param flavour [CivilopediaText] + * @param flavour [ICivilopediaText] * @param y Y coordinate for scrolling to * @param height Cell height */ @@ -269,12 +269,10 @@ class CivilopediaScreen( categoryToEntries[CivilopediaCategories.Tutorial] = tutorialController.getCivilopediaTutorials() .map { CivilopediaEntry( - it.key.replace("_", " "), + it.name, "", // CivilopediaCategories.Tutorial.getImage?.invoke(it.name, imageSize) - flavour = SimpleCivilopediaText( - sequenceOf(FormattedLine(extraImage = it.key)), - it.value.asSequence(), true) + flavour = it ) } diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt index dc6458f7f6..b61f410d05 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt @@ -258,7 +258,7 @@ class FormattedLine ( var iconCount = 0 val iconSize = max(minIconSize, fontSize * 1.5f) if (linkType != LinkType.None && !noLinkImages) { - table.add( ImageGetter.getImage(linkImage) ).size(iconSize).padRight(iconPad) + table.add(ImageGetter.getImage(linkImage)).size(iconSize).padRight(iconPad) iconCount++ } if (!noLinkImages) @@ -385,22 +385,23 @@ 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() } /** Storage class for instantiation of the simplest form containing only the lines collection */ -class SimpleCivilopediaText(lines: List, val isComplete: Boolean = false) : CivilopediaText() { - init { - civilopediaText = lines - } - override fun hasCivilopediaTextLines() = true - override fun replacesCivilopediaDescription() = isComplete - override fun makeLink() = "" - +open class SimpleCivilopediaText( + override var civilopediaText: List, + val isComplete: Boolean = false +) : ICivilopediaText { constructor(strings: Sequence, isComplete: Boolean = false) : this( strings.map { FormattedLine(it) }.toList(), isComplete) constructor(first: Sequence, strings: Sequence, isComplete: Boolean = false) : this( (first + strings.map { FormattedLine(it) }).toList(), isComplete) + + override fun hasCivilopediaTextLines() = true + override fun replacesCivilopediaDescription() = isComplete + override fun makeLink() = "" } /** Addon common to most ruleset game objects managing civilopedia display @@ -424,7 +425,7 @@ interface ICivilopediaText { * @return A [FormattedLine] that will be inserted on top */ fun getCivilopediaTextHeader(): FormattedLine? = - if (this is INamed) FormattedLine(name, icon=makeLink(), header = 2) + if (this is INamed) FormattedLine(name, icon = makeLink(), header = 2) else null /** Generate automatic lines from object metadata. @@ -456,7 +457,7 @@ interface ICivilopediaText { * @param ruleset The current ruleset for the Civilopedia viewer * @return A new CivilopediaText instance containing original [civilopediaText] lines merged with those from [getCivilopediaTextHeader] and [getCivilopediaTextLines] calls. */ - fun assembleCivilopediaText(ruleset: Ruleset): CivilopediaText { + fun assembleCivilopediaText(ruleset: Ruleset): ICivilopediaText { val outerLines = civilopediaText.iterator() val newLines = sequence { var middleDone = false diff --git a/core/src/com/unciv/ui/tutorials/TutorialController.kt b/core/src/com/unciv/ui/tutorials/TutorialController.kt index f960bc1848..185239fc53 100644 --- a/core/src/com/unciv/ui/tutorials/TutorialController.kt +++ b/core/src/com/unciv/ui/tutorials/TutorialController.kt @@ -4,6 +4,9 @@ import com.badlogic.gdx.utils.Array import com.unciv.JsonParser import com.unciv.UncivGame import com.unciv.models.Tutorial +import com.unciv.models.stats.INamed +import com.unciv.ui.civilopedia.FormattedLine +import com.unciv.ui.civilopedia.SimpleCivilopediaText import com.unciv.ui.utils.CameraStageBaseScreen class TutorialController(screen: CameraStageBaseScreen) { @@ -45,11 +48,33 @@ class TutorialController(screen: CameraStageBaseScreen) { } } - fun getCivilopediaTutorials(): Map> { - return tutorials.filter { Tutorial.findByName(it.key)!!.isCivilopedia } - } - private fun getTutorial(tutorial: Tutorial): Array { return tutorials[tutorial.value] ?: Array() } + + /** Wrapper for a Tutorial, supports INamed and ICivilopediaText, + * and already provisions for the display of an ExtraImage on top. + * @param rawName from Tutorial.value, with underscores (this wrapper replaces them with spaces) + * @param lines Array of lines exactly as stored in a TutorialController.tutorials MapEntry + */ + class CivilopediaTutorial( + rawName: String, + lines: Array + ) : INamed, SimpleCivilopediaText( + sequenceOf(FormattedLine(extraImage = rawName)), + lines.asSequence(), + true + ) { + override var name = rawName.replace("_", " ") + } + + /** Get all Tutorials intended to be displayed in the Civilopedia + * as a List of wrappers supporting INamed and ICivilopediaText + */ + fun getCivilopediaTutorials() = + tutorials.filter { + Tutorial.findByName(it.key)!!.isCivilopedia + }.map { + CivilopediaTutorial(it.key, it.value) + } }