diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 0f10fcf57e..88bd5336b7 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -228,7 +228,12 @@ Please don't spread your religion to us. = Don't spread religion in our cities. = Very well, we shall spread our faith elsewhere. = We noticed you have continued spreading your faith, despite your promise. This will have...consequences. = - + +Stop spying on us. = +We see our people are not welcome in your lands... we will take our attention elsewhere. = +I'll do what's necessary for my empire to survive. = +Take back your spy and your broken promises. = + I've been informed that my armies have taken tribute from [civName], a city-state under your protection.\nI assure you, this was quite unintentional, and I hope that this does not serve to drive us apart. = We asked [civName] for a tribute recently and they gave in.\nYou promised to protect them from such things, but we both know you cannot back that up. = It's come to my attention that I may have attacked [civName].\nWhile it was not my goal to be at odds with your empire, this was deemed a necessary course of action. = @@ -1098,6 +1103,8 @@ One of our trades with [nation] has been cut short = [nation] refused to stop settling cities near us! = [nation] agreed to stop spreading religion to us! = [nation] refused to stop spreading religion to us! = +[nation] agreed to stop spying on us!" = +[nation] refused to stop spying on us! = We have allied with [nation]. = We have lost alliance with [nation]. = We have discovered [naturalWonder]! = diff --git a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt index 70f66ba531..ed690d9947 100644 --- a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt @@ -110,7 +110,6 @@ object NextTurnAutomation { } private fun respondToPopupAlerts(civInfo: Civilization) { for (popupAlert in civInfo.popupAlerts.toList()) { // toList because this can trigger other things that give alerts, like Golden Age - if (popupAlert.type == AlertType.DemandToStopSettlingCitiesNear) { // we're called upon to make a decision val demandingCiv = civInfo.gameInfo.getCivilization(popupAlert.value) val diploManager = civInfo.getDiplomacyManager(demandingCiv)!! @@ -127,6 +126,15 @@ object NextTurnAutomation { diploManager.agreeNotToSpreadReligionTo() else diploManager.refuseNotToSpreadReligionTo() } + + if (popupAlert.type == AlertType.DemandToStopSpyingOnUs) { + val demandingCiv = civInfo.gameInfo.getCivilization(popupAlert.value) + val diploManager = civInfo.getDiplomacyManager(demandingCiv)!! + if (Automation.threatAssessment(civInfo, demandingCiv) >= ThreatLevel.High + || diploManager.isRelationshipLevelGT(RelationshipLevel.Ally)) + diploManager.agreeNotToSpreadSpiesTo() + else diploManager.refuseNotToSpreadSpiesTo() + } if (popupAlert.type == AlertType.DeclarationOfFriendship) { val requestingCiv = civInfo.gameInfo.getCivilization(popupAlert.value) @@ -596,6 +604,8 @@ object NextTurnAutomation { onCitySettledNearBorders(civInfo, otherCiv) if (diploManager.hasFlag(DiplomacyFlags.SpreadReligionInOurCities)) onReligionSpreadInOurCity(civInfo, otherCiv) + if (diploManager.hasFlag(DiplomacyFlags.DiscoveredSpiesInOurCities)) + onSpyDiscoveredInOurCity(civInfo, otherCiv) } } @@ -637,6 +647,26 @@ object NextTurnAutomation { } diplomacyManager.removeFlag(DiplomacyFlags.SpreadReligionInOurCities) } + + private fun onSpyDiscoveredInOurCity(civInfo: Civilization, otherCiv: Civilization) { + val diplomacyManager = civInfo.getDiplomacyManager(otherCiv)!! + when { + diplomacyManager.hasFlag(DiplomacyFlags.IgnoreThemSendingSpies) -> {} + diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSendSpies) -> { + otherCiv.popupAlerts.add(PopupAlert(AlertType.SpyingOnUsDespiteOurPromise, civInfo.civName)) + diplomacyManager.setFlag(DiplomacyFlags.IgnoreThemSendingSpies, 100) + diplomacyManager.setModifier(DiplomaticModifiers.BetrayedPromiseToNotSendingSpiesToUs, -20f) + diplomacyManager.removeFlag(DiplomacyFlags.AgreedToNotSendSpies) + } + 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.DemandToStopSpyingOnUs, civInfo.civName)) + } + } + diplomacyManager.removeFlag(DiplomacyFlags.DiscoveredSpiesInOurCities) + } + fun getMinDistanceBetweenCities(civ1: Civilization, civ2: Civilization): Int { return getClosestCities(civ1, civ2)?.aerialDistance ?: Int.MAX_VALUE diff --git a/core/src/com/unciv/logic/civilization/PopupAlert.kt b/core/src/com/unciv/logic/civilization/PopupAlert.kt index 787d691327..75f7bfe1fc 100644 --- a/core/src/com/unciv/logic/civilization/PopupAlert.kt +++ b/core/src/com/unciv/logic/civilization/PopupAlert.kt @@ -18,6 +18,9 @@ enum class AlertType : IsPartOfGameInfoSerialization { DemandToStopSpreadingReligion, ReligionSpreadDespiteOurPromise, + + DemandToStopSpyingOnUs, + SpyingOnUsDespiteOurPromise, GoldenAge, DeclarationOfFriendship, diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index 17ac1ebd91..17eaf45bff 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -62,6 +62,10 @@ enum class DiplomacyFlags { AgreedToNotSpreadReligion, IgnoreThemSpreadingReligion, + DiscoveredSpiesInOurCities, + AgreedToNotSendSpies, + IgnoreThemSendingSpies, + ProvideMilitaryUnit, MarriageCooldown, NotifiedAfraid, @@ -94,8 +98,11 @@ enum class DiplomaticModifiers(val text: String) { DenouncedOurAllies("You have denounced our allies"), RefusedToNotSettleCitiesNearUs("You refused to stop settling cities near us"), RefusedToNotSpreadReligionToUs("You refused to stop spreading religion to us"), + RefusedToNotSendingSpiesToUs("You refused to stop spying on us"), BetrayedPromiseToNotSettleCitiesNearUs("You betrayed your promise to not settle cities near us"), BetrayedPromiseToNotSpreadReligionToUs("You betrayed your promise to not spread your religion to us"), + BetrayedPromiseToNotSendingSpiesToUs("You betrayed your promise to stop spying on us"), + UnacceptableDemands("Your arrogant demands are in bad taste"), UsedNuclearWeapons("Your use of nuclear weapons is disgusting!"), StealingTerritory("You have stolen our lands!"), @@ -706,6 +713,21 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization { otherCiv().addNotification("[${civInfo.civName}] refused to stop spreading religion to us!", NotificationCategory.Diplomacy, NotificationIcon.Diplomacy, civInfo.civName) } + + fun agreeNotToSpreadSpiesTo() { + otherCivDiplomacy().setFlag(DiplomacyFlags.AgreedToNotSendSpies, 100) + addModifier(DiplomaticModifiers.UnacceptableDemands, -10f) + otherCiv().addNotification("[${civInfo.civName}] agreed to stop spying on us!", + NotificationCategory.Diplomacy, NotificationIcon.Diplomacy, civInfo.civName) + } + + fun refuseNotToSpreadSpiesTo() { + addModifier(DiplomaticModifiers.UnacceptableDemands, -20f) + otherCivDiplomacy().setFlag(DiplomacyFlags.IgnoreThemSendingSpies, 100) + otherCivDiplomacy().addModifier(DiplomaticModifiers.RefusedToNotSendingSpiesToUs, -15f) + otherCiv().addNotification("[${civInfo.civName}] refused to stop spying on us!", + NotificationCategory.Diplomacy, NotificationIcon.Diplomacy, civInfo.civName) + } fun sideWithCityState() { otherCivDiplomacy().setModifier(DiplomaticModifiers.SidedWithProtectedMinor, -5f) diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt index ed3e9c4f0c..23509cdfa7 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt @@ -286,6 +286,7 @@ object DiplomacyTurnManager { revertToZero(DiplomaticModifiers.RefusedToNotSettleCitiesNearUs, 1 / 4f) revertToZero(DiplomaticModifiers.BetrayedPromiseToNotSettleCitiesNearUs, 1 / 8f) // That's a bastardly thing to do revertToZero(DiplomaticModifiers.BetrayedPromiseToNotSpreadReligionToUs, 1 / 8f) + revertToZero(DiplomaticModifiers.BetrayedPromiseToNotSendingSpiesToUs, 1 / 8f) revertToZero(DiplomaticModifiers.UnacceptableDemands, 1 / 4f) revertToZero(DiplomaticModifiers.StealingTerritory, 1 / 4f) revertToZero(DiplomaticModifiers.DenouncedOurAllies, 1 / 4f) diff --git a/core/src/com/unciv/models/Spy.kt b/core/src/com/unciv/models/Spy.kt index 4496e6d439..69a51922e2 100644 --- a/core/src/com/unciv/models/Spy.kt +++ b/core/src/com/unciv/models/Spy.kt @@ -9,6 +9,7 @@ import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.EspionageAction import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationIcon +import com.unciv.logic.civilization.diplomacy.DiplomacyFlags import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers import com.unciv.logic.civilization.managers.EspionageManager import com.unciv.models.ruleset.unique.Unique @@ -226,7 +227,6 @@ class Spy private constructor() : IsPartOfGameInfoSerialization { if (detectionString != null) // Not using Spy.addNotification, shouldn't open the espionage screen otherCiv.addNotification(detectionString, city.location, NotificationCategory.Espionage, NotificationIcon.Spy) - if (spyResult < 200 && stolenTech != null) { civInfo.tech.addTechnology(stolenTech) addNotification("Your spy [$name] stole the Technology [$stolenTech] from [$city]!") @@ -237,12 +237,20 @@ class Spy private constructor() : IsPartOfGameInfoSerialization { addNotification("Your spy [$name] was killed trying to steal Technology in [$city]!") defendingSpy?.levelUpSpy() killSpy() + // if they kill your spy they should know that you are spying on them and able to demande to not be speid on. + demandeToNotBeSpiedOn(otherCiv) } else startStealingTech() // reset progress if (spyResult >= 100) { - otherCiv.getDiplomacyManager(civInfo)?.addModifier(DiplomaticModifiers.SpiedOnUs, -15f) + demandeToNotBeSpiedOn(otherCiv) } } + + private fun demandeToNotBeSpiedOn(otherCiv: Civilization) { + val otherCivDiplomacyManager = otherCiv.getDiplomacyManager(civInfo)!! + otherCivDiplomacyManager.addModifier(DiplomaticModifiers.SpiedOnUs, -15f) + otherCivDiplomacyManager.setFlag(DiplomacyFlags.DiscoveredSpiesInOurCities, 30) + } fun canDoCoup(): Boolean = getCityOrNull() != null && getCity().civ.isCityState && isSetUp() && getCity().civ.getAllyCivName() != civInfo.civName @@ -340,8 +348,30 @@ class Spy private constructor() : IsPartOfGameInfoSerialization { this.city = city setAction(SpyAction.Moving, 1) } + + private fun canDismissAgreementToNotSendSpies(city: City): Boolean { + val otherCivDiplomacyManager = city.civ.getDiplomacyManager(civInfo) ?: return false + val otherCiv = otherCivDiplomacyManager.civInfo + + val techSize = civInfo.gameInfo.ruleset.technologies.values.size.toFloat() + val ourResearchTechSize = civInfo.tech.techsResearched.size.toFloat() + val otherCivResearchTechSize = otherCiv.tech.techsResearched.size.toFloat() + + val ourTechResearchLevel = ((ourResearchTechSize/techSize)*100f) + val otherCivTechResearchLevel = ((otherCivResearchTechSize/techSize)*100f) + if (otherCivDiplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSendSpies) && + /* + * if there is equal or more than 10% difference in tech, + * compare to the other civ in tech we the Ai will bypass the agreement + * */ + otherCivTechResearchLevel <= ourTechResearchLevel + 10f) { + return true + } + return false + } fun canMoveTo(city: City): Boolean { + if (canDismissAgreementToNotSendSpies(city)) return false if (getCityOrNull() == city) return true if (!city.getCenterTile().isExplored(civInfo)) return false return espionageManager.getSpyAssignedToCity(city) == null diff --git a/core/src/com/unciv/ui/screens/diplomacyscreen/MajorCivDiplomacyTable.kt b/core/src/com/unciv/ui/screens/diplomacyscreen/MajorCivDiplomacyTable.kt index 294bf4ed9b..6f61d1ff56 100644 --- a/core/src/com/unciv/ui/screens/diplomacyscreen/MajorCivDiplomacyTable.kt +++ b/core/src/com/unciv/ui/screens/diplomacyscreen/MajorCivDiplomacyTable.kt @@ -203,6 +203,16 @@ class MajorCivDiplomacyTable(private val diplomacyScreen: DiplomacyScreen) { "They promised not to spread religion to us ([${diplomacyManager.getFlag(DiplomacyFlags.AgreedToNotSpreadReligion)}] turns remaining)" promisesTable.add(text.toLabel(Color.LIGHT_GRAY)).row() } + if (otherCivDiplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSendSpies)) { + val text = + "We promised not to send spies to them ([${otherCivDiplomacyManager.getFlag(DiplomacyFlags.AgreedToNotSendSpies)}] turns remaining)" + promisesTable.add(text.toLabel(Color.LIGHT_GRAY)).row() + } + if (diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSendSpies)) { + val text = + "They promised not to send spies to us ([${diplomacyManager.getFlag(DiplomacyFlags.AgreedToNotSendSpies)}] turns remaining)" + promisesTable.add(text.toLabel(Color.LIGHT_GRAY)).row() + } return if (promisesTable.cells.isEmpty) null else promisesTable } @@ -257,6 +267,24 @@ class MajorCivDiplomacyTable(private val diplomacyScreen: DiplomacyScreen) { dontSpreadReligionButton.disable() } demandsTable.add(dontSpreadReligionButton).row() + + if (viewingCiv.gameInfo.gameParameters.espionageEnabled) { + val dontSpyButton = "Stop spying on us.".toTextButton() + val diplomacyManager = viewingCiv.getDiplomacyManager(otherCiv)!! + if (otherCiv.popupAlerts.any { it.type == AlertType.DemandToStopSpyingOnUs && it.value == viewingCiv.civName} || + diplomacyManager.hasFlag(DiplomacyFlags.AgreedToNotSendSpies)) + dontSpyButton.disable() + dontSpyButton.onClick { + otherCiv.popupAlerts.add( + PopupAlert( + AlertType.DemandToStopSpyingOnUs, + viewingCiv.civName + ) + ) + dontSpyButton.disable() + } + demandsTable.add(dontSpyButton).row() + } demandsTable.add(Constants.close.toTextButton().onClick { diplomacyScreen.updateRightSide(otherCiv) }) return demandsTable diff --git a/core/src/com/unciv/ui/screens/worldscreen/AlertPopup.kt b/core/src/com/unciv/ui/screens/worldscreen/AlertPopup.kt index 1344f039c9..a5dc177414 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/AlertPopup.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/AlertPopup.kt @@ -98,6 +98,8 @@ class AlertPopup( AlertType.CitySettledNearOtherCivDespiteOurPromise -> shouldOpen = addCitySettledNearOtherCivDespiteOurPromise() AlertType.DemandToStopSpreadingReligion -> shouldOpen = addDemandToStopSpreadingReligion() AlertType.ReligionSpreadDespiteOurPromise -> shouldOpen = addReligionSpreadDespiteOurPromise() + AlertType.DemandToStopSpyingOnUs -> shouldOpen = addDemandToStopSendingSpiesToUs() + AlertType.SpyingOnUsDespiteOurPromise -> shouldOpen = addSpyingOnUsDespiteOurPromise() AlertType.DeclarationOfFriendship -> shouldOpen = addDeclarationOfFriendship() AlertType.BulliedProtectedMinor, AlertType.AttackedProtectedMinor, AlertType.AttackedAllyMinor -> shouldOpen = addBulliedOrAttackedProtectedOrAlliedMinor() @@ -298,6 +300,30 @@ class AlertPopup( addCloseButton("Very well.") return true } + private fun addDemandToStopSendingSpiesToUs(): Boolean { + val otherciv = getCiv(popupAlert.value) + if (otherciv.isDefeated()) return false + val playerDiploManager = viewingCiv.getDiplomacyManager(otherciv)!! + addLeaderName(otherciv) + addGoodSizedLabel("Stop spying on us.").row() + addCloseButton("We see our people are not welcome in your lands... we will take our attention elsewhere.", KeyboardBinding.Confirm) { + playerDiploManager.agreeNotToSpreadSpiesTo() + }.row() + addCloseButton("I'll do what's necessary for my empire to survive.", KeyboardBinding.Cancel) { + playerDiploManager.refuseNotToSpreadSpiesTo() + } + return true + } + + private fun addSpyingOnUsDespiteOurPromise(): Boolean { + val otherciv = getCiv(popupAlert.value) + if (otherciv.isDefeated()) return false + addGoodSizedLabel("Take back your spy and your broken promises.").row() + addCloseButton("Very well.") + return true + } + + private fun addDiplomaticMarriage() { val city = getCity(popupAlert.value)