From bb3335aaa854e83397c05210e3f1edbb3f3f748f Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Sun, 3 Sep 2023 08:32:28 +0200 Subject: [PATCH] Pedia Search (#9997) * Minor Civilopedia linting * Civilopedia Search Popup * Add missing "entire current complex ruleset" scope * Address comments * Wording change * Remove comment --- .../jsons/translations/template.properties | 7 + core/src/com/unciv/models/ruleset/Belief.kt | 2 - core/src/com/unciv/models/ruleset/Speed.kt | 2 +- .../screens/basescreen/TutorialController.kt | 4 +- .../CivilopediaCategories.kt | 65 +++--- .../civilopediascreen/CivilopediaScreen.kt | 58 +++--- .../CivilopediaSearchPopup.kt | 187 ++++++++++++++++++ .../civilopediascreen/FormattedLine.kt | 1 - .../civilopediascreen/ICivilopediaText.kt | 10 +- 9 files changed, 271 insertions(+), 65 deletions(-) create mode 100644 core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaSearchPopup.kt diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 634cc1d6e4..e4712f64cd 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -1604,6 +1604,13 @@ Toggle UI (World Screen only) = Overrides yields from underlying terrain = No yields = Mod: [modname] = +Search text: = +Invalid regular expression = +Mod filter: = +-Combined- = +Search! = +Results = +Nothing found! = # Policies diff --git a/core/src/com/unciv/models/ruleset/Belief.kt b/core/src/com/unciv/models/ruleset/Belief.kt index ab52d3fee9..a81bf2608c 100644 --- a/core/src/com/unciv/models/ruleset/Belief.kt +++ b/core/src/com/unciv/models/ruleset/Belief.kt @@ -7,7 +7,6 @@ import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.translations.tr import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen.Companion.showReligionInCivilopedia import com.unciv.ui.screens.civilopediascreen.FormattedLine -import kotlin.collections.ArrayList class Belief() : RulesetObject() { var type: BeliefType = BeliefType.None @@ -23,7 +22,6 @@ class Belief() : RulesetObject() { override fun makeLink() = "Belief/$name" override fun getCivilopediaTextHeader() = FormattedLine(name, icon = makeLink(), header = 2, color = if (type == BeliefType.None) "#e34a2b" else "") override fun getSortGroup(ruleset: Ruleset) = type.ordinal - override fun getIconName() = if (type == BeliefType.None) "Religion" else type.name override fun getCivilopediaTextLines(ruleset: Ruleset): List { return getCivilopediaTextLines(false) diff --git a/core/src/com/unciv/models/ruleset/Speed.kt b/core/src/com/unciv/models/ruleset/Speed.kt index 412d83a385..128487ff0c 100644 --- a/core/src/com/unciv/models/ruleset/Speed.kt +++ b/core/src/com/unciv/models/ruleset/Speed.kt @@ -56,7 +56,7 @@ class Speed : RulesetObject(), IsPartOfGameInfoSerialization { override fun getUniqueTarget(): UniqueTarget = UniqueTarget.Speed - override fun makeLink(): String = "GameSpeed/$name" + override fun makeLink(): String = "Speed/$name" override fun getCivilopediaTextHeader() = FormattedLine(name, header = 2) override fun getCivilopediaTextLines(ruleset: Ruleset) = sequence { yield(FormattedLine("General speed modifier: [${modifier * 100}]%${Fonts.turn}")) diff --git a/core/src/com/unciv/ui/screens/basescreen/TutorialController.kt b/core/src/com/unciv/ui/screens/basescreen/TutorialController.kt index b3ae7636c0..2ec70c6dc1 100644 --- a/core/src/com/unciv/ui/screens/basescreen/TutorialController.kt +++ b/core/src/com/unciv/ui/screens/basescreen/TutorialController.kt @@ -92,7 +92,9 @@ class TutorialController(screen: BaseScreen) { ) : INamed, SimpleCivilopediaText( sequenceOf(FormattedLine(extraImage = name.replace(' ', '_'))) + tutorial.civilopediaText.asSequence(), tutorial.steps?.asSequence() ?: emptySequence() - ) + ) { + override fun makeLink() = "Tutorial/$name" + } /** Get all Tutorials intended to be displayed in the Civilopedia * as a List of wrappers supporting INamed and ICivilopediaText diff --git a/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaCategories.kt b/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaCategories.kt index 64602a4d31..36394b0ff2 100644 --- a/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaCategories.kt +++ b/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaCategories.kt @@ -10,13 +10,16 @@ import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.tile.Terrain import com.unciv.models.ruleset.tile.TerrainType import com.unciv.models.ruleset.unit.UnitMovementType -import com.unciv.ui.images.ImageGetter -import com.unciv.ui.components.tilegroups.TileGroup -import com.unciv.ui.components.tilegroups.TileSetStrings -import com.unciv.ui.components.input.KeyCharAndCode import com.unciv.ui.components.extensions.setSize import com.unciv.ui.components.extensions.surroundWithCircle +import com.unciv.ui.components.input.KeyCharAndCode +import com.unciv.ui.components.tilegroups.TileGroup +import com.unciv.ui.components.tilegroups.TileSetStrings import com.unciv.ui.images.IconCircleGroup +import com.unciv.ui.images.ImageGetter +import com.unciv.ui.screens.basescreen.TutorialController +import com.unciv.models.ruleset.Belief as BaseBelief +import com.unciv.models.ruleset.unit.UnitType as BaseUnitType /** Encapsulates the knowledge on how to get an icon for each of the Civilopedia categories */ @@ -115,87 +118,105 @@ enum class CivilopediaCategories ( val hide: Boolean, // Omitted on CivilopediaScreen val getImage: ((name: String, size: Float) -> Actor?)?, val key: KeyCharAndCode = KeyCharAndCode.UNKNOWN, - val headerIcon: String - ) { + val headerIcon: String, + val getCategoryIterator: (ruleset: Ruleset, tutorialController: TutorialController) -> Collection +) { Building ("Buildings", false, CivilopediaImageGetters.construction, KeyCharAndCode('B'), - "OtherIcons/Cities" + "OtherIcons/Cities", + { ruleset, _ -> ruleset.buildings.values.filter { !it.isAnyWonder() } } ), Wonder ("Wonders", false, CivilopediaImageGetters.construction, KeyCharAndCode('W'), - "OtherIcons/Wonders" + "OtherIcons/Wonders", + { ruleset, _ -> ruleset.buildings.values.filter { it.isAnyWonder() } } ), Resource ("Resources", false, CivilopediaImageGetters.resource, KeyCharAndCode('R'), - "OtherIcons/Resources" + "OtherIcons/Resources", + { ruleset, _ -> ruleset.tileResources.values } ), Terrain ("Terrains", false, CivilopediaImageGetters.terrain, KeyCharAndCode('T'), - "OtherIcons/Terrains" + "OtherIcons/Terrains", + { ruleset, _ -> ruleset.terrains.values } ), Improvement ("Tile Improvements", false, CivilopediaImageGetters.improvement, KeyCharAndCode('T'), - "OtherIcons/Improvements" + "OtherIcons/Improvements", + { ruleset, _ -> ruleset.tileImprovements.values } ), Unit ("Units", false, CivilopediaImageGetters.construction, KeyCharAndCode('U'), - "OtherIcons/Shield" + "OtherIcons/Shield", + { ruleset, _ -> ruleset.units.values } ), UnitType ("Unit types", false, CivilopediaImageGetters.unitType, KeyCharAndCode('U'), - "UnitTypeIcons/UnitTypes" + "UnitTypeIcons/UnitTypes", + { ruleset, _ -> BaseUnitType.getCivilopediaIterator(ruleset) } ), Nation ("Nations", false, CivilopediaImageGetters.nation, KeyCharAndCode('N'), - "OtherIcons/Nations" + "OtherIcons/Nations", + { ruleset, _ -> ruleset.nations.values.filter { !it.isSpectator } } ), Technology ("Technologies", false, CivilopediaImageGetters.technology, KeyCharAndCode('T'), - "TechIcons/Philosophy" + "TechIcons/Philosophy", + { ruleset, _ -> ruleset.technologies.values } ), Promotion ("Promotions", false, CivilopediaImageGetters.promotion, KeyCharAndCode('P'), - "UnitPromotionIcons/Mobility" + "UnitPromotionIcons/Mobility", + { ruleset, _ -> ruleset.unitPromotions.values } ), Policy ("Policies", false, CivilopediaImageGetters.policy, KeyCharAndCode('P'), - "PolicyIcons/Constitution" + "PolicyIcons/Constitution", + { ruleset, _ -> ruleset.policies.values } ), Belief("Religions and Beliefs", false, CivilopediaImageGetters.belief, KeyCharAndCode('R'), - "ReligionIcons/Religion" + "ReligionIcons/Religion", + { ruleset, _ -> (ruleset.beliefs.values.asSequence() + + BaseBelief.getCivilopediaReligionEntry(ruleset)).toList() } ), Tutorial ("Tutorials", false, getImage = null, KeyCharAndCode(Input.Keys.F1), - "OtherIcons/ExclamationMark" + "OtherIcons/ExclamationMark", + { _, tutorialController -> tutorialController.getCivilopediaTutorials() } ), Difficulty ("Difficulty levels", false, getImage = null, KeyCharAndCode('D'), - "OtherIcons/Quickstart" + "OtherIcons/Quickstart", + { ruleset, _ -> ruleset.difficulties.values } ), Era ("Eras", false, getImage = null, KeyCharAndCode('D'), - "OtherIcons/Tyrannosaurus" + "OtherIcons/Tyrannosaurus", + { ruleset, _ -> ruleset.eras.values } ), Speed ("Speeds", false, getImage = null, KeyCharAndCode('S'), - "OtherIcons/Timer" + "OtherIcons/Timer", + { ruleset, _ -> ruleset.speeds.values } ); private fun getByOffset(offset: Int) = values()[(ordinal + count + offset) % count] diff --git a/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaScreen.kt b/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaScreen.kt index 39a3cf78e9..13dcbad69d 100644 --- a/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaScreen.kt +++ b/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaScreen.kt @@ -7,23 +7,23 @@ import com.badlogic.gdx.scenes.scene2d.Touchable import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.SplitPane import com.badlogic.gdx.scenes.scene2d.ui.Table -import com.unciv.Constants import com.unciv.UncivGame -import com.unciv.models.ruleset.Belief import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.unique.IHasUniques import com.unciv.models.ruleset.unique.UniqueType -import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.stats.INamed import com.unciv.models.translations.tr import com.unciv.ui.components.Fonts -import com.unciv.ui.components.input.KeyCharAndCode import com.unciv.ui.components.UncivTooltip.Companion.addTooltip import com.unciv.ui.components.extensions.colorFromRGB -import com.unciv.ui.components.input.onClick +import com.unciv.ui.components.extensions.toImageButton import com.unciv.ui.components.extensions.toLabel -import com.unciv.ui.components.extensions.toTextButton +import com.unciv.ui.components.input.KeyCharAndCode +import com.unciv.ui.components.input.KeyboardBinding +import com.unciv.ui.components.input.keyShortcuts +import com.unciv.ui.components.input.onActivation +import com.unciv.ui.components.input.onClick import com.unciv.ui.images.IconTextButton import com.unciv.ui.images.ImageGetter import com.unciv.ui.screens.basescreen.BaseScreen @@ -75,6 +75,11 @@ class CivilopediaScreen( private var currentEntry: String = "" private val currentEntryPerCategory = HashMap() + private val searchPopup by lazy { CivilopediaSearchPopup(this, tutorialController) { + selectLink(it) + } } + + /** Jump to a "link" selecting both category and entry * * Calls [selectCategory] with the substring before the first '/', @@ -160,7 +165,7 @@ class CivilopediaScreen( * @param name Entry (Ruleset object) name * @param noScrollAnimation Disable scroll animation */ - fun selectEntry(name: String, noScrollAnimation: Boolean = false) { + private fun selectEntry(name: String, noScrollAnimation: Boolean = false) { val entry = entryIndex[name] ?: return // fails: entrySelectScroll.scrollTo(0f, entry.y, 0f, entry.h, false, true) entrySelectScroll.scrollY = (entry.y + (entry.height - entrySelectScroll.height) / 2) @@ -195,7 +200,6 @@ class CivilopediaScreen( init { val imageSize = 50f - globalShortcuts.add(KeyCharAndCode.BACK) { game.popScreen() } val religionEnabled = showReligionInCivilopedia(ruleset) val victoryTypes = game.gameInfo?.gameParameters?.victoryTypes ?: ruleset.victories.keys @@ -209,32 +213,11 @@ class CivilopediaScreen( } } - fun getCategoryIterator(category: CivilopediaCategories): Collection = - when (category) { - CivilopediaCategories.Building -> ruleset.buildings.values.filter { !it.isAnyWonder() } - CivilopediaCategories.Wonder -> ruleset.buildings.values.filter { it.isAnyWonder() } - CivilopediaCategories.Resource -> ruleset.tileResources.values - CivilopediaCategories.Terrain -> ruleset.terrains.values - CivilopediaCategories.Improvement -> ruleset.tileImprovements.values - CivilopediaCategories.Unit -> ruleset.units.values - CivilopediaCategories.UnitType -> UnitType.getCivilopediaIterator(ruleset) - 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() - CivilopediaCategories.Era -> ruleset.eras.values - CivilopediaCategories.Speed -> ruleset.speeds.values - } - for (loopCategory in CivilopediaCategories.values()) { if (loopCategory.hide) continue if (!religionEnabled && loopCategory == CivilopediaCategories.Belief) continue categoryToEntries[loopCategory] = - getCategoryIterator(loopCategory) + loopCategory.getCategoryIterator(ruleset, tutorialController) .filter { (it as? IHasUniques)?.let { obj -> shouldBeDisplayed(obj) } ?: true } .map { CivilopediaEntry( (it as INamed).name, @@ -254,7 +237,6 @@ class CivilopediaScreen( val icon = if (categoryKey.headerIcon.isNotEmpty()) ImageGetter.getImage(categoryKey.headerIcon) else null val button = IconTextButton(categoryKey.label, icon) button.addTooltip(categoryKey.key) -// button.style = ImageButton.ImageButtonStyle(button.style) button.onClick { selectCategory(categoryKey) } val cell = buttonTable.add(button) categoryToButtons[categoryKey] = CategoryButtonInfo(button, currentX, cell.prefWidth) @@ -265,14 +247,18 @@ class CivilopediaScreen( buttonTableScroll = ScrollPane(buttonTable) buttonTableScroll.setScrollingDisabled(false, true) - val goToGameButton = Constants.close.toTextButton() - goToGameButton.onClick { - game.popScreen() - } + val searchButton = "OtherIcons/Search".toImageButton(imageSize - 16f, imageSize, skinStrings.skinConfig.baseColor, Color.GOLD) + searchButton.onActivation { searchPopup.open(true) } + searchButton.keyShortcuts.add(KeyboardBinding.Civilopedia) // "hit twice to search" + + val closeButton = "OtherIcons/Close".toImageButton(imageSize - 20f, imageSize, skinStrings.skinConfig.baseColor, Color.RED) + closeButton.onActivation { game.popScreen() } + closeButton.keyShortcuts.add(KeyCharAndCode.BACK) val topTable = Table() - topTable.add(goToGameButton).pad(10f) topTable.add(buttonTableScroll).growX() + topTable.add(searchButton).padLeft(10f) + topTable.add(closeButton).padLeft(10f).padRight(10f) topTable.width = stage.width topTable.layout() diff --git a/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaSearchPopup.kt b/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaSearchPopup.kt new file mode 100644 index 0000000000..460a0636d2 --- /dev/null +++ b/core/src/com/unciv/ui/screens/civilopediascreen/CivilopediaSearchPopup.kt @@ -0,0 +1,187 @@ +package com.unciv.ui.screens.civilopediascreen + +import com.badlogic.gdx.scenes.scene2d.Actor +import com.badlogic.gdx.scenes.scene2d.ui.Cell +import com.badlogic.gdx.scenes.scene2d.ui.SelectBox +import com.badlogic.gdx.scenes.scene2d.ui.TextButton +import com.badlogic.gdx.utils.Align +import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.RulesetCache +import com.unciv.models.stats.INamed +import com.unciv.models.translations.tr +import com.unciv.ui.components.ExpanderTab +import com.unciv.ui.components.UncivTextField +import com.unciv.ui.components.extensions.disable +import com.unciv.ui.components.extensions.enable +import com.unciv.ui.components.extensions.toLabel +import com.unciv.ui.components.input.KeyCharAndCode +import com.unciv.ui.components.input.onClick +import com.unciv.ui.popups.Popup +import com.unciv.ui.popups.ToastPopup +import com.unciv.ui.screens.basescreen.BaseScreen +import com.unciv.ui.screens.basescreen.TutorialController +import com.unciv.utils.Concurrency +import com.unciv.utils.launchOnGLThread +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import com.badlogic.gdx.utils.Array as GdxArray + +class CivilopediaSearchPopup( + private val pediaScreen: CivilopediaScreen, + private val tutorialController: TutorialController, + private val linkAction: (String) -> Unit +) : Popup(pediaScreen) { + private var ruleset = pediaScreen.ruleset + private val searchText = UncivTextField.create("") // Always focused, "hint" never seen + private val modSelect = ModSelectBox() + private lateinit var resultExpander: ExpanderTab + private val resultCell: Cell + private val searchButton: TextButton + + private var searchJob: Job? = null + private var checkLine: (String) -> Boolean = { _ -> false } + + init { + searchText.maxLength = 100 + + add("Search text:".toLabel()) + add(searchText).growX().row() + add("Mod filter:".toLabel()) + add(modSelect).growX().row() + resultCell = add().colspan(2).growX() + row() + + searchButton = addButton("Search!", KeyCharAndCode.RETURN) { + startSearch(searchText.text) + }.actor + addCloseButton() + showListeners.add { + keyboardFocus = searchText + searchText.selectAll() + } + closeListeners.add { + if (isSearchRunning()) searchJob!!.cancel() + } + } + + private fun isSearchRunning() = searchJob?.isActive == true + + private fun startSearch(text: String) { + searchButton.disable() + + @Suppress("LiftReturnOrAssignment") + if (text.isEmpty()) { + checkLine = { true } + } else if (".*" in text || '\\' in text || '|' in text) { + try { + val regex = Regex(text, RegexOption.IGNORE_CASE) + checkLine = { regex.containsMatchIn(it) } + } catch (ex: Exception) { + ToastPopup("Invalid regular expression", pediaScreen, 4000).open(true) + searchButton.enable() + return + } + } else { + val words = text.split(' ').toSet() + checkLine = { line -> words.all { line.contains(it, ignoreCase = true) } } + } + + ruleset = modSelect.selectedRuleset() + + if (::resultExpander.isInitialized) { + resultExpander.innerTable.clear() + } else { + resultExpander = ExpanderTab("Results") {} + resultCell.setActor(resultExpander) + resultExpander.innerTable.defaults().growX().pad(2f) + } + + searchJob = Concurrency.run("PediaSearch") { + searchLoop() + } + searchJob!!.invokeOnCompletion { + searchJob = null + Concurrency.runOnGLThread { + finishSearch() + } + } + } + + private fun CoroutineScope.searchLoop() { + for (category in CivilopediaCategories.values()) { + if (!isActive) break + if (category.hide) continue + if (!ruleset.modOptions.isBaseRuleset && category == CivilopediaCategories.Tutorial) + continue // Search tutorials only when the mod filter is a base ruleset + for (entry in category.getCategoryIterator(ruleset, tutorialController)) { + if (!isActive) break + if (entry !is INamed) continue + if (!ruleset.modOptions.isBaseRuleset) { + val sort = entry.getSortGroup(ruleset) + if (category == CivilopediaCategories.UnitType && sort < 2) + continue // Search "Domain:" entries only when the mod filter is a base ruleset + if (category == CivilopediaCategories.Belief && sort == 0) + continue // Search "Religions" from `getCivilopediaReligionEntry` only when the mod filter is a base ruleset + } + searchEntry(entry) + } + } + } + + private fun CoroutineScope.searchEntry(entry: ICivilopediaText) { + val scope = sequence { + entry.getCivilopediaTextHeader()?.let { yield(it) } + yieldAll(entry.civilopediaText) + yieldAll(entry.getCivilopediaTextLines(ruleset)) + } + for (line in scope) { + if (!isActive) break + val lineText = line.text.tr(hideIcons = true) + if (!checkLine(lineText)) continue + addResult(entry) + break + } + } + + private fun CoroutineScope.addResult(entry: ICivilopediaText) { + launchOnGLThread { + val actor = entry.getIconName().toLabel(alignment = Align.left) + val link = entry.makeLink() + resultExpander.innerTable.add(actor).row() + actor.onClick { + linkAction(link) + close() + } + } + } + + private fun finishSearch() { + searchButton.enable() + if (!resultExpander.innerTable.cells.isEmpty) return + val nothingFound = FormattedLine("Nothing found!", color = "#f53", header = 3, centered = true) + .render(0f) + resultExpander.innerTable.add(nothingFound) + } + + class ModSelectEntry(val key: String, val translate: Boolean = false) { + override fun toString() = if (translate) key.tr() else key + } + + private inner class ModSelectBox : SelectBox(BaseScreen.skin) { + init { + val mods = pediaScreen.ruleset.mods + val entries = GdxArray(mods.size + 1) + entries.add(ModSelectEntry("-Combined-", true)) + // This intersect is needed when pedia was called from the MainMenuScreen with an easter egg ruleset active - + // they are not in the cache and have their elements not marked with originRuleset anyway. + for (mod in mods.intersect(RulesetCache.keys)) entries.add(ModSelectEntry(mod)) + items = entries + selectedIndex = 0 + } + + fun selectedRuleset(): Ruleset = + if (selectedIndex == 0) pediaScreen.ruleset + else RulesetCache[selected.key]!! // `!!` guarded by the intersect above + } +} diff --git a/core/src/com/unciv/ui/screens/civilopediascreen/FormattedLine.kt b/core/src/com/unciv/ui/screens/civilopediascreen/FormattedLine.kt index b81aae1812..4be65ff1c1 100644 --- a/core/src/com/unciv/ui/screens/civilopediascreen/FormattedLine.kt +++ b/core/src/com/unciv/ui/screens/civilopediascreen/FormattedLine.kt @@ -15,7 +15,6 @@ import com.unciv.ui.components.ColorMarkupLabel import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.images.ImageGetter import com.unciv.ui.screens.basescreen.BaseScreen -import com.unciv.ui.components.extensions.toLabel import com.unciv.utils.Log import kotlin.math.max diff --git a/core/src/com/unciv/ui/screens/civilopediascreen/ICivilopediaText.kt b/core/src/com/unciv/ui/screens/civilopediascreen/ICivilopediaText.kt index a854c8016a..ab7aa8a4c0 100644 --- a/core/src/com/unciv/ui/screens/civilopediascreen/ICivilopediaText.kt +++ b/core/src/com/unciv/ui/screens/civilopediascreen/ICivilopediaText.kt @@ -4,9 +4,9 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.UncivGame import com.unciv.models.ruleset.IRulesetObject import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.RulesetObject import com.unciv.models.stats.INamed - /** Addon common to most ruleset game objects managing civilopedia display * * ### Usage: @@ -91,7 +91,13 @@ interface ICivilopediaText { return SimpleCivilopediaText(newLines.toList()) } - /** Create the correct string for a Civilopedia link */ + /** Create the correct string for a Civilopedia link. + * + * To actually make it work both as link and as icon identifier, return a string in the form + * category/entryname where `category` **must** correspond exactly to either name or label of + * the correct [CivilopediaCategories] member. `entryname` must equal the + * [ruleset object name][RulesetObject] as defined by the [INamed] interface. + */ fun makeLink(): String /** Overrides alphabetical sorting in Civilopedia