Units/Buildings can now be given optional Portraits to be displayed instead of flags. (#8107)

* Units can now be given optional Portraits to be displayed everywhere (except world map) instead of unit Flags.

* Add section to wiki for adding Unit Portraits

* Generify portrait functions

* Small nitpicks fixed

* Reverted one change

* Fix docs

* Fix docs

Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
vegeta1k95
2022-12-11 21:58:11 +01:00
committed by GitHub
parent 154c083d3f
commit a134242487
12 changed files with 467 additions and 448 deletions

View File

@ -309,7 +309,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
table.defaults().pad(2f).minWidth(40f) table.defaults().pad(2f).minWidth(40f)
if (isFirstConstructionOfItsKind) table.add(getProgressBar(constructionName)).minWidth(5f) if (isFirstConstructionOfItsKind) table.add(getProgressBar(constructionName)).minWidth(5f)
else table.add().minWidth(5f) else table.add().minWidth(5f)
table.add(ImageGetter.getConstructionImage(constructionName).surroundWithCircle(40f)).padRight(10f) table.add(ImageGetter.getPortraitImage(constructionName, 40f)).padRight(10f)
table.add(text.toLabel()).expandX().fillX().left() table.add(text.toLabel()).expandX().fillX().left()
if (constructionQueueIndex > 0) table.add(getRaisePriorityButton(constructionQueueIndex, constructionName, city)).right() if (constructionQueueIndex > 0) table.add(getRaisePriorityButton(constructionQueueIndex, constructionName, city)).right()
@ -362,7 +362,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
} }
pickConstructionButton.add(getProgressBar(construction.name)).padRight(5f) pickConstructionButton.add(getProgressBar(construction.name)).padRight(5f)
pickConstructionButton.add(ImageGetter.getConstructionImage(construction.name).surroundWithCircle(40f)).padRight(10f) pickConstructionButton.add(ImageGetter.getPortraitImage(construction.name, 40f)).padRight(10f)
pickConstructionButton.add(constructionButtonDTO.buttonText.toLabel()).expandX().fillX() pickConstructionButton.add(constructionButtonDTO.buttonText.toLabel()).expandX().fillX()
if (!cannotAddConstructionToQueue(construction, cityScreen.city, cityScreen.city.cityConstructions)) { if (!cannotAddConstructionToQueue(construction, cityScreen.city, cityScreen.city.cityConstructions)) {

View File

@ -120,7 +120,7 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(BaseScreen.skin)
} }
private fun addBuildingInfo(building: Building, destinationTable: Table) { private fun addBuildingInfo(building: Building, destinationTable: Table) {
val icon = ImageGetter.getConstructionImage(building.name).surroundWithCircle(30f) val icon = ImageGetter.getPortraitImage(building.name, 30f)
val isFree = building.name in cityScreen.city.civInfo.civConstructions.getFreeBuildings(cityScreen.city.id) val isFree = building.name in cityScreen.city.civInfo.civConstructions.getFreeBuildings(cityScreen.city.id)
val displayName = if (isFree) "{${building.name}} ({Free})" else building.name val displayName = if (isFree) "{${building.name}} ({Free})" else building.name
val buildingNameAndIconTable = ExpanderTab( val buildingNameAndIconTable = ExpanderTab(

View File

@ -56,7 +56,7 @@ class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
selectedConstructionTable.run { selectedConstructionTable.run {
pad(10f) pad(10f)
add(ImageGetter.getConstructionImage(construction.name).surroundWithCircle(50f)) add(ImageGetter.getPortraitImage(construction.name, 50f))
.pad(5f) .pad(5f)
var buildingText = construction.name.tr() var buildingText = construction.name.tr()

View File

@ -52,8 +52,7 @@ object CivilopediaImageGetters {
} }
val construction = { name: String, size: Float -> val construction = { name: String, size: Float ->
ImageGetter.getConstructionImage(name) ImageGetter.getPortraitImage(name, size)
.surroundWithCircle(size)
} }
val improvement = { name: String, size: Float -> val improvement = { name: String, size: Float ->
ImageGetter.getImprovementIcon(name, size) ImageGetter.getImprovementIcon(name, size)

View File

@ -252,11 +252,24 @@ object ImageGetter {
return iconGroup return iconGroup
} }
fun getConstructionImage(construction: String): Image { fun getPortraitImage(construction: String, size: Float): Group {
if (ruleset.buildings.containsKey(construction)) return getImage("BuildingIcons/$construction") if (ruleset.buildings.containsKey(construction)) {
if (ruleset.units.containsKey(construction)) return getUnitIcon(construction) val buildingPortraitLocation = "BuildingPortraits/$construction"
if (construction == "Nothing") return getImage("OtherIcons/Sleep") return if (imageExists(buildingPortraitLocation)) {
return getStatIcon(construction) getImage(buildingPortraitLocation).toGroup(size)
} else
getImage("BuildingIcons/$construction").surroundWithCircle(size)
}
if (ruleset.units.containsKey(construction)) {
val unitPortraitLocation = "UnitPortraits/$construction"
return if (imageExists(unitPortraitLocation)) {
getImage(unitPortraitLocation).toGroup(size)
} else
getUnitIcon(construction).surroundWithCircle(size)
}
if (construction == "Nothing")
return getImage("OtherIcons/Sleep").surroundWithCircle(size)
return getStatIcon(construction).surroundWithCircle(size)
} }
fun getPromotionIcon(promotionName: String, size: Float = 30f): Actor { fun getPromotionIcon(promotionName: String, size: Float = 30f): Actor {

View File

@ -194,7 +194,7 @@ class CityOverviewTab(
val construction = city.cityConstructions.currentConstructionFromQueue val construction = city.cityConstructions.currentConstructionFromQueue
if (construction.isNotEmpty()) { if (construction.isNotEmpty()) {
cityInfoTableDetails.add(ImageGetter.getConstructionImage(construction).surroundWithCircle(iconSize*0.8f)).padRight(paddingHorz) cityInfoTableDetails.add(ImageGetter.getPortraitImage(construction, iconSize*0.8f)).padRight(paddingHorz)
} else { } else {
cityInfoTableDetails.add() cityInfoTableDetails.add()
} }

View File

@ -64,15 +64,14 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
val tech = ruleset.technologies[techName]!! val tech = ruleset.technologies[techName]!!
for (unit in tech.getEnabledUnits(ruleset, techManager.civInfo)) for (unit in tech.getEnabledUnits(ruleset, techManager.civInfo))
techEnabledIcons.add(ImageGetter.getConstructionImage(unit.name).surroundWithCircle(techIconSize)) techEnabledIcons.add(ImageGetter.getPortraitImage(unit.name, techIconSize))
for (building in tech.getEnabledBuildings(ruleset, techManager.civInfo)) for (building in tech.getEnabledBuildings(ruleset, techManager.civInfo))
techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize)) techEnabledIcons.add(ImageGetter.getPortraitImage(building.name, techIconSize))
for (obj in tech.getObsoletedObjects(ruleset, techManager.civInfo)) { for (obj in tech.getObsoletedObjects(ruleset, techManager.civInfo)) {
val obsoletedIcon = when (obj) { val obsoletedIcon = when (obj) {
is Building -> ImageGetter.getConstructionImage(obj.name) is Building -> ImageGetter.getPortraitImage(obj.name, techIconSize)
.surroundWithCircle(techIconSize)
is TileResource -> ImageGetter.getResourceImage(obj.name, techIconSize) is TileResource -> ImageGetter.getResourceImage(obj.name, techIconSize)
is TileImprovement -> ImageGetter.getImprovementIcon(obj.name, techIconSize) is TileImprovement -> ImageGetter.getImprovementIcon(obj.name, techIconSize)
else -> continue else -> continue

View File

@ -383,19 +383,9 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
val groupHeight = 25f val groupHeight = 25f
val groupWidth = if (cityCurrentConstruction is PerpetualConstruction) 15f else 40f val groupWidth = if (cityCurrentConstruction is PerpetualConstruction) 15f else 40f
group.setSize(groupWidth, groupHeight) group.setSize(groupWidth, groupHeight)
val constructionImage = ImageGetter.getPortraitImage(cityConstructions.currentConstructionFromQueue, 25f)
val circle = ImageGetter.getCircle()
circle.setSize(25f, 25f)
val constructionImage = ImageGetter.getConstructionImage(cityConstructions.currentConstructionFromQueue)
constructionImage.setSize(18f, 18f)
constructionImage.centerY(group) constructionImage.centerY(group)
constructionImage.x = group.width - constructionImage.width constructionImage.x = group.width - constructionImage.width
// center the circle on the production image
circle.x = constructionImage.x + (constructionImage.width - circle.width) / 2
circle.y = constructionImage.y + (constructionImage.height - circle.height) / 2
group.addActor(circle)
group.addActor(constructionImage) group.addActor(constructionImage)
val secondaryColor = cityConstructions.cityInfo.civInfo.nation.getInnerColor() val secondaryColor = cityConstructions.cityInfo.civInfo.nation.getInnerColor()

View File

@ -494,3 +494,13 @@ fun WidgetGroup.packIfNeeded(): WidgetGroup {
/** @return `true` if the screen is narrower than 4:3 landscape */ /** @return `true` if the screen is narrower than 4:3 landscape */
fun Stage.isNarrowerThan4to3() = viewport.screenHeight * 4 > viewport.screenWidth * 3 fun Stage.isNarrowerThan4to3() = viewport.screenHeight * 4 > viewport.screenWidth * 3
/** Wraps and returns an image in a [Group] of a given size*/
fun Image.toGroup(size: Float): Group {
return Group().apply {
setSize(size, size)
this@toGroup.setSize(size, size)
this@toGroup.center(this)
this@toGroup.setOrigin(Align.center)
addActor(this@toGroup) }
}

View File

@ -207,7 +207,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
.row() .row()
} }
} else { // Fallback } else { // Fallback
add(ImageGetter.getConstructionImage(wonder.name).surroundWithCircle(100f)).pad(20f).row() add(ImageGetter.getPortraitImage(wonder.name, 100f)).pad(20f).row()
} }
val centerTable = Table() val centerTable = Table()

View File

@ -41,6 +41,14 @@ These work best if they are square, between 100x100 and 256x256 pixels, and incl
For example, [here](https://github.com/yairm210/Unciv-leader-portrait-mod-example) is mod showing how to add leader portraits, which can complement the base game. For example, [here](https://github.com/yairm210/Unciv-leader-portrait-mod-example) is mod showing how to add leader portraits, which can complement the base game.
### Adding Unit and Building Portraits
The base game uses flat icons, colored to fit the civilization's flag colors, to denote units both on the map and in other UI elements. A mod can supply "Portraits" - static images that will remain uncolored - by adding their images to `/Images/UnitPortraits/` and `/Images/BuildingPortraits/`, which will be used in all UI elements except for the world map. The file name must correspond exactly with the unit/building name as defined in Units.json and Buildings.json, or they will be ignored.
These work best if they are full RGB square, between 100x100 and 256x256 pixels, and include some transparent border within that area.
For example, [here](https://github.com/vegeta1k95/Civ-5-Icons) is mod showing how to add unit portraits, which can complement the base game.
## Sounds ## Sounds
Standard values are below. The sounds themselves can be found [here](/sounds). Standard values are below. The sounds themselves can be found [here](/sounds).