mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-22 02:07:43 +07:00
Spruced up Civilopedia - phase 3 - Interface, flavour text, new Tutorial (#4334)
This commit is contained in:
parent
0776d9b36e
commit
8fdb4f413e
BIN
android/ImagesToNotAddToGame/WorldScreenHelp.xcf
Normal file
BIN
android/ImagesToNotAddToGame/WorldScreenHelp.xcf
Normal file
Binary file not shown.
BIN
android/assets/ExtraImages/World_Screen.png
Normal file
BIN
android/assets/ExtraImages/World_Screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 434 KiB |
@ -100,14 +100,20 @@
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "The Wheel",
|
||||
"uniques": ["Can be built outside your borders", "Costs [1] gold per turn when in your territory"],
|
||||
"shortcutKey": "R"
|
||||
"shortcutKey": "R",
|
||||
"civilopediaText": [
|
||||
{text:"Reduces movement cost to ½ if the other tile also has a Road or Railroad"},
|
||||
{text:"Reduces movement cost to ⅓ with Machinery",link:"Technology/Machinery"},
|
||||
{text:"Requires Engineering to bridge rivers",link:"Technology/Engineering"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Railroad",
|
||||
"turnsToBuild": 4,
|
||||
"techRequired": "Railroad",
|
||||
"uniques": ["Can be built outside your borders", "Costs [2] gold per turn when in your territory"],
|
||||
"shortcutKey": "R"
|
||||
"shortcutKey": "R",
|
||||
"civilopediaText": [{text:"Reduces movement cost to ⅒ if the other tile also has a Railroad"}]
|
||||
},
|
||||
|
||||
// Removals
|
||||
@ -117,7 +123,8 @@
|
||||
"terrainsCanBeBuiltOn": ["Forest"],
|
||||
"techRequired": "Mining",
|
||||
"uniques": ["Can be built outside your borders"],
|
||||
"shortcutKey": "X"
|
||||
"shortcutKey": "X",
|
||||
"civilopediaText": [{text:"Provides a one-time Production bonus depending on distance to the closest city once finished"}]
|
||||
},
|
||||
{
|
||||
"name": "Remove Jungle",
|
||||
@ -184,7 +191,8 @@
|
||||
},
|
||||
{
|
||||
"name": "Citadel",
|
||||
"uniques": ["Gives a defensive bonus of [100]%", "Deal 30 damage to adjacent enemy units", "Great Improvement", "Can be built just outside your borders"]
|
||||
"uniques": ["Gives a defensive bonus of [100]%", "Deal 30 damage to adjacent enemy units", "Great Improvement", "Can be built just outside your borders"],
|
||||
"civilopediaText": [{text:"Constructing it will take over the tiles around it and assign them to your closest city"}]
|
||||
},
|
||||
|
||||
//Civilization unique improvements
|
||||
@ -210,8 +218,12 @@
|
||||
"shortcutKey": "F"
|
||||
},
|
||||
|
||||
{ "name": "Ancient ruins", "uniques": ["Unpillagable"] },
|
||||
{ "name": "City ruins", "uniques": ["Unpillagable"] },
|
||||
{ "name": "City center", "uniques": ["Unpillagable"] },
|
||||
{ "name": "Barbarian encampment", "uniques": ["Unpillagable"] }
|
||||
{ "name": "Ancient ruins", "uniques": ["Unpillagable"],
|
||||
"civilopediaText": [{text:"Ancient ruins provide a one-time random bonus when explored"}] },
|
||||
{ "name": "City ruins", "uniques": ["Unpillagable"],
|
||||
"civilopediaText": [{text:"A bleak reminder of the destruction wreaked by War"}] },
|
||||
{ "name": "City center", "uniques": ["Unpillagable"],
|
||||
"civilopediaText": [{text:"Marks the center of a city"},{text:"Appearance changes with the technological era of the owning civilization"}] },
|
||||
{ "name": "Barbarian encampment", "uniques": ["Unpillagable"],
|
||||
"civilopediaText": [{text:"Home to uncivilized barbarians, will spawn a hostile unit from time to time"}] }
|
||||
]
|
||||
|
@ -132,5 +132,41 @@
|
||||
"On the world screen the hotkeys are as follows:", "Space or 'N' - Next unit or turn\n'E' - Empire overview (last viewed page)\n'+', '-' - Zoom in / out\nHome - center on capital",
|
||||
"F1 - Open Civilopedia\nF2 - Empire overview Trades\nF3 - Empire overview Units\nF4 - Empire overview Diplomacy\nF5 - Social policies\nF6 - Technologies\nF7 - Empire overview Cities\nF8 - Victory Progress\nF9 - Empire overview Stats\nF10 - Empire overview Resources\nF11 - Quicksave\nF12 - Quickload",
|
||||
"Ctrl-R - Toggle tile resource display\nCtrl-Y - Toggle tile yield display\nCtrl-O - Game options\nCtrl-S - Save game\nCtrl-L - Load game"
|
||||
],
|
||||
"World_Screen": [
|
||||
"",
|
||||
"This is where you spend most of your time playing Unciv. See the world, control your units, access other screens from here.",
|
||||
"",
|
||||
"①: The menu button - civilopedia, save, load, options...",
|
||||
"②: The player/nation whose turn it is - click for diplomacy overview.",
|
||||
"③: The Technology Button - shows the tech tree which allows viewing or researching technologies.",
|
||||
"④: The Social Policies Button - shows enacted and selectable policies, and with enough culture points you can enact new ones.",
|
||||
"⑤: The Diplomacy Button - shows the diplomacy manager where you can talk to other civilizations.",
|
||||
"⑥: Unit Action Buttons - while a unit is selected its possible actions appear here.",
|
||||
"⑦: The unit/city info pane - shows information about a selected unit or city.",
|
||||
"⑧: The name (and unit icon) of the selected unit or city, with current health if wounded.",
|
||||
"⑨: The arrow buttons allow jumping to the next/previous unit.",
|
||||
"⑩: For a selected unit, its promotions appear here, and clicking leads to the promotions screen for that unit.",
|
||||
"⑪: Remaining/per turn movement points, strength and experience / XP needed for promotion. For cities, you get its combat strength.",
|
||||
"⑫: This button closes the selected unit/city info pane.",
|
||||
"⑬: This pane appears when you order a uint to attack an enemy. On top are attacker and defender with their respective base strengths.",
|
||||
"⑭: Below that are strength boni or mali and health bars projecting before / after the attack.",
|
||||
"⑮: The Attack Button - let blood flow!",
|
||||
"⑯: The minimap shows an overview over the world, with known cities, terrain and fog of war. Clicking will position the main map.",
|
||||
"⑰: To the side of the minimap are display feature toggling buttons - tile yield, worked indicator, show/hide resources. These mirror setting on the options screen and are hidden if you deactivate the minimap.",
|
||||
"⑱: Tile information for the selected hex - current or potential yield, terrain, effects, present units, city located there and such.",
|
||||
"⑲: Notifications - what happened during the last 'next turn' phase. Some are clickable to show a relevant place on the map, some even show several when you click repeatedly.",
|
||||
"⑳: The Next Turn Button - unless there are things to do, in which case the label changes to 'next unit', 'pick policy' and so on.",
|
||||
"ⓐ: The overview button leads to the empire overview screen with various tabs (the last one viewed is remembered) holding vital information about the state of your civilization in world.",
|
||||
"ⓑ: The culture icon shows accumulated culture and culture needed for the next policy - in this case, the exclamation mark tells us a next policy can be enacted. Clicking is another way to the policies manager.",
|
||||
"ⓒ: Your known strategic resources are displayed here with the available (usage already deducted) number - click to go to the resources overview screen.",
|
||||
"ⓓ: Happiness/unhappiness balance and either golden age with turns left or accumulated happines with amount needed for a golden age is shown next to the smiley. Clicking also leads to the resources overview screen as luxury resources are a way to improve happiness.",
|
||||
"ⓔ: The science icon shows the number of science points produced per turn. Clicking leads to the technology tree.",
|
||||
"ⓕ: Number of turns played with translation into calendar years. Click to see the victory overview.",
|
||||
"ⓖ: The number of gold coins in your treasury and income. Clicks lead to the Stats overview screen.",
|
||||
"ⓧ: In the center of all this - the world map! Here, the \"X\" marks a spot outside the map. Yes, unless the wrap option was used, Unciv worlds are flat. Don't worry, your ships won't fall off the edge.",
|
||||
"ⓨ: By the way, here's how an empire border looks like - it's in the national colours of the nation owning the territory.",
|
||||
"ⓩ: And this is the red targeting circle that led to the attack pane back under ⑬.",
|
||||
"What you don't see: The phone/tablet's back button will pop the question whether you wish to leave Unciv and go back to Real Life. On desktop versions, you can use the ESC key.",
|
||||
]
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ enum class Tutorial(val value: String, val isCivilopedia: Boolean = !value.start
|
||||
CityExpansion("City_Expansion"),
|
||||
GreatPeople("Great_People"),
|
||||
RemovingTerrainFeatures("Removing_Terrain_Features"),
|
||||
Keyboard("Keyboard")
|
||||
Keyboard("Keyboard"),
|
||||
WorldScreen("World_Screen"),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
@ -5,10 +5,12 @@ import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.Unique
|
||||
import com.unciv.models.stats.NamedStats
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.civilopedia.ICivilopediaText
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class TileImprovement : NamedStats() {
|
||||
class TileImprovement : NamedStats(), ICivilopediaText {
|
||||
|
||||
var terrainsCanBeBuiltOn: Collection<String> = ArrayList()
|
||||
var techRequired: String? = null
|
||||
@ -18,6 +20,9 @@ class TileImprovement : NamedStats() {
|
||||
val shortcutKey: Char? = null
|
||||
val turnsToBuild: Int = 0 // This is the base cost.
|
||||
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
|
||||
fun getTurnsToBuild(civInfo: CivilizationInfo): Int {
|
||||
var realTurnsToBuild = turnsToBuild.toFloat() * civInfo.gameInfo.gameParameters.gameSpeed.modifier
|
||||
for (unique in civInfo.getMatchingUniques("-[]% tile improvement construction time")) {
|
||||
|
@ -28,6 +28,7 @@ class CivilopediaScreen(
|
||||
* @param name From [Ruleset] object [INamed.name]
|
||||
* @param description Multiline text
|
||||
* @param image Icon for button
|
||||
* @param flavour [CivilopediaText]
|
||||
* @param y Y coordinate for scrolling to
|
||||
* @param height Cell height
|
||||
*/
|
||||
@ -35,10 +36,11 @@ class CivilopediaScreen(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val image: Actor? = null,
|
||||
val flavour: ICivilopediaText? = null,
|
||||
val y: Float = 0f, // coordinates of button cell used to scroll to entry
|
||||
val height: Float = 0f
|
||||
) {
|
||||
fun withCoordinates(y: Float, height: Float) = CivilopediaEntry(name, description, image, y, height)
|
||||
fun withCoordinates(y: Float, height: Float) = CivilopediaEntry(name, description, image, flavour, y, height)
|
||||
}
|
||||
|
||||
private val categoryToEntries = LinkedHashMap<CivilopediaCategories, Collection<CivilopediaEntry>>()
|
||||
@ -48,6 +50,7 @@ class CivilopediaScreen(
|
||||
private val entrySelectTable = Table().apply { defaults().pad(6f).left() }
|
||||
private val entrySelectScroll: ScrollPane
|
||||
private val descriptionLabel = "".toLabel()
|
||||
private val flavourTable = Table()
|
||||
|
||||
private var currentCategory: CivilopediaCategories = CivilopediaCategories.Tutorial
|
||||
private var currentEntry: String = ""
|
||||
@ -84,6 +87,7 @@ class CivilopediaScreen(
|
||||
entrySelectTable.clear()
|
||||
entryIndex.clear()
|
||||
descriptionLabel.setText("")
|
||||
flavourTable.clear()
|
||||
|
||||
for (button in categoryToButtons.values) button.color = Color.WHITE
|
||||
if (category !in categoryToButtons) return // defense against being passed a bad selector
|
||||
@ -134,7 +138,22 @@ class CivilopediaScreen(
|
||||
}
|
||||
private fun selectEntry(entry: CivilopediaEntry) {
|
||||
currentEntry = entry.name
|
||||
descriptionLabel.setText(entry.description)
|
||||
if(entry.flavour != null && entry.flavour.replacesCivilopediaDescription()) {
|
||||
descriptionLabel.setText("")
|
||||
descriptionLabel.isVisible = false
|
||||
} else {
|
||||
descriptionLabel.setText(entry.description)
|
||||
descriptionLabel.isVisible = true
|
||||
}
|
||||
flavourTable.clear()
|
||||
if (entry.flavour != null) {
|
||||
flavourTable.isVisible = true
|
||||
flavourTable.add(
|
||||
entry.flavour.assembleCivilopediaText(ruleset)
|
||||
.renderCivilopediaText(stage.width * 0.5f) { selectLink(it) })
|
||||
} else {
|
||||
flavourTable.isVisible = false
|
||||
}
|
||||
entrySelectTable.children.forEach {
|
||||
it.color = if (it.name == entry.name) Color.BLUE else Color.WHITE
|
||||
}
|
||||
@ -150,7 +169,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(false, null, ruleset),
|
||||
CivilopediaCategories.Building.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Building.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Wonder] = ruleset.buildings.values
|
||||
@ -159,7 +179,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(false, null, ruleset),
|
||||
CivilopediaCategories.Wonder.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Wonder.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Resource] = ruleset.tileResources.values
|
||||
@ -167,7 +188,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(ruleset),
|
||||
CivilopediaCategories.Resource.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Resource.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Terrain] = ruleset.terrains.values
|
||||
@ -175,7 +197,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(ruleset),
|
||||
CivilopediaCategories.Terrain.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Terrain.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Improvement] = ruleset.tileImprovements.values
|
||||
@ -183,7 +206,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(ruleset, false),
|
||||
CivilopediaCategories.Improvement.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Improvement.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Unit] = ruleset.units.values
|
||||
@ -192,7 +216,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(false),
|
||||
CivilopediaCategories.Unit.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Unit.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Nation] = ruleset.nations.values
|
||||
@ -201,7 +226,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getUniqueString(ruleset, false),
|
||||
CivilopediaCategories.Nation.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Nation.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Technology] = ruleset.technologies.values
|
||||
@ -209,7 +235,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(ruleset),
|
||||
CivilopediaCategories.Technology.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Technology.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
categoryToEntries[CivilopediaCategories.Promotion] = ruleset.unitPromotions.values
|
||||
@ -217,7 +244,8 @@ class CivilopediaScreen(
|
||||
CivilopediaEntry(
|
||||
it.name,
|
||||
it.getDescription(ruleset.unitPromotions.values, true, ruleset),
|
||||
CivilopediaCategories.Promotion.getImage?.invoke(it.name, imageSize)
|
||||
CivilopediaCategories.Promotion.getImage?.invoke(it.name, imageSize),
|
||||
(it as? ICivilopediaText).takeUnless { ct -> ct==null || ct.isCivilopediaTextEmpty() }
|
||||
)
|
||||
}
|
||||
|
||||
@ -227,6 +255,9 @@ class CivilopediaScreen(
|
||||
it.key.replace("_", " "),
|
||||
it.value.joinToString("\n\n") { line -> line.tr() },
|
||||
// CivilopediaCategories.Tutorial.getImage?.invoke(it.name, imageSize)
|
||||
flavour = SimpleCivilopediaText(
|
||||
sequenceOf(FormattedLine(extraImage = it.key)),
|
||||
it.value.asSequence(), true)
|
||||
)
|
||||
}
|
||||
|
||||
@ -279,6 +310,7 @@ class CivilopediaScreen(
|
||||
entrySelectTable.top()
|
||||
entrySelectScroll.setOverscroll(false, false)
|
||||
val descriptionTable = Table()
|
||||
descriptionTable.add(flavourTable).row()
|
||||
descriptionLabel.wrap = true // requires explicit cell width!
|
||||
descriptionTable.add(descriptionLabel).width(stage.width * 0.5f).padTop(10f).row()
|
||||
val entrySplitPane = SplitPane(entrySelectScroll, ScrollPane(descriptionTable), false, skin)
|
||||
|
@ -1,9 +1,12 @@
|
||||
package com.unciv.ui.civilopedia
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
|
||||
@ -14,10 +17,19 @@ import com.unciv.ui.utils.*
|
||||
* @param text Text to display.
|
||||
* @param link Create link: Line gets a 'Link' icon and is linked to either
|
||||
* an Unciv object (format `category/entryname`) or an external URL.
|
||||
* @param extraImage Display an Image instead of text. Can be a path as understood by
|
||||
* [ImageGetter.getImage] or the name of a png or jpg in ExtraImages.
|
||||
* @param imageSize Width of the [extraImage], height is calculated preserving aspect ratio. Defaults to available width.
|
||||
* @param header Header level. 1 means double text size and decreases from there.
|
||||
* @param separator Renders a separator line instead of text.
|
||||
*/
|
||||
class FormattedLine (
|
||||
val text: String = "",
|
||||
val link: String = "",
|
||||
val extraImage: String = "",
|
||||
val imageSize: Float = Float.NaN,
|
||||
val header: Int = 0,
|
||||
val separator: Boolean = false,
|
||||
) {
|
||||
// Note: This gets directly deserialized by Json - please keep all attributes meant to be read
|
||||
// from json in the primary constructor parameters above. Everything else should be a fun(),
|
||||
@ -46,7 +58,17 @@ class FormattedLine (
|
||||
}
|
||||
|
||||
/** Returns true if this formatted line will not display anything */
|
||||
fun isEmpty(): Boolean = text.isEmpty() && link.isEmpty()
|
||||
fun isEmpty(): Boolean = text.isEmpty() && extraImage.isEmpty() && link.isEmpty() && !separator
|
||||
|
||||
/** Constants used by [FormattedLine]
|
||||
* @property defaultSize Mirrors default text size as defined elsewhere
|
||||
* @property headerSizes Array of text sizes to translate the [header] attribute
|
||||
*/
|
||||
companion object {
|
||||
const val defaultSize = 18
|
||||
val headerSizes = arrayOf(defaultSize,36,32,27,24,21,15,12,9) // pretty arbitrary, yes
|
||||
val defaultColor: Color = Color.WHITE
|
||||
}
|
||||
|
||||
/** Extension: determines if a [String] looks like a link understood by the OS */
|
||||
private fun String.hasProtocol() = startsWith("http://") || startsWith("https://") || startsWith("mailto:")
|
||||
@ -56,9 +78,35 @@ class FormattedLine (
|
||||
* @param labelWidth Total width to render into, needed to support wrap on Labels.
|
||||
*/
|
||||
fun render(labelWidth: Float): Actor {
|
||||
if (extraImage.isNotEmpty()) {
|
||||
val table = Table(CameraStageBaseScreen.skin)
|
||||
try {
|
||||
val image = when {
|
||||
ImageGetter.imageExists(extraImage) ->
|
||||
ImageGetter.getImage(extraImage)
|
||||
Gdx.files.internal("ExtraImages/$extraImage.png").exists() ->
|
||||
ImageGetter.getExternalImage("$extraImage.png")
|
||||
Gdx.files.internal("ExtraImages/$extraImage.jpg").exists() ->
|
||||
ImageGetter.getExternalImage("$extraImage.jpg")
|
||||
else -> return table
|
||||
}
|
||||
val width = if (imageSize.isNaN()) labelWidth else imageSize
|
||||
val height = width * image.height / image.width
|
||||
table.add(image).size(width, height)
|
||||
} catch (exception: Exception) {
|
||||
println ("${exception.message}: ${exception.cause?.message}")
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
val fontSize = when {
|
||||
header in headerSizes.indices -> headerSizes[header]
|
||||
else -> defaultSize
|
||||
}
|
||||
val table = Table(CameraStageBaseScreen.skin)
|
||||
if (textToDisplay.isNotEmpty()) {
|
||||
val label = textToDisplay.toLabel()
|
||||
val label = if (fontSize == defaultSize) textToDisplay.toLabel()
|
||||
else textToDisplay.toLabel(defaultColor,fontSize)
|
||||
label.wrap = labelWidth > 0f
|
||||
if (labelWidth == 0f)
|
||||
table.add(label)
|
||||
@ -67,12 +115,26 @@ class FormattedLine (
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
// Debug visualization only
|
||||
override fun toString(): String {
|
||||
return when {
|
||||
isEmpty() -> "(empty)"
|
||||
separator -> "(separator)"
|
||||
extraImage.isNotEmpty() -> "(extraImage='$extraImage')"
|
||||
header > 0 -> "(header=$header)'$text'"
|
||||
linkType == LinkType.None -> "'$text'"
|
||||
else -> "'$text'->$link"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Makes [renderer][render] available outside [ICivilopediaText] */
|
||||
object MarkupRenderer {
|
||||
private const val emptyLineHeight = 10f
|
||||
private const val defaultPadding = 2.5f
|
||||
private const val separatorTopPadding = 5f
|
||||
private const val separatorBottomPadding = 15f
|
||||
|
||||
/**
|
||||
* Build a Gdx [Table] showing [formatted][FormattedLine] [content][lines].
|
||||
@ -94,6 +156,10 @@ object MarkupRenderer {
|
||||
table.add().padTop(emptyLineHeight).row()
|
||||
continue
|
||||
}
|
||||
if (line.separator) {
|
||||
table.addSeparator().pad(separatorTopPadding, 0f, separatorBottomPadding, 0f)
|
||||
continue
|
||||
}
|
||||
val actor = line.render(labelWidth)
|
||||
if (line.linkType == FormattedLine.LinkType.Internal && linkAction != null)
|
||||
actor.onClick {
|
||||
@ -111,3 +177,106 @@ object MarkupRenderer {
|
||||
return table.apply { pack() }
|
||||
}
|
||||
}
|
||||
|
||||
/** Storage class for interface [ICivilopediaText] for use as base class */
|
||||
open class CivilopediaText : ICivilopediaText {
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
}
|
||||
/** Storage class for instantiation of the simplest form containing only the lines collection */
|
||||
class SimpleCivilopediaText(lines: List<FormattedLine>, val isComplete: Boolean = false) : CivilopediaText() {
|
||||
init {
|
||||
civilopediaText = lines
|
||||
}
|
||||
override fun hasCivilopediaTextLines() = true
|
||||
override fun replacesCivilopediaDescription() = isComplete
|
||||
constructor(strings: Sequence<String>, isComplete: Boolean = false) : this(
|
||||
strings.map { FormattedLine(it) }.toList(), isComplete)
|
||||
constructor(first: Sequence<FormattedLine>, strings: Sequence<String>, isComplete: Boolean = false) : this(
|
||||
(first + strings.map { FormattedLine(it) }).toList(), isComplete)
|
||||
}
|
||||
|
||||
/** Addon common to most ruleset game objects managing civilopedia display
|
||||
*
|
||||
* ### Usage:
|
||||
* 1. Let [Ruleset] object implement this (e.g. by inheriting class [CivilopediaText] or adding var [civilopediaText] itself)
|
||||
* 2. Add `"civilopediaText": ["",…],` in the json for these objects
|
||||
* 3. Optionally override [getCivilopediaTextHeader] to supply a header line
|
||||
* 4. Optionally override [getCivilopediaTextLines] to supply automatic stuff like tech prerequisites, uniques, etc.
|
||||
* 4. Optionally override [assembleCivilopediaText] to handle assembly of the final set of lines yourself.
|
||||
*/
|
||||
interface ICivilopediaText {
|
||||
/** List of strings supporting simple [formatting rules][FormattedLine] that [CivilopediaScreen] can render.
|
||||
* May later be merged with automatic lines generated by the deriving class
|
||||
* through overridden [getCivilopediaTextHeader] and/or [getCivilopediaTextLines] methods.
|
||||
*
|
||||
*/
|
||||
var civilopediaText: List<FormattedLine>
|
||||
|
||||
/** Generate header line from object metadata.
|
||||
* Default implementation will pull [INamed.name] and render it in 150% normal font size.
|
||||
* @return A [FormattedLine] that will be inserted on top
|
||||
*/
|
||||
fun getCivilopediaTextHeader(): FormattedLine? =
|
||||
if (this is INamed) FormattedLine(name, header = 2)
|
||||
else null
|
||||
|
||||
/** Generate automatic lines from object metadata.
|
||||
*
|
||||
* Default implementation is empty - no need to call super in overrides.
|
||||
*
|
||||
* @param ruleset The current ruleset for the Civilopedia viewer
|
||||
* @return A list of [FormattedLine]s that will be inserted before
|
||||
* the first line of [civilopediaText] having a [link][FormattedLine.link]
|
||||
*/
|
||||
fun getCivilopediaTextLines(ruleset: Ruleset): List<FormattedLine> = listOf()
|
||||
|
||||
/** Override this and return true to tell the Civilopedia that the legacy description is no longer needed */
|
||||
fun replacesCivilopediaDescription() = false
|
||||
/** Override this and return true to tell the Civilopedia that this is not empty even if nothing came from json */
|
||||
fun hasCivilopediaTextLines() = false
|
||||
/** Indicates that neither json nor getCivilopediaTextLines have content */
|
||||
fun isCivilopediaTextEmpty() = civilopediaText.isEmpty() && !hasCivilopediaTextLines()
|
||||
|
||||
/** Build a Gdx [Table] showing our [formatted][FormattedLine] [content][civilopediaText]. */
|
||||
fun renderCivilopediaText (labelWidth: Float, linkAction: ((id: String)->Unit)? = null): Table {
|
||||
return MarkupRenderer.render(civilopediaText, labelWidth, linkAction = linkAction)
|
||||
}
|
||||
|
||||
/** Assemble json-supplied lines with automatically generated ones.
|
||||
*
|
||||
* The default implementation will insert [getCivilopediaTextLines] before the first [linked][FormattedLine.link] [civilopediaText] line and [getCivilopediaTextHeader] on top.
|
||||
*
|
||||
* @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 {
|
||||
val outerLines = civilopediaText.iterator()
|
||||
val newLines = sequence {
|
||||
var middleDone = false
|
||||
var outerNotEmpty = false
|
||||
val header = getCivilopediaTextHeader()
|
||||
if (header != null) {
|
||||
yield(header)
|
||||
yield(FormattedLine(separator = true))
|
||||
}
|
||||
while (outerLines.hasNext()) {
|
||||
val next = outerLines.next()
|
||||
if (!middleDone && !next.isEmpty() && next.linkType != FormattedLine.LinkType.None) {
|
||||
middleDone = true
|
||||
if (hasCivilopediaTextLines()) {
|
||||
if (outerNotEmpty) yield(FormattedLine())
|
||||
yieldAll(getCivilopediaTextLines(ruleset))
|
||||
yield(FormattedLine())
|
||||
}
|
||||
}
|
||||
outerNotEmpty = true
|
||||
yield(next)
|
||||
}
|
||||
if (!middleDone) {
|
||||
if (outerNotEmpty && hasCivilopediaTextLines()) yield(FormattedLine())
|
||||
yieldAll(getCivilopediaTextLines(ruleset))
|
||||
}
|
||||
}
|
||||
return SimpleCivilopediaText(newLines.toList(), isComplete = true)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user