From a025660fe0b689e4154a1ce4cda3697bb85fff75 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Mon, 21 Jun 2021 17:54:48 +0200 Subject: [PATCH] Split off all individual OverviewScreen panes (#4184) * Spliff off all individual OverviewScreen panes * Spliff off all individual OverviewScreen panes - remove comment * Spliff off all individual OverviewScreen panes - consistent file endings --- .../overviewscreen/DiplomacyOverviewTable.kt | 182 +++++++ .../ui/overviewscreen/EmpireOverviewScreen.kt | 504 +----------------- .../overviewscreen/ResourcesOverviewTable.kt | 86 +++ .../ui/overviewscreen/StatsOverviewTable.kt | 136 +++++ .../ui/overviewscreen/TradesOverviewTable.kt | 59 ++ .../ui/overviewscreen/UnitOverviewTable.kt | 73 +++ 6 files changed, 560 insertions(+), 480 deletions(-) create mode 100644 core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt create mode 100644 core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt create mode 100644 core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt create mode 100644 core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt create mode 100644 core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt diff --git a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt new file mode 100644 index 0000000000..e73709362c --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt @@ -0,0 +1,182 @@ +package com.unciv.ui.overviewscreen + +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.Table +import com.unciv.UncivGame +import com.unciv.logic.HexMath +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.civilization.diplomacy.DiplomaticStatus +import com.unciv.ui.trade.DiplomacyScreen +import com.unciv.ui.utils.* + +class DiplomacyOverviewTable ( + private val viewingPlayer: CivilizationInfo, + private val overviewScreen: EmpireOverviewScreen +): Table() { + + private var includeCityStates = false + + init { + update() + } + + fun update() { + clear() + val relevantCivs = viewingPlayer.gameInfo.civilizations + .filter { !it.isBarbarian() && (includeCityStates || !it.isCityState()) } + val diplomacyGroup = DiplomacyGroup(viewingPlayer, overviewScreen.centerAreaHeight, includeCityStates) + val playerKnowsAndUndefeatedCivs = relevantCivs.filter { diplomacyGroup.playerKnows(it) && !it.isDefeated() } + val playerKnowsAndDefeatedCivs = relevantCivs.filter { diplomacyGroup.playerKnows(it) && it.isDefeated() } + if (playerKnowsAndUndefeatedCivs.size > 1) + add(diplomacyGroup) + + val titleTable = Table() + titleTable.add("Our Civilization:".toLabel()) + titleTable.add(ImageGetter.getNationIndicator(viewingPlayer.nation, 25f)).pad(5f) + titleTable.add(viewingPlayer.civName.toLabel()).left().row() + + + val civTableScrollPane = getCivTableScroll(relevantCivs, titleTable, playerKnowsAndUndefeatedCivs, playerKnowsAndDefeatedCivs) + + val toggleCityStatesButton = "City-States".toTextButton() + toggleCityStatesButton.color = if(includeCityStates) Color.RED else Color.GREEN + toggleCityStatesButton.onClick { includeCityStates = !includeCityStates; update() } + + val floatingTable = Table() + floatingTable.add(toggleCityStatesButton).row() + floatingTable.add(civTableScrollPane.addBorder(2f, Color.WHITE)).pad(10f) + add(floatingTable) + } + + + private fun getCivMiniTable(civInfo: CivilizationInfo): Table { + val table = Table() + table.add(ImageGetter.getNationIndicator(civInfo.nation, 25f)).pad(5f) + table.add(civInfo.civName.toLabel()).left() + table.touchable = Touchable.enabled + table.onClick { + if (civInfo.isDefeated() || viewingPlayer.isSpectator() || civInfo == viewingPlayer) return@onClick + UncivGame.Current.setScreen(DiplomacyScreen(viewingPlayer).apply { updateRightSide(civInfo) }) + } + return table + } + + private fun getCivTableScroll(relevantCivs: List, titleTable: Table, + playerKnowsAndUndefeatedCivs: List, + playerKnowsAndDefeatedCivs: List): AutoScrollPane { + val civTable = Table() + civTable.defaults().pad(5f) + civTable.background = ImageGetter.getBackground(Color.BLACK) + civTable.add("[${relevantCivs.size}] Civilizations in the game".toLabel()).pad(5f).colspan(2).row() + civTable.add(titleTable).colspan(2).row() + civTable.addSeparator() + civTable.add("Known and alive ([${playerKnowsAndUndefeatedCivs.size - 1}])".toLabel()) + .pad(5f).colspan(2).row() + if (playerKnowsAndUndefeatedCivs.size > 1) { + civTable.addSeparator() + playerKnowsAndUndefeatedCivs.filter { it != viewingPlayer }.forEach { + civTable.add(getCivMiniTable(it)).left() + if (playerKnowsAndUndefeatedCivs.indexOf(it) % 2 == 0) civTable.row() + } + } + civTable.addSeparator() + civTable.add("Known and defeated ([${playerKnowsAndDefeatedCivs.size}])".toLabel()) + .pad(5f).colspan(2).row() + if (playerKnowsAndDefeatedCivs.isNotEmpty()) { + civTable.addSeparator() + playerKnowsAndDefeatedCivs.forEach { + civTable.add(getCivMiniTable(it)).left() + if (playerKnowsAndDefeatedCivs.indexOf(it) % 2 == 0) civTable.row() + } + } + val civTableScrollPane = AutoScrollPane(civTable) + civTableScrollPane.setOverscroll(false, false) + return civTableScrollPane + } + + private class DiplomacyGroup(val viewingPlayer: CivilizationInfo, freeHeight: Float, includeCityStates: Boolean): Group() { + private fun onCivClicked(civLines: HashMap>, 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 } + } + + + fun playerKnows(civ: CivilizationInfo) = civ == viewingPlayer || + viewingPlayer.diplomacy.containsKey(civ.civName) + + init { + val relevantCivs = viewingPlayer.gameInfo.civilizations.filter { !it.isBarbarian() && (includeCityStates || !it.isCityState()) } + val playerKnowsAndUndefeatedCivs = relevantCivs.filter { playerKnows(it) && !it.isDefeated() } + setSize(freeHeight, freeHeight) + val civGroups = HashMap() + val civLines = HashMap>() + for (i in 0..playerKnowsAndUndefeatedCivs.lastIndex) { + val civ = playerKnowsAndUndefeatedCivs[i] + + val civGroup = ImageGetter.getNationIndicator(civ.nation, 30f) + + val vector = HexMath.getVectorForAngle(2 * Math.PI.toFloat() * i / playerKnowsAndUndefeatedCivs.size) + civGroup.center(this) + civGroup.moveBy(vector.x * freeHeight / 2.25f, vector.y * freeHeight / 2.25f) + civGroup.touchable = Touchable.enabled + civGroup.onClick { + onCivClicked(civLines, civ.civName) + } + + civGroups[civ.civName] = civGroup + addActor(civGroup) + } + + for (civ in relevantCivs.filter { playerKnows(it) && !it.isDefeated() }) + for (diplomacy in civ.diplomacy.values.filter { + (it.otherCiv().isMajorCiv() || includeCityStates) && 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, 2f) + + statusLine.color = if (diplomacy.diplomaticStatus == DiplomaticStatus.Peace) Color.GREEN else Color.RED + + civLines[civ.civName]!!.add(statusLine) + + addActor(statusLine) + statusLine.toBack() + } + } + } +} diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt index f2a4ab9348..4bfdf17d3f 100644 --- a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt @@ -1,37 +1,29 @@ package com.unciv.ui.overviewscreen 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.scenes.scene2d.ui.Button +import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Align import com.unciv.Constants -import com.unciv.UncivGame -import com.unciv.logic.HexMath 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.pickerscreens.PromotionPickerScreen -import com.unciv.ui.trade.DiplomacyScreen import com.unciv.ui.utils.* import com.unciv.ui.utils.KeyPressDispatcher.Companion.keyboardAvailable import com.unciv.ui.utils.StaticTooltip.Companion.addStaticTip -import java.text.DecimalFormat -import kotlin.math.abs -import kotlin.math.roundToInt import com.unciv.ui.utils.AutoScrollPane as ScrollPane class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPage: String = "") : CameraStageBaseScreen(){ private val topTable = Table().apply { defaults().pad(10f) } private val centerTable = Table().apply { defaults().pad(5f) } - private val setCategoryActions = HashMap Unit>() + internal val setCategoryActions = HashMap Unit>() private val categoryButtons = HashMap() + // 50 normal button height + 2*10 topTable padding + 2 Separator + 2*5 centerTable padding + // Since a resize recreates this screen this should be fine as a val + internal val centerAreaHeight = stage.height - 82f + private object ButtonDecorations { data class IconAndKey (val icon: String, val key: Char = Char.MIN_VALUE) val keyIconMap: HashMap = hashMapOf( @@ -51,10 +43,13 @@ class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPa val setCategoryAction = { centerTable.clear() centerTable.add(ScrollPane(table).apply { setOverscroll(false, false) }) - .height(stage.height * 0.8f) + .height(centerAreaHeight) + .width(stage.width) centerTable.pack() for ((key, categoryButton) in categoryButtons.filterNot { it.value.touchable == Touchable.disabled }) categoryButton.color = if (key == name) Color.BLUE else Color.WHITE + if (name == "Stats") + game.settings.addCompletedTutorialTask("See your stats breakdown") game.settings.lastOverviewPage = name } val button = Button(skin) @@ -83,13 +78,13 @@ class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPa else game.settings.lastOverviewPage onBackButtonClicked { game.setWorldScreen() } - - addCategory("Cities", CityOverviewTable(viewingPlayer, this)) - addCategory("Stats", getStatsTable()) - addCategory("Trades", getTradesTable(), viewingPlayer.diplomacy.values.all { it.trades.isEmpty() }) - addCategory("Units", getUnitTable()) - addCategory("Diplomacy", DiplomacyTable(viewingPlayer, stage.height * 0.8f)) - addCategory("Resources", getResourcesTable(), viewingPlayer.detailedCivResources.isEmpty()) + + addCategory("Cities", CityOverviewTable(viewingPlayer, this), viewingPlayer.cities.none()) + addCategory("Stats", StatsOverviewTable(viewingPlayer, this)) + addCategory("Trades", TradesOverviewTable(viewingPlayer, this), viewingPlayer.diplomacy.values.all { it.trades.isEmpty() }) + addCategory("Units", UnitOverviewTable(viewingPlayer, this), viewingPlayer.getCivUnits().none()) + addCategory("Diplomacy", DiplomacyOverviewTable(viewingPlayer, this), viewingPlayer.diplomacy.isEmpty()) + addCategory("Resources", ResourcesOverviewTable(viewingPlayer, this), viewingPlayer.detailedCivResources.isEmpty()) val closeButton = Constants.close.toTextButton().apply { setColor(0.75f, 0.1f, 0.1f, 1f) @@ -99,391 +94,25 @@ class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPa topTable.add(closeButton) topTable.pack() + val topScroll = ScrollPane(topTable).apply { setScrollingDisabled(false, true) } setCategoryActions[page]?.invoke() val table = Table() - table.add(topTable).row() + table.add(topScroll).row() table.addSeparator() table.add(centerTable).height(stage.height - topTable.height).expand().row() table.setFillParent(true) stage.addActor(table) } - private fun getStatsTable(): Table { - game.settings.addCompletedTutorialTask("See your stats breakdown") - return Table().apply { - defaults().pad(40f) - add(getHappinessTable()).top() - add(getGoldTable()).top() - add(getScienceTable()).top() - add(getGreatPeopleTable()).top() + override fun resize(width: Int, height: Int) { + if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) { + game.setScreen(EmpireOverviewScreen(viewingPlayer, game.settings.lastOverviewPage)) } } - private class DiplomacyTable(val viewingPlayer: CivilizationInfo, val diplomacyGroupHeight:Float):Table(){ - var includeCityStates = false - init { - update() - } - - fun update(){ - clear() - val relevantCivs = viewingPlayer.gameInfo.civilizations - .filter { !it.isBarbarian() && (includeCityStates || !it.isCityState()) } - val diplomacyGroup = DiplomacyGroup(viewingPlayer, diplomacyGroupHeight, includeCityStates) - val playerKnowsAndUndefeatedCivs = relevantCivs.filter { diplomacyGroup.playerKnows(it) && !it.isDefeated() } - val playerKnowsAndDefeatedCivs = relevantCivs.filter { diplomacyGroup.playerKnows(it) && it.isDefeated() } - if (playerKnowsAndUndefeatedCivs.size > 1) - add(diplomacyGroup) - - val titleTable = Table() - titleTable.add("Our Civilization:".toLabel()) - titleTable.add(ImageGetter.getNationIndicator(viewingPlayer.nation, 25f)).pad(5f) - titleTable.add(viewingPlayer.civName.toLabel()).left().row() - - - val civTableScrollPane = getCivTableScroll(relevantCivs, titleTable, playerKnowsAndUndefeatedCivs, playerKnowsAndDefeatedCivs) - - val toggleCityStatesButton = "City-States".toTextButton() - toggleCityStatesButton.color = if(includeCityStates)Color.RED else Color.GREEN - toggleCityStatesButton.onClick { includeCityStates=!includeCityStates; update() } - - val floatingTable = Table() - floatingTable.add(toggleCityStatesButton).row() - floatingTable.add(civTableScrollPane.addBorder(2f, Color.WHITE)).pad(10f) - add(floatingTable) - } - - - fun getCivMinitable(civInfo: CivilizationInfo):Table { - val table = Table() - table.add(ImageGetter.getNationIndicator(civInfo.nation, 25f)).pad(5f) - table.add(civInfo.civName.toLabel()).left() - table.touchable = Touchable.enabled - table.onClick { - if (civInfo.isDefeated() || viewingPlayer.isSpectator() || civInfo == viewingPlayer) return@onClick - UncivGame.Current.setScreen(DiplomacyScreen(viewingPlayer).apply { updateRightSide(civInfo) }) - } - return table - } - - private fun getCivTableScroll(relevantCivs: List, titleTable: Table, - playerKnowsAndUndefeatedCivs: List, - playerKnowsAndDefeatedCivs: List): ScrollPane { - val civTable = Table() - civTable.defaults().pad(5f) - civTable.background = ImageGetter.getBackground(Color.BLACK) - civTable.add("[${relevantCivs.size}] Civilizations in the game".toLabel()).pad(5f).colspan(2).row() - civTable.add(titleTable).colspan(2).row() - civTable.addSeparator() - civTable.add("Known and alive ([${playerKnowsAndUndefeatedCivs.size - 1}])".toLabel()) - .pad(5f).colspan(2).row() - if (playerKnowsAndUndefeatedCivs.size > 1) { - civTable.addSeparator() - playerKnowsAndUndefeatedCivs.filter { it != viewingPlayer }.forEach { - civTable.add(getCivMinitable(it)).left() - if (playerKnowsAndUndefeatedCivs.indexOf(it) % 2 == 0) civTable.row() - } - } - civTable.addSeparator() - civTable.add("Known and defeated ([${playerKnowsAndDefeatedCivs.size}])".toLabel()) - .pad(5f).colspan(2).row() - if (playerKnowsAndDefeatedCivs.isNotEmpty()) { - civTable.addSeparator() - playerKnowsAndDefeatedCivs.forEach { - civTable.add(getCivMinitable(it)).left() - if (playerKnowsAndDefeatedCivs.indexOf(it) % 2 == 0) civTable.row() - } - } - val civTableScrollPane = ScrollPane(civTable) - civTableScrollPane.setOverscroll(false, false) - return civTableScrollPane - } - } - - private fun getTradesTable(): Table { - val tradesTable = Table().apply { defaults().pad(10f) } - val diplomacies = viewingPlayer.diplomacy.values.filter { it.trades.isNotEmpty() } - .sortedWith { diplomacyManager1, diplomacyManager2 -> - val d1OffersFromFirstTrade = diplomacyManager1.trades.first().ourOffers - val d2OffersFromFirstTrade = diplomacyManager2.trades.first().ourOffers - val d1MaxDuration = if (d1OffersFromFirstTrade.isEmpty()) 0 else d1OffersFromFirstTrade.maxByOrNull { it.duration }!!.duration - val d2MaxDuration = if (d2OffersFromFirstTrade.isEmpty()) 0 else d2OffersFromFirstTrade.maxByOrNull { it.duration }!!.duration - when { - d1MaxDuration > d2MaxDuration -> 1 - d1MaxDuration == d2MaxDuration -> 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)).minWidth(stage.width/4).fillY() - generalTable.add(createOffersTable(otherCiv, trade.theirOffers, trade.ourOffers.size)).minWidth(stage.width/4).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() - - if(viewingPlayer.gameInfo.ruleSet.modOptions.uniques.contains("Can convert gold to science with sliders")) { - goldTable.addSeparator() - val sliderTable = Table() - sliderTable.add("Convert gold to science".toLabel()).row() - val slider = Slider(0f, 1f, 0.1f, false, skin) - slider.value = viewingPlayer.tech.goldPercentConvertedToScience - - slider.onChange { - viewingPlayer.tech.goldPercentConvertedToScience = slider.value - viewingPlayer.cities.forEach { it.cityStats.update() } - setCategoryActions["Stats"]!!() - } - sliderTable.add(slider) - goldTable.add(sliderTable).colspan(2) - } - - 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 getUnitTable(): Table { - val table = Table(skin).apply { defaults().pad(5f) } - table.add("Name".tr()) - table.add("Action".tr()) - table.add(Fonts.strength.toString()) - table.add(Fonts.rangedStrength.toString()) - table.add(Fonts.movement.toString()) - 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.displayName() }, { !it.due }, - { it.currentMovement < 0.1f }, { abs(it.currentTile.position.x) + abs(it.currentTile.position.y) }))) { - val baseUnit = unit.baseUnit() - - val button = Button(skin) - button.add(UnitGroup(unit,20f)).padRight(5f) - button.add(unit.displayName().toLabel()) - button.onClick { - game.setWorldScreen() - game.worldScreen.mapHolder.setCenterPosition(unit.currentTile.position) - } - table.add(button).left() - val mapUnitAction = unit.action - if (mapUnitAction == null) table.add() - else table.add(if (mapUnitAction.startsWith("Fortify")) "Fortify".tr() else mapUnitAction.tr()) - 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(unit.getUnitToUpgradeTo().name, Color.GREEN)).size(28f).padLeft(8f) - promotionsTable.onClick { - if (unit.promotions.canBePromoted() || unit.promotions.promotions.isNotEmpty()) { - game.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 getResourcesTable(): Table { - val resourcesTable = Table().apply { defaults().pad(10f) } - val resourceDrilldown = viewingPlayer.detailedCivResources - - // First row of table has all the icons - resourcesTable.add() - // Order of source ResourceSupplyList: by tiles, enumerating the map in that spiral pattern - // UI should not surprise player, thus we need a deterministic and guessable order - val resources = resourceDrilldown.map { it.resource } - .filter { it.resourceType != ResourceType.Bonus }.distinct() - .sortedWith(compareBy({ it.resourceType }, { it.name.tr() })) - - 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!!.isVisible = false - resourceLabel.isVisible = true - visibleLabel = resourceLabel - } - holder.addActor(resourceImage) - holder.addActor(resourceLabel) - holder.setSize(resourceImage.width, - resourceImage.height + resourceLabel.height + 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.width - resourceLabel.width) / alignFactor, - resourceImage.height + labelPadding) - resourceLabel.isVisible = 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.toLabel()) - } - - return resourcesTable - } - + //todo this belongs in VictoryScreen as it's only ever used there companion object { fun getCivGroup(civ: CivilizationInfo, afterCivNameText:String, currentPlayer:CivilizationInfo): Table { val civGroup = Table() @@ -522,88 +151,3 @@ class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPa } } } - - - -class DiplomacyGroup(val viewingPlayer: CivilizationInfo, freeHeight: Float, includeCityStates: Boolean): Group() { - private fun onCivClicked(civLines: HashMap>, 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 } - } - - - fun playerKnows(civ: CivilizationInfo) = civ == viewingPlayer || - viewingPlayer.diplomacy.containsKey(civ.civName) - - init { - val relevantCivs = viewingPlayer.gameInfo.civilizations.filter { !it.isBarbarian() && (includeCityStates || !it.isCityState()) } - val playerKnowsAndUndefeatedCivs = relevantCivs.filter { playerKnows(it) && !it.isDefeated() } - setSize(freeHeight, freeHeight) - val civGroups = HashMap() - val civLines = HashMap>() - for (i in 0..playerKnowsAndUndefeatedCivs.lastIndex) { - val civ = playerKnowsAndUndefeatedCivs[i] - - val civGroup = ImageGetter.getNationIndicator(civ.nation, 30f) - - val vector = HexMath.getVectorForAngle(2 * Math.PI.toFloat() * i / playerKnowsAndUndefeatedCivs.size) - civGroup.center(this) - civGroup.moveBy(vector.x * freeHeight / 2.25f, vector.y * freeHeight / 2.25f) - civGroup.touchable = Touchable.enabled - civGroup.onClick { - onCivClicked(civLines, civ.civName) - } - - civGroups[civ.civName] = civGroup - addActor(civGroup) - } - - for (civ in relevantCivs.filter { playerKnows(it) && !it.isDefeated() }) - for (diplomacy in civ.diplomacy.values.filter { - (it.otherCiv().isMajorCiv() || includeCityStates) && 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, 2f) - - statusLine.color = if (diplomacy.diplomaticStatus == DiplomaticStatus.Peace) Color.GREEN else Color.RED - - civLines[civ.civName]!!.add(statusLine) - - addActor(statusLine) - statusLine.toBack() - } - } -} diff --git a/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt new file mode 100644 index 0000000000..c91c5241f5 --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/ResourcesOverviewTable.kt @@ -0,0 +1,86 @@ +package com.unciv.ui.overviewscreen + +import com.badlogic.gdx.scenes.scene2d.Group +import com.badlogic.gdx.scenes.scene2d.ui.Label +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.models.ruleset.tile.ResourceType +import com.unciv.models.translations.tr +import com.unciv.ui.utils.ImageGetter +import com.unciv.ui.utils.addSeparator +import com.unciv.ui.utils.onClick +import com.unciv.ui.utils.toLabel + +@Suppress("UNUSED_PARAMETER") // Keep all OverviewScreen Pages compatible +class ResourcesOverviewTable ( + viewingPlayer: CivilizationInfo, + overviewScreen: EmpireOverviewScreen +) : Table() { + //val game = overviewScreen.game + + init { + defaults().pad(10f) + + val resourceDrilldown = viewingPlayer.detailedCivResources + + // First row of table has all the icons + add() + + // Order of source ResourceSupplyList: by tiles, enumerating the map in that spiral pattern + // UI should not surprise player, thus we need a deterministic and guessable order + val resources = resourceDrilldown.map { it.resource } + .filter { it.resourceType != ResourceType.Bonus }.distinct() + .sortedWith(compareBy({ it.resourceType }, { it.name.tr() })) + + 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!!.isVisible = false + resourceLabel.isVisible = true + visibleLabel = resourceLabel + } + holder.addActor(resourceImage) + holder.addActor(resourceLabel) + holder.setSize(resourceImage.width, + resourceImage.height + resourceLabel.height + 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.width - resourceLabel.width) / alignFactor, + resourceImage.height + labelPadding) + resourceLabel.isVisible = false + add(holder) + } + addSeparator() + + val origins = resourceDrilldown.map { it.origin }.distinct() + for (origin in origins) { + add(origin.toLabel()) + for (resource in resources) { + val resourceSupply = resourceDrilldown.firstOrNull { it.resource == resource && it.origin == origin } + if (resourceSupply == null) add() + else add(resourceSupply.amount.toString().toLabel()) + } + row() + } + + add("Total".toLabel()) + for (resource in resources) { + val sum = resourceDrilldown.filter { it.resource == resource }.sumBy { it.amount } + add(sum.toLabel()) + } + } +} diff --git a/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt new file mode 100644 index 0000000000..98923a8da9 --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt @@ -0,0 +1,136 @@ +package com.unciv.ui.overviewscreen + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.scenes.scene2d.ui.Slider +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.models.translations.tr +import com.unciv.ui.utils.* +import kotlin.math.roundToInt + +class StatsOverviewTable ( + private val viewingPlayer: CivilizationInfo, + private val overviewScreen: EmpireOverviewScreen +) : Table() { + //val game = overviewScreen.game + + init { + defaults().pad(40f) + add(getHappinessTable()).top() + add(getGoldTable()).top() + add(getScienceTable()).top() + add(getGreatPeopleTable()).top() + } + + private fun getHappinessTable(): Table { + val happinessTable = Table(CameraStageBaseScreen.skin) + happinessTable.defaults().pad(5f) + val happinessHeader = Table(CameraStageBaseScreen.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(CameraStageBaseScreen.skin) + goldTable.defaults().pad(5f) + val goldHeader = Table(CameraStageBaseScreen.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() + + if (viewingPlayer.gameInfo.ruleSet.modOptions.uniques.contains("Can convert gold to science with sliders")) { + goldTable.addSeparator() + val sliderTable = Table() + sliderTable.add("Convert gold to science".toLabel()).row() + val slider = Slider(0f, 1f, 0.1f, false, CameraStageBaseScreen.skin) + slider.value = viewingPlayer.tech.goldPercentConvertedToScience + + slider.onChange { + viewingPlayer.tech.goldPercentConvertedToScience = slider.value + viewingPlayer.cities.forEach { it.cityStats.update() } + overviewScreen.setCategoryActions["Stats"]!!() // ? will probably steal focus and so prevent dragging the slider + } + sliderTable.add(slider) + goldTable.add(sliderTable).colspan(2) + } + + goldTable.pack() + return goldTable + } + + private fun getScienceTable(): Table { + val scienceTable = Table(CameraStageBaseScreen.skin) + scienceTable.defaults().pad(5f) + val scienceHeader = Table(CameraStageBaseScreen.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(CameraStageBaseScreen.skin) + + val greatPersonPoints = viewingPlayer.greatPeople.greatPersonPoints.toHashMap() + val greatPersonPointsPerTurn = viewingPlayer.getGreatPersonPointsForNextTurn().toHashMap() + val pointsToGreatPerson = viewingPlayer.greatPeople.pointsForNextGreatPerson + + greatPeopleTable.defaults().pad(5f) + val greatPeopleHeader = Table(CameraStageBaseScreen.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 + } +} diff --git a/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt new file mode 100644 index 0000000000..8fc65bfafe --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/TradesOverviewTable.kt @@ -0,0 +1,59 @@ +package com.unciv.ui.overviewscreen + +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.trade.Trade +import com.unciv.logic.trade.TradeOffersList +import com.unciv.ui.utils.CameraStageBaseScreen +import com.unciv.ui.utils.ImageGetter +import com.unciv.ui.utils.addSeparator +import com.unciv.ui.utils.toLabel + +class TradesOverviewTable ( + private val viewingPlayer: CivilizationInfo, + private val overviewScreen: EmpireOverviewScreen +) : Table() { + + init { + defaults().pad(10f) + val diplomacies = viewingPlayer.diplomacy.values.filter { it.trades.isNotEmpty() } + .sortedWith { diplomacyManager1, diplomacyManager2 -> + val d1OffersFromFirstTrade = diplomacyManager1.trades.first().ourOffers + val d2OffersFromFirstTrade = diplomacyManager2.trades.first().ourOffers + val d1MaxDuration = if (d1OffersFromFirstTrade.isEmpty()) 0 else d1OffersFromFirstTrade.maxByOrNull { it.duration }!!.duration + val d2MaxDuration = if (d2OffersFromFirstTrade.isEmpty()) 0 else d2OffersFromFirstTrade.maxByOrNull { it.duration }!!.duration + when { + d1MaxDuration > d2MaxDuration -> 1 + d1MaxDuration == d2MaxDuration -> 0 + else -> -1 + } + } + for(diplomacy in diplomacies) { + for (trade in diplomacy.trades) + add(createTradeTable(trade, diplomacy.otherCiv())).row() + } + } + + private fun createTradeTable(trade: Trade, otherCiv: CivilizationInfo): Table { + val generalTable = Table(CameraStageBaseScreen.skin) + generalTable.add(createOffersTable(viewingPlayer, trade.ourOffers, trade.theirOffers.size)).minWidth(overviewScreen.stage.width/4).fillY() + generalTable.add(createOffersTable(otherCiv, trade.theirOffers, trade.ourOffers.size)).minWidth(overviewScreen.stage.width/4).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 + } +} diff --git a/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt new file mode 100644 index 0000000000..075b43c97a --- /dev/null +++ b/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt @@ -0,0 +1,73 @@ +package com.unciv.ui.overviewscreen + +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.scenes.scene2d.ui.Button +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.models.translations.tr +import com.unciv.ui.pickerscreens.PromotionPickerScreen +import com.unciv.ui.utils.* +import java.text.DecimalFormat +import kotlin.math.abs + +/** + * Supplies the Unit sub-table for the Empire Overview + */ +class UnitOverviewTable( + viewingPlayer: CivilizationInfo, + overviewScreen: EmpireOverviewScreen +) : Table(CameraStageBaseScreen.skin) { + + init { + val game = overviewScreen.game + + defaults().pad(5f) + add("Name".tr()) + add("Action".tr()) + add(Fonts.strength.toString()) + add(Fonts.rangedStrength.toString()) + add(Fonts.movement.toString()) + add("Closest city".tr()) + add("Promotions".tr()) + add("Health".tr()) + row() + addSeparator() + + for (unit in viewingPlayer.getCivUnits().sortedWith(compareBy({ it.displayName() }, { !it.due }, + { it.currentMovement < 0.1f }, { abs(it.currentTile.position.x) + abs(it.currentTile.position.y) }))) { + val baseUnit = unit.baseUnit() + + val button = Button(skin) + button.add(UnitGroup(unit,20f)).padRight(5f) + button.add(unit.displayName().toLabel()) + button.onClick { + game.setWorldScreen() + game.worldScreen.mapHolder.setCenterPosition(unit.currentTile.position) + } + add(button).left() + val mapUnitAction = unit.action + if (mapUnitAction == null) add() + else add(if (mapUnitAction.startsWith("Fortify")) "Fortify".tr() else mapUnitAction.tr()) + if (baseUnit.strength > 0) add(baseUnit.strength.toString()) else add() + if (baseUnit.rangedStrength > 0) add(baseUnit.rangedStrength.toString()) else add() + add(DecimalFormat("0.#").format(unit.currentMovement) + "/" + unit.getMaxMovement()) + val closestCity = unit.getTile().getTilesInDistance(3).firstOrNull { it.isCityCenter() } + if (closestCity != null) add(closestCity.getCity()!!.name.tr()) else 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(unit.getUnitToUpgradeTo().name, Color.GREEN)).size(28f).padLeft(8f) + promotionsTable.onClick { + if (unit.promotions.canBePromoted() || unit.promotions.promotions.isNotEmpty()) { + game.setScreen(PromotionPickerScreen(unit)) + } + } + add(promotionsTable) + if (unit.health < 100) add(unit.health.toString()) else add() + row() + } + pack() + } +}