Implemented "Don't settle cities near us", which goes both ways - a good basis for further demands (#831)

This commit is contained in:
Yair Morgenstern
2019-06-07 13:04:18 +03:00
parent 96cb7a5646
commit f713d266db
8 changed files with 132 additions and 27 deletions

View File

@ -126,7 +126,7 @@
Portuguese:"Dessa vez não." Portuguese:"Dessa vez não."
} }
"Excellent!":{ // AI statement after we accept a trade they proposed, and when we meet a city state "Excellent!":{ // AI statement after we accept a trade they proposed, and when we meet a city state, and our response when they agree to our diplomatic demand
Italian:"Eccellente!" Italian:"Eccellente!"
Simplified_Chinese:"好极了!和和气气才是生财之道。" Simplified_Chinese:"好极了!和和气气才是生财之道。"
French:"Excellent!" French:"Excellent!"
@ -358,8 +358,18 @@
French:"Vous avez dénoncé nos ennemies" French:"Vous avez dénoncé nos ennemies"
} }
// Demands
"Demands":{}
"Please don't settle new cities near us.":{}
"Very well, we shall look for new lands to settle.":{}
"We shall do as we please.":{}
"You betrayed your promise to not settle cities near us":{} "You betrayed your promise to not settle cities near us":{}
"You refused to stop settling cities near us":{}
"We noticed your new city near our borders, despite your promise. This will have....implications.":{}
////// Trade ////// Trade

View File

@ -147,7 +147,8 @@ class GameInfo {
for (civ in civilizations.filter { !it.isBarbarianCivilization() && !it.isDefeated() }) { for (civ in civilizations.filter { !it.isBarbarianCivilization() && !it.isDefeated() }) {
allResearchedTechs.retainAll(civ.tech.techsResearched) allResearchedTechs.retainAll(civ.tech.techsResearched)
} }
val unitList = GameBasics.Units.values.filter { !it.unitType.isCivilian() && it.uniqueTo == null } val unitList = GameBasics.Units.values
.filter { !it.unitType.isCivilian() && it.uniqueTo == null }
.filter{ allResearchedTechs.contains(it.requiredTech) .filter{ allResearchedTechs.contains(it.requiredTech)
&& (it.obsoleteTech == null || !allResearchedTechs.contains(it.obsoleteTech!!)) } && (it.obsoleteTech == null || !allResearchedTechs.contains(it.obsoleteTech!!)) }

View File

@ -1,7 +1,6 @@
package com.unciv.logic.automation package com.unciv.logic.automation
import com.unciv.Constants import com.unciv.Constants
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.* import com.unciv.logic.civilization.*
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
@ -22,6 +21,7 @@ class NextTurnAutomation{
offerPeaceTreaty(civInfo) offerPeaceTreaty(civInfo)
exchangeTechs(civInfo) exchangeTechs(civInfo)
exchangeLuxuries(civInfo) exchangeLuxuries(civInfo)
issueRequests(civInfo)
} }
chooseTechToResearch(civInfo) chooseTechToResearch(civInfo)
@ -36,6 +36,7 @@ class NextTurnAutomation{
civInfo.popupAlerts.clear() // AIs don't care about popups. civInfo.popupAlerts.clear() // AIs don't care about popups.
} }
private fun buyBuildingOrUnit(civInfo: CivilizationInfo) { private fun buyBuildingOrUnit(civInfo: CivilizationInfo) {
//allow AI spending money to purchase building & unit //allow AI spending money to purchase building & unit
for (city in civInfo.cities.sortedByDescending{ it.population.population }) { for (city in civInfo.cities.sortedByDescending{ it.population.population }) {
@ -349,17 +350,30 @@ class NextTurnAutomation{
} }
fun onCitySettledNearBorders(civInfo: CivilizationInfo, newCity: CityInfo){ private fun issueRequests(civInfo: CivilizationInfo) {
val diplomacyManager = civInfo.getDiplomacyManager(newCity.civInfo) for(otherCiv in civInfo.getKnownCivs().filter { it.isMajorCiv() }){
if(diplomacyManager.hasFlag(DiplomacyFlags.IgnoreThemSettlingNearUs)) return val diploManager = civInfo.getDiplomacyManager(otherCiv)
if(diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSettleNearUs)){ if(diploManager.hasFlag(DiplomacyFlags.SettledCitiesNearUs))
newCity.civInfo.popupAlerts.add(PopupAlert(AlertType.CitySettledNearOtherCivDespiteOurPromise, civInfo.civName)) onCitySettledNearBorders(civInfo,otherCiv)
diplomacyManager.setFlag(DiplomacyFlags.IgnoreThemSettlingNearUs,200)
diplomacyManager.setModifier(DiplomaticModifiers.BetrayedPromiseToNotSettleCitiesNearUs,-20f)
}
else{
newCity.civInfo.popupAlerts.add(PopupAlert(AlertType.CitySettledNearOtherCiv, civInfo.civName))
} }
} }
fun onCitySettledNearBorders(civInfo: CivilizationInfo, otherCiv:CivilizationInfo){
val diplomacyManager = civInfo.getDiplomacyManager(otherCiv)
when {
diplomacyManager.hasFlag(DiplomacyFlags.IgnoreThemSettlingNearUs) -> {}
diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSettleNearUs) -> {
otherCiv.popupAlerts.add(PopupAlert(AlertType.CitySettledNearOtherCivDespiteOurPromise, civInfo.civName))
diplomacyManager.setFlag(DiplomacyFlags.IgnoreThemSettlingNearUs,200)
diplomacyManager.setModifier(DiplomaticModifiers.BetrayedPromiseToNotSettleCitiesNearUs,-20f)
}
else -> {
val threatLevel = Automation().threatAssessment(civInfo,otherCiv)
if(threatLevel<ThreatLevel.High) // don't piss them off for no reason please.
otherCiv.popupAlerts.add(PopupAlert(AlertType.CitySettledNearOtherCiv, civInfo.civName))
}
}
diplomacyManager.removeFlag(DiplomacyFlags.SettledCitiesNearUs)
}
} }

