Rework of PromotionPicker UI (#8325)

Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
vegeta1k95
2023-01-08 10:45:51 +01:00
committed by GitHub
parent b669c2ab1b
commit 4d8f056df7
7 changed files with 451 additions and 95 deletions

View File

@ -37,7 +37,7 @@ import java.lang.Integer.max
import kotlin.math.abs
import kotlin.math.min
object Colors {
object PolicyColors {
val policyPickable = colorFromRGB(47,67,92).darken(0.3f)
val policyNotPickable = colorFromRGB(20, 20, 20)
@ -48,14 +48,6 @@ object Colors {
val branchAdopted = colorFromRGB(100, 90, 10).darken(0.5f)
}
object Sizes {
val paddingVertical = 10f
val paddingHorizontal = 20f
val paddingBetweenHor = 10f
val paddingBetweenVer = 20f
val iconSize = 50f
}
fun Policy.isAdopted() : Boolean {
val worldScreen = UncivGame.Current.worldScreen ?: return false
val viewingCiv = worldScreen.viewingCiv
@ -135,21 +127,21 @@ class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable(
when {
isSelected && isPickable -> {
setBackgroundColor(Colors.policySelected)
setBackgroundColor(PolicyColors.policySelected)
}
isPickable -> {
setBackgroundColor(Colors.policyPickable)
setBackgroundColor(PolicyColors.policyPickable)
}
isAdopted -> {
icon.color = Color.GOLD
icon.color = Color.GOLD.cpy()
setBackgroundColor(colorFromRGB(10,90,100).darken(0.8f))
}
else -> {
icon.color.a = 0.2f
setBackgroundColor(Colors.policyNotPickable)
setBackgroundColor(PolicyColors.policyNotPickable)
}
}
@ -162,6 +154,14 @@ class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable(
class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo = worldScreen.viewingCiv)
: PickerScreen(), RecreateOnResize {
object Sizes {
val paddingVertical = 10f
val paddingHorizontal = 20f
val paddingBetweenHor = 10f
val paddingBetweenVer = 20f
val iconSize = 50f
}
internal val viewingCiv: CivilizationInfo = civInfo
private var policyNameToButton = HashMap<String, BorderedTable>()
@ -302,8 +302,10 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
val prefHeight = Sizes.paddingVertical*2 + Sizes.iconSize*maxRow + Sizes.paddingBetweenVer*(maxRow - 1)
// Main table
val colorBg = if (branch.isAdopted()) Colors.branchAdopted else Colors.branchNotAdopted
val branchGroup = BorderedTable(innerColor = colorBg)
val colorBg = if (branch.isAdopted()) PolicyColors.branchAdopted else PolicyColors.branchNotAdopted
val branchGroup = BorderedTable(
path="PolicyScree/PolicyBranchBackground",
innerColor = colorBg)
// Header
val header = getBranchHeader(branch)
@ -343,7 +345,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
for ((k, v) in conditionals) {
warning += "" + k.text.fillPlaceholders(v.joinToString()).tr() + "\n"
}
val warningLabel = warning.toLabel(Color.RED, 13)
val warningLabel = warning.toLabel(Color.RED.cpy(), 13)
warningLabel.setFillParent(false)
warningLabel.setAlignment(Align.topLeft)
warningLabel.wrap = true
@ -479,7 +481,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
private fun drawLine(group: Group, policyX: Float, policyY: Float, prereqX: Float, prereqY:Float) {
val lineColor = Color.WHITE
val lineColor = Color.WHITE.cpy()
val lineSize = 2f
if (policyX != prereqX) {
@ -554,7 +556,9 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
}
private fun getBranchHeader(branch: PolicyBranch): Table {
val header = BorderedTable(innerColor = colorFromRGB(47,90,92), borderSize = 5f)
val header = BorderedTable(
path="PolicyScreen/PolicyBranchHeader",
innerColor = colorFromRGB(47,90,92), borderSize = 5f)
header.pad(5f)
val table = Table()
@ -582,7 +586,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
val text: String
val lockIcon = ImageGetter.getImage("OtherIcons/LockSmall")
.apply { color = Color.WHITE }.toGroup(15f)
.apply { color = Color.WHITE.cpy() }.toGroup(15f)
lockIcon.isVisible = false
if (viewingCiv.policies.isAdopted(branch.name)) {
policy = branch.policies.last()
@ -599,18 +603,19 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
label.setAlignment(Align.center)
val color = when {
policy.isPickable() -> Colors.policyPickable
policy.isPickable() -> PolicyColors.policyPickable
viewingCiv.policies.isAdopted(policy.name) -> {
label.color = colorFromRGB(150, 70, 40)
Colors.branchCompleted
PolicyColors.branchCompleted
}
else -> {
lockIcon.isVisible = true
label.color.a = 0.5f
Colors.policyNotPickable}
PolicyColors.policyNotPickable}
}
val table = BorderedTable(
path="PolicyScreen/PolicyBranchAdoptButton",
defaultInner = skinStrings.roundedEdgeRectangleSmallShape,
defaultBorder = skinStrings.roundedEdgeRectangleSmallShape,
innerColor = color, borderSize = 2f)

View File

@ -1,34 +1,153 @@
package com.unciv.ui.pickerscreens
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Cell
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.map.MapUnit
import com.unciv.models.TutorialTrigger
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.Promotion
import com.unciv.models.translations.tr
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.BorderedTable
import com.unciv.ui.utils.RecreateOnResize
import com.unciv.ui.utils.extensions.colorFromRGB
import com.unciv.ui.utils.extensions.darken
import com.unciv.ui.utils.extensions.isEnabled
import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.setFontColor
import com.unciv.ui.utils.extensions.toLabel
import com.unciv.ui.utils.extensions.toTextButton
import com.unciv.utils.Log
import java.lang.Integer.max
import kotlin.math.abs
class PromotionNode(val promotion: Promotion) {
var maxDepth = 0
/** How many level this promotion has */
var levels = 1
val successors: ArrayList<PromotionNode> = ArrayList()
val predecessors: ArrayList<PromotionNode> = ArrayList()
val baseName = getBasePromotionName()
fun isRoot() : Boolean {
return predecessors.isEmpty()
}
fun calculateDepth(excludeNodes: ArrayList<PromotionNode>, currentDepth: Int) {
maxDepth = max(maxDepth, currentDepth)
excludeNodes.add(this)
successors.filter { !excludeNodes.contains(it) }.forEach { it.calculateDepth(excludeNodes,currentDepth+1) }
}
private fun getBasePromotionName(): String {
val nameWithoutBrackets = promotion.name.replace("[", "").replace("]", "")
val level = when {
nameWithoutBrackets.endsWith(" I") -> 1
nameWithoutBrackets.endsWith(" II") -> 2
nameWithoutBrackets.endsWith(" III") -> 3
else -> 0
}
return nameWithoutBrackets.dropLast(if (level == 0) 0 else level + 1)
}
class CustomComparator(
val baseNode: PromotionNode
) : Comparator<PromotionNode> {
override fun compare(a: PromotionNode, b: PromotionNode): Int {
val baseName = baseNode.baseName
val aName = a.baseName
val bName = b.baseName
return when (aName) {
baseName -> -1
bName -> 0
else -> 1
}
}
}
}
class PromotionButton(
val node: PromotionNode,
val isPickable: Boolean = true,
val isPromoted: Boolean = false
) : BorderedTable(
path="PromotionScreen/PromotionButton",
defaultInner = BaseScreen.skinStrings.roundedEdgeRectangleMidShape,
defaultBorder = BaseScreen.skinStrings.roundedEdgeRectangleMidBorderShape,
borderColor = Color.WHITE.cpy(),
innerColor = Color.BLACK,
borderSize = 5f
) {
var isSelected = false
val label = node.promotion.name.toLabel().apply {
wrap = false
setAlignment(Align.left)
setEllipsis(true)
}
init {
pad(5f)
align(Align.left)
add(ImageGetter.getPromotionIcon(node.promotion.name)).padRight(10f)
add(label).left().maxWidth(130f)
updateColor()
}
fun updateColor() {
val color = when {
isSelected -> PromotionPickerScreen.Selected
isPickable -> PromotionPickerScreen.Pickable
isPromoted -> PromotionPickerScreen.Promoted
else -> PromotionPickerScreen.Default
}
setBackgroundColor(color)
val textColor = when {
isSelected -> Color.WHITE
isPromoted -> PromotionPickerScreen.Promoted.cpy().darken(0.8f)
else -> Color.WHITE
}
label.setFontColor(textColor)
}
}
class PromotionPickerScreen(val unit: MapUnit) : PickerScreen(), RecreateOnResize {
private var selectedPromotion: Promotion? = null
private fun acceptPromotion(promotion: Promotion?) {
companion object Colors {
val Default:Color = Color.BLACK
val Selected:Color = colorFromRGB(72, 147, 175)
val Promoted:Color = colorFromRGB(255, 215, 0).darken(0.2f)
val Pickable:Color = colorFromRGB(28, 80, 0)
val Prerequisite:Color = colorFromRGB(14, 92, 86)
}
private val promotionsTable = Table()
private val promotionToButton = LinkedHashMap<String, PromotionButton>()
private var selectedPromotion: PromotionButton? = null
private var lines = ArrayList<Image>()
private fun acceptPromotion(node: PromotionNode?) {
// if user managed to click disabled button, still do nothing
if (promotion == null) return
if (node == null) return
unit.promotions.addPromotion(promotion.name)
if (unit.promotions.canBePromoted())
unit.promotions.addPromotion(node.promotion.name)
game.replaceCurrentScreen(recreate())
else
game.popScreen()
}
init {
@ -36,7 +155,8 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen(), RecreateOnResiz
rightSideButton.setText("Pick promotion".tr())
rightSideButton.onClick(UncivSound.Promote) {
acceptPromotion(selectedPromotion)
if (selectedPromotion?.isPickable == true)
acceptPromotion(selectedPromotion?.node)
}
val canBePromoted = unit.promotions.canBePromoted()
@ -53,8 +173,6 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen(), RecreateOnResiz
val promotionsForUnitType = unit.civInfo.gameInfo.ruleSet.unitPromotions.values.filter {
it.unitTypes.contains(unitType.name) || unit.promotions.promotions.contains(it.name)
}
val unitAvailablePromotions = unit.promotions.getAvailablePromotions()
//Always allow the user to rename the unit as many times as they like.
val renameButton = "Choose name for [${unit.name}]".toTextButton()
renameButton.isEnabled = true
@ -70,57 +188,288 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen(), RecreateOnResiz
)
}
availablePromotionsGroup.add(renameButton)
availablePromotionsGroup.row()
topTable.add(availablePromotionsGroup).row()
fillTable(promotionsForUnitType)
val promotionsTable = Table()
val width = promotionsForUnitType.maxOf { it.column } +1
val height = promotionsForUnitType.maxOf { it.row } +1
val cellMatrix = ArrayList<ArrayList<Table>>()
for (y in 0..height) {
displayTutorial(TutorialTrigger.Experience)
}
private fun fillTable(promotions: Collection<Promotion>) {
val map = LinkedHashMap<String, PromotionNode>()
val availablePromotions = unit.promotions.getAvailablePromotions()
val canBePromoted = unit.promotions.canBePromoted()
val canChangeState = game.worldScreen!!.canChangeState
// Create nodes
// Pass 1 - create nodes for all promotions
for (promotion in promotions)
map[promotion.name] = PromotionNode(promotion)
// Pass 2 - remove nodes which are unreachable (dependent only on absent promotions)
for (promotion in promotions) {
if (promotion.prerequisites.isNotEmpty()) {
val isReachable = promotion.prerequisites.any { map.containsKey(it) }
if (!isReachable)
map.remove(promotion.name)
}
}
// Pass 3 - fill nodes successors/predecessors, based on promotions prerequisites
for (node in map.values) {
for (prerequisiteName in node.promotion.prerequisites) {
val prerequisiteNode = map[prerequisiteName]
if (prerequisiteNode != null) {
node.predecessors.add(prerequisiteNode)
prerequisiteNode.successors.add(node)
// Prerequisite has the same base name -> +1 more level
if (prerequisiteNode.baseName == node.baseName)
prerequisiteNode.levels += 1
}
}
}
// Traverse each root node tree and calculate max possible depths of each node
for (node in map.values) {
if (node.isRoot())
node.calculateDepth(arrayListOf(node), 0)
}
// For each non-root node remove all predecessors except the one with the least max depth.
// This is needed to compactify trees and remove circular dependencies (A -> B -> C -> A)
for (node in map.values) {
if (node.isRoot())
continue
// Choose best predecessor - the one with less depth
var best: PromotionNode? = null
for (predecessor in node.predecessors) {
if (best == null)
best = predecessor
else
best = if (predecessor.maxDepth < best.maxDepth) predecessor else best
}
// Remove everything else, leave only best
for (predecessor in node.predecessors)
predecessor.successors.remove(node)
node.predecessors.clear()
node.predecessors.add(best!!)
best.successors.add(node)
}
// Sort nodes successors so promotions with same base name go first
for (node in map.values) {
Log.debug("MYTAG: Node ${node.promotion.name} depth: ${node.maxDepth}")
node.successors.sortWith(PromotionNode.CustomComparator(node))
}
// Create cell matrix
val maxColumns = map.size + 1
val maxRows = map.size + 1
val cellMatrix = ArrayList<ArrayList<Cell<Actor>>>()
for (y in 0..maxRows) {
cellMatrix.add(ArrayList())
for (x in 0..width*2) {
val cell = promotionsTable.add(Table())
cellMatrix[y].add(cell.actor)
for (x in 0..maxColumns) {
val cell = promotionsTable.add()
cellMatrix[y].add(cell)
}
promotionsTable.row()
}
for (promotion in promotionsForUnitType) {
if (promotion.hasUnique(UniqueType.OneTimeUnitHeal) && unit.health == 100) continue
val isPromotionAvailable = promotion in unitAvailablePromotions
val unitHasPromotion = unit.promotions.promotions.contains(promotion.name)
val selectPromotionButton = PickerPane.getPickerOptionButton(ImageGetter.getPromotionIcon(promotion.name), promotion.name)
selectPromotionButton.isEnabled = true
selectPromotionButton.onClick {
val enable = canBePromoted && isPromotionAvailable && !unitHasPromotion && canChangeState
selectedPromotion = if (enable) promotion else null
rightSideButton.isEnabled = enable
rightSideButton.setText(promotion.name.tr())
descriptionLabel.setText(updateDescriptionLabel(promotion.getDescription(promotionsForUnitType)))
/** Check whether cell is inhabited by actor already */
fun isTherePlace(row: Int, col: Int, levels: Int) : Boolean {
for (i in 0 until levels) {
if (cellMatrix[row][col+i].actor != null)
return false
}
return true
}
val group = cellMatrix[promotion.row][promotion.column*2]
group.pad(5f)
group.add(selectPromotionButton)
/** Recursively place buttons for node and it's successors into free cells */
fun placeButton(col: Int, row: Int, node: PromotionNode) : Int {
val name = node.promotion.name
// If promotion button not yet placed
if (promotionToButton[name] == null) {
// If place is free - we place button
if (isTherePlace(row, col, node.levels)) {
val cell = cellMatrix[row][col]
val isPromotionAvailable = node.promotion in availablePromotions
val hasPromotion = unit.promotions.promotions.contains(name)
val isPickable = canBePromoted && isPromotionAvailable && !hasPromotion && canChangeState
val button = getButton(promotions, node, isPickable, hasPromotion)
promotionToButton[name] = button
cell.setActor(button)
cell.pad(5f)
cell.padRight(20f)
cell.minWidth(190f)
cell.maxWidth(190f)
}
// If place is not free - try to find another in the next row
else {
return placeButton(col, row+1, node)
}
}
if (canPromoteNow && isPromotionAvailable) {
val pickNow = "Pick now!".toLabel()
pickNow.setAlignment(Align.center)
pickNow.onClick {
acceptPromotion(promotion)
// Filter successors who haven't been placed yet (to avoid circular dependencies)
// and try to place them in the next column.
// Return the max row this whole tree ever reached.
return node.successors.filter {
!promotionToButton.containsKey(it.promotion.name)
}.map {
placeButton(col+1, row, it)
}.maxOfOrNull { it }?: row
}
cellMatrix[promotion.row][promotion.column*2+1].add(pickNow).padLeft(10f).fillY()
}
else if (unitHasPromotion) selectPromotionButton.color = Color.GREEN
else selectPromotionButton.color= Color.GRAY
}
availablePromotionsGroup.add(promotionsTable)
topTable.add(availablePromotionsGroup)
displayTutorial(TutorialTrigger.Experience)
// Build each tree starting from root nodes
var row = 0
for (node in map.values) {
if (node.isRoot()) {
row = placeButton(0, row, node)
// Each root tree should start from a completely empty row.
row += 1
}
}
topTable.add(promotionsTable)
addConnectingLines()
}
private fun getButton(allPromotions: Collection<Promotion>, node: PromotionNode,
isPickable: Boolean = true, isPromoted: Boolean = false) : PromotionButton {
val button = PromotionButton(
node = node,
isPromoted = isPromoted,
isPickable = isPickable
)
button.onClick {
selectedPromotion?.isSelected = false
selectedPromotion?.updateColor()
selectedPromotion = button
button.isSelected = true
button.updateColor()
for (btn in promotionToButton.values)
btn.updateColor()
button.node.promotion.prerequisites.forEach { promotionToButton[it]?.apply {
if (!this.isPromoted)
setBackgroundColor(Prerequisite) }}
rightSideButton.isEnabled = isPickable
rightSideButton.setText(node.promotion.name.tr())
descriptionLabel.setText(updateDescriptionLabel(node.promotion.getDescription(allPromotions)))
addConnectingLines()
}
return button
}
private fun addConnectingLines() {
promotionsTable.pack()
scrollPane.updateVisualScroll()
for (line in lines) line.remove()
lines.clear()
for (button in promotionToButton.values) {
for (prerequisite in button.node.promotion.prerequisites) {
val prerequisiteButton = promotionToButton[prerequisite] ?: continue
var buttonCoords = Vector2(0f, button.height / 2)
button.localToStageCoordinates(buttonCoords)
promotionsTable.stageToLocalCoordinates(buttonCoords)
var prerequisiteCoords = Vector2(prerequisiteButton.width, prerequisiteButton.height / 2)
prerequisiteButton.localToStageCoordinates(prerequisiteCoords)
promotionsTable.stageToLocalCoordinates(prerequisiteCoords)
val lineColor = when {
button.isSelected -> Selected
prerequisiteButton.node.baseName == button.node.baseName -> Color.WHITE.cpy()
else -> Color.CLEAR
}
val lineSize = when {
button.isSelected -> 4f
else -> 2f
}
if (buttonCoords.x < prerequisiteCoords.x) {
val temp = buttonCoords.cpy()
buttonCoords = prerequisiteCoords
prerequisiteCoords = temp
}
if (buttonCoords.y != prerequisiteCoords.y) {
val deltaX = buttonCoords.x - prerequisiteCoords.x
val deltaY = buttonCoords.y - prerequisiteCoords.y
val halfLength = deltaX / 2f
val line = ImageGetter.getWhiteDot().apply {
width = halfLength+lineSize/2
height = lineSize
x = prerequisiteCoords.x
y = prerequisiteCoords.y - lineSize / 2
}
val line1 = ImageGetter.getWhiteDot().apply {
width = halfLength + lineSize/2
height = lineSize
x = buttonCoords.x - width
y = buttonCoords.y - lineSize / 2
}
val line2 = ImageGetter.getWhiteDot().apply {
width = lineSize
height = abs(deltaY)
x = buttonCoords.x - halfLength - lineSize / 2
y = buttonCoords.y + (if (deltaY > 0f) -height-lineSize/2 else lineSize/2)
}
line.color = lineColor
line1.color = lineColor
line2.color = lineColor
promotionsTable.addActor(line)
promotionsTable.addActor(line1)
promotionsTable.addActor(line2)
line.toBack()
line1.toBack()
line2.toBack()
lines.add(line)
lines.add(line1)
lines.add(line2)
} else {
val line = ImageGetter.getWhiteDot().apply {
width = buttonCoords.x - prerequisiteCoords.x
height = lineSize
x = prerequisiteCoords.x
y = prerequisiteCoords.y - lineSize / 2
}
line.color = lineColor
promotionsTable.addActor(line)
line.toBack()
lines.add(line)
}
}
}
for (line in lines) {
if (line.color == Selected)
line.zIndex = lines.size
}
}
private fun setScrollY(scrollY: Float) {
@ -130,17 +479,13 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen(), RecreateOnResiz
}
private fun updateDescriptionLabel(): String {
var newDescriptionText = unit.displayName().tr()
return newDescriptionText.toString()
return unit.displayName().tr()
}
private fun updateDescriptionLabel(promotionDescription: String): String {
var newDescriptionText = unit.displayName().tr()
newDescriptionText += "\n" + promotionDescription
return newDescriptionText.toString()
return newDescriptionText
}
override fun recreate(): BaseScreen {

View File

@ -45,7 +45,7 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
init {
touchable = Touchable.enabled
background = BaseScreen.skinStrings.getUiBackground("TechPickerScreen/TechButton", BaseScreen.skinStrings.roundedEdgeRectangleMidShape,
tintColor = Color.WHITE.darken(0.3f))
tintColor = Color.WHITE.cpy().darken(0.3f))
bg.toBack()
addActor(bg)
@ -65,9 +65,9 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
val percentWillBeComplete = (techCost - (remainingTech-techThisTurn)) / techCost.toFloat()
val progressBar = ImageGetter.ProgressBar(2f, 48f, true)
.setBackground(Color.WHITE)
.setSemiProgress(Color.BLUE.brighten(0.3f), percentWillBeComplete)
.setProgress(Color.BLUE.darken(0.5f), percentComplete)
add(progressBar.addBorder(1f, Color.GRAY)).padLeft(0f).padRight(5f)
.setSemiProgress(Color.BLUE.cpy().brighten(0.3f), percentWillBeComplete)
.setProgress(Color.BLUE.cpy().darken(0.5f), percentComplete)
add(progressBar.addBorder(1f, Color.GRAY.cpy())).padLeft(0f).padRight(5f)
}
val rightSide = Table()

View File

@ -139,7 +139,7 @@ class TechPickerScreen(
val color = when {
civTech.era.name == era -> queuedTechColor
civInfo.gameInfo.ruleSet.eras[era]!!.eraNumber < civTech.era.eraNumber -> colorFromRGB(255, 175, 0)
else -> Color.BLACK
else -> Color.BLACK.cpy()
}
val table1 = Table().pad(1f)
@ -197,7 +197,7 @@ class TechPickerScreen(
tempTechsToResearch.firstOrNull() == techName && !freeTechPick -> currentTechColor
researchableTechs.contains(techName) -> researchableTechColor
tempTechsToResearch.contains(techName) -> queuedTechColor
else -> Color.BLACK
else -> Color.BLACK.cpy()
})
if (civTech.isResearched(techName) && techName != Constants.futureTech) {
@ -236,7 +236,7 @@ class TechPickerScreen(
eraLabel.localToStageCoordinates(coords)
techTable.stageToLocalCoordinates(coords)
val line = ImageGetter.getLine(coords.x-1f, coords.y, coords.x-1f, coords.y - 1000f, 1f)
line.color = Color.GRAY.apply { a = 0.6f }
line.color = Color.GRAY.cpy().apply { a = 0.6f }
line.toBack()
techTable.addActor(line)
lines.add(line)
@ -263,10 +263,10 @@ class TechPickerScreen(
techTable.stageToLocalCoordinates(prerequisiteCoords)
val lineColor = when {
civTech.isResearched(tech.name) && !tech.isContinuallyResearchable() -> Color.WHITE
civTech.isResearched(tech.name) && !tech.isContinuallyResearchable() -> Color.WHITE.cpy()
civTech.isResearched(prerequisite) -> researchableTechColor
tempTechsToResearch.contains(tech.name) -> currentTechColor
else -> Color.WHITE
else -> Color.WHITE.cpy()
}
val lineSize = when {

View File

@ -13,12 +13,12 @@ open class BorderedTable(
val defaultBorder: String = BaseScreen.skinStrings.rectangleWithOutlineShape,
val borderColor: Color = Color.WHITE,
val innerColor: Color = Color.BLACK,
val borderSize: Float = 5f,
var borderSize: Float = 5f,
val borderOnTop: Boolean = false
) : Table() {
var bgBorder: Image = Image(BaseScreen.skinStrings.getUiBackground(path, defaultBorder, borderColor))
var bgInner: Image = Image(BaseScreen.skinStrings.getUiBackground(path, defaultInner, innerColor))
var bgInner: Image = Image(BaseScreen.skinStrings.getUiBackground(path, defaultInner, innerColor.cpy()))
var bgBorder: Image = Image(BaseScreen.skinStrings.getUiBackground(path + "Border", defaultBorder, borderColor.cpy()))
init {
if (borderSize != 0f)
@ -40,7 +40,7 @@ open class BorderedTable(
fun setBackgroundColor(color: Color) {
bgInner.remove()
bgInner = Image(BaseScreen.skinStrings.getUiBackground(path, defaultInner, color))
bgInner = Image(BaseScreen.skinStrings.getUiBackground(path, defaultInner, color.cpy()))
addActor(bgInner)
if (borderSize != 0f) {
if (borderOnTop)

View File

@ -189,7 +189,12 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
}
if (!unit.isCivilian()) {
unitDescriptionTable.add("XP".tr())
unitDescriptionTable.add("XP".tr().toLabel().apply {
onClick {
if (selectedUnit == null) return@onClick
worldScreen.game.pushScreen(PromotionPickerScreen(unit))
}
})
unitDescriptionTable.add(unit.promotions.XP.toString() + "/" + unit.promotions.xpForNextPromotion())
}

View File

@ -34,6 +34,7 @@ These shapes are used all over Unciv and can be replaced to make a lot of UI ele
<!--- DO NOT REMOVE OR MODIFY THIS LINE UI_ELEMENT_TABLE_REGION -->
| Directory | Name | Default shape | Image |
|---|:---:|:---:|---|
| | Border | null | |
| CityScreen/ | CityPickerTable | roundedEdgeRectangle | |
| CityScreen/CitizenManagementTable/ | AvoidCell | null | |
| CityScreen/CitizenManagementTable/ | FocusCell | null | |