633 lines
No EOL
29 KiB
Kotlin
633 lines
No EOL
29 KiB
Kotlin
package com.unciv.ui
|
|
|
|
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
|
import com.badlogic.gdx.graphics.Color
|
|
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.ui.*
|
|
import com.badlogic.gdx.utils.Align
|
|
import com.unciv.UncivGame
|
|
import com.unciv.logic.HexMath
|
|
import com.unciv.logic.city.CityInfo
|
|
import com.unciv.logic.civilization.CivilizationInfo
|
|
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
|
import com.unciv.logic.trade.Trade
|
|
import com.unciv.logic.trade.TradeOffersList
|
|
import com.unciv.models.ruleset.tile.ResourceType
|
|
import com.unciv.models.translations.tr
|
|
import com.unciv.ui.cityscreen.CityScreen
|
|
import com.unciv.ui.pickerscreens.PromotionPickerScreen
|
|
import com.unciv.ui.utils.*
|
|
import com.unciv.Constants
|
|
import java.text.DecimalFormat
|
|
import kotlin.math.*
|
|
|
|
class EmpireOverviewScreen(private val viewingPlayer:CivilizationInfo, private val defaultPage: String = "Cities") : CameraStageBaseScreen(){
|
|
private val topTable = Table().apply { defaults().pad(10f) }
|
|
private val centerTable = Table().apply { defaults().pad(20f) }
|
|
|
|
init {
|
|
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
|
|
val clicks = HashMap<String,() -> Unit>()
|
|
|
|
val closeButton = Constants.close.toTextButton()
|
|
closeButton.onClick { UncivGame.Current.setWorldScreen() }
|
|
closeButton.y = stage.height - closeButton.height - 5
|
|
topTable.add(closeButton)
|
|
|
|
val setCityInfoButton = "Cities".toTextButton()
|
|
val setCities = {
|
|
centerTable.clear()
|
|
centerTable.add(getCityInfoTable())
|
|
centerTable.pack()
|
|
}
|
|
clicks["Cities"] = setCities
|
|
setCityInfoButton.onClick(setCities)
|
|
topTable.add(setCityInfoButton)
|
|
|
|
val setStatsInfoButton = "Stats".toTextButton()
|
|
val setStats = {
|
|
game.settings.addCompletedTutorialTask("See your stats breakdown")
|
|
centerTable.clear()
|
|
centerTable.add(ScrollPane(Table().apply {
|
|
defaults().pad(40f)
|
|
add(getHappinessTable()).top()
|
|
add(getGoldTable()).top()
|
|
add(getScienceTable()).top()
|
|
add(getGreatPeopleTable()).top()
|
|
}))
|
|
centerTable.pack()
|
|
}
|
|
clicks["Stats"] = setStats
|
|
setStatsInfoButton.onClick(setStats)
|
|
topTable.add(setStatsInfoButton)
|
|
|
|
val setCurrentTradesButton = "Trades".toTextButton()
|
|
setCurrentTradesButton.onClick {
|
|
centerTable.clear()
|
|
centerTable.add(ScrollPane(getTradesTable())).height(stage.height * 0.8f) // so it doesn't cover the navigation buttons
|
|
centerTable.pack()
|
|
}
|
|
topTable.add(setCurrentTradesButton)
|
|
if (viewingPlayer.diplomacy.values.all { it.trades.isEmpty() })
|
|
setCurrentTradesButton.disable()
|
|
|
|
val setUnitsButton = "Units".toTextButton()
|
|
setUnitsButton.onClick {
|
|
centerTable.clear()
|
|
centerTable.add(ScrollPane(getUnitTable()).apply { setOverscroll(false,false) }).height(stage.height * 0.8f)
|
|
centerTable.pack()
|
|
}
|
|
topTable.add(setUnitsButton)
|
|
|
|
|
|
val setDiplomacyButton = "Diplomacy".toTextButton()
|
|
setDiplomacyButton.onClick {
|
|
centerTable.clear()
|
|
centerTable.add(getDiplomacyGroup()).height(stage.height * 0.8f)
|
|
centerTable.pack()
|
|
}
|
|
topTable.add(setDiplomacyButton)
|
|
|
|
val setResourcesButton = "Resources".toTextButton()
|
|
val setResources = {
|
|
centerTable.clear()
|
|
centerTable.add(ScrollPane(getResourcesTable())).size(stage.width * 0.8f, stage.height * 0.8f)
|
|
centerTable.pack()
|
|
}
|
|
clicks["Resources"] = setResources
|
|
setResourcesButton.onClick(setResources)
|
|
topTable.add(setResourcesButton)
|
|
if (viewingPlayer.detailedCivResources.isEmpty())
|
|
setResourcesButton.disable()
|
|
|
|
topTable.pack()
|
|
|
|
clicks[defaultPage]?.invoke()
|
|
|
|
val table = Table()
|
|
table.add(topTable).row()
|
|
table.add(centerTable).expand().row()
|
|
table.setFillParent(true)
|
|
stage.addActor(table)
|
|
}
|
|
|
|
private fun getTradesTable(): Table {
|
|
val tradesTable = Table().apply { defaults().pad(10f) }
|
|
val diplomacies = viewingPlayer.diplomacy.values.filter { it.trades.isNotEmpty() }
|
|
.sortedWith(Comparator { d0, d1 ->
|
|
val d0offers = d0.trades.first().ourOffers
|
|
val d1offers = d1.trades.first().ourOffers
|
|
val d0max = if (d0offers.isEmpty()) 0 else d0offers.maxBy { it.duration }!!.duration
|
|
val d1max = if (d1offers.isEmpty()) 0 else d1offers.maxBy { it.duration }!!.duration
|
|
when {
|
|
d0max > d1max -> 1
|
|
d0max == d1max -> 0
|
|
else -> -1
|
|
}
|
|
})
|
|
for(diplomacy in diplomacies) {
|
|
for (trade in diplomacy.trades)
|
|
tradesTable.add(createTradeTable(trade, diplomacy.otherCiv())).row()
|
|
}
|
|
|
|
return tradesTable
|
|
}
|
|
|
|
private fun createTradeTable(trade: Trade, otherCiv:CivilizationInfo): Table {
|
|
val generalTable = Table(skin)
|
|
generalTable.add(createOffersTable(viewingPlayer,trade.ourOffers, trade.theirOffers.size)).fillY()
|
|
generalTable.add(createOffersTable(otherCiv, trade.theirOffers, trade.ourOffers.size)).fillY()
|
|
return generalTable
|
|
}
|
|
|
|
private fun createOffersTable(civ: CivilizationInfo, offersList: TradeOffersList, numberOfOtherSidesOffers: Int): Table {
|
|
val table = Table()
|
|
table.defaults().pad(10f)
|
|
table.background = ImageGetter.getBackground(civ.nation.getOuterColor())
|
|
table.add(civ.civName.toLabel(civ.nation.getInnerColor())).row()
|
|
table.addSeparator()
|
|
for(offer in offersList){
|
|
var offerText = offer.getOfferText()
|
|
if(!offerText.contains("\n")) offerText+="\n"
|
|
table.add(offerText.toLabel(civ.nation.getInnerColor())).row()
|
|
}
|
|
for(i in 1..numberOfOtherSidesOffers - offersList.size)
|
|
table.add("\n".toLabel()).row() // we want both sides of the general table to have the same number of rows
|
|
return table
|
|
}
|
|
|
|
|
|
private fun getHappinessTable(): Table {
|
|
val happinessTable = Table(skin)
|
|
happinessTable.defaults().pad(5f)
|
|
val happinessHeader = Table(skin)
|
|
happinessHeader.add(ImageGetter.getStatIcon("Happiness")).pad(5f,0f,5f,12f).size(20f)
|
|
happinessHeader.add("Happiness".toLabel(fontSize = 24)).padTop(5f)
|
|
happinessTable.add(happinessHeader).colspan(2).row()
|
|
happinessTable.addSeparator()
|
|
|
|
val happinessBreakdown = viewingPlayer.stats().getHappinessBreakdown()
|
|
|
|
for (entry in happinessBreakdown.filterNot { it.value.roundToInt()==0 }) {
|
|
happinessTable.add(entry.key.tr())
|
|
happinessTable.add(entry.value.roundToInt().toString()).right().row()
|
|
}
|
|
happinessTable.add("Total".tr())
|
|
happinessTable.add(happinessBreakdown.values.sum().roundToInt().toString()).right()
|
|
happinessTable.pack()
|
|
return happinessTable
|
|
}
|
|
|
|
private fun getGoldTable(): Table {
|
|
val goldTable = Table(skin)
|
|
goldTable.defaults().pad(5f)
|
|
val goldHeader = Table(skin)
|
|
goldHeader.add(ImageGetter.getStatIcon("Gold")).pad(5f,0f,5f,12f).size(20f)
|
|
goldHeader.add("Gold".toLabel(fontSize = 24)).padTop(5f)
|
|
goldTable.add(goldHeader).colspan(2).row()
|
|
goldTable.addSeparator()
|
|
var total=0f
|
|
for (entry in viewingPlayer.stats().getStatMapForNextTurn()) {
|
|
if(entry.value.gold==0f) continue
|
|
goldTable.add(entry.key.tr())
|
|
goldTable.add(entry.value.gold.roundToInt().toString()).right().row()
|
|
total += entry.value.gold
|
|
}
|
|
goldTable.add("Total".tr())
|
|
goldTable.add(total.roundToInt().toString()).right()
|
|
goldTable.pack()
|
|
return goldTable
|
|
}
|
|
|
|
|
|
private fun getScienceTable(): Table {
|
|
val scienceTable = Table(skin)
|
|
scienceTable.defaults().pad(5f)
|
|
val scienceHeader = Table(skin)
|
|
scienceHeader.add(ImageGetter.getStatIcon("Science")).pad(5f,0f,5f,12f).size(20f)
|
|
scienceHeader.add("Science".toLabel(fontSize = 24)).padTop(5f)
|
|
scienceTable.add(scienceHeader).colspan(2).row()
|
|
scienceTable.addSeparator()
|
|
val scienceStats = viewingPlayer.stats().getStatMapForNextTurn()
|
|
.filter { it.value.science!=0f }
|
|
for (entry in scienceStats) {
|
|
scienceTable.add(entry.key.tr())
|
|
scienceTable.add(entry.value.science.roundToInt().toString()).right().row()
|
|
}
|
|
scienceTable.add("Total".tr())
|
|
scienceTable.add(scienceStats.values.map { it.science }.sum().roundToInt().toString()).right()
|
|
scienceTable.pack()
|
|
return scienceTable
|
|
}
|
|
|
|
|
|
private fun getGreatPeopleTable(): Table {
|
|
val greatPeopleTable = Table(skin)
|
|
|
|
val greatPersonPoints = viewingPlayer.greatPeople.greatPersonPoints.toHashMap()
|
|
val greatPersonPointsPerTurn = viewingPlayer.getGreatPersonPointsForNextTurn().toHashMap()
|
|
val pointsToGreatPerson = viewingPlayer.greatPeople.pointsForNextGreatPerson
|
|
|
|
greatPeopleTable.defaults().pad(5f)
|
|
val greatPeopleHeader = Table(skin)
|
|
val greatPeopleIcon = ImageGetter.getStatIcon("Specialist")
|
|
greatPeopleIcon.color = Color.ROYAL
|
|
greatPeopleHeader.add(greatPeopleIcon).padRight(12f).size(30f)
|
|
greatPeopleHeader.add("Great person points".toLabel(fontSize = 24)).padTop(5f)
|
|
greatPeopleTable.add(greatPeopleHeader).colspan(3).row()
|
|
greatPeopleTable.addSeparator()
|
|
greatPeopleTable.add()
|
|
greatPeopleTable.add("Current points".tr())
|
|
greatPeopleTable.add("Points per turn".tr()).row()
|
|
|
|
val mapping = viewingPlayer.greatPeople.statToGreatPersonMapping
|
|
for(entry in mapping){
|
|
greatPeopleTable.add(entry.value.tr())
|
|
greatPeopleTable.add(greatPersonPoints[entry.key]!!.toInt().toString()+"/"+pointsToGreatPerson)
|
|
greatPeopleTable.add(greatPersonPointsPerTurn[entry.key]!!.toInt().toString()).row()
|
|
}
|
|
val pointsForGreatGeneral = viewingPlayer.greatPeople.greatGeneralPoints.toString()
|
|
val pointsForNextGreatGeneral = viewingPlayer.greatPeople.pointsForNextGreatGeneral.toString()
|
|
greatPeopleTable.add("Great General".tr())
|
|
greatPeopleTable.add("$pointsForGreatGeneral/$pointsForNextGreatGeneral").row()
|
|
greatPeopleTable.pack()
|
|
return greatPeopleTable
|
|
}
|
|
|
|
|
|
|
|
private fun getCityInfoTable(): Table {
|
|
val iconSize = 50f//if you set this too low, there is a chance that the tables will be misaligned
|
|
val padding = 5f
|
|
var sortedBy = "City"
|
|
|
|
val cityInfoTableDetails = Table(skin)
|
|
cityInfoTableDetails.defaults().pad(padding).minWidth(iconSize).align(Align.left)//we need the min width so we can align the different tables
|
|
|
|
fun sortOnClick(iconName: String) {
|
|
val descending = sortedBy == iconName
|
|
sortedBy = iconName
|
|
// sort the table: clear and fill with sorted data
|
|
cityInfoTableDetails.clear()
|
|
fillCitiesTable(cityInfoTableDetails, iconName, descending)
|
|
// reset to return back for ascending next time
|
|
if (descending) sortedBy = ""
|
|
}
|
|
|
|
val cityInfoTableIcons = Table(skin)
|
|
cityInfoTableIcons.defaults().pad(padding).align(Align.center)
|
|
|
|
cityInfoTableIcons.add("Cities".toLabel(fontSize = 24)).colspan(8).align(Align.center).row()
|
|
|
|
val citySortIcon = ImageGetter.getUnitIcon("Settler").surroundWithCircle(iconSize)
|
|
citySortIcon.onClick { sortOnClick("City") }
|
|
cityInfoTableIcons.add(citySortIcon).align(Align.left)
|
|
|
|
val columnsNames = arrayListOf("Population", "Food", "Gold", "Science", "Production", "Culture", "Happiness")
|
|
for (name in columnsNames) {
|
|
val icon = ImageGetter.getStatIcon(name)
|
|
icon.onClick { sortOnClick(name) }
|
|
cityInfoTableIcons.add(icon).size(iconSize)
|
|
}
|
|
cityInfoTableIcons.pack()
|
|
|
|
fillCitiesTable(cityInfoTableDetails, "City", false)
|
|
|
|
val cityInfoScrollPane = ScrollPane(cityInfoTableDetails)
|
|
cityInfoScrollPane.pack()
|
|
cityInfoScrollPane.setOverscroll(false, false)//I think it feels better with no overscroll
|
|
|
|
val cityInfoTableTotal = Table(skin)
|
|
cityInfoTableTotal.defaults().pad(padding).minWidth(iconSize)//we need the min width so we can align the different tables
|
|
|
|
cityInfoTableTotal.add("Total".tr())
|
|
cityInfoTableTotal.add(viewingPlayer.cities.sumBy { it.population.population }.toString()).actor!!.setAlignment(Align.center)
|
|
cityInfoTableTotal.add()//an intended empty space
|
|
cityInfoTableTotal.add(viewingPlayer.cities.sumBy { it.cityStats.currentCityStats.gold.toInt() }.toString()).actor!!.setAlignment(Align.center)
|
|
cityInfoTableTotal.add(viewingPlayer.cities.sumBy { it.cityStats.currentCityStats.science.toInt() }.toString()).actor!!.setAlignment(Align.center)
|
|
cityInfoTableTotal.add()//an intended empty space
|
|
cityInfoTableTotal.add(viewingPlayer.cities.sumBy { it.cityStats.currentCityStats.culture.toInt() }.toString()).actor!!.setAlignment(Align.center)
|
|
cityInfoTableTotal.add(viewingPlayer.cities.sumBy { it.cityStats.currentCityStats.happiness.toInt() }.toString()).actor!!.setAlignment(Align.center)
|
|
|
|
cityInfoTableTotal.pack()
|
|
|
|
val table = Table(skin)
|
|
//since the names of the cities are on the left, and the length of the names varies
|
|
//we align every row to the right, coz we set the size of the other(number) cells to the image size
|
|
//and thus, we can guarantee that the tables will be aligned
|
|
table.defaults().pad(padding).align(Align.right)
|
|
|
|
// place the button for sorting by city name on top of the cities names
|
|
citySortIcon.width = max(iconSize, cityInfoTableDetails.width - (iconSize+padding) * 8)
|
|
|
|
table.add(cityInfoTableIcons).row()
|
|
table.add(cityInfoScrollPane).width(cityInfoTableDetails.width).row()
|
|
table.add(cityInfoTableTotal)
|
|
table.pack()
|
|
|
|
return table
|
|
}
|
|
|
|
private fun fillCitiesTable(citiesTable: Table, sortType: String, descending: Boolean) {
|
|
|
|
val sorter = Comparator {city2, city1 : CityInfo -> when (sortType) {
|
|
"Population" -> city1.population.population - city2.population.population
|
|
"Food" -> (city1.cityStats.currentCityStats.food - city2.cityStats.currentCityStats.food).toInt()
|
|
"Gold" -> (city1.cityStats.currentCityStats.gold - city2.cityStats.currentCityStats.gold).toInt()
|
|
"Science" -> (city1.cityStats.currentCityStats.science - city2.cityStats.currentCityStats.science).toInt()
|
|
"Production" -> (city1.cityStats.currentCityStats.production - city2.cityStats.currentCityStats.production).toInt()
|
|
"Culture" -> (city1.cityStats.currentCityStats.culture - city2.cityStats.currentCityStats.culture).toInt()
|
|
"Happiness" -> (city1.cityStats.currentCityStats.happiness - city2.cityStats.currentCityStats.happiness).toInt()
|
|
else -> city2.name.compareTo(city1.name)
|
|
} }
|
|
|
|
var cityList = viewingPlayer.cities.sortedWith(sorter)
|
|
|
|
if (descending)
|
|
cityList = cityList.reversed()
|
|
|
|
for (city in cityList) {
|
|
val button = Button(city.name.toLabel(), skin)
|
|
button.onClick {
|
|
UncivGame.Current.setScreen(CityScreen(city))
|
|
}
|
|
citiesTable.add(button)
|
|
citiesTable.add(city.cityConstructions.getCityProductionTextForCityButton()).actor!!.setAlignment(Align.left)
|
|
citiesTable.add(city.population.population.toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.add(city.cityStats.currentCityStats.food.roundToInt().toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.add(city.cityStats.currentCityStats.gold.roundToInt().toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.add(city.cityStats.currentCityStats.science.roundToInt().toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.add(city.cityStats.currentCityStats.production.roundToInt().toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.add(city.cityStats.currentCityStats.culture.roundToInt().toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.add(city.cityStats.currentCityStats.happiness.roundToInt().toString()).actor!!.setAlignment(Align.center)
|
|
citiesTable.row()
|
|
}
|
|
citiesTable.pack()
|
|
}
|
|
|
|
private fun getUnitTable(): Table {
|
|
val table=Table(skin).apply { defaults().pad(5f) }
|
|
table.add("Name".tr())
|
|
table.add("Action".tr())
|
|
table.add("Strength".tr())
|
|
table.add("Ranged strength".tr())
|
|
table.add("Movement".tr())
|
|
table.add("Closest city".tr())
|
|
table.add("Promotions".tr())
|
|
table.add("Health".tr())
|
|
table.row()
|
|
table.addSeparator()
|
|
|
|
for(unit in viewingPlayer.getCivUnits().sortedWith(compareBy({it.name},{!it.due},
|
|
{it.currentMovement<0.1f},{abs(it.currentTile.position.x)+abs(it.currentTile.position.y)}))) {
|
|
val baseUnit = unit.baseUnit()
|
|
val button = TextButton(unit.name.tr(), skin)
|
|
button.onClick {
|
|
UncivGame.Current.setWorldScreen()
|
|
UncivGame.Current.worldScreen.mapHolder.setCenterPosition(unit.currentTile.position)
|
|
}
|
|
table.add(button).left()
|
|
val mapUnitAction = unit.mapUnitAction
|
|
if (mapUnitAction != null) table.add(if(mapUnitAction.name().startsWith("Fortify")) "Fortify".tr() else mapUnitAction.name().tr()) else table.add()
|
|
if(baseUnit.strength>0) table.add(baseUnit.strength.toString()) else table.add()
|
|
if(baseUnit.rangedStrength>0) table.add(baseUnit.rangedStrength.toString()) else table.add()
|
|
table.add(DecimalFormat("0.#").format(unit.currentMovement)+"/"+unit.getMaxMovement())
|
|
val closestCity = unit.getTile().getTilesInDistance(3).firstOrNull{it.isCityCenter()}
|
|
if (closestCity!=null) table.add(closestCity.getCity()!!.name.tr()) else table.add()
|
|
val promotionsTable = Table()
|
|
val promotionsForUnit = unit.civInfo.gameInfo.ruleSet.unitPromotions.values.filter { unit.promotions.promotions.contains(it.name) } // force same sorting as on picker (.sorted() would be simpler code, but...)
|
|
for(promotion in promotionsForUnit)
|
|
promotionsTable.add(ImageGetter.getPromotionIcon(promotion.name))
|
|
if (unit.promotions.canBePromoted()) promotionsTable.add(ImageGetter.getImage("OtherIcons/Star").apply { color= Color.GOLDENROD }).size(24f).padLeft(8f)
|
|
if (unit.canUpgrade()) promotionsTable.add(ImageGetter.getUnitIcon(baseUnit.upgradesTo!!, Color.GREEN)).size(28f).padLeft(8f)
|
|
promotionsTable.onClick {
|
|
if (unit.promotions.canBePromoted() || unit.promotions.promotions.isNotEmpty()) {
|
|
UncivGame.Current.setScreen(PromotionPickerScreen(unit))
|
|
}
|
|
}
|
|
table.add(promotionsTable)
|
|
if (unit.health < 100) table.add(unit.health.toString()) else table.add()
|
|
table.row()
|
|
}
|
|
table.pack()
|
|
return table
|
|
}
|
|
|
|
|
|
private fun playerKnows(civ:CivilizationInfo) = civ==viewingPlayer ||
|
|
viewingPlayer.diplomacy.containsKey(civ.civName)
|
|
|
|
private fun getDiplomacyGroup(): Group {
|
|
val relevantCivs = viewingPlayer.gameInfo.civilizations.filter { !it.isBarbarian() && !it.isCityState() }
|
|
val freeWidth = stage.width
|
|
val freeHeight = stage.height - topTable.height
|
|
val group = Group()
|
|
group.setSize(freeWidth,freeHeight)
|
|
val civGroups = HashMap<String, Actor>()
|
|
val civLines = HashMap<String, MutableSet<Actor>>()
|
|
for(i in 0..relevantCivs.lastIndex){
|
|
val civ = relevantCivs[i]
|
|
|
|
val civGroup = getCivGroup(civ, "", viewingPlayer)
|
|
|
|
val vector = HexMath.getVectorForAngle(2 * Math.PI.toFloat() *i / relevantCivs.size)
|
|
civGroup.center(group)
|
|
civGroup.moveBy(vector.x*freeWidth/2.5f, vector.y*freeHeight/2.5f)
|
|
civGroup.touchable = Touchable.enabled
|
|
civGroup.onClick {
|
|
onCivClicked(civLines, civ.civName)
|
|
}
|
|
|
|
civGroups[civ.civName]=civGroup
|
|
group.addActor(civGroup)
|
|
}
|
|
|
|
for(civ in relevantCivs.filter { playerKnows(it) && !it.isDefeated() })
|
|
for(diplomacy in civ.diplomacy.values.
|
|
filter { !it.otherCiv().isBarbarian() && !it.otherCiv().isCityState()
|
|
&& playerKnows(it.otherCiv()) && !it.otherCiv().isDefeated()}){
|
|
val civGroup = civGroups[civ.civName]!!
|
|
val otherCivGroup = civGroups[diplomacy.otherCivName]!!
|
|
|
|
if (!civLines.containsKey(civ.civName))
|
|
civLines[civ.civName] = mutableSetOf()
|
|
|
|
val statusLine = ImageGetter.getLine(civGroup.x+civGroup.width/2,civGroup.y+civGroup.height/2,
|
|
otherCivGroup.x+otherCivGroup.width/2,otherCivGroup.y+otherCivGroup.height/2,3f)
|
|
|
|
// draw a parallel line for additional relationships
|
|
if (diplomacy.hasOpenBorders ||
|
|
diplomacy.diplomaticStatus == DiplomaticStatus.War ||
|
|
diplomacy.totalOfScienceDuringRA > 0) {
|
|
val lineAngle = (statusLine.rotation-90) * Math.PI / 180
|
|
val shiftX = 4f*cos(lineAngle).toFloat()
|
|
val shiftY = 4f*sin(lineAngle).toFloat()
|
|
val secondaryLine = ImageGetter.getLine(civGroup.x+civGroup.width/2+shiftX,civGroup.y+civGroup.height/2+shiftY,
|
|
otherCivGroup.x+otherCivGroup.width/2+shiftX,otherCivGroup.y+otherCivGroup.height/2+shiftY,2f)
|
|
|
|
secondaryLine.color = when {
|
|
diplomacy.diplomaticStatus == DiplomaticStatus.War -> Color.RED
|
|
diplomacy.hasOpenBorders -> Color.CYAN
|
|
diplomacy.totalOfScienceDuringRA > 0 -> Color.BLUE
|
|
else -> Color.WHITE
|
|
}
|
|
|
|
civLines[civ.civName]!!.add(secondaryLine)
|
|
|
|
group.addActor(secondaryLine)
|
|
secondaryLine.toBack()
|
|
}
|
|
|
|
val diplomacyLevel = diplomacy.diplomaticModifiers.values.sum()
|
|
statusLine.color = getColorForDiplomacyLevel(diplomacyLevel)
|
|
|
|
civLines[civ.civName]!!.add(statusLine)
|
|
|
|
group.addActor(statusLine)
|
|
statusLine.toBack()
|
|
}
|
|
|
|
return group
|
|
}
|
|
|
|
private fun onCivClicked(civLines: HashMap<String, MutableSet<Actor>>, name: String) {
|
|
// ignore the clicks on "dead" civilizations, and remember the selected one
|
|
val selectedLines = civLines[name] ?: return
|
|
|
|
// let's check whether lines of all civs are visible (except selected one)
|
|
var atLeastOneLineVisible = false
|
|
var allAreLinesInvisible = true
|
|
for (lines in civLines.values) {
|
|
// skip the civilization selected by user, and civilizations with no lines
|
|
if (lines == selectedLines || lines.isEmpty()) continue
|
|
|
|
val visibility = lines.first().isVisible
|
|
atLeastOneLineVisible = atLeastOneLineVisible || visibility
|
|
allAreLinesInvisible = allAreLinesInvisible && visibility
|
|
|
|
// check whether both visible and invisible lines are present
|
|
if (atLeastOneLineVisible && !allAreLinesInvisible) {
|
|
// invert visibility of the selected civ's lines
|
|
selectedLines.forEach{ it.isVisible = !it.isVisible }
|
|
return
|
|
}
|
|
}
|
|
|
|
if (selectedLines.first().isVisible)
|
|
// invert visibility of all lines except selected one
|
|
civLines.filter{ it.key != name }.forEach{ it.value.forEach{line -> line.isVisible = !line.isVisible} }
|
|
else
|
|
// it happens only when all are visible except selected one
|
|
// invert visibility of the selected civ's lines
|
|
selectedLines.forEach{ it.isVisible = !it.isVisible }
|
|
}
|
|
|
|
private fun getColorForDiplomacyLevel(value: Float): Color {
|
|
|
|
var amplitude = min(1.0f,abs(value)/80) // 80 = RelationshipLevel.Ally
|
|
val shade = max(0.5f - amplitude, 0.0f)
|
|
amplitude = max(amplitude, 0.5f)
|
|
|
|
return Color( if (sign(value) < 0) amplitude else shade,
|
|
if (sign(value) > 0) amplitude else shade,
|
|
shade,1.0f)
|
|
}
|
|
|
|
|
|
private fun getResourcesTable(): Table {
|
|
val resourcesTable=Table().apply { defaults().pad(10f) }
|
|
val resourceDrilldown = viewingPlayer.detailedCivResources
|
|
|
|
// First row of table has all the icons
|
|
resourcesTable.add()
|
|
val resources = resourceDrilldown.map { it.resource }
|
|
.filter { it.resourceType!=ResourceType.Bonus }.distinct().sortedBy { it.resourceType }
|
|
|
|
var visibleLabel: Label? = null
|
|
for(resource in resources) {
|
|
// Create a group of label and icon for each resource.
|
|
val resourceImage = ImageGetter.getResourceImage(resource.name,50f)
|
|
val resourceLabel = resource.name.toLabel()
|
|
val labelPadding = 10f
|
|
// Using a table here leads to spacing issues
|
|
// due to different label lengths.
|
|
val holder = Group()
|
|
resourceImage.onClick {
|
|
if (visibleLabel != null)
|
|
visibleLabel!!.setVisible(false)
|
|
resourceLabel.setVisible(true)
|
|
visibleLabel = resourceLabel
|
|
}
|
|
holder.addActor(resourceImage)
|
|
holder.addActor(resourceLabel)
|
|
holder.setSize(resourceImage.getWidth(),
|
|
resourceImage.getHeight() + resourceLabel.getHeight() + labelPadding)
|
|
// Center-align all labels, but right-align the last couple resources' labels
|
|
// because they may get clipped otherwise. The leftmost label should be fine
|
|
// center-aligned (if there are more than 2 resources), because the left side
|
|
// has more padding.
|
|
val alignFactor = when {
|
|
(resources.indexOf(resource) + 2 >= resources.count()) -> 1
|
|
else -> 2
|
|
}
|
|
resourceLabel.moveBy((resourceImage.getWidth() - resourceLabel.getWidth()) / alignFactor,
|
|
resourceImage.getHeight() + labelPadding)
|
|
resourceLabel.setVisible(false)
|
|
resourcesTable.add(holder)
|
|
}
|
|
resourcesTable.addSeparator()
|
|
|
|
val origins = resourceDrilldown.map { it.origin }.distinct()
|
|
for(origin in origins){
|
|
resourcesTable.add(origin.toLabel())
|
|
for(resource in resources){
|
|
val resourceSupply = resourceDrilldown.firstOrNull { it.resource==resource && it.origin==origin }
|
|
if(resourceSupply==null) resourcesTable.add()
|
|
else resourcesTable.add(resourceSupply.amount.toString().toLabel())
|
|
}
|
|
resourcesTable.row()
|
|
}
|
|
|
|
resourcesTable.add("Total".toLabel())
|
|
for(resource in resources){
|
|
val sum = resourceDrilldown.filter { it.resource==resource }.sumBy { it.amount }
|
|
resourcesTable.add(sum.toString().toLabel())
|
|
}
|
|
|
|
return resourcesTable
|
|
}
|
|
|
|
companion object {
|
|
fun getCivGroup(civ: CivilizationInfo, afterCivNameText:String,currentPlayer:CivilizationInfo): Table {
|
|
val civGroup = Table()
|
|
|
|
var labelText = civ.civName.tr()+afterCivNameText
|
|
var labelColor=Color.WHITE
|
|
val backgroundColor:Color
|
|
|
|
if (civ.isDefeated()) {
|
|
civGroup.add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
|
|
backgroundColor = Color.LIGHT_GRAY
|
|
labelColor = Color.BLACK
|
|
} else if (currentPlayer==civ || UncivGame.Current.viewEntireMapForDebug
|
|
|| currentPlayer.knows(civ) || currentPlayer.isDefeated() || currentPlayer.victoryManager.hasWon()) {
|
|
civGroup.add(ImageGetter.getNationIndicator(civ.nation, 30f))
|
|
backgroundColor = civ.nation.getOuterColor()
|
|
labelColor = civ.nation.getInnerColor()
|
|
} else {
|
|
backgroundColor = Color.DARK_GRAY
|
|
labelText = "???"
|
|
}
|
|
|
|
civGroup.background = ImageGetter.getRoundedEdgeTableBackground(backgroundColor)
|
|
val label = labelText.toLabel(labelColor)
|
|
label.setAlignment(Align.center)
|
|
|
|
civGroup.add(label).pad(10f)
|
|
civGroup.pack()
|
|
return civGroup
|
|
}
|
|
}
|
|
} |