View File

@ -3,6 +3,7 @@ package com.unciv.logic.automation
import com.unciv.UnCivGame import com.unciv.UnCivGame
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.GreatPersonManager import com.unciv.logic.civilization.GreatPersonManager
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
@ -81,7 +82,15 @@ class SpecificUnitAutomation{
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit
val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities } val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities }
.flatMap { it.getCenterTile().getTilesInDistance(3) }.toHashSet() .flatMap {
val distanceAwayFromCity =
if (it.civInfo.knows(unit.civInfo.civName)
&& it.civInfo.getDiplomacyManager(unit.civInfo).hasFlag(DiplomacyFlags.WeAgreedNotToSettleNearThem))
6
else 3
it.getCenterTile().getTilesInDistance(distanceAwayFromCity)
}
.toHashSet()
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once. // This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
val nearbyTileRankings = unit.getTile().getTilesInDistance(7) val nearbyTileRankings = unit.getTile().getTilesInDistance(7)

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.unciv.Constants import com.unciv.Constants
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap import com.unciv.logic.map.TileMap
@ -39,7 +40,7 @@ class CityInfo {
var hasSoldBuildingThisTurn = false var hasSoldBuildingThisTurn = false
constructor() // for json parsing, we need to have a default constructor constructor() // for json parsing, we need to have a default constructor
constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { // new city!
this.civInfo = civInfo this.civInfo = civInfo
this.location = cityLocation this.location = cityLocation
setTransients() setTransients()
@ -78,6 +79,15 @@ class CityInfo {
workedTiles = hashSetOf() //reassign 1st working tile workedTiles = hashSetOf() //reassign 1st working tile
population.autoAssignPopulation() population.autoAssignPopulation()
cityStats.update() cityStats.update()
val citiesWithin6Tiles = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() && it!=civInfo }
.flatMap { it.cities }
.filter { it.getCenterTile().arialDistanceTo(getCenterTile()) <= 6 }
val civsWithCloseCities = citiesWithin6Tiles.map { it.civInfo }.distinct()
.filter { it.exploredTiles.contains(location) }
for(otherCiv in civsWithCloseCities)
otherCiv.getDiplomacyManager(civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs,30)
} }
//region pure functions //region pure functions

View File

