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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 Skin.png
size: 128, 128 size: 512, 64
format: RGBA8888 format: RGBA8888
filter: Linear, Linear filter: Linear, Linear
repeat: none repeat: none
Skins/Minimal/checkbox Skins/Minimal/checkbox
rotate: false rotate: false
xy: 52, 16 xy: 291, 23
size: 31, 31 size: 31, 31
split: 0, 0, 0, 0 split: 0, 0, 0, 0
orig: 31, 31 orig: 31, 31
@ -14,7 +14,7 @@ Skins/Minimal/checkbox
index: -1 index: -1
Skins/Minimal/checkbox-pressed Skins/Minimal/checkbox-pressed
rotate: false rotate: false
xy: 64, 55 xy: 252, 23
size: 31, 31 size: 31, 31
split: 0, 0, 0, 0 split: 0, 0, 0, 0
orig: 31, 31 orig: 31, 31
@ -22,7 +22,7 @@ Skins/Minimal/checkbox-pressed
index: -1 index: -1
Skins/Minimal/rectangleWithOutline Skins/Minimal/rectangleWithOutline
rotate: false rotate: false
xy: 112, 121 xy: 64, 5
size: 3, 3 size: 3, 3
split: 0, 0, 0, 0 split: 0, 0, 0, 0
orig: 3, 3 orig: 3, 3
@ -30,25 +30,59 @@ Skins/Minimal/rectangleWithOutline
index: -1 index: -1
Skins/Minimal/roundedEdgeRectangle Skins/Minimal/roundedEdgeRectangle
rotate: false rotate: false
xy: 4, 74 xy: 4, 4
size: 52, 50 size: 52, 50
split: 19, 20, 19, 21 split: 19, 20, 19, 21
pad: 12, 13, 7, 7 pad: 12, 13, 7, 7
orig: 52, 50 orig: 52, 50
offset: 0, 0 offset: 0, 0
index: -1 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 Skins/Minimal/roundedEdgeRectangle-small
rotate: false rotate: false
xy: 4, 4 xy: 330, 30
size: 24, 24 size: 24, 24
split: 10, 10, 10, 10 split: 10, 10, 10, 10
pad: 10, 10, 2, 2 pad: 10, 10, 2, 2
orig: 24, 24 orig: 24, 24
offset: 0, 0 offset: 0, 0
index: -1 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 Skins/Minimal/select-box
rotate: false rotate: false
xy: 64, 94 xy: 204, 24
size: 40, 30 size: 40, 30
split: 7, 9, 7, 7 split: 7, 9, 7, 7
orig: 40, 30 orig: 40, 30
@ -56,7 +90,7 @@ Skins/Minimal/select-box
index: -1 index: -1
Skins/Minimal/select-box-pressed Skins/Minimal/select-box-pressed
rotate: false rotate: false
xy: 4, 36 xy: 156, 24
size: 40, 30 size: 40, 30
split: 7, 9, 7, 7 split: 7, 9, 7, 7
orig: 40, 30 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 // Default shapes must always end with "Shape" so the UiElementDocsWriter can identify them
val roundedEdgeRectangleSmallShape = skinLocation + "roundedEdgeRectangle-small" 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 roundedEdgeRectangleShape = skinLocation + "roundedEdgeRectangle"
val rectangleWithOutlineShape = skinLocation + "rectangleWithOutline" val rectangleWithOutlineShape = skinLocation + "rectangleWithOutline"
val selectBoxShape = skinLocation + "select-box" 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) 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) { if (city.isPuppet) {
val starImage = ImageGetter.getImage("OtherIcons/Puppet").apply { color = Color.LIGHT_GRAY } val starImage = ImageGetter.getImage("OtherIcons/Puppet").apply { color = Color.LIGHT_GRAY }
cityNameTable.add(starImage).size(20f).padRight(5f) 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) 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()) val currentCityLabel = city.name.toLabel(fontSize = 30, fontColor = civInfo.nation.getInnerColor())
if (cityScreen.canChangeState) currentCityLabel.onClick { if (cityScreen.canChangeState) currentCityLabel.onClick {
AskTextPopup( AskTextPopup(

View File

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

View File

@ -77,7 +77,9 @@ fun Policy.isPickable() : Boolean {
} }
class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable( 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 borderSize = 2f
) { ) {
@ -93,10 +95,10 @@ class PolicyButton(val policy: Policy, size: Float = 30f) : BorderedTable(
init { init {
icon.setSize(size*0.7f, size*0.7f) 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) addActor(icon)
add(highlight).center() addActor(highlight)
updateState() updateState()
pack() pack()
@ -595,7 +597,10 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo
Colors.policyNotPickable} 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.add(label).minHeight(30f).minWidth(150f).growX()
table.addActor(lockIcon) 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.Group
import com.badlogic.gdx.scenes.scene2d.Touchable import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.actions.Actions 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.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.logic.battle.CityCombatant import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.CityInfo import com.unciv.logic.city.CityInfo
import com.unciv.logic.city.INonPerpetualConstruction import com.unciv.logic.city.INonPerpetualConstruction
import com.unciv.logic.city.PerpetualConstruction import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.translations.tr
import com.unciv.ui.cityscreen.CityReligionInfoTable import com.unciv.ui.cityscreen.CityReligionInfoTable
import com.unciv.ui.cityscreen.CityScreen import com.unciv.ui.cityscreen.CityScreen
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popup.Popup import com.unciv.ui.popup.Popup
import com.unciv.ui.trade.DiplomacyScreen import com.unciv.ui.trade.DiplomacyScreen
import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.BorderedTable
import com.unciv.ui.utils.Fonts import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.extensions.brighten 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.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.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 com.unciv.ui.utils.extensions.toLabel
import kotlin.math.max import kotlin.math.max
import kotlin.math.min 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){ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Table(BaseScreen.skin){
val worldScreen = tileGroup.worldScreen val worldScreen = tileGroup.worldScreen
val uncivGame = worldScreen.game val uncivGame = worldScreen.game
@ -51,10 +77,8 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
clear() clear()
setButtonActions() setButtonActions()
addAirUnitTable() addAirUnitTable()
if (showAdditionalInfoTags && city.health < city.getMaxHealth().toFloat()) {
val healthBar = ImageGetter.getHealthBar(city.health.toFloat(), city.getMaxHealth().toFloat(), 100f) add(getDefenceTable()).row()
add(healthBar).row()
}
iconTable = getIconTable() iconTable = getIconTable()
add(iconTable).row() 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)) { if (city.civInfo.isCityState() && city.civInfo.knows(worldScreen.viewingCiv)) {
val diplomacyManager = city.civInfo.getDiplomacyManager(worldScreen.viewingCiv) val diplomacyManager = city.civInfo.getDiplomacyManager(worldScreen.viewingCiv)
val influenceBar = getInfluenceBar(diplomacyManager.getInfluence(), diplomacyManager.relationshipLevel()) val influenceBar = getInfluenceBar(diplomacyManager.getInfluence(), diplomacyManager.relationshipLevel())
add(influenceBar).row() add(influenceBar).padTop(1f).row()
} }
add(getStatuses()).padTop(3f)
pack() 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) setOrigin(Align.center)
centerX(tileGroup) centerX(tileGroup)
@ -128,9 +161,9 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
setOrigin(Align.center) setOrigin(Align.center)
if (!isButtonMoved) { if (!isButtonMoved) {
rotation = 180f rotation = 180f
setPosition(positionX - width/2, 0f) setPosition(positionX - width/2, -height)
} else } 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) iconTable.addActor(indicator)
listOfHiddenUnitMarkers.add(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 { private fun getDefenceTable(forPopup: Boolean = false): Group {
val secondaryColor = city.civInfo.nation.getInnerColor() val borderColor = if (city.civInfo == worldScreen.viewingCiv)
class IconTable: Table() { colorFromRGB(255, 237, 200) else Color.BLACK
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) }
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()) { if (city.isInResistance()) {
val resistanceImage = ImageGetter.getImage("StatIcons/Resistance") val resistanceImage = ImageGetter.getImage("StatIcons/Resistance")
iconTable.add(resistanceImage).size(20f).padLeft(5f) table.add(resistanceImage).size(16f).padLeft(2f)
} }
if (city.isPuppet) { if (city.isPuppet) {
val puppetImage = ImageGetter.getImage("OtherIcons/Puppet") val puppetImage = ImageGetter.getImage("OtherIcons/Puppet")
puppetImage.color = secondaryColor table.add(puppetImage).size(16f).padLeft(2f)
iconTable.add(puppetImage).size(20f).padLeft(5f)
} }
if (city.isBeingRazed) { if (city.isBeingRazed) {
val fireImage = ImageGetter.getImage("OtherIcons/Fire") 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.isCapital()) {
if (city.civInfo.isCityState()) { if (city.civInfo.isCityState()) {
val cityStateImage = ImageGetter.getNationIcon("CityState") val cityStateImage = ImageGetter.getNationIcon("CityState")
.apply { color = secondaryColor } .apply { color = secondaryColor }
iconTable.add(cityStateImage).size(20f).padLeft(5f) labelTable.add(cityStateImage).size(20f).padRight(5f)
} else { } else {
val starImage = ImageGetter.getImage("OtherIcons/Star").apply { color = Color.LIGHT_GRAY } val starImage = ImageGetter.getImage("OtherIcons/Capital")
iconTable.add(starImage).size(20f).padLeft(5f) 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 val cityButtonText = city.name.tr()
|| belongsToViewingCiv() val label = cityButtonText.toLabel(secondaryColor).apply { setAlignment(Align.center) }
|| worldScreen.viewingCiv.isSpectator()) labelTable.add(label).growY().center()
iconTable.add(populationGroup).padLeft(5f)
populationGroup.toBack()
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) { if (!forPopup) {
// City strength is added NOT inside the table, but rather - top-center to it val cityReligion = city.religion.getMajorityReligion()
iconTable.addActor(cityStrengthLabel) // We create this here to we can .toBack() it as well. if (cityReligion != null) {
cityStrengthLabel.toBack() 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()) { if (city.civInfo.isCityState()) {
val cityStateImage = ImageGetter.getImage("CityStateIcons/" +city.civInfo.cityStateType.name).apply { color = secondaryColor } 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()) { if (uncivGame.viewEntireMapForDebug || belongsToViewingCiv() || worldScreen.viewingCiv.isSpectator()) {
val constructionGroup = getConstructionGroup(city.cityConstructions) val constructionGroup = getConstructionGroup(city.cityConstructions)
iconTable.add(constructionGroup) 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()) { } else if (city.civInfo.isMajorCiv()) {
val nationIcon = ImageGetter.getNationIcon(city.civInfo.nation.name) val nationIcon = ImageGetter.getNationIcon(city.civInfo.nation.name)
nationIcon.color = secondaryColor nationIcon.color = secondaryColor
iconTable.add(nationIcon).size(20f) iconTable.add(nationIcon).size(20f).padRight(7f)
} popGroupCell.padLeft(5f)
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()
}
} }
if (city.civInfo == worldScreen.viewingCiv)
iconTable.bgBorder.toFront()
else
iconTable.bgBorder.toBack()
iconTable.pack() 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 return iconTable
} }
@ -309,23 +367,20 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
} }
private fun getPopulationGroup(showGrowth: Boolean): Group { private fun getPopulationGroup(showGrowth: Boolean): Group {
val growthGreen = Color(0.0f, 0.5f, 0.0f, 1.0f) val secondaryColor = city.civInfo.nation.getInnerColor()
val table = Table().apply { isTransform = false }
val popLabel = city.population.population.toString()
class PopulationGroup:Group() { // for recognition in the profiler .toLabel(fontColor = secondaryColor, fontSize = 18).apply {
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) } 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() table.pack()
populationLabel.color = city.civInfo.nation.getInnerColor()
group.addActor(populationLabel)
val groupHeight = 25f
var groupWidth = populationLabel.width
if (showGrowth) groupWidth += 12f
group.setSize(groupWidth, groupHeight)
if (showGrowth) { if (showGrowth) {
var growthPercentage = city.population.foodStored / city.population.getFoodToNextPopulation().toFloat() 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. // Without it, it caused the growth bar's height to exceed that of the group's.
if (growthPercentage > 1) growthPercentage = 1.0f if (growthPercentage > 1) growthPercentage = 1.0f
val growthBar = ImageGetter.getProgressBarVertical(2f, groupHeight, val growthBar = ImageGetter.getProgressBarVertical(4f, 30f,
if (city.isStarving()) 1.0f else growthPercentage, if (city.isStarving()) 1.0f else growthPercentage,
if (city.isStarving()) Color.RED else growthGreen, Color.BLACK) if (city.isStarving()) Color.RED else Colors.growh, Color.BLACK, 1f)
growthBar.x = populationLabel.width + 3 growthBar.color.a = 0.8f
growthBar.centerY(group) table.add(growthBar).padTop(1f).padBottom(1f)
group.addActor(growthBar) val turnLabelText = when {
val turnLabel: Label = when {
city.isGrowing() -> { city.isGrowing() -> {
val turnsToGrowth = city.getNumTurnsToNewPopulation() val turnsToGrowth = city.getNumTurnsToNewPopulation()
if (turnsToGrowth != null && turnsToGrowth < 100) turnsToGrowth.toString().toLabel() else "".toLabel() if (turnsToGrowth != null && turnsToGrowth < 100) turnsToGrowth.toString() else ""
} }
city.isStarving() -> { city.isStarving() -> {
val turnsToStarvation = city.getNumTurnsToStarvation() 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) val turnLabel = turnLabelText.toLabel(fontColor = secondaryColor, fontSize = 13)
turnLabel.toBack() // this is so both labels are rendered next to each other - table.add(turnLabel).expandY().bottom().padLeft(3f)
// this is important because when switching to a label, we switch out the texture we're using to use the font texture, turnLabel.toBack()
// so this has a direct impact on framerate!
turnLabel.x = growthBar.x + growthBar.width + 1
} }
populationLabel.centerY(group) return table
return group
} }
private fun getConstructionGroup(cityConstructions: CityConstructions): Group { private fun getConstructionGroup(cityConstructions: CityConstructions): Group {
val secondaryColor = city.civInfo.nation.getInnerColor()
val cityCurrentConstruction = cityConstructions.getCurrentConstruction() val cityCurrentConstruction = cityConstructions.getCurrentConstruction()
class ConstructionGroup : Group() { // for recognition in the profiler val table = Table().apply { isTransform = false }
override fun draw(batch: Batch?, parentAlpha: Float) { val tableHeight = 30f
super.draw(batch, parentAlpha)
}
}
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()) { if (cityConstructions.currentConstructionFromQueue.isNotEmpty()) {
val constructionImage = ImageGetter.getPortraitImage(cityCurrentConstruction.name, 25f)
constructionImage.centerY(group) if (cityCurrentConstruction !is PerpetualConstruction) {
constructionImage.x = group.width - constructionImage.width val turnsToConstruction = cityConstructions.turnsToConstruction(cityCurrentConstruction.name)
group.addActor(constructionImage) 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) / return table
(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
} }
private fun foreignCityInfoPopup() { private fun foreignCityInfoPopup() {

View File

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