Demanding tribute from city states (#4976)

* tribute willingness calculations

* implement demanding gold and workers

* Revisions

* unit power calculation

* show modifiers in the diplo screen

* template.properties

* G&K modifiers

* promotions start at 0

* notiifications, AI

* conflict

* conflict?

* template and translation, failing test?

* another missing string

* missing space

* afraid relationship status

* missing space 2

* Slight optimization

* optimization pt 2

* reviews
This commit is contained in:
SimonCeder 2021-08-30 13:17:20 +02:00 committed by GitHub
parent f3a53516da
commit a01a6270fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 438 additions and 23 deletions

View File

@ -104,6 +104,7 @@ We promised not to settle near them ([count] turns remaining) = Vi lovade att in
They promised not to settle near us ([count] turns remaining) = De lovade att inte bygga städer nära oss ([count] drag kvar)
Unforgivable = Oförlåtlig
Afraid = Rädd
Enemy = Fiende
Competitor = Konkurrent
Neutral = Neutral
@ -173,6 +174,7 @@ Personality = Personlighet
Influence = Inflytande
Reach 30 for friendship. = Nå 30 för vänskap.
Reach highest influence above 60 for alliance. = Nå högsta inflytande över 60 för allians.
# Requires translation!
When Friends: =
# Requires translation!
@ -180,6 +182,27 @@ When Allies: =
# Requires translation!
The unique luxury is one of: =
Demand Tribute = Kräv Brandskatt
Tribute Willingness = Brandskattsvillighet
>0 to take gold, >30 and size 4 city for worker = >0 för att ta guld, >30 och stad av storlek 4 för arbetare
Major Civ = Stor Civilisation
No Cities = Inga Städer
Base value = Grundvärde
Has Ally = Har Allierad
Has Protector = Har Beskyddare
Demanding a Worker = Kräver Arbetare
Demanding a Worker from small City-State = Kräver Arbetare från liten Stadsstat
Very recently paid tribute = Alldeles nyss brandskattad
Recently paid tribute = Nyligen brandskattad
Influence below -30 = Inflytande under -30
Military Rank = Militär Rangordning
Military near City-State = Militär nära Stadsstaten
Sum: = Summa:
Take [amount] gold (-15 Influence) = Ta [amount] guld (-15 Inflytande)
Take worker (-50 Influence) = Ta arbetare (-50 Inflytande)
[civName] is afraid of your military power! = [civName] är rädd för din militära makt!
# Trades
Trade = Handelsavtal

View File

@ -104,6 +104,7 @@ We promised not to settle near them ([count] turns remaining) =
They promised not to settle near us ([count] turns remaining) =
Unforgivable =
Afraid =
Enemy =
Competitor =
Neutral =
@ -178,6 +179,27 @@ When Friends: =
When Allies: =
The unique luxury is one of: =
Demand Tribute =
Tribute Willingness =
>0 to take gold, >30 and size 4 city for worker =
Major Civ =
No Cities =
Base value =
Has Ally =
Has Protector =
Demanding a Worker =
Demanding a Worker from small City-State =
Very recently paid tribute =
Recently paid tribute =
Influence below -30 =
Military Rank =
Military near City-State =
Sum: =
Take [amount] gold (-15 Influence) =
Take worker (-50 Influence) =
[civName] is afraid of your military power! =
# Trades
Trade =

View File

@ -16,6 +16,7 @@ import com.unciv.logic.trade.*
import com.unciv.models.ruleset.ModOptionsConstants
import com.unciv.models.ruleset.VictoryType
import com.unciv.models.ruleset.tech.Technology
import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
@ -49,7 +50,10 @@ object NextTurnAutomation {
chooseTechToResearch(civInfo)
automateCityBombardment(civInfo)
useGold(civInfo)
if (!civInfo.isCityState()) {
protectCityStates(civInfo)
bullyCityStates(civInfo)
}
automateUnits(civInfo) // this is the most expensive part
reassignWorkedTiles(civInfo) // second most expensive
trainSettler(civInfo)
@ -114,29 +118,19 @@ object NextTurnAutomation {
/** allow AI to spend money to purchase city-state friendship, buildings & unit */
private fun useGold(civInfo: CivilizationInfo) {
if (civInfo.victoryType() == VictoryType.Cultural) {
for (cityState in civInfo.getKnownCivs()
.filter { it.isCityState() && it.cityStateType == CityStateType.Cultured }) {
val diploManager = cityState.getDiplomacyManager(civInfo)
if (diploManager.influence < 40) { // we want to gain influence with them
if (!civInfo.isCityState()) {
val potentialAllies = civInfo.getKnownCivs().filter { it.isCityState() }
if (potentialAllies.isNotEmpty()) {
val cityState =
potentialAllies.maxByOrNull { valueCityStateAlliance(civInfo, it) }!!
if (cityState.getAllyCiv() != civInfo.civName && valueCityStateAlliance(civInfo, cityState) > 0) {
tryGainInfluence(civInfo, cityState)
return
}
}
}
if (civInfo.getHappiness() < 5) {
for (cityState in civInfo.getKnownCivs()
.filter { it.isCityState() && it.cityStateType == CityStateType.Mercantile }) {
val diploManager = cityState.getDiplomacyManager(civInfo)
if (diploManager.influence < 40) { // we want to gain influence with them
tryGainInfluence(civInfo, cityState)
return
}
}
}
for (city in civInfo.cities.sortedByDescending { it.population.population }) {
val construction = city.cityConstructions.getCurrentConstruction()
if (construction is PerpetualConstruction) continue
@ -147,6 +141,47 @@ object NextTurnAutomation {
}
}
private fun valueCityStateAlliance(civInfo: CivilizationInfo, cityState: CivilizationInfo): Int {
var value = 0
if (!cityState.isAlive() || cityState.cities.isEmpty())
return value
if (civInfo.victoryType() == VictoryType.Cultural && cityState.canGiveStat(Stat.Culture)) {
value += 10
}
else if (civInfo.victoryType() == VictoryType.Scientific && cityState.canGiveStat(Stat.Science)) {
// In case someone mods this in
value += 10
}
else if (civInfo.victoryType() == VictoryType.Domination) {
// Don't ally close city-states, conquer them instead
val distance = getMinDistanceBetweenCities(civInfo, cityState)
if (distance < 20)
value -= (20 - distance) / 4
}
if (civInfo.gold < 100) {
// Consider bullying for cash
value -= 5
}
if (civInfo.getHappiness() < 5 && cityState.canGiveStat(Stat.Happiness)) {
value += 10 - civInfo.getHappiness()
}
if (civInfo.getHappiness() > 5 && cityState.canGiveStat(Stat.Food)) {
value += 5
}
if (cityState.getAllyCiv() != null && cityState.getAllyCiv() != civInfo.civName) {
// easier not to compete if a third civ has this locked down
val thirdCivInfluence = cityState.getDiplomacyManager(cityState.getAllyCiv()!!).influence.toInt()
value -= (thirdCivInfluence - 60) / 10
}
// Bonus for luxury resources we can get from them
value += cityState.detailedCivResources.count { it.resource.resourceType == ResourceType.Luxury
&& !(it.resource in civInfo.detailedCivResources) }
return value
}
private fun protectCityStates(civInfo: CivilizationInfo) {
for (state in civInfo.getKnownCivs().filter{!it.isDefeated() && it.isCityState()}) {
val diplomacyManager = state.getDiplomacyManager(civInfo.civName)
@ -161,6 +196,21 @@ object NextTurnAutomation {
}
}
private fun bullyCityStates(civInfo: CivilizationInfo) {
for (state in civInfo.getKnownCivs().filter{!it.isDefeated() && it.isCityState()}) {
val diplomacyManager = state.getDiplomacyManager(civInfo.civName)
if(diplomacyManager.relationshipLevel() < RelationshipLevel.Friend
&& diplomacyManager.diplomaticStatus == DiplomaticStatus.Peace
&& valueCityStateAlliance(civInfo, state) <= 0
&& state.getTributeWillingness(civInfo) > 0) {
if (state.getTributeWillingness(civInfo, demandingWorker = true) > 0)
civInfo.demandWorker(state)
else
civInfo.demandGold(state)
}
}
}
private fun getFreeTechForCityStates(civInfo: CivilizationInfo) {
// City-States automatically get all techs that at least half of the major civs know
val researchableTechs = civInfo.gameInfo.ruleSet.technologies.keys

View File

@ -1,6 +1,7 @@
package com.unciv.logic.city
import com.badlogic.gdx.math.Vector2
import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.GreatPersonManager
import com.unciv.logic.civilization.ReligionState
@ -22,6 +23,7 @@ import kotlin.collections.HashMap
import kotlin.collections.HashSet
import kotlin.math.ceil
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.roundToInt
class CityInfo {
@ -673,6 +675,11 @@ class CityInfo {
return !isOriginalCapital && !isHolyCity() && (!isCapital() || justCaptured)
}
fun getForceEvaluation(): Int {
// Same as for units, so higher values count more
return CityCombatant(this).getCityStrength().toFloat().pow(1.5f).toInt()
}
fun getNeighbouringCivs(): List<String> {
val tilesList: HashSet<TileInfo> = getTiles().toHashSet()

View File

@ -7,11 +7,13 @@ import com.unciv.logic.GameInfo
import com.unciv.logic.UncivShowableException
import com.unciv.logic.automation.NextTurnAutomation
import com.unciv.logic.automation.WorkerAutomation
import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.RuinsManager.RuinsManager
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.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.UnitMovementAlgorithms
@ -26,14 +28,20 @@ import com.unciv.models.ruleset.tile.TileResource
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats
import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.models.translations.getPlaceholderText
import com.unciv.models.translations.tr
import com.unciv.ui.victoryscreen.RankingType
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.system.measureNanoTime
import kotlin.system.measureTimeMillis
class CivilizationInfo {
@ -977,6 +985,156 @@ class CivilizationInfo {
}
}
fun getTributeWillingness(demandingCiv: CivilizationInfo, demandingWorker: Boolean = false): Int {
return getTributeModifiers(demandingCiv, demandingWorker).values.sum()
}
fun getTributeModifiers(demandingCiv: CivilizationInfo, demandingWorker: Boolean = false, requireWholeList: Boolean = false): HashMap<String, Int> {
val modifiers = LinkedHashMap<String, Int>() // Linked to preserve order when presenting the modifiers table
// Can't bully major civs or unsettled CS's
if (!isCityState()) {
modifiers["Major Civ"] = -999
return modifiers
}
if (cities.isEmpty()) {
modifiers["No Cities"] = -999
return modifiers
}
modifiers["Base value"] = -110
if (cityStatePersonality == CityStatePersonality.Hostile)
modifiers["Hostile"] = -10
if (cityStateType == CityStateType.Militaristic)
modifiers["Militaristic"] = -10
if (allyCivName != null && allyCivName != demandingCiv.civName)
modifiers["Has Ally"] = -10
if (getProtectorCivs().any { it != demandingCiv })
modifiers["Has Protector"] = -20
if (demandingWorker)
modifiers["Demanding a Worker"] = -30
if (demandingWorker && getCapital().population.population < 4)
modifiers["Demanding a Worker from small City-State"] = -300
val recentBullying = flagsCountdown[CivFlags.RecentlyBullied.name]
if (recentBullying != null && recentBullying > 10)
modifiers["Very recently paid tribute"] = -300
else if (recentBullying != null)
modifiers["Recently paid tribute"] = -40
if (getDiplomacyManager(demandingCiv).influence < -30)
modifiers["Influence below -30"] = -300
// Slight optimization, we don't do the expensive stuff if we have no chance of getting a positive result
if (!requireWholeList && modifiers.values.sum() <= -200)
return modifiers
val forceRank = gameInfo.getAliveMajorCivs().sortedByDescending { it.getStatForRanking(RankingType.Force) }.indexOf(demandingCiv)
modifiers["Military Rank"] = 100 - ((100 / gameInfo.gameParameters.players.size) * forceRank)
if (!requireWholeList && modifiers.values.sum() <= -100)
return modifiers
val bullyRange = max(5, gameInfo.tileMap.tileMatrix.size / 10) // Longer range for larger maps
val inRangeTiles = getCapital().getCenterTile().getTilesInDistanceRange(1..bullyRange)
val forceNearCity = inRangeTiles
.sumBy { if (it.militaryUnit?.civInfo == demandingCiv)
it.militaryUnit!!.getForceEvaluation()
else 0
}
val csForce = getCapital().getForceEvaluation() + inRangeTiles
.sumBy { if (it.militaryUnit?.civInfo == this)
it.militaryUnit!!.getForceEvaluation()
else 0
}
val forceRatio = forceNearCity.toFloat() / csForce.toFloat()
modifiers["Military near City-State"] = when {
forceRatio > 3f -> 100
forceRatio > 2f -> 80
forceRatio > 1.5f -> 60
forceRatio > 1f -> 40
forceRatio > 0.5f -> 20
else -> 0
}
return modifiers
}
fun goldGainedByTribute(): Int {
// These values are close enough, linear increase throughout the game
var gold = when (gameInfo.gameParameters.gameSpeed) {
GameSpeed.Quick -> 60
GameSpeed.Standard -> 50
GameSpeed.Epic -> 35
GameSpeed.Marathon -> 30
}
val turnsToIncrement = when (gameInfo.gameParameters.gameSpeed) {
GameSpeed.Quick -> 5f
GameSpeed.Standard -> 6.5f
GameSpeed.Epic -> 14f
GameSpeed.Marathon -> 32f
}
gold += 5 * (gameInfo.turns / turnsToIncrement).toInt()
return gold
}
fun demandGold(cityState: CivilizationInfo) {
if (!cityState.isCityState()) throw Exception("You can only demand gold from City-States!")
val goldAmount = goldGainedByTribute()
addGold(goldAmount)
cityState.getDiplomacyManager(this).influence -= 15
cityState.addFlag(CivFlags.RecentlyBullied.name, 20)
cityState.updateAllyCivForCityState()
updateStatsForNextTurn()
}
fun demandWorker(cityState: CivilizationInfo) {
if (!cityState.isCityState()) throw Exception("You can only demand workers from City-States!")
val buildableWorkerLikeUnits = gameInfo.ruleSet.units.filter {
it.value.uniqueObjects.any { it.placeholderText == Constants.canBuildImprovements }
&& it.value.isBuildable(this)
&& it.value.isCivilian()
}
if (buildableWorkerLikeUnits.isEmpty()) return // Bad luck?
placeUnitNearTile(cityState.getCapital().location, buildableWorkerLikeUnits.keys.random())
cityState.getDiplomacyManager(this).influence -= 50
cityState.addFlag(CivFlags.RecentlyBullied.name, 20)
cityState.updateAllyCivForCityState()
}
fun canGiveStat(statType: Stat): Boolean {
if (!isCityState())
return false
val eraInfo = getEraObject()
val bonuses = if (eraInfo == null) null
else eraInfo.allyBonus[cityStateType.name]
if (bonuses != null) {
// Defined city states in json
bonuses.addAll(eraInfo!!.friendBonus[cityStateType.name]!!)
for (bonus in bonuses) {
if (statType == Stat.Happiness && bonus.getPlaceholderText() == "Provides [] Happiness")
return true
if (bonus.getPlaceholderText() == "Provides [] [] per turn" && bonus.getPlaceholderParameters()[1] == statType.name)
return true
if (bonus.getPlaceholderText() == "Provides [] [] []" && bonus.getPlaceholderParameters()[1] == statType.name)
return true
}
} else {
// compatibility mode
return when {
cityStateType == CityStateType.Mercantile && statType == Stat.Happiness -> true
cityStateType == CityStateType.Cultured && statType == Stat.Culture -> true
cityStateType == CityStateType.Maritime && statType == Stat.Food -> true
else -> false
}
}
return false
}
//endregion
}
@ -993,4 +1151,5 @@ enum class CivFlags {
TurnsTillNextDiplomaticVote,
ShowDiplomaticVotingResults,
ShouldResetDiplomaticVotes,
RecentlyBullied
}

View File

@ -14,6 +14,7 @@ import kotlin.math.min
enum class RelationshipLevel{
Unforgivable,
Afraid,
Enemy,
Competitor,
Neutral,
@ -35,7 +36,8 @@ enum class DiplomacyFlags{
AgreedToNotSettleNearUs,
IgnoreThemSettlingNearUs,
ProvideMilitaryUnit,
EverBeenFriends
EverBeenFriends,
NotifiedAfraid
}
enum class DiplomaticModifiers{
@ -142,6 +144,7 @@ class DiplomacyManager() {
if (civInfo.isCityState()) {
if (influence <= -30 || civInfo.isAtWarWith(otherCiv())) return RelationshipLevel.Unforgivable
if (influence < 30 && civInfo.getTributeWillingness(otherCiv()) > 0) return RelationshipLevel.Afraid
if (influence < 0) return RelationshipLevel.Enemy
if (influence >= 60 && civInfo.getAllyCiv() == otherCivName) return RelationshipLevel.Ally
if (influence >= 30) return RelationshipLevel.Friend
@ -400,6 +403,16 @@ class DiplomacyManager() {
if (civCapitalLocation != null) otherCiv().addNotification(text, civCapitalLocation, civInfo.civName, NotificationIcon.Diplomacy)
else otherCiv().addNotification(text, civInfo.civName, NotificationIcon.Diplomacy)
}
// Potentially notify about afraid status
if (influence < 30 // We usually don't want to bully our friends
&& !hasFlag(DiplomacyFlags.NotifiedAfraid)
&& civInfo.getTributeWillingness(otherCiv()) > 0) {
setFlag(DiplomacyFlags.NotifiedAfraid, 20) // Wait 20 turns until next reminder
val text = "[${civInfo.civName}] is afraid of your military power!"
if (civCapitalLocation != null) otherCiv().addNotification(text, civCapitalLocation, civInfo.civName, NotificationIcon.Diplomacy)
else otherCiv().addNotification(text, civInfo.civName, NotificationIcon.Diplomacy)
}
}
}

View File

@ -16,6 +16,7 @@ import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.ruleset.unit.UnitType
import com.unciv.ui.utils.toPercent
import java.text.DecimalFormat
import kotlin.math.pow
/**
* The immutable properties and mutable game state of an individual unit present on the map
@ -985,5 +986,13 @@ class MapUnit {
if (isPreparingParadrop()) action = null
}
fun getForceEvaluation(): Int {
val promotionBonus = (promotions.numberOfPromotions + 1).toFloat().pow(0.3f)
var power = (baseUnit.getForceEvaluation() * promotionBonus).toInt()
power *= health
power /= 100
return power
}
//endregion
}

View File

@ -243,7 +243,7 @@ class TradeEvaluation {
RelationshipLevel.Unforgivable -> 10000
RelationshipLevel.Enemy -> 2000
RelationshipLevel.Competitor -> 500
RelationshipLevel.Neutral -> 200
RelationshipLevel.Neutral, RelationshipLevel.Afraid -> 200
RelationshipLevel.Favorable, RelationshipLevel.Friend, RelationshipLevel.Ally -> 100
}
}

View File

@ -19,6 +19,7 @@ import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.HashSet
import kotlin.math.pow
// This is BaseUnit because Unit is already a base Kotlin class and to avoid mixing the two up
@ -55,6 +56,9 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
var uniqueTo: String? = null
var attackSound: String? = null
@Transient
var cachedForceEvaluation: Int = -1
lateinit var ruleset: Ruleset
override var civilopediaText = listOf<FormattedLine>()
@ -444,4 +448,78 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText {
&& (uniqueObjects + getType().uniqueObjects)
.any { it.placeholderText == "+[]% Strength vs []" && it.params[1] == "City" }
)
fun getForceEvaluation(): Int {
if (cachedForceEvaluation < 0) evaluateForce()
return cachedForceEvaluation
}
private fun evaluateForce() {
if (strength == 0 && rangedStrength == 0) {
cachedForceEvaluation = 0
return
}
var power = strength.toFloat().pow(1.5f).toInt()
var rangedPower = rangedStrength.toFloat().pow(1.45f).toInt()
// Value ranged naval units less
if (isWaterUnit()) {
rangedPower /= 2
}
if (rangedPower > 0)
power = rangedPower
// Replicates the formula from civ V, which is a lower multiplier than probably intended, because math
// They did fix it in BNW so it was completely bugged and always 1, again math
power = (power * movement.toFloat().pow(0.3f)).toInt()
if (uniqueObjects.any { it.placeholderText =="Self-destructs when attacking" } )
power /= 2
if (uniqueObjects.any { it.placeholderText =="Nuclear weapon of Strength []" } )
power += 4000
// Uniques
for (unique in uniqueObjects) {
when {
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] == "City" // City Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] != "City" // Bonus vs something else - a quarter of the bonus
-> power += (power * unique.params[0].toInt()) / 400
unique.placeholderText == "+[]% Strength when attacking" // Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength when defending" // Defense - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "May Paradrop up to [] tiles from inside friendly territory" // Paradrop - 25% bonus
-> power += power / 4
unique.placeholderText == "Must set up to ranged attack" // Must set up - 20 % penalty
-> power -= power / 5
unique.placeholderText == "+[]% Strength in []" // Bonus in terrain or feature - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
}
}
// Base promotions
for (promotionName in promotions) {
for (unique in ruleset.unitPromotions[promotionName]!!.uniqueObjects) {
when {
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] == "City" // City Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] != "City" // Bonus vs something else - a quarter of the bonus
-> power += (power * unique.params[0].toInt()) / 400
unique.placeholderText == "+[]% Strength when attacking" // Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength when defending" // Defense - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "[] additional attacks per turn" // Extra attacks - 20% bonus per extra attack
-> power += (power * unique.params[0].toInt()) / 5
unique.placeholderText == "+[]% Strength in []" // Bonus in terrain or feature - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
}
}
}
cachedForceEvaluation = power
}
}

View File

@ -409,6 +409,7 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab
val color = when (relationshipLevel) {
RelationshipLevel.Unforgivable -> Color.RED
RelationshipLevel.Enemy -> Color.ORANGE
RelationshipLevel.Afraid -> Color.YELLOW
RelationshipLevel.Neutral, RelationshipLevel.Friend -> Color.LIME
RelationshipLevel.Ally -> Color.SKY
else -> Color.DARK_GRAY

View File

@ -239,6 +239,14 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
if (isNotPlayersTurn()) protectionButton.disable()
}
val demandTributeButton = "Demand Tribute".toTextButton()
demandTributeButton.onClick {
rightSideTable.clear()
rightSideTable.add(ScrollPane(getDemandTributeTable(otherCiv)))
}
diplomacyTable.add(demandTributeButton).row()
if (isNotPlayersTurn()) demandTributeButton.disable()
val diplomacyManager = viewingCiv.getDiplomacyManager(otherCiv)
if (!viewingCiv.gameInfo.ruleSet.modOptions.uniques.contains(ModOptionsConstants.diplomaticRelationshipsCannotChange)) {
if (viewingCiv.isAtWarWith(otherCiv)) {
@ -384,6 +392,50 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
}
private fun getDemandTributeTable(otherCiv: CivilizationInfo): Table {
val diplomacyTable = getCityStateDiplomacyTableHeader(otherCiv)
diplomacyTable.addSeparator()
diplomacyTable.add("Tribute Willingness".toLabel()).row()
diplomacyTable.add(">0 to take gold, >30 and size 4 city for worker".toLabel()).row()
val modifierTable = Table()
val tributeModifiers = otherCiv.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.addSeparator()
val demandGoldButton = "Take [${otherCiv.goldGainedByTribute()}] gold (-15 Influence)".toTextButton()
demandGoldButton.onClick {
viewingCiv.demandGold(otherCiv)
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(demandGoldButton).row()
if (otherCiv.getTributeWillingness(viewingCiv, demandingWorker = false) <= 0) demandGoldButton.disable()
val demandWorkerButton = "Take worker (-50 Influence)".toTextButton()
demandWorkerButton.onClick {
viewingCiv.demandWorker(otherCiv)
rightSideTable.clear()
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
}
diplomacyTable.add(demandWorkerButton).row()
if (otherCiv.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)
@ -630,6 +682,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
RelationshipLevel.Neutral -> Color.WHITE
RelationshipLevel.Favorable, RelationshipLevel.Friend,
RelationshipLevel.Ally -> Color.GREEN
RelationshipLevel.Afraid -> Color.YELLOW
else -> Color.RED
}