Big rework of City plates (#8239)

* Big rework of City plates

* New city plates design, new status icons

* A little bit of padding for construction icon

* Better border for defence counter

* Better border for defence counter

* Solid colored icons

* Solid colored icons

* Credits

* Credits

Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
vegeta1k95
2022-12-28 16:32:26 +01:00
committed by GitHub
parent 301cd899b2
commit be573f2b76
22 changed files with 540 additions and 415 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,12 +1,12 @@
Skin.png
size: 128, 128
size: 512, 64
format: RGBA8888
filter: Linear, Linear
repeat: none
Skins/Minimal/checkbox
rotate: false
xy: 52, 16
xy: 291, 23
size: 31, 31
split: 0, 0, 0, 0
orig: 31, 31
@ -14,7 +14,7 @@ Skins/Minimal/checkbox
index: -1
Skins/Minimal/checkbox-pressed
rotate: false
xy: 64, 55
xy: 252, 23
size: 31, 31
split: 0, 0, 0, 0
orig: 31, 31
@ -22,7 +22,7 @@ Skins/Minimal/checkbox-pressed
index: -1
Skins/Minimal/rectangleWithOutline
rotate: false
xy: 112, 121
xy: 64, 5
size: 3, 3
split: 0, 0, 0, 0
orig: 3, 3
@ -30,25 +30,59 @@ Skins/Minimal/rectangleWithOutline
index: -1
Skins/Minimal/roundedEdgeRectangle
rotate: false
xy: 4, 74
xy: 4, 4
size: 52, 50
split: 19, 20, 19, 21
pad: 12, 13, 7, 7
orig: 52, 50
offset: 0, 0
index: -1
Skins/Minimal/roundedEdgeRectangle-mid
rotate: false
xy: 110, 16
size: 38, 38
split: 17, 17, 15, 15
pad: 17, 17, -1, -1
orig: 38, 38
offset: 0, 0
index: -1
Skins/Minimal/roundedEdgeRectangle-mid-border
rotate: false
xy: 64, 16
size: 38, 38
split: 18, 18, 18, 18
orig: 38, 38
offset: 0, 0
index: -1
Skins/Minimal/roundedEdgeRectangle-small
rotate: false
xy: 4, 4
xy: 330, 30
size: 24, 24
split: 10, 10, 10, 10
pad: 10, 10, 2, 2
orig: 24, 24
offset: 0, 0
index: -1
Skins/Minimal/roundedTopEdgeRectangle-small
rotate: false
xy: 394, 42
size: 24, 12
split: 10, 10, 10, 0
pad: 10, 10, -1, -1
orig: 24, 12
offset: 0, 0
index: -1
Skins/Minimal/roundedTopEdgeRectangle-small-border
rotate: false
xy: 362, 42
size: 24, 12
split: 10, 10, 10, 0
orig: 24, 12
offset: 0, 0
index: -1
Skins/Minimal/select-box
rotate: false
xy: 64, 94
xy: 204, 24
size: 40, 30
split: 7, 9, 7, 7
orig: 40, 30
@ -56,7 +90,7 @@ Skins/Minimal/select-box
index: -1
Skins/Minimal/select-box-pressed
rotate: false
xy: 4, 36
xy: 156, 24
size: 40, 30
split: 7, 9, 7, 7
orig: 40, 30

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 580 KiB

After

Width:  |  Height:  |  Size: 578 KiB

View File

@ -11,6 +11,10 @@ class SkinStrings(skin: String = UncivGame.Current.settings.skin) {
// Default shapes must always end with "Shape" so the UiElementDocsWriter can identify them
val roundedEdgeRectangleSmallShape = skinLocation + "roundedEdgeRectangle-small"
val roundedTopEdgeRectangleSmallShape = skinLocation + "roundedTopEdgeRectangle-small"
val roundedTopEdgeRectangleSmallBorderShape = skinLocation + "roundedTopEdgeRectangle-small-border"
val roundedEdgeRectangleMidShape = skinLocation + "roundedEdgeRectangle-mid"
val roundedEdgeRectangleMidBorderShape = skinLocation + "roundedEdgeRectangle-mid-border"
val roundedEdgeRectangleShape = skinLocation + "roundedEdgeRectangle"
val rectangleWithOutlineShape = skinLocation + "rectangleWithOutline"
val selectBoxShape = skinLocation + "select-box"

View File

@ -37,11 +37,6 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() {
cityNameTable.add(fireImage).size(20f).padRight(5f)
}
if (city.isCapital()) {
val starImage = ImageGetter.getImage("OtherIcons/Star").apply { color = Color.LIGHT_GRAY }
cityNameTable.add(starImage).size(20f).padRight(5f)
}
if (city.isPuppet) {
val starImage = ImageGetter.getImage("OtherIcons/Puppet").apply { color = Color.LIGHT_GRAY }
cityNameTable.add(starImage).size(20f).padRight(5f)
@ -52,6 +47,11 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() {
cityNameTable.add(resistanceImage).size(20f).padRight(5f)
}
if (city.isCapital()) {
val starImage = ImageGetter.getImage("OtherIcons/Star").apply { color = Color.LIGHT_GRAY }
cityNameTable.add(starImage).size(20f).padRight(5f)
}
val currentCityLabel = city.name.toLabel(fontSize = 30, fontColor = civInfo.nation.getInnerColor())
if (cityScreen.canChangeState) currentCityLabel.onClick {
AskTextPopup(

View File

@ -386,16 +386,26 @@ object ImageGetter {
.surroundWithThinCircle(techIconColor)
}
fun getProgressBarHorizontal(width: Float, height: Float, percentComplete: Float, progressColor: Color, backgroundColor: Color): Group {
fun getProgressBarHorizontal(
width: Float, height: Float,
percentComplete: Float,
progressColor: Color,
backgroundColor: Color): Group {
return ProgressBar(width, height, false)
.setBackground(backgroundColor)
.setProgress(progressColor, percentComplete)
}
fun getProgressBarVertical(width: Float, height: Float, percentComplete: Float, progressColor: Color, backgroundColor: Color): Group {
fun getProgressBarVertical(
width: Float,
height: Float,
percentComplete: Float,
progressColor: Color,
backgroundColor: Color,
progressPadding: Float = 0f): Group {
return ProgressBar(width, height, true)
.setBackground(backgroundColor)
.setProgress(progressColor, percentComplete)
.setProgress(progressColor, percentComplete, padding = progressPadding)
}
class ProgressBar(width: Float, height: Float, val vertical: Boolean = true):Group() {
@ -427,7 +437,7 @@ object ImageGetter {
fun setBackground(color: Color): ProgressBar {
background = getWhiteDot()
background?.color = color
background?.color = color.cpy()
background?.setSize(width, height) //clamp between 0 and 1
background?.toBack()
background?.center(this)
@ -436,34 +446,44 @@ object ImageGetter {
return this
}
fun setSemiProgress(color: Color, percentage: Float): ProgressBar {
fun setSemiProgress(color: Color, percentage: Float, padding: Float = 0f): ProgressBar {
secondaryPercentage = percentage
secondaryProgress = getWhiteDot()
secondaryProgress?.color = color
secondaryProgress?.color = color.cpy()
if (vertical)
secondaryProgress?.setSize(width, height * max(min(percentage, 1f),0f))
secondaryProgress?.setSize(width-padding*2, height * max(min(percentage, 1f),0f))
else
secondaryProgress?.setSize(width * max(min(percentage, 1f),0f), height)
if (secondaryProgress != null)
secondaryProgress?.setSize(width * max(min(percentage, 1f),0f), height-padding*2)
if (secondaryProgress != null) {
addActor(secondaryProgress)
if (vertical)
secondaryProgress?.centerX(this)
else
secondaryProgress?.centerY(this)
}
return this
}
fun setProgress(color: Color, percentage: Float): ProgressBar {
fun setProgress(color: Color, percentage: Float, padding: Float = 0f): ProgressBar {
primaryPercentage = percentage
primaryProgress = getWhiteDot()
primaryProgress?.color = color
primaryProgress?.color = color.cpy()
if (vertical)
primaryProgress?.setSize(width, height * max(min(percentage, 1f),0f))
primaryProgress?.setSize(width-padding*2, height * max(min(percentage, 1f),0f))
else
primaryProgress?.setSize(width * max(min(percentage, 1f),0f), height)
if (primaryProgress != null)
primaryProgress?.setSize(width * max(min(percentage, 1f),0f), height-padding*2)
if (primaryProgress != null) {
addActor(primaryProgress)
if (vertical)
primaryProgress?.centerX(this)
else
primaryProgress?.centerY(this)
}
return this
}
}
fun getHealthBar(currentHealth: Float, maxHealth: Float, healthBarSize: Float): Table {
fun getHealthBar(currentHealth: Float, maxHealth: Float, healthBarSize: Float, height: Float=5f): Table {
val healthPercent = currentHealth / maxHealth
val healthBar = Table()
@ -473,10 +493,10 @@ object ImageGetter {
healthPercent > 1 / 3f -> Color.ORANGE
else -> Color.RED
}
healthBar.add(healthPartOfBar).size(healthBarSize * healthPercent, 5f)
healthBar.add(healthPartOfBar).size(healthBarSize * healthPercent, height)
val emptyPartOfBar = getDot(Color.BLACK)
healthBar.add(emptyPartOfBar).size(healthBarSize * (1 - healthPercent), 5f)
healthBar.add(emptyPartOfBar).size(healthBarSize * (1 - healthPercent), height)
healthBar.pad(1f)
healthBar.pack()

View File

@ -77,7 +77,9 @@ fun Policy.isPickable() : Boolean {
}
class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable(
style = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
path = "PolicyScreen/PolicyButton",
defaultBorder = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
defaultInner = BaseScreen.skinStrings.roundedEdgeRectangleSmallShape,
borderSize = 2f
) {
@ -93,10 +95,10 @@ class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable(
init {
icon.setSize(size*0.7f, size*0.7f)
highlight.setSize(size*1.35f, size*1.35f)
highlight.setSize(size*1.38f, size*1.38f)
addActor(icon)
add(highlight).center()
addActor(highlight)
updateState()
pack()
@ -595,7 +597,10 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
Colors.policyNotPickable}
}
val table = BorderedTable(style = skinStrings.roundedEdgeRectangleSmallShape, innerColor = color, borderSize = 2f)
val table = BorderedTable(
defaultInner = skinStrings.roundedEdgeRectangleSmallShape,
defaultBorder = skinStrings.roundedEdgeRectangleSmallShape,
innerColor = color, borderSize = 2f)
table.add(label).minHeight(30f).minWidth(150f).growX()
table.addActor(lockIcon)

View File

@ -7,31 +7,57 @@ import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.scenes.scene2d.ui.Label
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.Constants
import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.CityInfo
import com.unciv.logic.city.INonPerpetualConstruction
import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.translations.tr
import com.unciv.ui.cityscreen.CityReligionInfoTable
import com.unciv.ui.cityscreen.CityScreen
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popup.Popup
import com.unciv.ui.trade.DiplomacyScreen
import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.BorderedTable
import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.extensions.brighten
import com.unciv.ui.utils.extensions.center
import com.unciv.ui.utils.extensions.centerX
import com.unciv.ui.utils.extensions.centerY
import com.unciv.ui.utils.extensions.colorFromRGB
import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.setFontSize
import com.unciv.ui.utils.extensions.setSize
import com.unciv.ui.utils.extensions.surroundWithCircle
import com.unciv.ui.utils.extensions.surroundWithThinCircle
import com.unciv.ui.utils.extensions.toGroup
import com.unciv.ui.utils.extensions.toLabel
import kotlin.math.max
import kotlin.math.min
object Colors {
val construction = colorFromRGB(196,140,62)
val growh = colorFromRGB(130,225,78)
}
class IconTable(borderColor: Color, innerColor: Color, borderSize: Float, borderOnTop:Boolean=true): BorderedTable(
path = "WorldScreen/CityButton/IconTable",
defaultInner = BaseScreen.skinStrings.roundedEdgeRectangleMidShape,
defaultBorder = BaseScreen.skinStrings.roundedEdgeRectangleMidBorderShape,
borderColor = borderColor,
innerColor = innerColor,
borderSize = borderSize,
borderOnTop = borderOnTop
) {
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) }
}
class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Table(BaseScreen.skin){
val worldScreen = tileGroup.worldScreen
val uncivGame = worldScreen.game
@ -51,10 +77,8 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
clear()
setButtonActions()
addAirUnitTable()
if (showAdditionalInfoTags && city.health < city.getMaxHealth().toFloat()) {
val healthBar = ImageGetter.getHealthBar(city.health.toFloat(), city.getMaxHealth().toFloat(), 100f)
add(healthBar).row()
}
add(getDefenceTable()).row()
iconTable = getIconTable()
add(iconTable).row()
@ -62,12 +86,21 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
if (city.civInfo.isCityState() && city.civInfo.knows(worldScreen.viewingCiv)) {
val diplomacyManager = city.civInfo.getDiplomacyManager(worldScreen.viewingCiv)
val influenceBar = getInfluenceBar(diplomacyManager.getInfluence(), diplomacyManager.relationshipLevel())
add(influenceBar).row()
add(influenceBar).padTop(1f).row()
}
add(getStatuses()).padTop(3f)
pack()
if (showAdditionalInfoTags && city.health < city.getMaxHealth().toFloat()) {
val healthBar = ImageGetter.getHealthBar(city.health.toFloat(),
city.getMaxHealth().toFloat(), 100f, 3f)
addActor(healthBar)
healthBar.center(this)
healthBar.y = iconTable.y + iconTable.height-healthBar.height - 1f
}
setOrigin(Align.center)
centerX(tileGroup)
@ -128,9 +161,9 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
setOrigin(Align.center)
if (!isButtonMoved) {
rotation = 180f
setPosition(positionX - width/2, 0f)
setPosition(positionX - width/2, -height)
} else
setPosition(positionX - width/2, -height/4) // height compensation because of asymmetrical icon
setPosition(positionX - width/2, -height) // height compensation because of asymmetrical icon
}
iconTable.addActor(indicator)
listOfHiddenUnitMarkers.add(indicator)
@ -187,102 +220,127 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
}
}
private fun getIconTable(forPopup: Boolean = false): Table {
val secondaryColor = city.civInfo.nation.getInnerColor()
class IconTable: Table() {
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) }
private fun getDefenceTable(forPopup: Boolean = false): Group {
val borderColor = if (city.civInfo == worldScreen.viewingCiv)
colorFromRGB(255, 237, 200) else Color.BLACK
val table = BorderedTable(
path="WorldScreen/CityButton/DefenceTable",
innerColor = Color.BLACK,
borderColor = borderColor,
defaultInner = BaseScreen.skinStrings.roundedTopEdgeRectangleSmallShape,
defaultBorder = BaseScreen.skinStrings.roundedTopEdgeRectangleSmallBorderShape,
borderSize = 5f)
table.pad(2f, 3f, 0f, 3f)
val cityStrength = CityCombatant(city).getDefendingStrength()
val cityStrengthLabel = "${Fonts.strength}$cityStrength".toLabel(fontSize = 12)
cityStrengthLabel.setAlignment(Align.center)
if (!forPopup)
table.add(cityStrengthLabel).grow().center()
return table
}
private fun getStatuses() : Table {
val table = Table()
if (belongsToViewingCiv() && city.isConnectedToCapital() && !city.isCapital()) {
val connectionImage = ImageGetter.getStatIcon("CityConnection")
table.add(connectionImage).size(16f)
}
val iconTable = IconTable().apply { isTransform = false }
iconTable.touchable = Touchable.enabled
iconTable.background = BaseScreen.skinStrings.getUiBackground("WorldScreen/CityButton/IconTable", BaseScreen.skinStrings.roundedEdgeRectangleShape, city.civInfo.nation.getOuterColor())
if (city.isInResistance()) {
val resistanceImage = ImageGetter.getImage("StatIcons/Resistance")
iconTable.add(resistanceImage).size(20f).padLeft(5f)
table.add(resistanceImage).size(16f).padLeft(2f)
}
if (city.isPuppet) {
val puppetImage = ImageGetter.getImage("OtherIcons/Puppet")
puppetImage.color = secondaryColor
iconTable.add(puppetImage).size(20f).padLeft(5f)
table.add(puppetImage).size(16f).padLeft(2f)
}
if (city.isBeingRazed) {
val fireImage = ImageGetter.getImage("OtherIcons/Fire")
iconTable.add(fireImage).size(20f).padLeft(5f)
table.add(fireImage).size(16f).padLeft(2f)
}
return table
}
private fun getIconTable(forPopup: Boolean = false): Table {
val secondaryColor = city.civInfo.nation.getInnerColor()
val borderColor = if (city.civInfo == worldScreen.viewingCiv)
colorFromRGB(233, 233, 172) else Color.BLACK
val borderSize = if (city.civInfo == worldScreen.viewingCiv) 4f else 2f /* 7 */
val iconTable = IconTable(
borderColor = borderColor,
innerColor = city.civInfo.nation.getOuterColor().cpy().apply { a = 0.9f },
borderSize = borderSize
).apply {
isTransform = false
}
iconTable.pad(0f).padLeft(5f)
iconTable.touchable = Touchable.enabled
val popGroup = getPopulationGroup(uncivGame.viewEntireMapForDebug
|| belongsToViewingCiv()
|| worldScreen.viewingCiv.isSpectator())
val popGroupCell = iconTable.add(popGroup).minHeight(34f)
val labelTable = Table()
if (city.isCapital()) {
if (city.civInfo.isCityState()) {
val cityStateImage = ImageGetter.getNationIcon("CityState")
.apply { color = secondaryColor }
iconTable.add(cityStateImage).size(20f).padLeft(5f)
.apply { color = secondaryColor }
labelTable.add(cityStateImage).size(20f).padRight(5f)
} else {
val starImage = ImageGetter.getImage("OtherIcons/Star").apply { color = Color.LIGHT_GRAY }
iconTable.add(starImage).size(20f).padLeft(5f)
val starImage = ImageGetter.getImage("OtherIcons/Capital")
labelTable.add(starImage).size(20f).padRight(5f)
}
} else if (belongsToViewingCiv() && city.isConnectedToCapital()) {
val connectionImage = ImageGetter.getStatIcon("CityConnection")
connectionImage.color = secondaryColor
iconTable.add(connectionImage).size(20f).padLeft(5f)
}
val populationGroup = getPopulationGroup(uncivGame.viewEntireMapForDebug
|| belongsToViewingCiv()
|| worldScreen.viewingCiv.isSpectator())
iconTable.add(populationGroup).padLeft(5f)
populationGroup.toBack()
val cityButtonText = city.name.tr()
val label = cityButtonText.toLabel(secondaryColor).apply { setAlignment(Align.center) }
labelTable.add(label).growY().center()
val cityButtonText = city.name
val label = cityButtonText.toLabel(secondaryColor)
val rightPadding = if (city.civInfo.isCityState()) 10f else 20f // CS needs less padding here as there will be an icon
iconTable.add(label).padRight(rightPadding).padLeft(20f) // sufficient horizontal padding
.fillY() // provide full-height clicking area
label.toBack() // this is so the label is rendered right before the population group,
// so we save the font texture and avoid another texture switch
val cityStrength = CityCombatant(city).getDefendingStrength()
val cityStrengthLabel =
"${Fonts.strength}$cityStrength".toLabel(city.civInfo.nation.getInnerColor(), 10)
if (!forPopup) {
// City strength is added NOT inside the table, but rather - top-center to it
iconTable.addActor(cityStrengthLabel) // We create this here to we can .toBack() it as well.
cityStrengthLabel.toBack()
val cityReligion = city.religion.getMajorityReligion()
if (cityReligion != null) {
val religionImage = ImageGetter.getReligionImage(cityReligion.getIconName()).apply {
color = secondaryColor }.toGroup(20f)
labelTable.add(religionImage).size(20f).padLeft(5f)
}
}
labelTable.pack()
iconTable.add(labelTable).padLeft(10f).padRight(10f).expandY().minHeight(32f).center()
label.toFront()
if (city.civInfo.isCityState()) {
val cityStateImage = ImageGetter.getImage("CityStateIcons/" +city.civInfo.cityStateType.name).apply { color = secondaryColor }
iconTable.add(cityStateImage).size(20f).fillY()
iconTable.padLeft(10f)
iconTable.add(cityStateImage).size(20f).fillY().padRight(5f)
}
if (uncivGame.viewEntireMapForDebug || belongsToViewingCiv() || worldScreen.viewingCiv.isSpectator()) {
val constructionGroup = getConstructionGroup(city.cityConstructions)
iconTable.add(constructionGroup)
constructionGroup.toBack() // We do this so the construction group is right before the label.
// What we end up with is construction group > label > population group.
// Since the label in the construction group is rendered *last* (toFront()),
// and the two labels in the the population group are rendered *first* (toBack()),
// What we get is that ALL 4 LABELS are rendered one after the other,
// and so the glyph texture only needs to be swapped in once rather than 4 times! :)
} else if (city.civInfo.isMajorCiv()) {
val nationIcon = ImageGetter.getNationIcon(city.civInfo.nation.name)
nationIcon.color = secondaryColor
iconTable.add(nationIcon).size(20f)
}
if (!forPopup) {
val cityReligion = city.religion.getMajorityReligion()
if (cityReligion != null) {
val religionImage = ImageGetter.getReligionImage(cityReligion.getIconName()).apply { color = city.civInfo.nation.getInnerColor() }
iconTable.add(religionImage).size(20f).padLeft(5f).fillY()
}
iconTable.add(nationIcon).size(20f).padRight(7f)
popGroupCell.padLeft(5f)
}
if (city.civInfo == worldScreen.viewingCiv)
iconTable.bgBorder.toFront()
else
iconTable.bgBorder.toBack()
iconTable.pack()
if (!forPopup) {
cityStrengthLabel.x = label.x // so it'll be aligned right above the city name
cityStrengthLabel.setY(iconTable.height, Align.top)
}
return iconTable
}
@ -309,23 +367,20 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
}
private fun getPopulationGroup(showGrowth: Boolean): Group {
val growthGreen = Color(0.0f, 0.5f, 0.0f, 1.0f)
class PopulationGroup:Group() { // for recognition in the profiler
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) }
val secondaryColor = city.civInfo.nation.getInnerColor()
val table = Table().apply { isTransform = false }
val popLabel = city.population.population.toString()
.toLabel(fontColor = secondaryColor, fontSize = 18).apply {
setAlignment(Align.center)
}
val group = PopulationGroup().apply { isTransform=false }
table.add(popLabel).minHeight(30f).apply {
if (showGrowth)
minWidth(26f)
else
minWidth(17f)
}.pad(0f)
val populationLabel = city.population.population.toLabel()
populationLabel.color = city.civInfo.nation.getInnerColor()
group.addActor(populationLabel)
val groupHeight = 25f
var groupWidth = populationLabel.width
if (showGrowth) groupWidth += 12f
group.setSize(groupWidth, groupHeight)
table.pack()
if (showGrowth) {
var growthPercentage = city.population.foodStored / city.population.getFoodToNextPopulation().toFloat()
@ -335,83 +390,64 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
// Without it, it caused the growth bar's height to exceed that of the group's.
if (growthPercentage > 1) growthPercentage = 1.0f
val growthBar = ImageGetter.getProgressBarVertical(2f, groupHeight,
if (city.isStarving()) 1.0f else growthPercentage,
if (city.isStarving()) Color.RED else growthGreen, Color.BLACK)
growthBar.x = populationLabel.width + 3
growthBar.centerY(group)
val growthBar = ImageGetter.getProgressBarVertical(4f, 30f,
if (city.isStarving()) 1.0f else growthPercentage,
if (city.isStarving()) Color.RED else Colors.growh, Color.BLACK, 1f)
growthBar.color.a = 0.8f
table.add(growthBar).padTop(1f).padBottom(1f)
group.addActor(growthBar)
val turnLabel: Label = when {
val turnLabelText = when {
city.isGrowing() -> {
val turnsToGrowth = city.getNumTurnsToNewPopulation()
if (turnsToGrowth != null && turnsToGrowth < 100) turnsToGrowth.toString().toLabel() else "".toLabel()
if (turnsToGrowth != null && turnsToGrowth < 100) turnsToGrowth.toString() else ""
}
city.isStarving() -> {
val turnsToStarvation = city.getNumTurnsToStarvation()
if (turnsToStarvation != null && turnsToStarvation < 100) turnsToStarvation.toString().toLabel() else "".toLabel()
if (turnsToStarvation != null && turnsToStarvation < 100) turnsToStarvation.toString() else ""
}
else -> "".toLabel()
else -> ""
}
turnLabel.color = city.civInfo.nation.getInnerColor()
turnLabel.setFontSize(14)
turnLabel.pack()
group.addActor(turnLabel)
turnLabel.toBack() // this is so both labels are rendered next to each other -
// this is important because when switching to a label, we switch out the texture we're using to use the font texture,
// so this has a direct impact on framerate!
turnLabel.x = growthBar.x + growthBar.width + 1
val turnLabel = turnLabelText.toLabel(fontColor = secondaryColor, fontSize = 13)
table.add(turnLabel).expandY().bottom().padLeft(3f)
turnLabel.toBack()
}
populationLabel.centerY(group)
return group
return table
}
private fun getConstructionGroup(cityConstructions: CityConstructions): Group {
val secondaryColor = city.civInfo.nation.getInnerColor()
val cityCurrentConstruction = cityConstructions.getCurrentConstruction()
class ConstructionGroup : Group() { // for recognition in the profiler
override fun draw(batch: Batch?, parentAlpha: Float) {
super.draw(batch, parentAlpha)
}
}
val table = Table().apply { isTransform = false }
val tableHeight = 30f
val group = ConstructionGroup().apply { isTransform = false }
val groupHeight = 25f
val groupWidth = if (cityCurrentConstruction is PerpetualConstruction) 15f else 40f
group.setSize(groupWidth, groupHeight)
if (cityConstructions.currentConstructionFromQueue.isNotEmpty()) {
val constructionImage = ImageGetter.getPortraitImage(cityCurrentConstruction.name, 25f)
constructionImage.centerY(group)
constructionImage.x = group.width - constructionImage.width
group.addActor(constructionImage)
if (cityCurrentConstruction !is PerpetualConstruction) {
val turnsToConstruction = cityConstructions.turnsToConstruction(cityCurrentConstruction.name)
val label = (if (turnsToConstruction < 100) turnsToConstruction.toString() else "").toLabel(secondaryColor, 13)
table.add(label).expandY().bottom().padRight(3f)
val constructionPercentage = cityConstructions.getWorkDone(cityCurrentConstruction.name) /
(cityCurrentConstruction as INonPerpetualConstruction).getProductionCost(cityConstructions.cityInfo.civInfo).toFloat()
val productionBar = ImageGetter.getProgressBarVertical(4f, tableHeight, constructionPercentage,
Colors.construction, Color.BLACK, 1f)
productionBar.color.a = 0.8f
table.add(productionBar).padTop(1f).padBottom(1f)
}
val constructionImage = ImageGetter.getPortraitImage(cityCurrentConstruction.name, 24f)
table.add(constructionImage).minHeight(32f).minWidth(26f)
.expand().center().right().pad(0f).padRight(4f).padLeft(3f)
table.pack()
}
val secondaryColor = cityConstructions.cityInfo.civInfo.nation.getInnerColor()
if (cityCurrentConstruction !is PerpetualConstruction) {
val turnsToConstruction = cityConstructions.turnsToConstruction(cityCurrentConstruction.name)
val label = (if (turnsToConstruction < 100) turnsToConstruction.toString() else "").toLabel(secondaryColor, 14)
label.pack()
group.addActor(label)
val constructionPercentage = cityConstructions.getWorkDone(cityCurrentConstruction.name) /
(cityCurrentConstruction as INonPerpetualConstruction).getProductionCost(cityConstructions.cityInfo.civInfo).toFloat()
val productionBar = ImageGetter.getProgressBarVertical(2f, groupHeight, constructionPercentage,
Color.BROWN.brighten(0.5f), Color.BLACK)
productionBar.x = 10f
label.x = productionBar.x - label.width - 3
group.addActor(productionBar)
productionBar.toBack() // Since the production bar is based on whiteDot.png in the MAIN texture,
// and the constructionImage may be a building or unit which have their own textures,
// we move the production bar's rendering to be next to the circle's rendering,
// so we have circle - bar - constructionImage - label (2 texture switches and ending with label)
// which is the minimal amount of switches we can have here
label.toFront()
}
return group
return table
}
private fun foreignCityInfoPopup() {

View File

@ -1,44 +1,64 @@
package com.unciv.ui.utils
import com.badlogic.gdx.graphics.Color
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.unciv.ui.utils.extensions.center
open class BorderedTable(
val style: String = BaseScreen.skinStrings.rectangleWithOutlineShape,
val path: String = "",
val defaultInner: String = BaseScreen.skinStrings.rectangleWithOutlineShape,
val defaultBorder: String = BaseScreen.skinStrings.rectangleWithOutlineShape,
val borderColor: Color = Color.WHITE,
val innerColor: Color = Color.BLACK,
val borderSize: Float = 5f
val borderSize: Float = 5f,
val borderOnTop: Boolean = false
) : Table() {
private var bgBorder: Image = Image(BaseScreen.skinStrings.getUiBackground("", style, Color.WHITE))
private var bgInner: Image = Image(BaseScreen.skinStrings.getUiBackground("", style, innerColor))
var bgBorder: Image = Image(BaseScreen.skinStrings.getUiBackground(path, defaultBorder, borderColor))
var bgInner: Image = Image(BaseScreen.skinStrings.getUiBackground(path, defaultInner, innerColor))
init {
this.addActor(bgBorder)
if (borderSize != 0f)
this.addActor(bgBorder)
this.addActor(bgInner)
bgInner.toBack()
bgBorder.toBack()
if (borderOnTop) {
if (borderSize != 0f)
bgBorder.toBack()
bgInner.toBack()
} else {
bgInner.toBack()
if (borderSize != 0f)
bgBorder.toBack()
}
}
fun setBackgroundColor(color: Color) {
bgInner.remove()
bgInner = Image(BaseScreen.skinStrings.getUiBackground("", style, color))
bgInner = Image(BaseScreen.skinStrings.getUiBackground(path, defaultInner, color))
addActor(bgInner)
bgInner.zIndex = bgBorder.zIndex + 1
if (borderSize != 0f) {
if (borderOnTop)
bgBorder.zIndex = bgInner.zIndex + 1
else
bgInner.zIndex = bgBorder.zIndex + 1
}
sizeChanged()
}
override fun sizeChanged() {
super.sizeChanged()
bgBorder.setSize(width + borderSize, height + borderSize)
if (borderSize != 0f)
bgBorder.setSize(width + borderSize, height + borderSize)
bgInner.setSize(width, height)
bgBorder.center(this)
if (borderSize != 0f)
bgBorder.center(this)
bgInner.center(this)
}
}

View File

@ -313,7 +313,7 @@ fun Actor.surroundWithCircle(size: Float, resizeActor: Boolean = true, color: Co
return IconCircleGroup(size, this, resizeActor, color)
}
fun Actor.surroundWithThinCircle(color: Color=Color.BLACK): IconCircleGroup = surroundWithCircle(width*1.05f, false, color)
fun Actor.surroundWithThinCircle(color: Color=Color.BLACK): IconCircleGroup = surroundWithCircle(width+2f, false, color)
fun Actor.addBorder(size:Float, color: Color, expandCell:Boolean = false): Table {

View File

@ -676,7 +676,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
- [Aircraft](https://thenounproject.com/search/?q=aircraft&i=1629000) By Tom Fricker for aircraft icon in city button
- [radar scan](https://thenounproject.com/search/?q=range&i=1500234) By icon 54 for Range
- [short range radar](https://thenounproject.com/search/?q=air%20range&i=2612731) by Vectors Point for Intercept range
- [Puppet](https://thenounproject.com/search/?q=puppet&i=285735) By Ben Davis for puppeted cities
- Puppet by [vegeta1k95](https://github.com/veget1k95) based on [Puppet](https://thenounproject.com/search/?q=puppet&i=285735) by Ben Davis, for puppeted cities
- [City](https://thenounproject.com/search/?q=city&i=1765370) By Muhajir ila Robbi in the Icon center
- [Lock](https://thenounproject.com/search/?q=lock&i=3217613) by Vadim Solomakhin for locked tiles
- [Hourglass](https://thenounproject.com/search/?q=hourglass&i=142268) by I Create Stuff for the 'Turn' icon

View File

@ -104,7 +104,6 @@ These shapes are used all over Unciv and can be replaced to make a lot of UI ele
| WorldScreen/ | TutorialTaskTable | null | |
| WorldScreen/ | UnitTable | null | |
| WorldScreen/CityButton/ | AirUnitTable | roundedEdgeRectangle | |
| WorldScreen/CityButton/ | IconTable | roundedEdgeRectangle | |
| WorldScreen/CityButton/ | InfluenceBar | null | |
| WorldScreen/Minimap/ | Background | null | |
| WorldScreen/Minimap/ | Border | null | |