mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-28 13:48:49 +07:00
Empire Overview Diplomacy (#6375)
* Empire Overview Diplomacy: Preparations * Empire Overview Diplomacy: DiplomacyScreen linkable * Empire Overview Diplomacy: Overhaul * Empire Overview Diplomacy: List always begins a new row between major and minor civs
This commit is contained in:
@ -283,15 +283,15 @@ class GameInfo {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun checkForTimeVictory() {
|
||||
if (turns != gameParameters.maxTurns || !gameParameters.victoryTypes.contains(VictoryType.Time)) return
|
||||
|
||||
|
||||
val winningCiv = civilizations
|
||||
.filter { it.isMajorCiv() && !it.isSpectator() && !it.isBarbarian() }
|
||||
.maxByOrNull { it.calculateScoreBreakdown().values.sum() }
|
||||
.maxByOrNull { it.calculateTotalScore() }
|
||||
?: return // Are there no civs left?
|
||||
|
||||
|
||||
winningCiv.victoryManager.hasWonTimeVictory = true
|
||||
}
|
||||
|
||||
@ -322,19 +322,18 @@ class GameInfo {
|
||||
// Calling with `maxDistance = 0` removes distance limitation.
|
||||
data class CityTileAndDistance(val city: CityInfo, val tile: TileInfo, val distance: Int)
|
||||
|
||||
var exploredRevealTiles:Sequence<TileInfo>
|
||||
|
||||
if (ruleSet.tileResources[resourceName]!!.hasUnique(UniqueType.CityStateOnlyResource)) {
|
||||
// Look for matching mercantile CS centers
|
||||
exploredRevealTiles = getAliveCityStates()
|
||||
.asSequence()
|
||||
.filter { it.cityStateResource == resourceName }
|
||||
.map { it.getCapital().getCenterTile() }
|
||||
} else {
|
||||
exploredRevealTiles = tileMap.values
|
||||
.asSequence()
|
||||
.filter { it.resource == resourceName }
|
||||
}
|
||||
val exploredRevealTiles: Sequence<TileInfo> =
|
||||
if (ruleSet.tileResources[resourceName]!!.hasUnique(UniqueType.CityStateOnlyResource)) {
|
||||
// Look for matching mercantile CS centers
|
||||
getAliveCityStates()
|
||||
.asSequence()
|
||||
.filter { it.cityStateResource == resourceName }
|
||||
.map { it.getCapital().getCenterTile() }
|
||||
} else {
|
||||
tileMap.values
|
||||
.asSequence()
|
||||
.filter { it.resource == resourceName }
|
||||
}
|
||||
|
||||
val exploredRevealInfo = exploredRevealTiles
|
||||
.filter { it.position in civInfo.exploredTiles }
|
||||
|
@ -288,6 +288,20 @@ class CivilizationInfo {
|
||||
fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName)
|
||||
fun knows(otherCiv: CivilizationInfo) = knows(otherCiv.civName)
|
||||
|
||||
/** A sorted Sequence of all other civs we know (excluding barbarians and spectators) */
|
||||
fun getKnownCivsSorted(includeCityStates: Boolean = true, includeDefeated: Boolean = false) =
|
||||
gameInfo.civilizations.asSequence()
|
||||
.filterNot {
|
||||
it == this ||
|
||||
it.isBarbarian() || it.isSpectator() ||
|
||||
!this.knows(it) ||
|
||||
(!includeDefeated && it.isDefeated()) ||
|
||||
(!includeCityStates && it.isCityState())
|
||||
}
|
||||
.sortedWith(
|
||||
compareByDescending<CivilizationInfo> { it.isMajorCiv() }
|
||||
.thenBy (UncivGame.Current.settings.getCollatorFromLocale()) { it.civName.tr() }
|
||||
)
|
||||
fun getCapital() = cities.first { it.isCapital() }
|
||||
fun isPlayerCivilization() = playerType == PlayerType.Human
|
||||
fun isOneCityChallenger() = (
|
||||
@ -611,7 +625,7 @@ class CivilizationInfo {
|
||||
|
||||
fun getStatForRanking(category: RankingType): Int {
|
||||
return when (category) {
|
||||
RankingType.Score -> calculateScoreBreakdown().values.sum().toInt()
|
||||
RankingType.Score -> calculateTotalScore().toInt()
|
||||
RankingType.Population -> cities.sumOf { it.population.population }
|
||||
RankingType.Crop_Yield -> statsForNextTurn.food.roundToInt()
|
||||
RankingType.Production -> statsForNextTurn.production.roundToInt()
|
||||
@ -673,7 +687,7 @@ class CivilizationInfo {
|
||||
var mapSizeModifier = 1276 / gameInfo.tileMap.mapParameters.numberOfTiles().toDouble()
|
||||
if (mapSizeModifier > 1)
|
||||
mapSizeModifier = (mapSizeModifier - 1) / 3 + 1
|
||||
|
||||
|
||||
scoreBreakdown["Cities"] = cities.count() * 10 * mapSizeModifier
|
||||
scoreBreakdown["Population"] = cities.sumOf { it.population.population } * 3 * mapSizeModifier
|
||||
scoreBreakdown["Tiles"] = cities.sumOf { city -> city.getTiles().filter { !it.isWater}.count() } * 1 * mapSizeModifier
|
||||
@ -683,10 +697,12 @@ class CivilizationInfo {
|
||||
}.toDouble()
|
||||
scoreBreakdown["Techs"] = tech.getNumberOfTechsResearched() * 4.toDouble()
|
||||
scoreBreakdown["Future Tech"] = tech.repeatingTechsResearched * 10.toDouble()
|
||||
|
||||
|
||||
return scoreBreakdown
|
||||
}
|
||||
|
||||
|
||||
fun calculateTotalScore() = calculateScoreBreakdown().values.sum()
|
||||
|
||||
//endregion
|
||||
|
||||
//region state-changing functions
|
||||
|
@ -95,14 +95,13 @@ data class CityAction(val city: Vector2 = Vector2.Zero): NotificationAction {
|
||||
/** enter diplomacy screen */
|
||||
data class DiplomacyAction(val otherCivName: String = ""): NotificationAction {
|
||||
override fun execute(worldScreen: WorldScreen) {
|
||||
val screen = DiplomacyScreen(worldScreen.viewingCiv)
|
||||
screen.updateRightSide(worldScreen.gameInfo.getCivilization(otherCivName))
|
||||
worldScreen.game.setScreen(screen)
|
||||
val otherCiv = worldScreen.gameInfo.getCivilization(otherCivName)
|
||||
worldScreen.game.setScreen(DiplomacyScreen(worldScreen.viewingCiv, otherCiv))
|
||||
}
|
||||
}
|
||||
|
||||
/** enter Maya Long Count popup */
|
||||
class MayaLongCountAction() : NotificationAction {
|
||||
class MayaLongCountAction : NotificationAction {
|
||||
override fun execute(worldScreen: WorldScreen) {
|
||||
MayaCalendar.openPopup(worldScreen, worldScreen.selectedCiv, worldScreen.gameInfo.getYear())
|
||||
}
|
||||
|
@ -5,12 +5,17 @@ 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.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup
|
||||
import com.badlogic.gdx.utils.Align
|
||||
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.*
|
||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class DiplomacyOverviewTab (
|
||||
viewingPlayer: CivilizationInfo,
|
||||
@ -24,41 +29,91 @@ class DiplomacyOverviewTab (
|
||||
}
|
||||
override val persistableData = (persistedData as? DiplomacyTabPersistableData) ?: DiplomacyTabPersistableData()
|
||||
|
||||
// Widgets that are kept between updates
|
||||
private val fixedContent = Table()
|
||||
private val civTable = Table().apply {
|
||||
defaults().pad(5f)
|
||||
background = ImageGetter.getBackground(Color.BLACK)
|
||||
}
|
||||
val toggleCityStatesButton: TextButton = "City-States".toTextButton().apply {
|
||||
onClick {
|
||||
persistableData.includeCityStates = !persistableData.includeCityStates
|
||||
update()
|
||||
}
|
||||
}
|
||||
private val civTableScroll = AutoScrollPane(civTable).apply {
|
||||
setOverscroll(false, false)
|
||||
}
|
||||
private val floatingTable = Table().apply {
|
||||
add(toggleCityStatesButton).row()
|
||||
add(civTableScroll.addBorder(2f, Color.WHITE)).pad(10f)
|
||||
}
|
||||
|
||||
// Reusable sequences for the Civilizations to display
|
||||
private var undefeatedCivs = sequenceOf<CivilizationInfo>()
|
||||
private var defeatedCivs = sequenceOf<CivilizationInfo>()
|
||||
|
||||
private var relevantCivsCount = 0 // includes unknown civs
|
||||
private var showDiplomacyGroup = false
|
||||
private var portraitMode = false
|
||||
|
||||
init {
|
||||
update()
|
||||
}
|
||||
|
||||
fun update() {
|
||||
clear()
|
||||
val relevantCivs = gameInfo.civilizations
|
||||
.filter { !it.isBarbarian() && !it.isSpectator() && (persistableData.includeCityStates || !it.isCityState()) }
|
||||
val diplomacyGroup = DiplomacyGroup(viewingPlayer, overviewScreen.centerAreaHeight, persistableData.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).top()
|
||||
|
||||
val titleTable = Table()
|
||||
titleTable.add("Our Civilization:".toLabel()).colspan(2).row()
|
||||
titleTable.add(ImageGetter.getNationIndicator(viewingPlayer.nation, 25f)).pad(5f)
|
||||
titleTable.add(viewingPlayer.civName.toLabel()).left().padRight(10f)
|
||||
titleTable.add(viewingPlayer.calculateScoreBreakdown().values.sum().toInt().toLabel()).row()
|
||||
|
||||
val civTableScrollPane = getCivTableScroll(relevantCivs, titleTable, playerKnowsAndUndefeatedCivs, playerKnowsAndDefeatedCivs)
|
||||
|
||||
val toggleCityStatesButton = "City-States".toTextButton()
|
||||
toggleCityStatesButton.color = if (persistableData.includeCityStates) Color.RED else Color.GREEN
|
||||
toggleCityStatesButton.onClick {
|
||||
persistableData.includeCityStates = !persistableData.includeCityStates
|
||||
update()
|
||||
}
|
||||
|
||||
val floatingTable = Table()
|
||||
floatingTable.add(toggleCityStatesButton).row()
|
||||
floatingTable.add(civTableScrollPane.addBorder(2f, Color.WHITE)).pad(10f)
|
||||
add(floatingTable)
|
||||
override fun getFixedContent(): WidgetGroup {
|
||||
return fixedContent
|
||||
}
|
||||
|
||||
// Refresh content and determine landscape/portrait layout
|
||||
private fun update() {
|
||||
relevantCivsCount = gameInfo.civilizations.count {
|
||||
!it.isSpectator() && !it.isBarbarian() && (persistableData.includeCityStates || !it.isCityState())
|
||||
}
|
||||
undefeatedCivs = sequenceOf(viewingPlayer) +
|
||||
viewingPlayer.getKnownCivsSorted(persistableData.includeCityStates)
|
||||
defeatedCivs = viewingPlayer.getKnownCivsSorted(persistableData.includeCityStates, true)
|
||||
.filter { it.isDefeated() }
|
||||
|
||||
clear()
|
||||
fixedContent.clear()
|
||||
|
||||
showDiplomacyGroup = undefeatedCivs.any { it != viewingPlayer }
|
||||
updateCivTable(2)
|
||||
portraitMode = !showDiplomacyGroup ||
|
||||
civTable.minWidth > overviewScreen.stage.width / 2 ||
|
||||
overviewScreen.isPortrait()
|
||||
val table = if (portraitMode) this else fixedContent
|
||||
|
||||
if (showDiplomacyGroup) {
|
||||
val diplomacySize = (overviewScreen.stage.width - (if (portraitMode) 0f else civTable.minWidth))
|
||||
.coerceAtMost(overviewScreen.centerAreaHeight)
|
||||
val diplomacyGroup = DiplomacyGroup(undefeatedCivs, diplomacySize)
|
||||
table.add(diplomacyGroup).top()
|
||||
}
|
||||
|
||||
if (portraitMode) {
|
||||
if (showDiplomacyGroup) table.row()
|
||||
val columns = 2 * (overviewScreen.stage.width / civTable.minWidth).roundToInt()
|
||||
if (columns > 2) {
|
||||
updateCivTable(columns)
|
||||
if (civTable.minWidth > overviewScreen.stage.width)
|
||||
updateCivTable(columns - 2)
|
||||
}
|
||||
}
|
||||
|
||||
table.add(floatingTable)
|
||||
toggleCityStatesButton.color = if (persistableData.includeCityStates) Color.RED else Color.GREEN
|
||||
civTableScroll.setScrollingDisabled(portraitMode, portraitMode)
|
||||
}
|
||||
|
||||
private fun updateCivTable(columns: Int) = civTable.apply {
|
||||
clear()
|
||||
addTitleInfo(columns)
|
||||
addCivsCategory(columns, "alive", undefeatedCivs.filter { it != viewingPlayer })
|
||||
addCivsCategory(columns, "defeated", defeatedCivs)
|
||||
layout()
|
||||
}
|
||||
|
||||
private fun getCivMiniTable(civInfo: CivilizationInfo): Table {
|
||||
val table = Table()
|
||||
@ -67,62 +122,56 @@ class DiplomacyOverviewTab (
|
||||
table.touchable = Touchable.enabled
|
||||
table.onClick {
|
||||
if (civInfo.isDefeated() || viewingPlayer.isSpectator() || civInfo == viewingPlayer) return@onClick
|
||||
UncivGame.Current.setScreen(DiplomacyScreen(viewingPlayer).apply { updateRightSide(civInfo) })
|
||||
overviewScreen.dispose()
|
||||
UncivGame.Current.setScreen(DiplomacyScreen(viewingPlayer, civInfo))
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
private fun getCivTableScroll(relevantCivs: List<CivilizationInfo>, titleTable: Table,
|
||||
playerKnowsAndUndefeatedCivs: List<CivilizationInfo>,
|
||||
playerKnowsAndDefeatedCivs: List<CivilizationInfo>): 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()
|
||||
val turnsTillNextDiplomaticVote = viewingPlayer.getTurnsTillNextDiplomaticVote()
|
||||
if (turnsTillNextDiplomaticVote != null)
|
||||
civTable.add("Turns until the next\ndiplomacy victory vote: [$turnsTillNextDiplomaticVote]".toLabel()).center().pad(5f).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()
|
||||
var cityStatesParsed = 0
|
||||
playerKnowsAndUndefeatedCivs.filter { it != viewingPlayer }.forEach {
|
||||
civTable.add(getCivMiniTable(it)).left()
|
||||
if (it.isCityState()) {
|
||||
cityStatesParsed++
|
||||
} else {
|
||||
civTable.add(it.calculateScoreBreakdown().values.sum().toInt().toLabel()).left()
|
||||
}
|
||||
if (!it.isCityState() || cityStatesParsed % 2 == 0)
|
||||
civTable.row()
|
||||
}
|
||||
}
|
||||
civTable.addSeparator()
|
||||
civTable.add("Known and defeated ([${playerKnowsAndDefeatedCivs.size}])".toLabel())
|
||||
.pad(5f).colspan(2).row()
|
||||
if (playerKnowsAndDefeatedCivs.isNotEmpty()) {
|
||||
civTable.addSeparator()
|
||||
var cityStatesParsed = 0
|
||||
playerKnowsAndDefeatedCivs.forEach {
|
||||
civTable.add(getCivMiniTable(it)).left()
|
||||
if (it.isCityState()) {
|
||||
cityStatesParsed++
|
||||
} else {
|
||||
civTable.add(it.calculateScoreBreakdown().values.sum().toInt().toLabel()).left()
|
||||
}
|
||||
if (!it.isCityState() || cityStatesParsed % 2 == 0)
|
||||
civTable.row()
|
||||
}
|
||||
}
|
||||
val civTableScrollPane = AutoScrollPane(civTable)
|
||||
civTableScrollPane.setOverscroll(false, false)
|
||||
return civTableScrollPane
|
||||
private fun Table.addTitleInfo(columns: Int) {
|
||||
add("[$relevantCivsCount] Civilizations in the game".toLabel()).colspan(columns).row()
|
||||
add("Our Civilization:".toLabel()).colspan(columns).left().padLeft(10f).padTop(10f).row()
|
||||
add(getCivMiniTable(viewingPlayer)).left()
|
||||
add(viewingPlayer.calculateTotalScore().toInt().toLabel()).left().row()
|
||||
val turnsTillNextDiplomaticVote = viewingPlayer.getTurnsTillNextDiplomaticVote() ?: return
|
||||
add("Turns until the next\ndiplomacy victory vote: [$turnsTillNextDiplomaticVote]".toLabel()).colspan(columns).row()
|
||||
}
|
||||
|
||||
private class DiplomacyGroup(val viewingPlayer: CivilizationInfo, freeHeight: Float, includeCityStates: Boolean): Group() {
|
||||
private fun Table.addCivsCategory(columns: Int, aliveOrDefeated: String, civs: Sequence<CivilizationInfo>) {
|
||||
addSeparator()
|
||||
val count = civs.count()
|
||||
add("Known and $aliveOrDefeated ([$count])".toLabel())
|
||||
.pad(5f).colspan(columns).row()
|
||||
if (count == 0) return
|
||||
addSeparator()
|
||||
var currentColumn = 0
|
||||
var lastCivWasMajor = false
|
||||
fun advanceCols(delta: Int) {
|
||||
currentColumn += delta
|
||||
if (currentColumn >= columns) {
|
||||
row()
|
||||
currentColumn = 0
|
||||
}
|
||||
lastCivWasMajor = delta == 2
|
||||
}
|
||||
for (civ in civs) {
|
||||
if (lastCivWasMajor && civ.isCityState())
|
||||
advanceCols(columns)
|
||||
add(getCivMiniTable(civ)).left()
|
||||
if (civ.isCityState()) {
|
||||
advanceCols(1)
|
||||
} else {
|
||||
add(civ.calculateTotalScore().toInt().toLabel()).left()
|
||||
advanceCols(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This is the 'spider net'-like polygon showing one line per civ-civ relation */
|
||||
private class DiplomacyGroup(
|
||||
undefeatedCivs: Sequence<CivilizationInfo>,
|
||||
freeSize: Float
|
||||
): 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
|
||||
@ -146,61 +195,59 @@ class DiplomacyOverviewTab (
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
setSize(freeSize, freeSize)
|
||||
val civGroups = HashMap<String, Actor>()
|
||||
val civLines = HashMap<String, MutableSet<Actor>>()
|
||||
for (i in 0..playerKnowsAndUndefeatedCivs.lastIndex) {
|
||||
val civ = playerKnowsAndUndefeatedCivs[i]
|
||||
val civCount = undefeatedCivs.count()
|
||||
|
||||
for ((i, civ) in undefeatedCivs.withIndex()) {
|
||||
val civGroup = ImageGetter.getNationIndicator(civ.nation, 30f)
|
||||
|
||||
val vector = HexMath.getVectorForAngle(2 * Math.PI.toFloat() * i / playerKnowsAndUndefeatedCivs.size)
|
||||
val vector = HexMath.getVectorForAngle(2 * Math.PI.toFloat() * i / civCount)
|
||||
civGroup.center(this)
|
||||
civGroup.moveBy(vector.x * freeHeight / 2.25f, vector.y * freeHeight / 2.25f)
|
||||
civGroup.moveBy(vector.x * freeSize / 2.25f, vector.y * freeSize / 2.25f)
|
||||
civGroup.touchable = Touchable.enabled
|
||||
civGroup.onClick {
|
||||
onCivClicked(civLines, civ.civName)
|
||||
}
|
||||
civGroup.addTooltip(civ.civName, tipAlign = Align.bottomLeft)
|
||||
|
||||
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()
|
||||
}) {
|
||||
for (civ in undefeatedCivs)
|
||||
for (diplomacy in civ.diplomacy.values) {
|
||||
if (diplomacy.otherCiv() !in undefeatedCivs) continue
|
||||
val civGroup = civGroups[civ.civName]!!
|
||||
val otherCivGroup = civGroups[diplomacy.otherCivName]!!
|
||||
|
||||
if (!civLines.containsKey(civ.civName))
|
||||
civLines[civ.civName] = mutableSetOf()
|
||||
val statusLine = ImageGetter.getLine(
|
||||
startX = civGroup.x + civGroup.width / 2,
|
||||
startY = civGroup.y + civGroup.height / 2,
|
||||
endX = otherCivGroup.x + otherCivGroup.width / 2,
|
||||
endY = otherCivGroup.y + otherCivGroup.height / 2,
|
||||
width = 2f)
|
||||
|
||||
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.War) Color.RED else Color.GREEN
|
||||
statusLine.color = if (diplomacy.diplomaticStatus == DiplomaticStatus.War) Color.RED
|
||||
else diplomacy.relationshipLevel().color
|
||||
|
||||
if (!civLines.containsKey(civ.civName)) civLines[civ.civName] = mutableSetOf()
|
||||
civLines[civ.civName]!!.add(statusLine)
|
||||
|
||||
addActor(statusLine)
|
||||
statusLine.toBack()
|
||||
addActorAt(0, statusLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,9 +415,7 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
|
||||
private fun foreignCityInfoPopup() {
|
||||
fun openDiplomacy() {
|
||||
// If city doesn't belong to you, go directly to its owner's diplomacy screen.
|
||||
val screen = DiplomacyScreen(worldScreen.viewingCiv)
|
||||
screen.updateRightSide(city.civInfo)
|
||||
worldScreen.game.setScreen(screen)
|
||||
worldScreen.game.setScreen(DiplomacyScreen(worldScreen.viewingCiv, city.civInfo))
|
||||
}
|
||||
|
||||
// If there's nothing to display cuz no Religion - skip popup
|
||||
|
@ -10,14 +10,15 @@ import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.*
|
||||
import com.unciv.logic.civilization.diplomacy.*
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers.*
|
||||
import com.unciv.logic.trade.Trade
|
||||
import com.unciv.logic.trade.TradeLogic
|
||||
import com.unciv.logic.trade.TradeOffer
|
||||
import com.unciv.logic.trade.TradeType
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Quest
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.fillPlaceholders
|
||||
import com.unciv.models.translations.tr
|
||||
@ -27,57 +28,77 @@ import com.unciv.ui.civilopedia.CivilopediaScreen
|
||||
import com.unciv.ui.tilegroups.CityButton
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.roundToInt
|
||||
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
||||
|
||||
class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
/**
|
||||
* Creates the diplomacy screen for [viewingCiv].
|
||||
*
|
||||
* When [selectCiv] is given and [selectTrade] is not, that Civilization is selected as if clicked on the left side.
|
||||
* When [selectCiv] and [selectTrade] are supplied, that Trade for that Civilization is selected, used for the counter-offer option from `TradePopup`.
|
||||
*/
|
||||
@Suppress("KDocUnresolvedReference") // Mentioning non-field parameters is flagged, but they work anyway
|
||||
class DiplomacyScreen(
|
||||
val viewingCiv: CivilizationInfo,
|
||||
selectCiv: CivilizationInfo? = null,
|
||||
selectTrade: Trade? = null
|
||||
): BaseScreen() {
|
||||
companion object {
|
||||
private const val nationIconSize = 100f
|
||||
private const val nationIconPad = 10f
|
||||
}
|
||||
|
||||
private val leftSideTable = Table().apply { defaults().pad(10f) }
|
||||
private val leftSideTable = Table().apply { defaults().pad(nationIconPad) }
|
||||
private val leftSideScroll = ScrollPane(leftSideTable)
|
||||
private val rightSideTable = Table()
|
||||
private val closeButton = Constants.close.toTextButton()
|
||||
|
||||
private fun isNotPlayersTurn() = !UncivGame.Current.worldScreen.canChangeState
|
||||
|
||||
init {
|
||||
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
|
||||
val splitPane = SplitPane(ScrollPane(leftSideTable), rightSideTable, false, skin)
|
||||
val splitPane = SplitPane(leftSideScroll, rightSideTable, false, skin)
|
||||
splitPane.splitAmount = 0.2f
|
||||
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(selectCiv)
|
||||
|
||||
splitPane.setFillParent(true)
|
||||
stage.addActor(splitPane)
|
||||
|
||||
|
||||
val closeButton = Constants.close.toTextButton()
|
||||
closeButton.onClick { UncivGame.Current.setWorldScreen() }
|
||||
closeButton.label.setFontSize(Constants.headingFontSize)
|
||||
closeButton.labelCell.pad(10f)
|
||||
closeButton.pack()
|
||||
closeButton.y = stage.height - closeButton.height - 10
|
||||
closeButton.x =
|
||||
(stage.width * 0.2f - closeButton.width) / 2 // center, leftSideTable.width not known yet
|
||||
positionCloseButton()
|
||||
stage.addActor(closeButton) // This must come after the split pane so it will be above, that the button will be clickable
|
||||
|
||||
if (selectCiv != null) {
|
||||
if (selectTrade != null) {
|
||||
val tradeTable = setTrade(selectCiv)
|
||||
tradeTable.tradeLogic.currentTrade.set(selectTrade)
|
||||
tradeTable.offerColumnsTable.update()
|
||||
} else
|
||||
updateRightSide(selectCiv)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLeftSideTable() {
|
||||
private fun positionCloseButton() {
|
||||
closeButton.setPosition(stage.width * 0.1f, stage.height - 10f, Align.top)
|
||||
}
|
||||
|
||||
private fun updateLeftSideTable(selectCiv: CivilizationInfo?) {
|
||||
leftSideTable.clear()
|
||||
leftSideTable.add().padBottom(60f).row() // room so the close button does not cover the first
|
||||
|
||||
val civsToDisplay = viewingCiv.gameInfo.civilizations.asSequence()
|
||||
.filterNot {
|
||||
it.isDefeated() || it == viewingCiv || it.isBarbarian() || it.isSpectator() ||
|
||||
!viewingCiv.knows(it)
|
||||
var selectCivY = 0f
|
||||
|
||||
for (civ in viewingCiv.getKnownCivsSorted()) {
|
||||
if (civ == selectCiv) {
|
||||
selectCivY = leftSideTable.prefHeight
|
||||
}
|
||||
.sortedWith(
|
||||
compareByDescending<CivilizationInfo>{ it.isMajorCiv() }
|
||||
.thenBy (UncivGame.Current.settings.getCollatorFromLocale(), { it.civName.tr() })
|
||||
)
|
||||
|
||||
for (civ in civsToDisplay) {
|
||||
|
||||
val civIndicator = ImageGetter.getNationIndicator(civ.nation, 100f)
|
||||
val civIndicator = ImageGetter.getNationIndicator(civ.nation, nationIconSize)
|
||||
|
||||
val relationLevel = civ.getDiplomacyManager(viewingCiv).relationshipLevel()
|
||||
val relationshipIcon = if (civ.isCityState() && relationLevel == RelationshipLevel.Ally)
|
||||
@ -114,9 +135,15 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
|
||||
civIndicator.onClick { updateRightSide(civ) }
|
||||
}
|
||||
|
||||
if (selectCivY != 0f) {
|
||||
leftSideScroll.layout()
|
||||
leftSideScroll.scrollY = selectCivY + (nationIconSize + 2 * nationIconPad - stage.height) / 2
|
||||
leftSideScroll.updateVisualScroll()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRightSide(otherCiv: CivilizationInfo) {
|
||||
private fun updateRightSide(otherCiv: CivilizationInfo) {
|
||||
rightSideTable.clear()
|
||||
if (otherCiv.isCityState()) rightSideTable.add(
|
||||
ScrollPane(getCityStateDiplomacyTable(otherCiv))
|
||||
@ -125,7 +152,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
.height(stage.height)
|
||||
}
|
||||
|
||||
fun setTrade(civ: CivilizationInfo): TradeTable {
|
||||
private fun setTrade(civ: CivilizationInfo): TradeTable {
|
||||
rightSideTable.clear()
|
||||
val tradeTable = TradeTable(civ, this)
|
||||
rightSideTable.add(tradeTable)
|
||||
@ -303,7 +330,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
revokeProtectionButton.onClick {
|
||||
YesNoPopup("Revoke protection for [${otherCiv.civName}]?", {
|
||||
otherCiv.removeProtectorCiv(viewingCiv)
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(otherCiv)
|
||||
updateRightSide(otherCiv)
|
||||
}, this).open()
|
||||
}
|
||||
@ -314,7 +341,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
protectionButton.onClick {
|
||||
YesNoPopup("Declare Protection of [${otherCiv.civName}]?", {
|
||||
otherCiv.addProtectorCiv(viewingCiv)
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(otherCiv)
|
||||
updateRightSide(otherCiv)
|
||||
}, this).open()
|
||||
}
|
||||
@ -350,7 +377,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
)
|
||||
)
|
||||
tradeLogic.acceptTrade()
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(otherCiv)
|
||||
updateRightSide(otherCiv)
|
||||
}, this).open()
|
||||
}
|
||||
@ -443,7 +470,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
"Gift [$giftAmount] gold (+[$influenceAmount] influence)".toTextButton()
|
||||
giftButton.onClick {
|
||||
otherCiv.receiveGoldGift(viewingCiv, giftAmount)
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(otherCiv)
|
||||
updateRightSide(otherCiv)
|
||||
}
|
||||
diplomacyTable.add(giftButton).row()
|
||||
@ -694,7 +721,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
denounceButton.onClick {
|
||||
YesNoPopup("Denounce [${otherCiv.civName}]?", {
|
||||
diplomacyManager.denounce()
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(otherCiv)
|
||||
setRightSideFlavorText(otherCiv, "We will remember this.", "Very well.")
|
||||
}, this).open()
|
||||
}
|
||||
@ -864,7 +891,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
YesNoPopup("Declare war on [${otherCiv.civName}]?", {
|
||||
diplomacyManager.declareWar()
|
||||
setRightSideFlavorText(otherCiv, otherCiv.nation.attacked, "Very well.")
|
||||
updateLeftSideTable()
|
||||
updateLeftSideTable(otherCiv)
|
||||
UncivGame.Current.musicController.chooseTrack(otherCiv.civName, MusicMood.War, MusicTrackChooserFlags.setSpecific)
|
||||
}, this).open()
|
||||
}
|
||||
@ -897,4 +924,8 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo): BaseScreen() {
|
||||
rightSideTable.add(diplomacyTable)
|
||||
}
|
||||
|
||||
override fun resize(width: Int, height: Int) {
|
||||
super.resize(width, height)
|
||||
positionCloseButton()
|
||||
}
|
||||
}
|
||||
|
@ -2,17 +2,11 @@ package com.unciv.ui.worldscreen
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.trade.TradeEvaluation
|
||||
import com.unciv.logic.trade.TradeLogic
|
||||
import com.unciv.logic.trade.TradeOffer
|
||||
import com.unciv.logic.trade.TradeType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.audio.MusicMood
|
||||
import com.unciv.ui.audio.MusicTrackChooserFlags
|
||||
import com.unciv.ui.trade.DiplomacyScreen
|
||||
import com.unciv.ui.trade.LeaderIntroTable
|
||||
import com.unciv.ui.utils.*
|
||||
@ -90,23 +84,15 @@ class TradePopup(worldScreen: WorldScreen): Popup(worldScreen){
|
||||
}
|
||||
|
||||
addButton("Not this time.", 'n') {
|
||||
|
||||
tradeRequest.decline(viewingCiv)
|
||||
|
||||
close()
|
||||
requestingCiv.addNotification("[${viewingCiv.civName}] has denied your trade request", viewingCiv.civName, NotificationIcon.Trade)
|
||||
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
|
||||
addButton("How about something else...", 'e') {
|
||||
close()
|
||||
|
||||
val diplomacyScreen = DiplomacyScreen(viewingCiv)
|
||||
val tradeTable = diplomacyScreen.setTrade(requestingCiv)
|
||||
tradeTable.tradeLogic.currentTrade.set(trade)
|
||||
tradeTable.offerColumnsTable.update()
|
||||
worldScreen.game.setScreen(diplomacyScreen)
|
||||
worldScreen.game.setScreen(DiplomacyScreen(viewingCiv, requestingCiv, trade))
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user