Removed the civ introduction trade option (#4888)

* Removed the civ introduction trade option

Left in the minimum amount of code to ensure that saves with a pending
introduction trade do not break (any such pending trades are cancelled).
The left-in introduction trade code can be fully removed in a later
version.

* Added back civ introduction trades as a mod option

Also made the gold value of introducation trades moddable.

* Made civ introduction trade invalid if receiver already knows the civ

This prevents players from sending two AI civs introduction trades for
each other in the same turn and profiting from both. This therefore
fixes #4272.

* Applied requested changes

* Removed a spurious space
This commit is contained in:
Arthur van der Staaij 2021-08-19 08:45:03 +02:00 committed by GitHub
parent 4114296cbe
commit ef5483e9d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 29 deletions

View File

@ -6,6 +6,8 @@ import com.unciv.logic.automation.ThreatLevel
import com.unciv.logic.city.CityInfo import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.ruleset.ModOptionsConstants
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import kotlin.math.min import kotlin.math.min
import kotlin.math.sqrt import kotlin.math.sqrt
@ -21,32 +23,32 @@ class TradeEvaluation {
return false return false
for (offer in trade.ourOffers) for (offer in trade.ourOffers)
if (!isOfferValid(offer, offerer)) if (!isOfferValid(offer, offerer, tradePartner))
return false return false
for (offer in trade.theirOffers) for (offer in trade.theirOffers)
if (!isOfferValid(offer, tradePartner)) if (!isOfferValid(offer, tradePartner, offerer))
return false return false
return true return true
} }
private fun isOfferValid(tradeOffer: TradeOffer, offerer: CivilizationInfo): Boolean { private fun isOfferValid(tradeOffer: TradeOffer, offerer: CivilizationInfo, tradePartner: CivilizationInfo): Boolean {
fun hasResource(tradeOffer: TradeOffer): Boolean { fun hasResource(tradeOffer: TradeOffer): Boolean {
val resourcesByName = offerer.getCivResourcesByName() val resourcesByName = offerer.getCivResourcesByName()
return resourcesByName.containsKey(tradeOffer.name) && resourcesByName[tradeOffer.name]!! >= 0 return resourcesByName.containsKey(tradeOffer.name) && resourcesByName[tradeOffer.name]!! >= 0
} }
when (tradeOffer.type) { return when (tradeOffer.type) {
TradeType.Gold -> return true // even if they go negative it's okay TradeType.Gold -> true // even if they go negative it's okay
TradeType.Gold_Per_Turn -> return true // even if they go negative it's okay TradeType.Gold_Per_Turn -> true // even if they go negative it's okay
TradeType.Treaty -> return true TradeType.Treaty -> true
TradeType.Agreement -> return true TradeType.Agreement -> true
TradeType.Luxury_Resource -> return hasResource(tradeOffer) TradeType.Luxury_Resource -> hasResource(tradeOffer)
TradeType.Strategic_Resource -> return hasResource(tradeOffer) TradeType.Strategic_Resource -> hasResource(tradeOffer)
TradeType.Technology -> return true TradeType.Technology -> true
TradeType.Introduction -> return true TradeType.Introduction -> !tradePartner.knows(tradeOffer.name) // You can't introduce them to someone they already know!
TradeType.WarDeclaration -> return true TradeType.WarDeclaration -> true
TradeType.City -> return offerer.cities.any { it.id == tradeOffer.name } TradeType.City -> offerer.cities.any { it.id == tradeOffer.name }
} }
} }
@ -68,7 +70,7 @@ class TradeEvaluation {
return sumOfOurOffers <= sumOfTheirOffers return sumOfOurOffers <= sumOfTheirOffers
} }
fun evaluateBuyCost(offer: TradeOffer, civInfo: CivilizationInfo, tradePartner: CivilizationInfo): Int { private fun evaluateBuyCost(offer: TradeOffer, civInfo: CivilizationInfo, tradePartner: CivilizationInfo): Int {
when (offer.type) { when (offer.type) {
TradeType.Gold -> return offer.amount TradeType.Gold -> return offer.amount
TradeType.Gold_Per_Turn -> return offer.amount * offer.duration TradeType.Gold_Per_Turn -> return offer.amount * offer.duration
@ -131,7 +133,7 @@ class TradeEvaluation {
TradeType.Technology -> TradeType.Technology ->
return (sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble()) return (sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble())
* civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() * 20 * civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() * 20
TradeType.Introduction -> return 250 TradeType.Introduction -> return introductionValue(civInfo.gameInfo.ruleSet)
TradeType.WarDeclaration -> { TradeType.WarDeclaration -> {
val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name) val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name)
val threatToThem = Automation.threatAssessment(civInfo, civToDeclareWarOn) val threatToThem = Automation.threatAssessment(civInfo, civToDeclareWarOn)
@ -160,7 +162,7 @@ class TradeEvaluation {
} }
} }
} }
fun surroundedByOurCities(city: CityInfo, civInfo: CivilizationInfo): Int{ private fun surroundedByOurCities(city: CityInfo, civInfo: CivilizationInfo): Int{
val borderingCivs: List<String> = city.getNeighbouringCivs() val borderingCivs: List<String> = city.getNeighbouringCivs()
if (borderingCivs.size == 1 && borderingCivs.contains(civInfo.civName)){ if (borderingCivs.size == 1 && borderingCivs.contains(civInfo.civName)){
return 10*civInfo.getEraNumber() // if the city is surrounded only by trading civ return 10*civInfo.getEraNumber() // if the city is surrounded only by trading civ
@ -171,7 +173,7 @@ class TradeEvaluation {
} }
fun evaluateSellCost(offer: TradeOffer, civInfo: CivilizationInfo, tradePartner: CivilizationInfo): Int { private fun evaluateSellCost(offer: TradeOffer, civInfo: CivilizationInfo, tradePartner: CivilizationInfo): Int {
when (offer.type) { when (offer.type) {
TradeType.Gold -> return offer.amount TradeType.Gold -> return offer.amount
TradeType.Gold_Per_Turn -> return offer.amount * offer.duration TradeType.Gold_Per_Turn -> return offer.amount * offer.duration
@ -213,7 +215,7 @@ class TradeEvaluation {
return totalCost return totalCost
} }
TradeType.Technology -> return sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble()).toInt() * 20 TradeType.Technology -> return sqrt(civInfo.gameInfo.ruleSet.technologies[offer.name]!!.cost.toDouble()).toInt() * 20
TradeType.Introduction -> return 250 TradeType.Introduction -> return introductionValue(civInfo.gameInfo.ruleSet)
TradeType.WarDeclaration -> { TradeType.WarDeclaration -> {
val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name) val civToDeclareWarOn = civInfo.gameInfo.getCivilization(offer.name)
val threatToUs = Automation.threatAssessment(civInfo, civToDeclareWarOn) val threatToUs = Automation.threatAssessment(civInfo, civToDeclareWarOn)
@ -274,4 +276,9 @@ class TradeEvaluation {
} }
} }
private fun introductionValue(ruleSet: Ruleset): Int {
val unique = ruleSet.modOptions.uniqueObjects.firstOrNull{ it.placeholderText == ModOptionsConstants.tradeCivIntroductions }
if (unique == null) return 0
return unique.params[0].toInt()
}
} }

View File

@ -13,7 +13,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
val theirAvailableOffers = getAvailableOffers(otherCivilization, ourCivilization) val theirAvailableOffers = getAvailableOffers(otherCivilization, ourCivilization)
val currentTrade = Trade() val currentTrade = Trade()
fun getAvailableOffers(civInfo: CivilizationInfo, otherCivilization: CivilizationInfo): TradeOffersList { private fun getAvailableOffers(civInfo: CivilizationInfo, otherCivilization: CivilizationInfo): TradeOffersList {
val offers = TradeOffersList() val offers = TradeOffersList()
if (civInfo.isCityState() && otherCivilization.isCityState()) return offers if (civInfo.isCityState() && otherCivilization.isCityState()) return offers
if (civInfo.isAtWarWith(otherCivilization)) if (civInfo.isAtWarWith(otherCivilization))
@ -43,12 +43,14 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
} }
val otherCivsWeKnow = civInfo.getKnownCivs() val otherCivsWeKnow = civInfo.getKnownCivs()
.filter { it.civName != otherCivilization.civName && it.isMajorCiv() && !it.isDefeated() } .filter { it.civName != otherCivilization.civName && it.isMajorCiv() && !it.isDefeated() }
val civsWeKnowAndTheyDont = otherCivsWeKnow
.filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() }
for (thirdCiv in civsWeKnowAndTheyDont) { if (civInfo.gameInfo.ruleSet.modOptions.uniqueObjects.any{ it.placeholderText == ModOptionsConstants.tradeCivIntroductions }) {
offers.add(TradeOffer(thirdCiv.civName, TradeType.Introduction)) val civsWeKnowAndTheyDont = otherCivsWeKnow
.filter { !otherCivilization.diplomacy.containsKey(it.civName) && !it.isDefeated() }
for (thirdCiv in civsWeKnowAndTheyDont) {
offers.add(TradeOffer(thirdCiv.civName, TradeType.Introduction))
}
} }
if (!civInfo.isCityState() && !otherCivilization.isCityState() if (!civInfo.isCityState() && !otherCivilization.isCityState()
@ -106,7 +108,6 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
} }
if (offer.type == TradeType.Introduction) if (offer.type == TradeType.Introduction)
to.makeCivilizationsMeet(to.gameInfo.getCivilization(offer.name)) to.makeCivilizationsMeet(to.gameInfo.getCivilization(offer.name))
if (offer.type == TradeType.WarDeclaration) { if (offer.type == TradeType.WarDeclaration) {
val nameOfCivToDeclareWarOn = offer.name val nameOfCivToDeclareWarOn = offer.name
from.getDiplomacyManager(nameOfCivToDeclareWarOn).declareWar() from.getDiplomacyManager(nameOfCivToDeclareWarOn).declareWar()

View File

@ -25,9 +25,10 @@ object ModOptionsConstants {
const val diplomaticRelationshipsCannotChange = "Diplomatic relationships cannot change" const val diplomaticRelationshipsCannotChange = "Diplomatic relationships cannot change"
const val convertGoldToScience = "Can convert gold to science with sliders" const val convertGoldToScience = "Can convert gold to science with sliders"
const val allowCityStatesSpawnUnits = "Allow City States to spawn with additional units" const val allowCityStatesSpawnUnits = "Allow City States to spawn with additional units"
const val tradeCivIntroductions = "Can trade civilization introductions for [] Gold"
} }
class ModOptions { class ModOptions : IHasUniques {
var isBaseRuleset = false var isBaseRuleset = false
var techsToRemove = HashSet<String>() var techsToRemove = HashSet<String>()
var buildingsToRemove = HashSet<String>() var buildingsToRemove = HashSet<String>()
@ -41,7 +42,10 @@ class ModOptions {
var modSize = 0 var modSize = 0
val maxXPfromBarbarians = 30 val maxXPfromBarbarians = 30
var uniques = HashSet<String>() // No reason for now to use IHasUniques here, in that case needs to change to ArrayList
override var uniques = ArrayList<String>()
// If this is delegated with "by lazy", the mod download process crashes and burns
override var uniqueObjects: List<Unique> = listOf()
} }
class Ruleset { class Ruleset {
@ -145,6 +149,7 @@ class Ruleset {
try { try {
modOptions = jsonParser.getFromJson(ModOptions::class.java, modOptionsFile) modOptions = jsonParser.getFromJson(ModOptions::class.java, modOptionsFile)
} catch (ex: Exception) {} } catch (ex: Exception) {}
modOptions.uniqueObjects = modOptions.uniques.map { Unique(it) }
} }
val techFile = folderHandle.child("Techs.json") val techFile = folderHandle.child("Techs.json")

View File

@ -29,7 +29,7 @@ class OffersListScroll(val onOfferClicked: (TradeOffer) -> Unit) : ScrollPane(nu
for (offerType in values()) { for (offerType in values()) {
val labelName = when(offerType){ val labelName = when(offerType){
Gold, Gold_Per_Turn, Treaty,Agreement,Introduction -> "" Gold, Gold_Per_Turn, Treaty, Agreement, Introduction -> ""
Luxury_Resource -> "Luxury resources" Luxury_Resource -> "Luxury resources"
Strategic_Resource -> "Strategic resources" Strategic_Resource -> "Strategic resources"
Technology -> "Technologies" Technology -> "Technologies"