@ -29,7 +29,9 @@ enum class DiplomacyFlags{
DeclarationOfFriendship, DeclarationOfFriendship,
Denunceation, Denunceation,
BorderConflict, BorderConflict,
SettledCitiesNearUs,
AgreedToNotSettleNearUs, AgreedToNotSettleNearUs,
WeAgreedNotToSettleNearThem,
IgnoreThemSettlingNearUs IgnoreThemSettlingNearUs
} }
@ -41,6 +43,7 @@ enum class DiplomaticModifiers{
BetrayedDeclarationOfFriendship, BetrayedDeclarationOfFriendship,
Denunciation, Denunciation,
DenouncedOurAllies, DenouncedOurAllies,
RefusedToNotSettleCitiesNearUs,
BetrayedPromiseToNotSettleCitiesNearUs, BetrayedPromiseToNotSettleCitiesNearUs,
YearsOfPeace, YearsOfPeace,
@ -48,7 +51,8 @@ enum class DiplomaticModifiers{
DeclarationOfFriendship, DeclarationOfFriendship,
DeclaredFriendshipWithOurAllies, DeclaredFriendshipWithOurAllies,
DenouncedOurEnemies, DenouncedOurEnemies,
OpenBorders OpenBorders,
FulfilledPromiseToNotSettleCitiesNearUs
} }
class DiplomacyManager() { class DiplomacyManager() {
@ -241,7 +245,10 @@ class DiplomacyManager() {
revertToZero(DiplomaticModifiers.DeclaredWarOnUs,1/8f) // this disappears real slow - it'll take 160 turns to really forget, this is war declaration we're talking about revertToZero(DiplomaticModifiers.DeclaredWarOnUs,1/8f) // this disappears real slow - it'll take 160 turns to really forget, this is war declaration we're talking about
revertToZero(DiplomaticModifiers.WarMongerer,1/2f) // warmongering gives a big negative boost when it happens but they're forgotten relatively quickly, like WWII amirite revertToZero(DiplomaticModifiers.WarMongerer,1/2f) // warmongering gives a big negative boost when it happens but they're forgotten relatively quickly, like WWII amirite
revertToZero(DiplomaticModifiers.CapturedOurCities,1/4f) // if you captured our cities, though, that's harder to forget revertToZero(DiplomaticModifiers.CapturedOurCities,1/4f) // if you captured our cities, though, that's harder to forget
revertToZero(DiplomaticModifiers.BetrayedDeclarationOfFriendship,1/2f) // if you captured our cities, though, that's harder to forget revertToZero(DiplomaticModifiers.BetrayedDeclarationOfFriendship,1/8f) // That's a bastardly thing to do
revertToZero(DiplomaticModifiers.RefusedToNotSettleCitiesNearUs,1/4f)
revertToZero(DiplomaticModifiers.BetrayedPromiseToNotSettleCitiesNearUs,1/8f) // That's a bastardly thing to do
if(!hasFlag(DiplomacyFlags.DeclarationOfFriendship)) if(!hasFlag(DiplomacyFlags.DeclarationOfFriendship))
revertToZero(DiplomaticModifiers.DeclarationOfFriendship, 1/2f) //decreases slowly and will revert to full if it is declared later revertToZero(DiplomaticModifiers.DeclarationOfFriendship, 1/2f) //decreases slowly and will revert to full if it is declared later
@ -249,6 +256,8 @@ class DiplomacyManager() {
flagsCountdown[flag] = flagsCountdown[flag]!! - 1 flagsCountdown[flag] = flagsCountdown[flag]!! - 1
if(flagsCountdown[flag]==0) { if(flagsCountdown[flag]==0) {
flagsCountdown.remove(flag) flagsCountdown.remove(flag)
if(flag==DiplomacyFlags.AgreedToNotSettleNearUs.name)
addModifier(DiplomaticModifiers.FulfilledPromiseToNotSettleCitiesNearUs,10f)
} }
} }

View File

@ -7,6 +7,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UnCivGame import com.unciv.UnCivGame
import com.unciv.logic.automation.Automation
import com.unciv.logic.automation.ThreatLevel
import com.unciv.logic.civilization.CityStateType import com.unciv.logic.civilization.CityStateType
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
@ -78,10 +80,10 @@ class DiplomacyScreen:CameraStageBaseScreen() {
} }
} }
fun updateRightSide(civ: CivilizationInfo){ fun updateRightSide(otherCiv: CivilizationInfo){
rightSideTable.clear() rightSideTable.clear()
if(civ.isCityState()) rightSideTable.add(getCityStateDiplomacyTable(civ)) if(otherCiv.isCityState()) rightSideTable.add(getCityStateDiplomacyTable(otherCiv))
else rightSideTable.add(getMajorCivDiplomacyTable(civ)) else rightSideTable.add(getMajorCivDiplomacyTable(otherCiv))
} }
fun setTrade(civ: CivilizationInfo): TradeTable { fun setTrade(civ: CivilizationInfo): TradeTable {
@ -209,7 +211,7 @@ class DiplomacyScreen:CameraStageBaseScreen() {
val declareFriendshipButton = TextButton("Declare Friendship ([30] turns)".tr(),skin) val declareFriendshipButton = TextButton("Declare Friendship ([30] turns)".tr(),skin)
declareFriendshipButton.onClick { declareFriendshipButton.onClick {
diplomacyManager.signDeclarationOfFriendship() diplomacyManager.signDeclarationOfFriendship()
setRightSideFlavorText(otherCiv,"May our nations forever remain united!".tr(),"Indeed!".tr()) setRightSideFlavorText(otherCiv,"May our nations forever remain united!","Indeed!")
} }
diplomacyTable.add(declareFriendshipButton).row() diplomacyTable.add(declareFriendshipButton).row()
} }
@ -219,7 +221,7 @@ class DiplomacyScreen:CameraStageBaseScreen() {
val denounceButton = TextButton("Denounce ([30] turns)".tr(),skin) val denounceButton = TextButton("Denounce ([30] turns)".tr(),skin)
denounceButton.onClick { denounceButton.onClick {
diplomacyManager.denounce() diplomacyManager.denounce()
setRightSideFlavorText(otherCiv,"We will remember this.".tr(),"Very well.".tr()) setRightSideFlavorText(otherCiv,"We will remember this.","Very well.")
} }
diplomacyTable.add(denounceButton).row() diplomacyTable.add(denounceButton).row()
} }
@ -228,6 +230,12 @@ class DiplomacyScreen:CameraStageBaseScreen() {
diplomacyTable.add(declareWarButton).row() diplomacyTable.add(declareWarButton).row()
} }
val demandsButton = TextButton("Demands".tr(),skin)
demandsButton.onClick {
rightSideTable.clear();
rightSideTable.add(getDemandsTable(currentPlayerCiv,otherCiv))
}
diplomacyTable.add(demandsButton).row()
diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row() diplomacyTable.add(getRelationshipTable(otherCivDiplomacyManager)).row()
@ -248,6 +256,8 @@ class DiplomacyScreen:CameraStageBaseScreen() {
DenouncedOurAllies -> "You have denounced our allies" DenouncedOurAllies -> "You have denounced our allies"
DenouncedOurEnemies -> "You have denounced our enemies" DenouncedOurEnemies -> "You have denounced our enemies"
BetrayedPromiseToNotSettleCitiesNearUs -> "You betrayed your promise to not settle cities near us" BetrayedPromiseToNotSettleCitiesNearUs -> "You betrayed your promise to not settle cities near us"
RefusedToNotSettleCitiesNearUs -> "You refused to stop settling cities near us"
FulfilledPromiseToNotSettleCitiesNearUs -> "You fulfilled your promise to stop settling cities near us!"
} }
text = text.tr() + " " text = text.tr() + " "
if (modifier.value > 0) text += "+" if (modifier.value > 0) text += "+"
@ -260,6 +270,23 @@ class DiplomacyScreen:CameraStageBaseScreen() {
return diplomacyTable return diplomacyTable
} }
private fun getDemandsTable(currentPlayerCiv: CivilizationInfo, otherCiv: CivilizationInfo): Table {
val demandsTable = Table()
demandsTable.defaults().pad(10f)
val dontSettleCitiesButton = TextButton("Please don't settle new cities near us.".tr(),skin)
dontSettleCitiesButton.onClick {
val threatLevel = Automation().threatAssessment(otherCiv,currentPlayerCiv)
if(threatLevel>ThreatLevel.Medium){
setRightSideFlavorText(otherCiv,"Very well, we shall look for new lands to settle.","Excellent!")
otherCiv.getDiplomacyManager(currentPlayerCiv).setFlag(DiplomacyFlags.WeAgreedNotToSettleNearThem,100)
}
else setRightSideFlavorText(otherCiv,"We shall do as we please.","Very well.")
}
demandsTable.add(dontSettleCitiesButton).row()
demandsTable.add(TextButton("Close".tr(),skin).onClick { updateRightSide(otherCiv) })
return demandsTable
}
fun getRelationshipTable(otherCivDiplomacyManager: DiplomacyManager): Table { fun getRelationshipTable(otherCivDiplomacyManager: DiplomacyManager): Table {
val relationshipTable = Table() val relationshipTable = Table()
@ -305,7 +332,7 @@ class DiplomacyScreen:CameraStageBaseScreen() {
diplomacyTable.addSeparator() diplomacyTable.addSeparator()
diplomacyTable.add(flavorText.toLabel()).row() diplomacyTable.add(flavorText.toLabel()).row()
val responseButton = TextButton(response,skin) val responseButton = TextButton(response.tr(),skin)
responseButton.onClick { updateRightSide(otherCiv) } responseButton.onClick { updateRightSide(otherCiv) }
diplomacyTable.add(responseButton) diplomacyTable.add(responseButton)

View File

@ -4,17 +4,24 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.logic.civilization.AlertType import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.models.gamebasics.Nation import com.unciv.models.gamebasics.Nation
import com.unciv.models.gamebasics.tr import com.unciv.models.gamebasics.tr
import com.unciv.ui.utils.addSeparator import com.unciv.ui.utils.addSeparator
import com.unciv.ui.utils.onClick import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toLabel import com.unciv.ui.utils.toLabel
import com.unciv.ui.worldscreen.optionstable.PopupTable import com.unciv.ui.worldscreen.optionstable.PopupTable
import kotlin.random.Random
class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): PopupTable(worldScreen){ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): PopupTable(worldScreen){
fun getCloseButton(text:String): TextButton { fun getCloseButton(text: String, action: (() -> Unit)?=null): TextButton {
val button = TextButton(text.tr(), skin) val button = TextButton(text.tr(), skin)
button.onClick { close(); worldScreen.shouldUpdate=true } button.onClick {
if(action!=null) action()
worldScreen.shouldUpdate=true
close()
}
return button return button
} }
@ -47,7 +54,6 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
val translatedNation = civ.getTranslatedNation() val translatedNation = civ.getTranslatedNation()
if (civ.isCityState()) { if (civ.isCityState()) {
addLeaderName(translatedNation) addLeaderName(translatedNation)
val cityStateType = civ.getCityStateType()
addGoodSizedLabel("We have encountered the City-State of [${translatedNation.getNameTranslation()}]!").row() addGoodSizedLabel("We have encountered the City-State of [${translatedNation.getNameTranslation()}]!").row()
add(getCloseButton("Excellent!")) add(getCloseButton("Excellent!"))
} else { } else {
@ -74,7 +80,26 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
responseTable.add(getCloseButton("Never!")) responseTable.add(getCloseButton("Never!"))
add(responseTable) add(responseTable)
} }
AlertType.CitySettledNearOtherCiv -> {
val otherciv= worldScreen.gameInfo.getCivilization(popupAlert.value)
val otherCivDiploManager = otherciv.getDiplomacyManager(worldScreen.currentPlayerCiv)
val translatedNation = otherciv.getTranslatedNation()
addLeaderName(translatedNation)
addGoodSizedLabel("Please don't settle new cities near us.").row()
add(getCloseButton("Very well, we shall look for new lands to settle."){
otherCivDiploManager.setFlag(DiplomacyFlags.AgreedToNotSettleNearUs,100+ Random.nextInt(-20,20))
}).row()
add(getCloseButton("We shall do as we please.") {
otherCivDiploManager.addModifier(DiplomaticModifiers.RefusedToNotSettleCitiesNearUs,-10f)
}).row()
}
AlertType.CitySettledNearOtherCivDespiteOurPromise -> {
val otherciv= worldScreen.gameInfo.getCivilization(popupAlert.value)
val translatedNation = otherciv.getTranslatedNation()
addLeaderName(translatedNation)
addGoodSizedLabel("We noticed your new city near our borders, despite your promise. This will have....implications.").row()
add(getCloseButton("Very well."))
}
} }
open() open()
isOpen = true isOpen = true