mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 13:55:54 -04:00
Rework City State functions into separate file (#5043)
* cut+paste new file * rewrite functions * wrapper functions * rewrite function calls * influenceGainedByGift * keep instantiated CityStateFunctions as Transient * use setter functions for influence
This commit is contained in:
parent
c116b9ba94
commit
34eb4758ff
@ -115,11 +115,11 @@ object NextTurnAutomation {
|
|||||||
private fun tryGainInfluence(civInfo: CivilizationInfo, cityState: CivilizationInfo) {
|
private fun tryGainInfluence(civInfo: CivilizationInfo, cityState: CivilizationInfo) {
|
||||||
if (civInfo.gold < 250) return // save up
|
if (civInfo.gold < 250) return // save up
|
||||||
if (cityState.getDiplomacyManager(civInfo).influence < 20) {
|
if (cityState.getDiplomacyManager(civInfo).influence < 20) {
|
||||||
civInfo.giveGoldGift(cityState, 250)
|
cityState.receiveGoldGift(civInfo, 250)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (civInfo.gold < 500) return // it's not worth it to invest now, wait until you have enough for 2
|
if (civInfo.gold < 500) return // it's not worth it to invest now, wait until you have enough for 2
|
||||||
civInfo.giveGoldGift(cityState, 500)
|
cityState.receiveGoldGift(civInfo, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,9 +230,9 @@ object NextTurnAutomation {
|
|||||||
&& valueCityStateAlliance(civInfo, state) <= 0
|
&& valueCityStateAlliance(civInfo, state) <= 0
|
||||||
&& state.getTributeWillingness(civInfo) > 0) {
|
&& state.getTributeWillingness(civInfo) > 0) {
|
||||||
if (state.getTributeWillingness(civInfo, demandingWorker = true) > 0)
|
if (state.getTributeWillingness(civInfo, demandingWorker = true) > 0)
|
||||||
civInfo.demandWorker(state)
|
state.tributeWorker(civInfo)
|
||||||
else
|
else
|
||||||
civInfo.demandGold(state)
|
state.tributeGold(civInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
347
core/src/com/unciv/logic/civilization/CityStateFunctions.kt
Normal file
347
core/src/com/unciv/logic/civilization/CityStateFunctions.kt
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
package com.unciv.logic.civilization
|
||||||
|
|
||||||
|
import com.unciv.Constants
|
||||||
|
import com.unciv.logic.automation.NextTurnAutomation
|
||||||
|
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||||
|
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||||
|
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||||
|
import com.unciv.models.metadata.GameSpeed
|
||||||
|
import com.unciv.models.stats.Stat
|
||||||
|
import com.unciv.models.translations.getPlaceholderParameters
|
||||||
|
import com.unciv.models.translations.getPlaceholderText
|
||||||
|
import com.unciv.ui.victoryscreen.RankingType
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
import kotlin.collections.LinkedHashMap
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
/** Class containing city-state-specific functions */
|
||||||
|
class CityStateFunctions(val civInfo: CivilizationInfo) {
|
||||||
|
|
||||||
|
/** Gain a random great person from the city state */
|
||||||
|
fun giveGreatPersonToPatron(receivingCiv: CivilizationInfo) {
|
||||||
|
|
||||||
|
var giftableUnits = civInfo.gameInfo.ruleSet.units.values.filter { it.isGreatPerson() }
|
||||||
|
if (!civInfo.gameInfo.hasReligionEnabled()) giftableUnits = giftableUnits.filterNot { it.uniques.contains("Great Person - [Faith]")}
|
||||||
|
if (giftableUnits.isEmpty()) // For badly defined mods that don't have great people but do have the policy that makes city states grant them
|
||||||
|
return
|
||||||
|
val giftedUnit = giftableUnits.random()
|
||||||
|
val cities = NextTurnAutomation.getClosestCities(receivingCiv, civInfo)
|
||||||
|
val placedUnit = receivingCiv.placeUnitNearTile(cities.city1.location, giftedUnit.name)
|
||||||
|
if (placedUnit == null) return
|
||||||
|
val locations = LocationAction(listOf(placedUnit.getTile().position, cities.city2.location))
|
||||||
|
receivingCiv.addNotification( "[${civInfo.civName}] gave us a [${giftedUnit.name}] as a gift!", locations, civInfo.civName, giftedUnit.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun giveMilitaryUnitToPatron(receivingCiv: CivilizationInfo) {
|
||||||
|
val cities = NextTurnAutomation.getClosestCities(receivingCiv, civInfo)
|
||||||
|
val city = cities.city1
|
||||||
|
val militaryUnit = city.cityConstructions.getConstructableUnits()
|
||||||
|
.filter { !it.isCivilian() && it.isLandUnit() && it.uniqueTo==null }
|
||||||
|
.toList().random()
|
||||||
|
// placing the unit may fail - in that case stay quiet
|
||||||
|
val placedUnit = receivingCiv.placeUnitNearTile(city.location, militaryUnit.name) ?: return
|
||||||
|
|
||||||
|
// The unit should have bonuses from Barracks, Alhambra etc as if it was built in the CS capital
|
||||||
|
militaryUnit.addConstructionBonuses(placedUnit, civInfo.getCapital().cityConstructions)
|
||||||
|
|
||||||
|
// Siam gets +10 XP for all CS units
|
||||||
|
for (unique in receivingCiv.getMatchingUniques("Military Units gifted from City-States start with [] XP")) {
|
||||||
|
placedUnit.promotions.XP += unique.params[0].toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point to the places mentioned in the message _in that order_ (debatable)
|
||||||
|
val placedLocation = placedUnit.getTile().position
|
||||||
|
val locations = LocationAction(listOf(placedLocation, cities.city2.location, city.location))
|
||||||
|
receivingCiv.addNotification("[${civInfo.civName}] gave us a [${militaryUnit.name}] as gift near [${city.name}]!", locations, civInfo.civName, militaryUnit.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun influenceGainedByGift(donorCiv: CivilizationInfo, giftAmount: Int): Int {
|
||||||
|
// https://github.com/Gedemon/Civ5-DLL/blob/aa29e80751f541ae04858b6d2a2c7dcca454201e/CvGameCoreDLL_Expansion1/CvMinorCivAI.cpp
|
||||||
|
// line 8681 and below
|
||||||
|
var influenceGained = giftAmount.toFloat().pow(1.01f) / 9.8f
|
||||||
|
val gameProgressApproximate = min(civInfo.gameInfo.turns / (400f * civInfo.gameInfo.gameParameters.gameSpeed.modifier), 1f)
|
||||||
|
influenceGained *= 1 - (2/3f) * gameProgressApproximate
|
||||||
|
influenceGained *= when (civInfo.gameInfo.gameParameters.gameSpeed) {
|
||||||
|
GameSpeed.Quick -> 1.25f
|
||||||
|
GameSpeed.Standard -> 1f
|
||||||
|
GameSpeed.Epic -> 0.75f
|
||||||
|
GameSpeed.Marathon -> 0.67f
|
||||||
|
}
|
||||||
|
for (unique in donorCiv.getMatchingUniques("Gifts of Gold to City-States generate []% more Influence"))
|
||||||
|
influenceGained *= 1f + unique.params[0].toFloat() / 100f
|
||||||
|
influenceGained -= influenceGained % 5
|
||||||
|
if (influenceGained < 5f) influenceGained = 5f
|
||||||
|
return influenceGained.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun receiveGoldGift(donorCiv: CivilizationInfo, giftAmount: Int) {
|
||||||
|
if (!civInfo.isCityState()) throw Exception("You can only gain influence with City-States!")
|
||||||
|
donorCiv.addGold(-giftAmount)
|
||||||
|
civInfo.addGold(giftAmount)
|
||||||
|
civInfo.getDiplomacyManager(donorCiv).addInfluence(influenceGainedByGift(donorCiv, giftAmount).toFloat())
|
||||||
|
updateAllyCivForCityState()
|
||||||
|
donorCiv.updateStatsForNextTurn()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProtectorCivs() : List<CivilizationInfo> {
|
||||||
|
if(civInfo.isMajorCiv()) return emptyList()
|
||||||
|
return civInfo.diplomacy.values
|
||||||
|
.filter{ !it.otherCiv().isDefeated() && it.diplomaticStatus == DiplomaticStatus.Protector }
|
||||||
|
.map{ it.otherCiv() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addProtectorCiv(otherCiv: CivilizationInfo) {
|
||||||
|
if(!civInfo.isCityState() or !otherCiv.isMajorCiv() or otherCiv.isDefeated()) return
|
||||||
|
if(!civInfo.knows(otherCiv) or civInfo.isAtWarWith(otherCiv)) return //Exception
|
||||||
|
|
||||||
|
val diplomacy = civInfo.getDiplomacyManager(otherCiv.civName)
|
||||||
|
diplomacy.diplomaticStatus = DiplomaticStatus.Protector
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeProtectorCiv(otherCiv: CivilizationInfo) {
|
||||||
|
if(!civInfo.isCityState() or !otherCiv.isMajorCiv() or otherCiv.isDefeated()) return
|
||||||
|
if(!civInfo.knows(otherCiv) or civInfo.isAtWarWith(otherCiv)) return //Exception
|
||||||
|
|
||||||
|
val diplomacy = civInfo.getDiplomacyManager(otherCiv.civName)
|
||||||
|
diplomacy.diplomaticStatus = DiplomaticStatus.Peace
|
||||||
|
diplomacy.addInfluence(-20f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateAllyCivForCityState() {
|
||||||
|
var newAllyName: String? = null
|
||||||
|
if (!civInfo.isCityState()) return
|
||||||
|
val maxInfluence = civInfo.diplomacy
|
||||||
|
.filter { !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() }
|
||||||
|
.maxByOrNull { it.value.influence }
|
||||||
|
if (maxInfluence != null && maxInfluence.value.influence >= 60) {
|
||||||
|
newAllyName = maxInfluence.key
|
||||||
|
}
|
||||||
|
|
||||||
|
if (civInfo.getAllyCiv() != newAllyName) {
|
||||||
|
val oldAllyName = civInfo.getAllyCiv()
|
||||||
|
civInfo.setAllyCiv(newAllyName)
|
||||||
|
|
||||||
|
// If the city-state is captured by a civ, it stops being the ally of the civ it was previously an ally of.
|
||||||
|
// This means that it will NOT HAVE a capital at that time, so if we run getCapital we'll get a crash!
|
||||||
|
val capitalLocation = if (civInfo.cities.isNotEmpty()) civInfo.getCapital().location else null
|
||||||
|
|
||||||
|
if (newAllyName != null) {
|
||||||
|
val newAllyCiv = civInfo.gameInfo.getCivilization(newAllyName)
|
||||||
|
val text = "We have allied with [${civInfo.civName}]."
|
||||||
|
if (capitalLocation != null) newAllyCiv.addNotification(text, capitalLocation, civInfo.civName, NotificationIcon.Diplomacy)
|
||||||
|
else newAllyCiv.addNotification(text, civInfo.civName, NotificationIcon.Diplomacy)
|
||||||
|
newAllyCiv.updateViewableTiles()
|
||||||
|
newAllyCiv.updateDetailedCivResources()
|
||||||
|
for (unique in newAllyCiv.getMatchingUniques("Can spend Gold to annex or puppet a City-State that has been your ally for [] turns."))
|
||||||
|
newAllyCiv.getDiplomacyManager(civInfo.civName).setFlag(DiplomacyFlags.MarriageCooldown, unique.params[0].toInt())
|
||||||
|
}
|
||||||
|
if (oldAllyName != null) {
|
||||||
|
val oldAllyCiv = civInfo.gameInfo.getCivilization(oldAllyName)
|
||||||
|
val text = "We have lost alliance with [${civInfo.civName}]."
|
||||||
|
if (capitalLocation != null) oldAllyCiv.addNotification(text, capitalLocation, civInfo.civName, NotificationIcon.Diplomacy)
|
||||||
|
else oldAllyCiv.addNotification(text, civInfo.civName, NotificationIcon.Diplomacy)
|
||||||
|
oldAllyCiv.updateViewableTiles()
|
||||||
|
oldAllyCiv.updateDetailedCivResources()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDiplomaticMarriageCost(): Int {
|
||||||
|
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvMinorCivAI.cpp, line 7812
|
||||||
|
var cost = (500 * civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||||
|
// Plus disband value of all units
|
||||||
|
for (unit in civInfo.getCivUnits()) {
|
||||||
|
cost += unit.baseUnit.getDisbandGold(civInfo)
|
||||||
|
}
|
||||||
|
// Round to lower multiple of 5
|
||||||
|
cost /= 5
|
||||||
|
cost *= 5
|
||||||
|
|
||||||
|
return cost
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canBeMarriedBy(otherCiv: CivilizationInfo): Boolean {
|
||||||
|
return (!civInfo.isDefeated()
|
||||||
|
&& civInfo.isCityState()
|
||||||
|
&& civInfo.getDiplomacyManager(otherCiv).relationshipLevel() == RelationshipLevel.Ally
|
||||||
|
&& !otherCiv.getDiplomacyManager(civInfo).hasFlag(DiplomacyFlags.MarriageCooldown)
|
||||||
|
&& otherCiv.getMatchingUniques("Can spend Gold to annex or puppet a City-State that has been your ally for [] turns.").any()
|
||||||
|
&& otherCiv.gold >= getDiplomaticMarriageCost())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun diplomaticMarriage(otherCiv: CivilizationInfo) {
|
||||||
|
if (!canBeMarriedBy(otherCiv)) // Just in case
|
||||||
|
return
|
||||||
|
|
||||||
|
otherCiv.addGold(-getDiplomaticMarriageCost())
|
||||||
|
otherCiv.addNotification("We have married into the ruling family of [${civInfo.civName}], bringing them under our control.",
|
||||||
|
civInfo.getCapital().location, civInfo.civName, NotificationIcon.Diplomacy, otherCiv.civName)
|
||||||
|
for (civ in civInfo.gameInfo.civilizations.filter { it != otherCiv })
|
||||||
|
civ.addNotification("[${otherCiv.civName}] has married into the ruling family of [${civInfo.civName}], bringing them under their control.",
|
||||||
|
civInfo.getCapital().location, civInfo.civName, NotificationIcon.Diplomacy, otherCiv.civName)
|
||||||
|
for (unit in civInfo.getCivUnits())
|
||||||
|
unit.gift(otherCiv)
|
||||||
|
for (city in civInfo.cities) {
|
||||||
|
city.moveToCiv(otherCiv)
|
||||||
|
city.isPuppet = true // Human players get a popup that allows them to annex instead
|
||||||
|
city.foundingCiv = "" // This is no longer a city-state
|
||||||
|
}
|
||||||
|
civInfo.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!civInfo.isCityState()) {
|
||||||
|
modifiers["Major Civ"] = -999
|
||||||
|
return modifiers
|
||||||
|
}
|
||||||
|
if (civInfo.cities.isEmpty()) {
|
||||||
|
modifiers["No Cities"] = -999
|
||||||
|
return modifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiers["Base value"] = -110
|
||||||
|
|
||||||
|
if (civInfo.cityStatePersonality == CityStatePersonality.Hostile)
|
||||||
|
modifiers["Hostile"] = -10
|
||||||
|
if (civInfo.cityStateType == CityStateType.Militaristic)
|
||||||
|
modifiers["Militaristic"] = -10
|
||||||
|
if (civInfo.getAllyCiv() != null && civInfo.getAllyCiv() != demandingCiv.civName)
|
||||||
|
modifiers["Has Ally"] = -10
|
||||||
|
if (getProtectorCivs().any { it != demandingCiv })
|
||||||
|
modifiers["Has Protector"] = -20
|
||||||
|
if (demandingWorker)
|
||||||
|
modifiers["Demanding a Worker"] = -30
|
||||||
|
if (demandingWorker && civInfo.getCapital().population.population < 4)
|
||||||
|
modifiers["Demanding a Worker from small City-State"] = -300
|
||||||
|
val recentBullying = civInfo.getRecentBullyingCountdown()
|
||||||
|
if (recentBullying != null && recentBullying > 10)
|
||||||
|
modifiers["Very recently paid tribute"] = -300
|
||||||
|
else if (recentBullying != null)
|
||||||
|
modifiers["Recently paid tribute"] = -40
|
||||||
|
if (civInfo.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 = civInfo.gameInfo.getAliveMajorCivs().sortedByDescending { it.getStatForRanking(
|
||||||
|
RankingType.Force) }.indexOf(demandingCiv)
|
||||||
|
modifiers["Military Rank"] = 100 - ((100 / civInfo.gameInfo.gameParameters.players.size) * forceRank)
|
||||||
|
|
||||||
|
if (!requireWholeList && modifiers.values.sum() <= -100)
|
||||||
|
return modifiers
|
||||||
|
|
||||||
|
val bullyRange = max(5, civInfo.gameInfo.tileMap.tileMatrix.size / 10) // Longer range for larger maps
|
||||||
|
val inRangeTiles = civInfo.getCapital().getCenterTile().getTilesInDistanceRange(1..bullyRange)
|
||||||
|
val forceNearCity = inRangeTiles
|
||||||
|
.sumBy { if (it.militaryUnit?.civInfo == demandingCiv)
|
||||||
|
it.militaryUnit!!.getForceEvaluation()
|
||||||
|
else 0
|
||||||
|
}
|
||||||
|
val csForce = civInfo.getCapital().getForceEvaluation() + inRangeTiles
|
||||||
|
.sumBy { if (it.militaryUnit?.civInfo == civInfo)
|
||||||
|
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 (civInfo.gameInfo.gameParameters.gameSpeed) {
|
||||||
|
GameSpeed.Quick -> 60
|
||||||
|
GameSpeed.Standard -> 50
|
||||||
|
GameSpeed.Epic -> 35
|
||||||
|
GameSpeed.Marathon -> 30
|
||||||
|
}
|
||||||
|
val turnsToIncrement = when (civInfo.gameInfo.gameParameters.gameSpeed) {
|
||||||
|
GameSpeed.Quick -> 5f
|
||||||
|
GameSpeed.Standard -> 6.5f
|
||||||
|
GameSpeed.Epic -> 14f
|
||||||
|
GameSpeed.Marathon -> 32f
|
||||||
|
}
|
||||||
|
gold += 5 * (civInfo.gameInfo.turns / turnsToIncrement).toInt()
|
||||||
|
|
||||||
|
return gold
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tributeGold(demandingCiv: CivilizationInfo) {
|
||||||
|
if (!civInfo.isCityState()) throw Exception("You can only demand gold from City-States!")
|
||||||
|
val goldAmount = goldGainedByTribute()
|
||||||
|
demandingCiv.addGold(goldAmount)
|
||||||
|
civInfo.getDiplomacyManager(demandingCiv).addInfluence(-15f)
|
||||||
|
civInfo.addFlag(CivFlags.RecentlyBullied.name, 20)
|
||||||
|
updateAllyCivForCityState()
|
||||||
|
civInfo.updateStatsForNextTurn()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tributeWorker(demandingCiv: CivilizationInfo) {
|
||||||
|
if (!civInfo.isCityState()) throw Exception("You can only demand workers from City-States!")
|
||||||
|
|
||||||
|
val buildableWorkerLikeUnits = civInfo.gameInfo.ruleSet.units.filter {
|
||||||
|
it.value.uniqueObjects.any { it.placeholderText == Constants.canBuildImprovements }
|
||||||
|
&& it.value.isBuildable(civInfo)
|
||||||
|
&& it.value.isCivilian()
|
||||||
|
}
|
||||||
|
if (buildableWorkerLikeUnits.isEmpty()) return // Bad luck?
|
||||||
|
demandingCiv.placeUnitNearTile(civInfo.getCapital().location, buildableWorkerLikeUnits.keys.random())
|
||||||
|
|
||||||
|
civInfo.getDiplomacyManager(demandingCiv).addInfluence(-50f)
|
||||||
|
civInfo.addFlag(CivFlags.RecentlyBullied.name, 20)
|
||||||
|
updateAllyCivForCityState()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canGiveStat(statType: Stat): Boolean {
|
||||||
|
if (!civInfo.isCityState())
|
||||||
|
return false
|
||||||
|
val eraInfo = civInfo.getEraObject()
|
||||||
|
val allyBonuses = if (eraInfo == null) null
|
||||||
|
else eraInfo.allyBonus[civInfo.cityStateType.name]
|
||||||
|
if (allyBonuses != null) {
|
||||||
|
// Defined city states in json
|
||||||
|
val bonuses = allyBonuses + eraInfo!!.friendBonus[civInfo.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 {
|
||||||
|
civInfo.cityStateType == CityStateType.Mercantile && statType == Stat.Happiness -> true
|
||||||
|
civInfo.cityStateType == CityStateType.Cultured && statType == Stat.Culture -> true
|
||||||
|
civInfo.cityStateType == CityStateType.Maritime && statType == Stat.Food -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -91,6 +91,9 @@ class CivilizationInfo {
|
|||||||
@Transient
|
@Transient
|
||||||
var detailedCivResources = ResourceSupplyList()
|
var detailedCivResources = ResourceSupplyList()
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
val cityStateFunctions = CityStateFunctions(this)
|
||||||
|
|
||||||
var playerType = PlayerType.AI
|
var playerType = PlayerType.AI
|
||||||
|
|
||||||
/** Used in online multiplayer for human players */
|
/** Used in online multiplayer for human players */
|
||||||
@ -668,7 +671,8 @@ class CivilizationInfo {
|
|||||||
if (flagsCountdown[flag]!! < min(cityStateAllies.count(), 10) && cities.isNotEmpty()
|
if (flagsCountdown[flag]!! < min(cityStateAllies.count(), 10) && cities.isNotEmpty()
|
||||||
&& cityStateAllies.any { it.cities.isNotEmpty() }
|
&& cityStateAllies.any { it.cities.isNotEmpty() }
|
||||||
) {
|
) {
|
||||||
gainGreatPersonFromCityState()
|
val givingCityState = getKnownCivs().filter { it.isCityState() && it.getAllyCiv() == civName && it.cities.isNotEmpty()}.random()
|
||||||
|
givingCityState.giveGreatPersonToPatron(this)
|
||||||
flagsCountdown[flag] = turnsForGreatPersonFromCityState()
|
flagsCountdown[flag] = turnsForGreatPersonFromCityState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,6 +711,8 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
|
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
|
||||||
|
|
||||||
|
fun getRecentBullyingCountdown() = flagsCountdown[CivFlags.RecentlyBullied.name]
|
||||||
|
|
||||||
fun mayVoteForDiplomaticVictory() =
|
fun mayVoteForDiplomaticVictory() =
|
||||||
getTurnsTillNextDiplomaticVote() == 0
|
getTurnsTillNextDiplomaticVote() == 0
|
||||||
&& civName !in gameInfo.diplomaticVictoryVotesCast.keys
|
&& civName !in gameInfo.diplomaticVictoryVotesCast.keys
|
||||||
@ -858,333 +864,50 @@ class CivilizationInfo {
|
|||||||
return (era.researchAgreementCost * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
return (era.researchAgreementCost * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////// Functions specific to City State civilizations ////////////////////////
|
//////////////////////// City State wrapper functions ////////////////////////
|
||||||
|
/** Gain a random great person from the city state */
|
||||||
|
private fun giveGreatPersonToPatron(receivingCiv: CivilizationInfo) {
|
||||||
fun influenceGainedByGift(giftAmount: Int): Int {
|
cityStateFunctions.giveGreatPersonToPatron(receivingCiv)
|
||||||
// https://github.com/Gedemon/Civ5-DLL/blob/aa29e80751f541ae04858b6d2a2c7dcca454201e/CvGameCoreDLL_Expansion1/CvMinorCivAI.cpp
|
|
||||||
// line 8681 and below
|
|
||||||
var influenceGained = giftAmount.toFloat().pow(1.01f) / 9.8f
|
|
||||||
val gameProgressApproximate = min(gameInfo.turns / (400f * gameInfo.gameParameters.gameSpeed.modifier), 1f)
|
|
||||||
influenceGained *= 1 - (2/3f) * gameProgressApproximate
|
|
||||||
influenceGained *= when (gameInfo.gameParameters.gameSpeed) {
|
|
||||||
GameSpeed.Quick -> 1.25f
|
|
||||||
GameSpeed.Standard -> 1f
|
|
||||||
GameSpeed.Epic -> 0.75f
|
|
||||||
GameSpeed.Marathon -> 0.67f
|
|
||||||
}
|
|
||||||
for (unique in getMatchingUniques("Gifts of Gold to City-States generate []% more Influence"))
|
|
||||||
influenceGained *= 1f + unique.params[0].toFloat() / 100f
|
|
||||||
influenceGained -= influenceGained % 5
|
|
||||||
if (influenceGained < 5f) influenceGained = 5f
|
|
||||||
return influenceGained.toInt()
|
|
||||||
}
|
}
|
||||||
|
fun giveMilitaryUnitToPatron(receivingCiv: CivilizationInfo) {
|
||||||
fun giveGoldGift(cityState: CivilizationInfo, giftAmount: Int) {
|
cityStateFunctions.giveMilitaryUnitToPatron(receivingCiv)
|
||||||
if (!cityState.isCityState()) throw Exception("You can only gain influence with City-States!")
|
|
||||||
addGold(-giftAmount)
|
|
||||||
cityState.addGold(giftAmount)
|
|
||||||
cityState.getDiplomacyManager(this).addInfluence(influenceGainedByGift(giftAmount).toFloat())
|
|
||||||
updateStatsForNextTurn()
|
|
||||||
}
|
}
|
||||||
|
fun influenceGainedByGift(donorCiv: CivilizationInfo, giftAmount: Int)
|
||||||
fun gainMilitaryUnitFromCityState(otherCiv: CivilizationInfo) {
|
= cityStateFunctions.influenceGainedByGift(donorCiv, giftAmount)
|
||||||
val cities = NextTurnAutomation.getClosestCities(this, otherCiv)
|
fun receiveGoldGift(donorCiv: CivilizationInfo, giftAmount: Int) {
|
||||||
val city = cities.city1
|
cityStateFunctions.receiveGoldGift(donorCiv, giftAmount)
|
||||||
val militaryUnit = city.cityConstructions.getConstructableUnits()
|
|
||||||
.filter { !it.isCivilian() && it.isLandUnit() && it.uniqueTo==null }
|
|
||||||
.toList().random()
|
|
||||||
// placing the unit may fail - in that case stay quiet
|
|
||||||
val placedUnit = placeUnitNearTile(city.location, militaryUnit.name) ?: return
|
|
||||||
|
|
||||||
// The unit should have bonuses from Barracks, Alhambra etc as if it was built in the CS capital
|
|
||||||
militaryUnit.addConstructionBonuses(placedUnit, otherCiv.getCapital().cityConstructions)
|
|
||||||
|
|
||||||
// Siam gets +10 XP for all CS units
|
|
||||||
for (unique in getMatchingUniques("Military Units gifted from City-States start with [] XP")) {
|
|
||||||
placedUnit.promotions.XP += unique.params[0].toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Point to the places mentioned in the message _in that order_ (debatable)
|
|
||||||
val placedLocation = placedUnit.getTile().position
|
|
||||||
val locations = LocationAction(listOf(placedLocation, cities.city2.location, city.location))
|
|
||||||
addNotification("[${otherCiv.civName}] gave us a [${militaryUnit.name}] as gift near [${city.name}]!", locations, otherCiv.civName, militaryUnit.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gain a random great person from a random city state */
|
|
||||||
private fun gainGreatPersonFromCityState() {
|
|
||||||
val givingCityState = getKnownCivs().filter { it.isCityState() && it.getAllyCiv() == civName && it.cities.isNotEmpty()}.random()
|
|
||||||
var giftableUnits = gameInfo.ruleSet.units.values.filter { it.isGreatPerson() }
|
|
||||||
if (!gameInfo.hasReligionEnabled()) giftableUnits = giftableUnits.filterNot { it.uniques.contains("Great Person - [Faith]")}
|
|
||||||
if (giftableUnits.isEmpty()) // For badly defined mods that don't have great people but do have the policy that makes city states grant them
|
|
||||||
return
|
|
||||||
val giftedUnit = giftableUnits.random()
|
|
||||||
val cities = NextTurnAutomation.getClosestCities(this, givingCityState)
|
|
||||||
val placedUnit = placeUnitNearTile(cities.city1.location, giftedUnit.name)
|
|
||||||
if (placedUnit == null) return
|
|
||||||
val locations = LocationAction(listOf(placedUnit.getTile().position, cities.city2.location))
|
|
||||||
addNotification( "[${givingCityState.civName}] gave us a [${giftedUnit.name}] as a gift!", locations, givingCityState.civName, giftedUnit.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun turnsForGreatPersonFromCityState(): Int = ((37 + Random().nextInt(7)) * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
fun turnsForGreatPersonFromCityState(): Int = ((37 + Random().nextInt(7)) * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
||||||
|
fun getProtectorCivs() = cityStateFunctions.getProtectorCivs()
|
||||||
|
fun addProtectorCiv(otherCiv: CivilizationInfo) {
|
||||||
|
cityStateFunctions.addProtectorCiv(otherCiv)
|
||||||
|
}
|
||||||
|
fun removeProtectorCiv(otherCiv: CivilizationInfo) {
|
||||||
|
cityStateFunctions.removeProtectorCiv(otherCiv)
|
||||||
|
}
|
||||||
|
fun updateAllyCivForCityState() {
|
||||||
|
cityStateFunctions.updateAllyCivForCityState()
|
||||||
|
}
|
||||||
|
fun getDiplomaticMarriageCost() = cityStateFunctions.getDiplomaticMarriageCost()
|
||||||
|
fun canBeMarriedBy(otherCiv: CivilizationInfo) = cityStateFunctions.canBeMarriedBy(otherCiv)
|
||||||
|
fun diplomaticMarriage(otherCiv: CivilizationInfo) {
|
||||||
|
cityStateFunctions.diplomaticMarriage(otherCiv)
|
||||||
|
}
|
||||||
|
fun getTributeWillingness(demandingCiv: CivilizationInfo, demandingWorker: Boolean = false)
|
||||||
|
= cityStateFunctions.getTributeWillingness(demandingCiv, demandingWorker)
|
||||||
|
fun getTributeModifiers(demandingCiv: CivilizationInfo, demandingWorker: Boolean = false, requireWholeList: Boolean = false)
|
||||||
|
= cityStateFunctions.getTributeModifiers(demandingCiv, demandingWorker, requireWholeList)
|
||||||
|
fun goldGainedByTribute() = cityStateFunctions.goldGainedByTribute()
|
||||||
|
fun tributeGold(demandingCiv: CivilizationInfo) {
|
||||||
|
cityStateFunctions.tributeGold(demandingCiv)
|
||||||
|
}
|
||||||
|
fun tributeWorker(demandingCiv: CivilizationInfo) {
|
||||||
|
cityStateFunctions.tributeWorker(demandingCiv)
|
||||||
|
}
|
||||||
|
fun canGiveStat(statType: Stat) = cityStateFunctions.canGiveStat(statType)
|
||||||
|
|
||||||
fun getAllyCiv() = allyCivName
|
fun getAllyCiv() = allyCivName
|
||||||
|
fun setAllyCiv(newAllyName: String?) { allyCivName = newAllyName }
|
||||||
fun getProtectorCivs() : List<CivilizationInfo> {
|
|
||||||
if(this.isMajorCiv()) return emptyList()
|
|
||||||
return diplomacy.values
|
|
||||||
.filter{ !it.otherCiv().isDefeated() && it.diplomaticStatus == DiplomaticStatus.Protector }
|
|
||||||
.map{ it.otherCiv() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addProtectorCiv(otherCiv: CivilizationInfo) {
|
|
||||||
if (!isCityState() || !otherCiv.isMajorCiv() || otherCiv.isDefeated()) return
|
|
||||||
if (!knows(otherCiv) || isAtWarWith(otherCiv)) return //Exception
|
|
||||||
|
|
||||||
val diplomacy = getDiplomacyManager(otherCiv.civName)
|
|
||||||
diplomacy.diplomaticStatus = DiplomaticStatus.Protector
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeProtectorCiv(otherCiv: CivilizationInfo) {
|
|
||||||
if (!isCityState() || !otherCiv.isMajorCiv() || otherCiv.isDefeated()) return
|
|
||||||
if (!knows(otherCiv) || isAtWarWith(otherCiv)) return //Exception
|
|
||||||
|
|
||||||
val diplomacy = getDiplomacyManager(otherCiv.civName)
|
|
||||||
diplomacy.diplomaticStatus = DiplomaticStatus.Peace
|
|
||||||
diplomacy.addInfluence(-20f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateAllyCivForCityState() {
|
|
||||||
var newAllyName: String? = null
|
|
||||||
if (!isCityState()) return
|
|
||||||
val maxInfluence = diplomacy
|
|
||||||
.filter { !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() }
|
|
||||||
.maxByOrNull { it.value.influence }
|
|
||||||
if (maxInfluence != null && maxInfluence.value.influence >= 60) {
|
|
||||||
newAllyName = maxInfluence.key
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allyCivName != newAllyName) {
|
|
||||||
val oldAllyName = allyCivName
|
|
||||||
allyCivName = newAllyName
|
|
||||||
|
|
||||||
// If the city-state is captured by a civ, it stops being the ally of the civ it was previously an ally of.
|
|
||||||
// This means that it will NOT HAVE a capital at that time, so if we run getCapital we'll get a crash!
|
|
||||||
val capitalLocation = if (cities.isNotEmpty()) getCapital().location else null
|
|
||||||
|
|
||||||
if (newAllyName != null) {
|
|
||||||
val newAllyCiv = gameInfo.getCivilization(newAllyName)
|
|
||||||
val text = "We have allied with [${civName}]."
|
|
||||||
if (capitalLocation != null) newAllyCiv.addNotification(text, capitalLocation, civName, NotificationIcon.Diplomacy)
|
|
||||||
else newAllyCiv.addNotification(text, civName, NotificationIcon.Diplomacy)
|
|
||||||
newAllyCiv.updateViewableTiles()
|
|
||||||
newAllyCiv.updateDetailedCivResources()
|
|
||||||
for (unique in newAllyCiv.getMatchingUniques("Can spend Gold to annex or puppet a City-State that has been your ally for [] turns."))
|
|
||||||
newAllyCiv.getDiplomacyManager(civName).setFlag(DiplomacyFlags.MarriageCooldown, unique.params[0].toInt())
|
|
||||||
}
|
|
||||||
if (oldAllyName != null) {
|
|
||||||
val oldAllyCiv = gameInfo.getCivilization(oldAllyName)
|
|
||||||
val text = "We have lost alliance with [${civName}]."
|
|
||||||
if (capitalLocation != null) oldAllyCiv.addNotification(text, capitalLocation, civName, NotificationIcon.Diplomacy)
|
|
||||||
else oldAllyCiv.addNotification(text, civName, NotificationIcon.Diplomacy)
|
|
||||||
oldAllyCiv.updateViewableTiles()
|
|
||||||
oldAllyCiv.updateDetailedCivResources()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDiplomaticMarriageCost(): Int {
|
|
||||||
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvMinorCivAI.cpp, line 7812
|
|
||||||
var cost = (500 * gameInfo.gameParameters.gameSpeed.modifier).toInt()
|
|
||||||
// Plus disband value of all units
|
|
||||||
for (unit in units) {
|
|
||||||
cost += unit.baseUnit.getDisbandGold(this)
|
|
||||||
}
|
|
||||||
// Round to lower multiple of 5
|
|
||||||
cost /= 5
|
|
||||||
cost *= 5
|
|
||||||
|
|
||||||
return cost
|
|
||||||
}
|
|
||||||
|
|
||||||
fun canBeMarriedBy(otherCiv: CivilizationInfo): Boolean {
|
|
||||||
return (!isDefeated()
|
|
||||||
&& isCityState()
|
|
||||||
&& getDiplomacyManager(otherCiv).relationshipLevel() == RelationshipLevel.Ally
|
|
||||||
&& !otherCiv.getDiplomacyManager(this).hasFlag(DiplomacyFlags.MarriageCooldown)
|
|
||||||
&& otherCiv.getMatchingUniques("Can spend Gold to annex or puppet a City-State that has been your ally for [] turns.").any()
|
|
||||||
&& otherCiv.gold >= getDiplomaticMarriageCost())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun diplomaticMarriage(otherCiv: CivilizationInfo) {
|
|
||||||
if (!canBeMarriedBy(otherCiv)) // Just in case
|
|
||||||
return
|
|
||||||
|
|
||||||
otherCiv.gold -= getDiplomaticMarriageCost()
|
|
||||||
otherCiv.addNotification("We have married into the ruling family of [${civName}], bringing them under our control.",
|
|
||||||
getCapital().location, civName, NotificationIcon.Diplomacy, otherCiv.civName)
|
|
||||||
for (civ in gameInfo.civilizations.filter { it != otherCiv })
|
|
||||||
civ.addNotification("[${otherCiv.civName}] has married into the ruling family of [${civName}], bringing them under their control.",
|
|
||||||
getCapital().location, civName, NotificationIcon.Diplomacy, otherCiv.civName)
|
|
||||||
for (unit in units)
|
|
||||||
unit.gift(otherCiv)
|
|
||||||
for (city in cities) {
|
|
||||||
city.moveToCiv(otherCiv)
|
|
||||||
city.isPuppet = true // Human players get a popup that allows them to annex instead
|
|
||||||
city.foundingCiv = "" // This is no longer a city-state
|
|
||||||
}
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
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).addInfluence(-15f)
|
|
||||||
cityState.addFlag(CivFlags.RecentlyBullied.name, 20)
|
|
||||||
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).addInfluence(-50f)
|
|
||||||
cityState.addFlag(CivFlags.RecentlyBullied.name, 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun canGiveStat(statType: Stat): Boolean {
|
|
||||||
if (!isCityState())
|
|
||||||
return false
|
|
||||||
val eraInfo = getEraObject()
|
|
||||||
val allyBonuses = if (eraInfo == null) null
|
|
||||||
else eraInfo.allyBonus[cityStateType.name]
|
|
||||||
if (allyBonuses != null) {
|
|
||||||
// Defined city states in json
|
|
||||||
val bonuses = allyBonuses + 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
|
//endregion
|
||||||
}
|
}
|
||||||
|
@ -464,7 +464,7 @@ class DiplomacyManager() {
|
|||||||
if (civInfo.cities.isEmpty() || otherCiv().cities.isEmpty())
|
if (civInfo.cities.isEmpty() || otherCiv().cities.isEmpty())
|
||||||
continue@loop
|
continue@loop
|
||||||
else
|
else
|
||||||
civInfo.gainMilitaryUnitFromCityState(otherCiv())
|
otherCiv().giveMilitaryUnitToPatron(civInfo)
|
||||||
}
|
}
|
||||||
DiplomacyFlags.AgreedToNotSettleNearUs.name -> {
|
DiplomacyFlags.AgreedToNotSettleNearUs.name -> {
|
||||||
addModifier(DiplomaticModifiers.FulfilledPromiseToNotSettleCitiesNearUs, 10f)
|
addModifier(DiplomaticModifiers.FulfilledPromiseToNotSettleCitiesNearUs, 10f)
|
||||||
|
@ -366,11 +366,11 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
|||||||
diplomacyTable.addSeparator()
|
diplomacyTable.addSeparator()
|
||||||
|
|
||||||
for (giftAmount in listOf(250, 500, 1000)) {
|
for (giftAmount in listOf(250, 500, 1000)) {
|
||||||
val influenceAmount = viewingCiv.influenceGainedByGift(giftAmount)
|
val influenceAmount = otherCiv.influenceGainedByGift(viewingCiv, giftAmount)
|
||||||
val giftButton =
|
val giftButton =
|
||||||
"Gift [$giftAmount] gold (+[$influenceAmount] influence)".toTextButton()
|
"Gift [$giftAmount] gold (+[$influenceAmount] influence)".toTextButton()
|
||||||
giftButton.onClick {
|
giftButton.onClick {
|
||||||
viewingCiv.giveGoldGift(otherCiv, giftAmount)
|
otherCiv.receiveGoldGift(viewingCiv, giftAmount)
|
||||||
updateLeftSideTable()
|
updateLeftSideTable()
|
||||||
updateRightSide(otherCiv)
|
updateRightSide(otherCiv)
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
|||||||
|
|
||||||
val demandGoldButton = "Take [${otherCiv.goldGainedByTribute()}] gold (-15 Influence)".toTextButton()
|
val demandGoldButton = "Take [${otherCiv.goldGainedByTribute()}] gold (-15 Influence)".toTextButton()
|
||||||
demandGoldButton.onClick {
|
demandGoldButton.onClick {
|
||||||
viewingCiv.demandGold(otherCiv)
|
otherCiv.tributeGold(viewingCiv)
|
||||||
rightSideTable.clear()
|
rightSideTable.clear()
|
||||||
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
|
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
|
||||||
}
|
}
|
||||||
@ -456,7 +456,7 @@ class DiplomacyScreen(val viewingCiv:CivilizationInfo):CameraStageBaseScreen() {
|
|||||||
|
|
||||||
val demandWorkerButton = "Take worker (-50 Influence)".toTextButton()
|
val demandWorkerButton = "Take worker (-50 Influence)".toTextButton()
|
||||||
demandWorkerButton.onClick {
|
demandWorkerButton.onClick {
|
||||||
viewingCiv.demandWorker(otherCiv)
|
otherCiv.tributeWorker(viewingCiv)
|
||||||
rightSideTable.clear()
|
rightSideTable.clear()
|
||||||
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
|
rightSideTable.add(ScrollPane(getCityStateDiplomacyTable(otherCiv)))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user