Add don't spy on us has a demand (#13351)

* Settler settle best tile when not escort and dangerous Tiles instead of running away

Settler unit will now settle on best tile in dangerous Tiles without escort instead of running away.

* Update WorkerAutomation.kt

* Update SpecificUnitAutomation.kt

* Update WorkerAutomation.kt

* Update SpecificUnitAutomation.kt

* Now city states get mad when you steal their Lands

* new version

* change to getDiplomacyManagerOrMeet

* added text to template.properties and changed AlertPopup.kt

* Update template.properties

* with period at the end :b

* add flag now

* Made Option to declare war when a city state is bullied unavailable

* added option to change the Maximum Autosave turns stored

* remove print

* change letter

* should fix issue with building test

* update with changes

* Added UniqueType.FoundPuppetCity

with "Founds a new puppet city" in "uniques" of an unit in Units.json.
Making it so you can now settle a puppet city.

* Added save promotion

* Updated for PR

* Updated with requested changes

* Removed unnecessary check

* updated PR

* Update PromotionPickerScreen.kt to save promotion cells too

* change name and added !

* updated name of variable

* updated version from unitType to BaseUnit

* updated variable name

* Added unitType to reduce the xp cost of promotions for all units in a civ

This was a unique type that the Zulu have in civ 5

* updated name

* remove UniqueTarget.FollowerBelief

* Experience from to XP

* fix ?

* XP

* change it back to Experience because it didn't want to build on git :(

* back to XP then

* update auto promotion and fix negative XP on unit

* Fix build issues and remove the XPForPromotionModifier from xpForNextPromotion and  xpForNextNPromotions

* remove XPForPromotionModifier

* re added Statuses and remove duplicate comment

* remove some white space and 1 used import "com.unciv.ui.components.extensions.toPercent"

* remove unique from uniques.md

* update 0

* name change from spys to spies

* Now in a working state

* change println

* mini update

* final update

* update

* mini update

* Update

* updated it for the ai can bypass agreement to stop spaying on x civ

* updated addSpyingOnUsDespiteOurPromise text

text by itanasi

* fixed build issue ?

* Apply suggestions from code review

* update comment

* Update core/src/com/unciv/models/Spy.kt

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>

* If true it will  return false in moveTo function

* Update

* Update AlertPopup.kt

---------

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
General_E 2025-06-11 16:31:57 +02:00 committed by GitHub
parent db60d83424
commit 0a41417fdd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 151 additions and 4 deletions

View File

@ -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]! =

View File

@ -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

View File

@ -18,6 +18,9 @@ enum class AlertType : IsPartOfGameInfoSerialization {
DemandToStopSpreadingReligion,
ReligionSpreadDespiteOurPromise,
DemandToStopSpyingOnUs,
SpyingOnUsDespiteOurPromise,
GoldenAge,
DeclarationOfFriendship,

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)