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.Constants
import com.unciv.GUI import com.unciv.GUI
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.Civilization 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.DiplomacyManager
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.diplomacy.RelationshipLevel 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.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.models.translations.tr
import com.unciv.ui.audio.MusicMood import com.unciv.ui.audio.MusicMood
import com.unciv.ui.audio.MusicTrackChooserFlags import com.unciv.ui.audio.MusicTrackChooserFlags
import com.unciv.ui.components.Fonts 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.addSeparator
import com.unciv.ui.components.extensions.disable import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.setFontSize 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.onActivation
import com.unciv.ui.components.input.onClick import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.tilegroups.InfluenceTable import com.unciv.ui.components.tilegroups.InfluenceTable
import com.unciv.ui.components.widgets.ColorMarkupLabel
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ConfirmPopup import com.unciv.ui.popups.ConfirmPopup
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.basescreen.RecreateOnResize import com.unciv.ui.screens.basescreen.RecreateOnResize
import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen
import kotlin.math.floor import kotlin.math.floor
import com.unciv.ui.components.widgets.AutoScrollPane as ScrollPane import com.unciv.ui.components.widgets.AutoScrollPane as ScrollPane
@ -168,465 +154,12 @@ class DiplomacyScreen(
UncivGame.Current.musicController.chooseTrack(otherCiv.civName, UncivGame.Current.musicController.chooseTrack(otherCiv.civName,
MusicMood.peaceOrWar(viewingCiv.isAtWarWith(otherCiv)),MusicTrackChooserFlags.setSelectNation) MusicMood.peaceOrWar(viewingCiv.isAtWarWith(otherCiv)),MusicTrackChooserFlags.setSelectNation)
rightSideTable.add(ScrollPane( rightSideTable.add(ScrollPane(
if (otherCiv.isCityState()) getCityStateDiplomacyTable(otherCiv) if (otherCiv.isCityState()) CityStateDiplomacyTable(this).getCityStateDiplomacyTable(otherCiv)
else MajorCivDiplomacyTable(this).getMajorCivDiplomacyTable(otherCiv) else MajorCivDiplomacyTable(this).getMajorCivDiplomacyTable(otherCiv)
)).height(stage.height) )).height(stage.height)
} }
//region City State Diplomacy //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 //endregion
//region Major Civ Diplomacy //region Major Civ Diplomacy