chore: Split 'city state civ diplomacy table' from DiplomacyScreen

This commit is contained in:
Yair Morgenstern 2023-10-08 12:44:48 +03:00
parent 9c2a8a6406
commit 8293d78ec5
2 changed files with 497 additions and 468 deletions

View File

@ -0,0 +1,496 @@
package com.unciv.ui.screens.diplomacyscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.civilization.managers.AssignedQuest
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.UniqueType
import com.unciv.models.translations.tr
import com.unciv.ui.components.Fonts
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.widgets.ColorMarkupLabel
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ConfirmPopup
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
class CityStateDiplomacyTable(private val diplomacyScreen: DiplomacyScreen) {
val viewingCiv = diplomacyScreen.viewingCiv
fun getCityStateDiplomacyTable(otherCiv: Civilization): Table {
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv)
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
val giveGiftButton = "Give a Gift".toTextButton()
giveGiftButton.onClick {
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getGoldGiftTable(otherCiv)))
}
diplomacyTable.add(giveGiftButton).row()
if (diplomacyScreen.isNotPlayersTurn() || viewingCiv.isAtWarWith(otherCiv)) giveGiftButton.disable()
val improveTileButton = getImproveTilesButton(otherCiv, otherCivDiplomacyManager)
if (improveTileButton != null) diplomacyTable.add(improveTileButton).row()
if (otherCivDiplomacyManager.diplomaticStatus != DiplomaticStatus.Protector)
diplomacyTable.add(getPledgeToProtectButton(otherCiv)).row()
else
diplomacyTable.add(getRevokeProtectionButton(otherCiv)).row()
val demandTributeButton = "Demand Tribute".toTextButton()
demandTributeButton.onClick {
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getDemandTributeTable(otherCiv)))
}
diplomacyTable.add(demandTributeButton).row()
if (diplomacyScreen.isNotPlayersTurn() || viewingCiv.isAtWarWith(otherCiv)) demandTributeButton.disable()
val diplomacyManager = viewingCiv.getDiplomacyManager(otherCiv)
if (!viewingCiv.gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.diplomaticRelationshipsCannotChange)) {
if (viewingCiv.isAtWarWith(otherCiv))
diplomacyTable.add(getNegotiatePeaceCityStateButton(otherCiv, diplomacyManager)).row()
else diplomacyTable.add(diplomacyScreen.getDeclareWarButton(diplomacyManager, otherCiv)).row()
}
if (otherCiv.cities.isNotEmpty() && otherCiv.getCapital() != null && viewingCiv.hasExplored(otherCiv.getCapital()!!.getCenterTile()))
diplomacyTable.add(diplomacyScreen.getGoToOnMapButton(otherCiv)).row()
val diplomaticMarriageButton = getDiplomaticMarriageButton(otherCiv)
if (diplomaticMarriageButton != null) diplomacyTable.add(diplomaticMarriageButton).row()
for (assignedQuest in otherCiv.questManager.assignedQuests.filter { it.assignee == viewingCiv.civName }) {
diplomacyTable.addSeparator()
diplomacyTable.add(getQuestTable(assignedQuest)).row()
}
for (target in otherCiv.getKnownCivs().filter { otherCiv.questManager.warWithMajorActive(it) && viewingCiv != it }) {
diplomacyTable.addSeparator()
diplomacyTable.add(getWarWithMajorTable(target, otherCiv)).row()
}
return diplomacyTable
}
private fun getCityStateDiplomacyTableHeader(otherCiv: Civilization): Table {
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv)
val diplomacyTable = Table()
diplomacyTable.defaults().pad(2.5f)
diplomacyTable.add(LeaderIntroTable(otherCiv)).padBottom(15f).row()
diplomacyTable.add("{Type}: {${otherCiv.cityStateType.name}}".toLabel()).row()
diplomacyTable.add("{Personality}: {${otherCiv.cityStatePersonality}}".toLabel()).row()
if (otherCiv.detailedCivResources.any { it.resource.resourceType != ResourceType.Bonus }) {
val resourcesTable = Table()
resourcesTable.add("{Resources}: ".toLabel()).padRight(10f)
val cityStateResources = otherCiv.cityStateFunctions.getCityStateResourcesForAlly()
for (supplyList in cityStateResources) {
if (supplyList.resource.resourceType == ResourceType.Bonus)
continue
val name = supplyList.resource.name
val wrapper = Table()
val image = ImageGetter.getResourcePortrait(name, 30f)
wrapper.add(image).padRight(5f)
wrapper.add(supplyList.amount.toLabel())
resourcesTable.add(wrapper).padRight(20f)
wrapper.addTooltip(name, 18f)
wrapper.onClick {
UncivGame.Current.pushScreen(CivilopediaScreen(UncivGame.Current.gameInfo!!.ruleset, link = "Resource/$name"))
}
}
diplomacyTable.add(resourcesTable).row()
}
diplomacyTable.row().padTop(15f)
otherCiv.cityStateFunctions.updateAllyCivForCityState()
var ally = otherCiv.getAllyCiv()
if (ally != null) {
val allyInfluence = otherCiv.getDiplomacyManager(ally).getInfluence().toInt()
if (!viewingCiv.knows(ally) && ally != viewingCiv.civName)
ally = "Unknown civilization"
diplomacyTable
.add("Ally: [$ally] with [$allyInfluence] Influence".toLabel())
.row()
}
val protectors = otherCiv.cityStateFunctions.getProtectorCivs()
if (protectors.isNotEmpty()) {
val newProtectors = arrayListOf<String>()
for (protector in protectors) {
if (!viewingCiv.knows(protector) && protector.civName != viewingCiv.civName)
newProtectors.add("Unknown civilization".tr())
else
newProtectors.add(protector.civName.tr())
}
val protectorString = "{Protected by}: " + newProtectors.joinToString(", ")
diplomacyTable.add(protectorString.toLabel()).row()
}
val atWar = otherCiv.isAtWarWith(viewingCiv)
val nextLevelString = when {
atWar -> ""
otherCivDiplomacyManager.getInfluence().toInt() < 30 -> "Reach 30 for friendship."
ally == viewingCiv.civName -> ""
else -> "Reach highest influence above 60 for alliance."
}
diplomacyTable.add(diplomacyScreen.getRelationshipTable(otherCivDiplomacyManager)).row()
if (nextLevelString.isNotEmpty()) {
diplomacyTable.add(nextLevelString.toLabel()).row()
}
diplomacyTable.row().padTop(15f)
var friendBonusText = "When Friends:".tr()+"\n"
val friendBonusObjects = viewingCiv.cityStateFunctions.getCityStateBonuses(otherCiv.cityStateType, RelationshipLevel.Friend)
friendBonusText += friendBonusObjects.joinToString(separator = "\n") { it.text.tr() }
var allyBonusText = "When Allies:".tr()+"\n"
val allyBonusObjects = viewingCiv.cityStateFunctions.getCityStateBonuses(otherCiv.cityStateType, RelationshipLevel.Ally)
allyBonusText += allyBonusObjects.joinToString(separator = "\n") { it.text.tr() }
val relationLevel = otherCivDiplomacyManager.relationshipIgnoreAfraid()
if (relationLevel >= RelationshipLevel.Friend) {
// RelationshipChange = Ally -> Friend or Friend -> Favorable
val turnsToRelationshipChange = otherCivDiplomacyManager.getTurnsToRelationshipChange()
if (turnsToRelationshipChange != 0)
diplomacyTable.add("Relationship changes in another [$turnsToRelationshipChange] turns".toLabel())
.row()
}
val friendBonusLabelColor = if (relationLevel == RelationshipLevel.Friend) Color.GREEN else Color.GRAY
val friendBonusLabel = ColorMarkupLabel(friendBonusText, friendBonusLabelColor)
.apply { setAlignment(Align.center) }
diplomacyTable.add(friendBonusLabel).row()
val allyBonusLabelColor = if (relationLevel == RelationshipLevel.Ally) Color.GREEN else Color.GRAY
val allyBonusLabel = ColorMarkupLabel(allyBonusText, allyBonusLabelColor)
.apply { setAlignment(Align.center) }
diplomacyTable.add(allyBonusLabel).row()
if (otherCiv.cityStateUniqueUnit != null) {
val unitName = otherCiv.cityStateUniqueUnit
val techName = viewingCiv.gameInfo.ruleset.units[otherCiv.cityStateUniqueUnit]!!.requiredTech
diplomacyTable.add("[${otherCiv.civName}] is able to provide [${unitName}] once [${techName}] is researched.".toLabel(fontSize = Constants.defaultFontSize)).row()
}
return diplomacyTable
}
private fun getRevokeProtectionButton(otherCiv: Civilization): TextButton {
val revokeProtectionButton = "Revoke Protection".toTextButton()
revokeProtectionButton.onClick {
ConfirmPopup(diplomacyScreen, "Revoke protection for [${otherCiv.civName}]?", "Revoke Protection") {
otherCiv.cityStateFunctions.removeProtectorCiv(viewingCiv)
diplomacyScreen.updateLeftSideTable(otherCiv)
diplomacyScreen.updateRightSide(otherCiv)
}.open()
}
if (diplomacyScreen.isNotPlayersTurn() || !otherCiv.cityStateFunctions.otherCivCanWithdrawProtection(viewingCiv))
revokeProtectionButton.disable()
return revokeProtectionButton
}
private fun getPledgeToProtectButton(otherCiv: Civilization): TextButton {
val protectionButton = "Pledge to protect".toTextButton()
protectionButton.onClick {
ConfirmPopup(
diplomacyScreen,
"Declare Protection of [${otherCiv.civName}]?",
"Pledge to protect",
true
) {
otherCiv.cityStateFunctions.addProtectorCiv(viewingCiv)
diplomacyScreen.updateLeftSideTable(otherCiv)
diplomacyScreen.updateRightSide(otherCiv)
}.open()
}
if (diplomacyScreen.isNotPlayersTurn() || !otherCiv.cityStateFunctions.otherCivCanPledgeProtection(viewingCiv))
protectionButton.disable()
return protectionButton
}
private fun getNegotiatePeaceCityStateButton(
otherCiv: Civilization,
otherCivDiplomacyManager: DiplomacyManager
): TextButton {
val peaceButton = "Negotiate Peace".toTextButton()
peaceButton.onClick {
ConfirmPopup(
diplomacyScreen,
"Peace with [${otherCiv.civName}]?",
"Negotiate Peace",
true
) {
val tradeLogic = TradeLogic(viewingCiv, otherCiv)
tradeLogic.currentTrade.ourOffers.add(
TradeOffer(
Constants.peaceTreaty,
TradeType.Treaty
)
)
tradeLogic.currentTrade.theirOffers.add(
TradeOffer(
Constants.peaceTreaty,
TradeType.Treaty
)
)
tradeLogic.acceptTrade()
diplomacyScreen.updateLeftSideTable(otherCiv)
diplomacyScreen.updateRightSide(otherCiv)
}.open()
}
val cityStatesAlly = otherCiv.getAllyCiv()
val atWarWithItsAlly = viewingCiv.getKnownCivs()
.any { it.civName == cityStatesAlly && it.isAtWarWith(viewingCiv) }
if (diplomacyScreen.isNotPlayersTurn() || atWarWithItsAlly) peaceButton.disable()
if (otherCivDiplomacyManager.hasFlag(DiplomacyFlags.DeclaredWar)) {
peaceButton.disable() // Can't trade for 10 turns after war was declared
val turnsLeft = otherCivDiplomacyManager.getFlag(DiplomacyFlags.DeclaredWar)
peaceButton.setText(peaceButton.text.toString() + "\n$turnsLeft" + Fonts.turn)
}
return peaceButton
}
private fun getImproveTilesButton(
otherCiv: Civilization,
otherCivDiplomacyManager: DiplomacyManager
): TextButton? {
if (otherCiv.cities.isEmpty()) return null
val improvableResourceTiles = getImprovableResourceTiles(otherCiv)
val improvements =
otherCiv.gameInfo.ruleset.tileImprovements.filter { it.value.turnsToBuild != -1 }
var needsImprovements = false
for (improvableTile in improvableResourceTiles)
for (tileImprovement in improvements.values)
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
&& improvableTile.improvementFunctions.canBuildImprovement(tileImprovement, otherCiv)
)
needsImprovements = true
if (!needsImprovements) return null
val improveTileButton = "Gift Improvement".toTextButton()
improveTileButton.onClick {
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getImprovementGiftTable(otherCiv)))
}
if (diplomacyScreen.isNotPlayersTurn() || otherCivDiplomacyManager.getInfluence() < 60)
improveTileButton.disable()
return improveTileButton
}
private fun getDiplomaticMarriageButton(otherCiv: Civilization): TextButton? {
if (!viewingCiv.hasUnique(UniqueType.CityStateCanBeBoughtForGold))
return null
val diplomaticMarriageButton =
"Diplomatic Marriage ([${otherCiv.cityStateFunctions.getDiplomaticMarriageCost()}] Gold)".toTextButton()
diplomaticMarriageButton.onClick {
val newCities = otherCiv.cities
otherCiv.cityStateFunctions.diplomaticMarriage(viewingCiv)
UncivGame.Current.popScreen() // The other civ will no longer exist
for (city in newCities)
viewingCiv.popupAlerts.add(PopupAlert(AlertType.DiplomaticMarriage, city.id)) // Player gets to choose between annex and puppet
}
if (diplomacyScreen.isNotPlayersTurn() || !otherCiv.cityStateFunctions.canBeMarriedBy(viewingCiv))
diplomaticMarriageButton.disable()
return diplomaticMarriageButton
}
private fun getGoldGiftTable(otherCiv: Civilization): Table {
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
for (giftAmount in listOf(250, 500, 1000)) {
val influenceAmount = otherCiv.cityStateFunctions.influenceGainedByGift(viewingCiv, giftAmount)
val giftButton =
"Gift [$giftAmount] gold (+[$influenceAmount] influence)".toTextButton()
giftButton.onClick {
otherCiv.cityStateFunctions.receiveGoldGift(viewingCiv, giftAmount)
diplomacyScreen.updateLeftSideTable(otherCiv)
diplomacyScreen.updateRightSide(otherCiv)
}
diplomacyTable.add(giftButton).row()
if (viewingCiv.gold < giftAmount || diplomacyScreen.isNotPlayersTurn()) giftButton.disable()
}
val backButton = "Back".toTextButton()
backButton.onClick {
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(backButton)
return diplomacyTable
}
private fun getImprovableResourceTiles(otherCiv:Civilization) = otherCiv.getCapital()!!.getTiles().filter {
it.hasViewableResource(otherCiv)
&& it.tileResource.resourceType != ResourceType.Bonus
&& (it.improvement == null || !it.tileResource.isImprovedBy(it.improvement!!))
}
private fun getImprovementGiftTable(otherCiv: Civilization): Table {
val improvementGiftTable = getCityStateDiplomacyTableHeader(otherCiv)
improvementGiftTable.addSeparator()
val improvableResourceTiles = getImprovableResourceTiles(otherCiv)
val tileImprovements =
otherCiv.gameInfo.ruleset.tileImprovements
for (improvableTile in improvableResourceTiles) {
for (tileImprovement in tileImprovements.values) {
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
&& improvableTile.improvementFunctions.canBuildImprovement(tileImprovement, otherCiv)
) {
val improveTileButton =
"Build [${tileImprovement}] on [${improvableTile.tileResource}] (200 Gold)".toTextButton()
improveTileButton.onClick {
viewingCiv.addGold(-200)
improvableTile.stopWorkingOnImprovement()
improvableTile.changeImprovement(tileImprovement.name)
otherCiv.cache.updateCivResources()
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
if (viewingCiv.gold < 200)
improveTileButton.disable()
improvementGiftTable.add(improveTileButton).row()
}
}
}
val backButton = "Back".toTextButton()
backButton.onClick {
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
improvementGiftTable.add(backButton)
return improvementGiftTable
}
private fun getDemandTributeTable(otherCiv: Civilization): Table {
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
diplomacyTable.add("Tribute Willingness".toLabel()).row()
val modifierTable = Table()
val tributeModifiers = otherCiv.cityStateFunctions.getTributeModifiers(viewingCiv, requireWholeList = true)
for (item in tributeModifiers) {
val color = if (item.value >= 0) Color.GREEN else Color.RED
modifierTable.add(item.key.toLabel(color))
modifierTable.add(item.value.toString().toLabel(color)).row()
}
modifierTable.add("Sum:".toLabel())
modifierTable.add(tributeModifiers.values.sum().toLabel()).row()
diplomacyTable.add(modifierTable).row()
diplomacyTable.add("At least 0 to take gold, at least 30 and size 4 city for worker".toLabel()).row()
diplomacyTable.addSeparator()
val demandGoldButton = "Take [${otherCiv.cityStateFunctions.goldGainedByTribute()}] gold (-15 Influence)".toTextButton()
demandGoldButton.onClick {
otherCiv.cityStateFunctions.tributeGold(viewingCiv)
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(demandGoldButton).row()
if (otherCiv.cityStateFunctions.getTributeWillingness(viewingCiv, demandingWorker = false) < 0) demandGoldButton.disable()
val demandWorkerButton = "Take worker (-50 Influence)".toTextButton()
demandWorkerButton.onClick {
otherCiv.cityStateFunctions.tributeWorker(viewingCiv)
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(demandWorkerButton).row()
if (otherCiv.cityStateFunctions.getTributeWillingness(viewingCiv, demandingWorker = true) < 0) demandWorkerButton.disable()
val backButton = "Back".toTextButton()
backButton.onClick {
diplomacyScreen.rightSideTable.clear()
diplomacyScreen.rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(backButton)
return diplomacyTable
}
private fun getQuestTable(assignedQuest: AssignedQuest): Table {
val questTable = Table()
questTable.defaults().pad(10f)
val quest: Quest = viewingCiv.gameInfo.ruleset.quests[assignedQuest.questName]!!
val remainingTurns: Int = assignedQuest.getRemainingTurns()
val title = if (quest.influence > 0)
"[${quest.name}] (+[${quest.influence.toInt()}] influence)"
else
quest.name
val description = assignedQuest.getDescription()
questTable.add(title.toLabel(fontSize = Constants.headingFontSize)).row()
questTable.add(description.toLabel().apply { wrap = true; setAlignment(Align.center) })
.width(diplomacyScreen.stage.width / 2).row()
if (quest.duration > 0)
questTable.add("[${remainingTurns}] turns remaining".toLabel()).row()
if (quest.isGlobal()) {
val leaderString = viewingCiv.gameInfo.getCivilization(assignedQuest.assigner).questManager.getLeaderStringForQuest(assignedQuest.questName)
if (leaderString != "")
questTable.add(leaderString.toLabel()).row()
}
questTable.onClick {
assignedQuest.onClickAction()
}
return questTable
}
private fun getWarWithMajorTable(target: Civilization, otherCiv: Civilization): Table {
val warTable = Table()
warTable.defaults().pad(10f)
val title = "War against [${target.civName}]"
val description = "We need you to help us defend against [${target.civName}]. Killing [${otherCiv.questManager.unitsToKill(target)}] of their military units would slow their offensive."
val progress = if (viewingCiv.knows(target)) "Currently you have killed [${otherCiv.questManager.unitsKilledSoFar(target, viewingCiv)}] of their military units."
else "You need to find them first!"
warTable.add(title.toLabel(fontSize = Constants.headingFontSize)).row()
warTable.add(description.toLabel().apply { wrap = true; setAlignment(Align.center) })
.width(diplomacyScreen.stage.width / 2).row()
warTable.add(progress.toLabel().apply { wrap = true; setAlignment(Align.center) })
.width(diplomacyScreen.stage.width / 2).row()
return warTable
}
}

View File

@ -8,27 +8,15 @@ import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.GUI
import com.unciv.UncivGame
import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.civilization.managers.AssignedQuest
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.UniqueType
import com.unciv.models.translations.tr
import com.unciv.ui.audio.MusicMood
import com.unciv.ui.audio.MusicTrackChooserFlags
import com.unciv.ui.components.Fonts
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.setFontSize
@ -40,12 +28,10 @@ import com.unciv.ui.components.input.keyShortcuts
import com.unciv.ui.components.input.onActivation
import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.tilegroups.InfluenceTable
import com.unciv.ui.components.widgets.ColorMarkupLabel
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ConfirmPopup
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.basescreen.RecreateOnResize
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
import kotlin.math.floor
import com.unciv.ui.components.widgets.AutoScrollPane as ScrollPane
@ -168,465 +154,12 @@ class DiplomacyScreen(
UncivGame.Current.musicController.chooseTrack(otherCiv.civName,
MusicMood.peaceOrWar(viewingCiv.isAtWarWith(otherCiv)),MusicTrackChooserFlags.setSelectNation)
rightSideTable.add(ScrollPane(
if (otherCiv.isCityState()) getCityStateDiplomacyTable(otherCiv)
if (otherCiv.isCityState()) CityStateDiplomacyTable(this).getCityStateDiplomacyTable(otherCiv)
else MajorCivDiplomacyTable(this).getMajorCivDiplomacyTable(otherCiv)
)).height(stage.height)
}
//region City State Diplomacy
private fun getCityStateDiplomacyTableHeader(otherCiv: Civilization): Table {
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv)
val diplomacyTable = Table()
diplomacyTable.defaults().pad(2.5f)
diplomacyTable.add(LeaderIntroTable(otherCiv)).padBottom(15f).row()
diplomacyTable.add("{Type}: {${otherCiv.cityStateType.name}}".toLabel()).row()
diplomacyTable.add("{Personality}: {${otherCiv.cityStatePersonality}}".toLabel()).row()
if (otherCiv.detailedCivResources.any { it.resource.resourceType != ResourceType.Bonus }) {
val resourcesTable = Table()
resourcesTable.add("{Resources}: ".toLabel()).padRight(10f)
val cityStateResources = otherCiv.cityStateFunctions.getCityStateResourcesForAlly()
for (supplyList in cityStateResources) {
if (supplyList.resource.resourceType == ResourceType.Bonus)
continue
val name = supplyList.resource.name
val wrapper = Table()
val image = ImageGetter.getResourcePortrait(name, 30f)
wrapper.add(image).padRight(5f)
wrapper.add(supplyList.amount.toLabel())
resourcesTable.add(wrapper).padRight(20f)
wrapper.addTooltip(name, 18f)
wrapper.onClick {
UncivGame.Current.pushScreen(CivilopediaScreen(UncivGame.Current.gameInfo!!.ruleset, link = "Resource/$name"))
}
}
diplomacyTable.add(resourcesTable).row()
}
diplomacyTable.row().padTop(15f)
otherCiv.cityStateFunctions.updateAllyCivForCityState()
var ally = otherCiv.getAllyCiv()
if (ally != null) {
val allyInfluence = otherCiv.getDiplomacyManager(ally).getInfluence().toInt()
if (!viewingCiv.knows(ally) && ally != viewingCiv.civName)
ally = "Unknown civilization"
diplomacyTable
.add("Ally: [$ally] with [$allyInfluence] Influence".toLabel())
.row()
}
val protectors = otherCiv.cityStateFunctions.getProtectorCivs()
if (protectors.isNotEmpty()) {
val newProtectors = arrayListOf<String>()
for (protector in protectors) {
if (!viewingCiv.knows(protector) && protector.civName != viewingCiv.civName)
newProtectors.add("Unknown civilization".tr())
else
newProtectors.add(protector.civName.tr())
}
val protectorString = "{Protected by}: " + newProtectors.joinToString(", ")
diplomacyTable.add(protectorString.toLabel()).row()
}
val atWar = otherCiv.isAtWarWith(viewingCiv)
val nextLevelString = when {
atWar -> ""
otherCivDiplomacyManager.getInfluence().toInt() < 30 -> "Reach 30 for friendship."
ally == viewingCiv.civName -> ""
else -> "Reach highest influence above 60 for alliance."
}
diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row()
if (nextLevelString.isNotEmpty()) {
diplomacyTable.add(nextLevelString.toLabel()).row()
}
diplomacyTable.row().padTop(15f)
var friendBonusText = "When Friends:".tr()+"\n"
val friendBonusObjects = viewingCiv.cityStateFunctions.getCityStateBonuses(otherCiv.cityStateType, RelationshipLevel.Friend)
friendBonusText += friendBonusObjects.joinToString(separator = "\n") { it.text.tr() }
var allyBonusText = "When Allies:".tr()+"\n"
val allyBonusObjects = viewingCiv.cityStateFunctions.getCityStateBonuses(otherCiv.cityStateType, RelationshipLevel.Ally)
allyBonusText += allyBonusObjects.joinToString(separator = "\n") { it.text.tr() }
val relationLevel = otherCivDiplomacyManager.relationshipIgnoreAfraid()
if (relationLevel >= RelationshipLevel.Friend) {
// RelationshipChange = Ally -> Friend or Friend -> Favorable
val turnsToRelationshipChange = otherCivDiplomacyManager.getTurnsToRelationshipChange()
if (turnsToRelationshipChange != 0)
diplomacyTable.add("Relationship changes in another [$turnsToRelationshipChange] turns".toLabel())
.row()
}
val friendBonusLabelColor = if (relationLevel == RelationshipLevel.Friend) Color.GREEN else Color.GRAY
val friendBonusLabel = ColorMarkupLabel(friendBonusText, friendBonusLabelColor)
.apply { setAlignment(Align.center) }
diplomacyTable.add(friendBonusLabel).row()
val allyBonusLabelColor = if (relationLevel == RelationshipLevel.Ally) Color.GREEN else Color.GRAY
val allyBonusLabel = ColorMarkupLabel(allyBonusText, allyBonusLabelColor)
.apply { setAlignment(Align.center) }
diplomacyTable.add(allyBonusLabel).row()
if (otherCiv.cityStateUniqueUnit != null) {
val unitName = otherCiv.cityStateUniqueUnit
val techName = viewingCiv.gameInfo.ruleset.units[otherCiv.cityStateUniqueUnit]!!.requiredTech
diplomacyTable.add("[${otherCiv.civName}] is able to provide [${unitName}] once [${techName}] is researched.".toLabel(fontSize = Constants.defaultFontSize)).row()
}
return diplomacyTable
}
private fun getCityStateDiplomacyTable(otherCiv: Civilization): Table {
val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(viewingCiv)
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
val giveGiftButton = "Give a Gift".toTextButton()
giveGiftButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getGoldGiftTable(otherCiv)))
}
diplomacyTable.add(giveGiftButton).row()
if (isNotPlayersTurn() || viewingCiv.isAtWarWith(otherCiv)) giveGiftButton.disable()
val improveTileButton = getImproveTilesButton(otherCiv, otherCivDiplomacyManager)
if (improveTileButton != null) diplomacyTable.add(improveTileButton).row()
if (otherCivDiplomacyManager.diplomaticStatus != DiplomaticStatus.Protector)
diplomacyTable.add(getPledgeToProtectButton(otherCiv)).row()
else
diplomacyTable.add(getRevokeProtectionButton(otherCiv)).row()
val demandTributeButton = "Demand Tribute".toTextButton()
demandTributeButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getDemandTributeTable(otherCiv)))
}
diplomacyTable.add(demandTributeButton).row()
if (isNotPlayersTurn() || viewingCiv.isAtWarWith(otherCiv)) demandTributeButton.disable()
val diplomacyManager = viewingCiv.getDiplomacyManager(otherCiv)
if (!viewingCiv.gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.diplomaticRelationshipsCannotChange)) {
if (viewingCiv.isAtWarWith(otherCiv))
diplomacyTable.add(getNegotiatePeaceCityStateButton(otherCiv, diplomacyManager)).row()
else diplomacyTable.add(getDeclareWarButton(diplomacyManager, otherCiv)).row()
}
if (otherCiv.cities.isNotEmpty() && otherCiv.getCapital() != null && viewingCiv.hasExplored(otherCiv.getCapital()!!.getCenterTile()))
diplomacyTable.add(getGoToOnMapButton(otherCiv)).row()
val diplomaticMarriageButton = getDiplomaticMarriageButton(otherCiv)
if (diplomaticMarriageButton != null) diplomacyTable.add(diplomaticMarriageButton).row()
for (assignedQuest in otherCiv.questManager.assignedQuests.filter { it.assignee == viewingCiv.civName }) {
diplomacyTable.addSeparator()
diplomacyTable.add(getQuestTable(assignedQuest)).row()
}
for (target in otherCiv.getKnownCivs().filter { otherCiv.questManager.warWithMajorActive(it) && viewingCiv != it }) {
diplomacyTable.addSeparator()
diplomacyTable.add(getWarWithMajorTable(target, otherCiv)).row()
}
return diplomacyTable
}
private fun getRevokeProtectionButton(otherCiv: Civilization): TextButton {
val revokeProtectionButton = "Revoke Protection".toTextButton()
revokeProtectionButton.onClick {
ConfirmPopup(this, "Revoke protection for [${otherCiv.civName}]?", "Revoke Protection") {
otherCiv.cityStateFunctions.removeProtectorCiv(viewingCiv)
updateLeftSideTable(otherCiv)
updateRightSide(otherCiv)
}.open()
}
if (isNotPlayersTurn() || !otherCiv.cityStateFunctions.otherCivCanWithdrawProtection(viewingCiv))
revokeProtectionButton.disable()
return revokeProtectionButton
}
private fun getPledgeToProtectButton(otherCiv: Civilization): TextButton {
val protectionButton = "Pledge to protect".toTextButton()
protectionButton.onClick {
ConfirmPopup(
this,
"Declare Protection of [${otherCiv.civName}]?",
"Pledge to protect",
true
) {
otherCiv.cityStateFunctions.addProtectorCiv(viewingCiv)
updateLeftSideTable(otherCiv)
updateRightSide(otherCiv)
}.open()
}
if (isNotPlayersTurn() || !otherCiv.cityStateFunctions.otherCivCanPledgeProtection(viewingCiv))
protectionButton.disable()
return protectionButton
}
private fun getNegotiatePeaceCityStateButton(
otherCiv: Civilization,
otherCivDiplomacyManager: DiplomacyManager
): TextButton {
val peaceButton = "Negotiate Peace".toTextButton()
peaceButton.onClick {
ConfirmPopup(
this,
"Peace with [${otherCiv.civName}]?",
"Negotiate Peace",
true
) {
val tradeLogic = TradeLogic(viewingCiv, otherCiv)
tradeLogic.currentTrade.ourOffers.add(
TradeOffer(
Constants.peaceTreaty,
TradeType.Treaty
)
)
tradeLogic.currentTrade.theirOffers.add(
TradeOffer(
Constants.peaceTreaty,
TradeType.Treaty
)
)
tradeLogic.acceptTrade()
updateLeftSideTable(otherCiv)
updateRightSide(otherCiv)
}.open()
}
val cityStatesAlly = otherCiv.getAllyCiv()
val atWarWithItsAlly = viewingCiv.getKnownCivs()
.any { it.civName == cityStatesAlly && it.isAtWarWith(viewingCiv) }
if (isNotPlayersTurn() || atWarWithItsAlly) peaceButton.disable()
if (otherCivDiplomacyManager.hasFlag(DiplomacyFlags.DeclaredWar)) {
peaceButton.disable() // Can't trade for 10 turns after war was declared
val turnsLeft = otherCivDiplomacyManager.getFlag(DiplomacyFlags.DeclaredWar)
peaceButton.setText(peaceButton.text.toString() + "\n$turnsLeft" + Fonts.turn)
}
return peaceButton
}
private fun getImproveTilesButton(
otherCiv: Civilization,
otherCivDiplomacyManager: DiplomacyManager
): TextButton? {
if (otherCiv.cities.isEmpty()) return null
val improvableResourceTiles = getImprovableResourceTiles(otherCiv)
val improvements =
otherCiv.gameInfo.ruleset.tileImprovements.filter { it.value.turnsToBuild != -1 }
var needsImprovements = false
for (improvableTile in improvableResourceTiles)
for (tileImprovement in improvements.values)
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
&& improvableTile.improvementFunctions.canBuildImprovement(tileImprovement, otherCiv)
)
needsImprovements = true
if (!needsImprovements) return null
val improveTileButton = "Gift Improvement".toTextButton()
improveTileButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getImprovementGiftTable(otherCiv)))
}
if (isNotPlayersTurn() || otherCivDiplomacyManager.getInfluence() < 60)
improveTileButton.disable()
return improveTileButton
}
private fun getDiplomaticMarriageButton(otherCiv: Civilization): TextButton? {
if (!viewingCiv.hasUnique(UniqueType.CityStateCanBeBoughtForGold))
return null
val diplomaticMarriageButton =
"Diplomatic Marriage ([${otherCiv.cityStateFunctions.getDiplomaticMarriageCost()}] Gold)".toTextButton()
diplomaticMarriageButton.onClick {
val newCities = otherCiv.cities
otherCiv.cityStateFunctions.diplomaticMarriage(viewingCiv)
UncivGame.Current.popScreen() // The other civ will no longer exist
for (city in newCities)
viewingCiv.popupAlerts.add(PopupAlert(AlertType.DiplomaticMarriage, city.id)) // Player gets to choose between annex and puppet
}
if (isNotPlayersTurn() || !otherCiv.cityStateFunctions.canBeMarriedBy(viewingCiv))
diplomaticMarriageButton.disable()
return diplomaticMarriageButton
}
private fun getGoldGiftTable(otherCiv: Civilization): Table {
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
for (giftAmount in listOf(250, 500, 1000)) {
val influenceAmount = otherCiv.cityStateFunctions.influenceGainedByGift(viewingCiv, giftAmount)
val giftButton =
"Gift [$giftAmount] gold (+[$influenceAmount] influence)".toTextButton()
giftButton.onClick {
otherCiv.cityStateFunctions.receiveGoldGift(viewingCiv, giftAmount)
updateLeftSideTable(otherCiv)
updateRightSide(otherCiv)
}
diplomacyTable.add(giftButton).row()
if (viewingCiv.gold < giftAmount || isNotPlayersTurn()) giftButton.disable()
}
val backButton = "Back".toTextButton()
backButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(backButton)
return diplomacyTable
}
private fun getImprovableResourceTiles(otherCiv:Civilization) = otherCiv.getCapital()!!.getTiles().filter {
it.hasViewableResource(otherCiv)
&& it.tileResource.resourceType != ResourceType.Bonus
&& (it.improvement == null || !it.tileResource.isImprovedBy(it.improvement!!))
}
private fun getImprovementGiftTable(otherCiv: Civilization): Table {
val improvementGiftTable = getCityStateDiplomacyTableHeader(otherCiv)
improvementGiftTable.addSeparator()
val improvableResourceTiles = getImprovableResourceTiles(otherCiv)
val tileImprovements =
otherCiv.gameInfo.ruleset.tileImprovements
for (improvableTile in improvableResourceTiles) {
for (tileImprovement in tileImprovements.values) {
if (improvableTile.tileResource.isImprovedBy(tileImprovement.name)
&& improvableTile.improvementFunctions.canBuildImprovement(tileImprovement, otherCiv)
) {
val improveTileButton =
"Build [${tileImprovement}] on [${improvableTile.tileResource}] (200 Gold)".toTextButton()
improveTileButton.onClick {
viewingCiv.addGold(-200)
improvableTile.stopWorkingOnImprovement()
improvableTile.changeImprovement(tileImprovement.name)
otherCiv.cache.updateCivResources()
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
if (viewingCiv.gold < 200)
improveTileButton.disable()
improvementGiftTable.add(improveTileButton).row()
}
}
}
val backButton = "Back".toTextButton()
backButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
improvementGiftTable.add(backButton)
return improvementGiftTable
}
private fun getDemandTributeTable(otherCiv: Civilization): Table {
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
diplomacyTable.add("Tribute Willingness".toLabel()).row()
val modifierTable = Table()
val tributeModifiers = otherCiv.cityStateFunctions.getTributeModifiers(viewingCiv, requireWholeList = true)
for (item in tributeModifiers) {
val color = if (item.value >= 0) Color.GREEN else Color.RED
modifierTable.add(item.key.toLabel(color))
modifierTable.add(item.value.toString().toLabel(color)).row()
}
modifierTable.add("Sum:".toLabel())
modifierTable.add(tributeModifiers.values.sum().toLabel()).row()
diplomacyTable.add(modifierTable).row()
diplomacyTable.add("At least 0 to take gold, at least 30 and size 4 city for worker".toLabel()).row()
diplomacyTable.addSeparator()
val demandGoldButton = "Take [${otherCiv.cityStateFunctions.goldGainedByTribute()}] gold (-15 Influence)".toTextButton()
demandGoldButton.onClick {
otherCiv.cityStateFunctions.tributeGold(viewingCiv)
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(demandGoldButton).row()
if (otherCiv.cityStateFunctions.getTributeWillingness(viewingCiv, demandingWorker = false) < 0) demandGoldButton.disable()
val demandWorkerButton = "Take worker (-50 Influence)".toTextButton()
demandWorkerButton.onClick {
otherCiv.cityStateFunctions.tributeWorker(viewingCiv)
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(demandWorkerButton).row()
if (otherCiv.cityStateFunctions.getTributeWillingness(viewingCiv, demandingWorker = true) < 0) demandWorkerButton.disable()
val backButton = "Back".toTextButton()
backButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(backButton)
return diplomacyTable
}
private fun getQuestTable(assignedQuest: AssignedQuest): Table {
val questTable = Table()
questTable.defaults().pad(10f)
val quest: Quest = viewingCiv.gameInfo.ruleset.quests[assignedQuest.questName]!!
val remainingTurns: Int = assignedQuest.getRemainingTurns()
val title = if (quest.influence > 0)
"[${quest.name}] (+[${quest.influence.toInt()}] influence)"
else
quest.name
val description = assignedQuest.getDescription()
questTable.add(title.toLabel(fontSize = Constants.headingFontSize)).row()
questTable.add(description.toLabel().apply { wrap = true; setAlignment(Align.center) })
.width(stage.width / 2).row()
if (quest.duration > 0)
questTable.add("[${remainingTurns}] turns remaining".toLabel()).row()
if (quest.isGlobal()) {
val leaderString = viewingCiv.gameInfo.getCivilization(assignedQuest.assigner).questManager.getLeaderStringForQuest(assignedQuest.questName)
if (leaderString != "")
questTable.add(leaderString.toLabel()).row()
}
questTable.onClick {
assignedQuest.onClickAction()
}
return questTable
}
private fun getWarWithMajorTable(target: Civilization, otherCiv: Civilization): Table {
val warTable = Table()
warTable.defaults().pad(10f)
val title = "War against [${target.civName}]"
val description = "We need you to help us defend against [${target.civName}]. Killing [${otherCiv.questManager.unitsToKill(target)}] of their military units would slow their offensive."
val progress = if (viewingCiv.knows(target)) "Currently you have killed [${otherCiv.questManager.unitsKilledSoFar(target, viewingCiv)}] of their military units."
else "You need to find them first!"
warTable.add(title.toLabel(fontSize = Constants.headingFontSize)).row()
warTable.add(description.toLabel().apply { wrap = true; setAlignment(Align.center) })
.width(stage.width / 2).row()
warTable.add(progress.toLabel().apply { wrap = true; setAlignment(Align.center) })
.width(stage.width / 2).row()
return warTable
}
//endregion
//region Major Civ Diplomacy