Basic version of EspionageManger, added a unique to gain spies (#7641)

* Handfull of comment questions, small refactorings

* Code changes

* Reworded a unique, removed a file

* Added spy names for all nations, minor consistency change

* Removed debug output

* Added an empty constructor so gdx can reconstruct it
This commit is contained in:
Xander Lenstra
2022-08-13 21:45:16 +02:00
committed by GitHub
parent 89979748a1
commit 202e0bcf47
18 changed files with 1077 additions and 922 deletions

View File

@ -155,6 +155,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
var religionManager = ReligionManager()
var goldenAges = GoldenAgeManager()
var greatPeople = GreatPersonManager()
var espionageManager = EspionageManager()
var victoryManager = VictoryManager()
var ruinsManager = RuinsManager()
var diplomacy = HashMap<String, DiplomacyManager>()
@ -261,6 +262,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
toReturn.goldenAges = goldenAges.clone()
toReturn.greatPeople = greatPeople.clone()
toReturn.ruinsManager = ruinsManager.clone()
toReturn.espionageManager = espionageManager.clone()
toReturn.victoryManager = victoryManager.clone()
toReturn.allyCivName = allyCivName
for (diplomacyManager in diplomacy.values.map { it.clone() })
@ -833,6 +835,8 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
diplomacyManager.updateHasOpenBorders()
}
espionageManager.setTransients(this)
victoryManager.civInfo = this
thingsToFocusOnForVictory = getPreferredVictoryTypeObject()?.getThingsToFocus(this) ?: setOf()

View File

@ -0,0 +1,50 @@
package com.unciv.logic.civilization
import com.unciv.logic.IsPartOfGameInfoSerialization
class Spy() : IsPartOfGameInfoSerialization {
lateinit var name: String
constructor(name: String) : this() {
this.name = name
}
fun clone(): Spy {
return Spy(name)
}
}
class EspionageManager : IsPartOfGameInfoSerialization {
var spyCount = 0
var spyList = mutableListOf<Spy>()
var erasSpyEarnedFor = mutableListOf<String>()
@Transient
lateinit var civInfo: CivilizationInfo
fun clone(): EspionageManager {
val toReturn = EspionageManager()
toReturn.spyCount = spyCount
toReturn.spyList.addAll(spyList.map { it.clone() })
toReturn.erasSpyEarnedFor.addAll(erasSpyEarnedFor)
return toReturn
}
fun setTransients(civInfo: CivilizationInfo) {
this.civInfo = civInfo
}
private fun getSpyName(): String {
val usedSpyNames = spyList.map { it.name }.toHashSet()
val validSpyNames = civInfo.nation.spyNames.filter { it !in usedSpyNames }
if (validSpyNames.isEmpty()) { return "Spy ${spyList.size+1}" } // +1 as non-programmers count from 1
return validSpyNames.random()
}
fun addSpy(): String {
val spyName = getSpyName()
spyList.add(Spy(spyName))
return spyName
}
}

View File

@ -14,28 +14,30 @@ import com.unciv.ui.worldscreen.WorldScreen
object NotificationIcon {
// Remember: The typical white-on-transparency icon will not be visible on Notifications
const val Barbarians = "ImprovementIcons/Barbarian encampment"
const val Citadel = "ImprovementIcons/Citadel"
const val City = "ImprovementIcons/City center"
const val CityState = "OtherIcons/CityState"
const val Crosshair = "OtherIcons/CrosshairB"
const val Culture = "StatIcons/Culture"
const val Construction = "StatIcons/Production"
const val Growth = "StatIcons/Population"
const val War = "OtherIcons/Pillage"
const val Trade = "StatIcons/Acquire"
const val Science = "StatIcons/Science"
const val Gold = "StatIcons/Gold"
const val Death = "OtherIcons/DisbandUnit"
const val Diplomacy = "OtherIcons/Diplomacy"
const val City = "ImprovementIcons/City center"
const val Citadel = "ImprovementIcons/Citadel"
const val Faith = "StatIcons/Faith"
const val Food = "StatIcons/Food"
const val Gold = "StatIcons/Gold"
const val Growth = "StatIcons/Population"
const val Happiness = "StatIcons/Happiness"
const val Population = "StatIcons/Population"
const val CityState = "OtherIcons/CityState"
const val Production = "StatIcons/Production"
const val Food = "StatIcons/Food"
const val Faith = "StatIcons/Faith"
const val Crosshair = "OtherIcons/CrosshairB"
const val Scout = "UnitIcons/Scout"
const val Ruins = "ImprovementIcons/Ancient ruins"
const val Barbarians = "ImprovementIcons/Barbarian encampment"
const val Question = "OtherIcons/Question"
const val Ruins = "ImprovementIcons/Ancient ruins"
const val Science = "StatIcons/Science"
const val Scout = "UnitIcons/Scout"
const val Spy = "OtherIcons/Spy"
const val Trade = "StatIcons/Acquire"
const val War = "OtherIcons/Pillage"
}
/**

View File

@ -193,15 +193,13 @@ class PolicyManager : IsPartOfGameInfoSerialization {
}
}
// Todo make this a triggerable unique for other objects
for (unique in policy.getMatchingUniques(UniqueType.OneTimeGlobalAlert)) {
triggerGlobalAlerts(
policy, unique.params[0]
)
triggerGlobalAlerts(policy, unique.params[0])
}
for (unique in policy.uniqueObjects) UniqueTriggerActivation.triggerCivwideUnique(
unique, civInfo
)
for (unique in policy.uniqueObjects)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
// This ALSO has the side-effect of updating the CivInfo statForNextTurn so we don't need to call it explicitly
for (cityInfo in civInfo.cities) cityInfo.cityStats.update()
@ -223,7 +221,7 @@ class PolicyManager : IsPartOfGameInfoSerialization {
) {
var extraNotificationTextCopy = extraNotificationText
if (extraNotificationText != "") {
extraNotificationTextCopy = "\n${extraNotificationText}"
extraNotificationTextCopy = " ${extraNotificationText}"
}
for (civ in civInfo.gameInfo.civilizations.filter { it.isMajorCiv() }) {
if (civ == civInfo) continue

View File

@ -1,4 +1,5 @@
package com.unciv.logic.civilization.RuinsManager
// Why is this the only file in its own package?
import com.unciv.logic.IsPartOfGameInfoSerialization
import com.unciv.logic.civilization.CivilizationInfo

View File

@ -238,9 +238,6 @@ class TechManager : IsPartOfGameInfoSerialization {
}
fun addTechnology(techName: String) {
techsInProgress.remove(techName)
val previousEra = civInfo.getEra()
techsResearched.add(techName)
// this is to avoid concurrent modification problems
@ -262,19 +259,6 @@ class TechManager : IsPartOfGameInfoSerialization {
civInfo.addNotification("Research of [$techName] has completed!", TechAction(techName), NotificationIcon.Science, techName)
civInfo.popupAlerts.add(PopupAlert(AlertType.TechResearched, techName))
val currentEra = civInfo.getEra()
if (previousEra != currentEra) {
civInfo.addNotification("You have entered the [$currentEra]!", NotificationIcon.Science)
if (civInfo.isMajorCiv()) {
for (knownCiv in civInfo.getKnownCivs()) {
knownCiv.addNotification("[${civInfo.civName}] has entered the [$currentEra]!", civInfo.civName, NotificationIcon.Science)
}
}
for (it in getRuleset().policyBranches.values.filter { it.era == currentEra.name && civInfo.policies.isAdoptable(it) }) {
civInfo.addNotification("[" + it.name + "] policy branch unlocked!", NotificationIcon.Culture)
}
}
if (civInfo.playerType == PlayerType.Human) {
for (revealedResource in getRuleset().tileResources.values.filter { techName == it.revealedBy }) {
civInfo.gameInfo.notifyExploredResources(civInfo, revealedResource.name, 5, false)
@ -334,7 +318,23 @@ class TechManager : IsPartOfGameInfoSerialization {
civInfo.addNotification("You have unlocked [The Long Count]!", MayaLongCountAction(), MayaCalendar.notificationIcon)
}
val previousEra = civInfo.getEra()
updateEra()
val currentEra = civInfo.getEra()
if (previousEra != currentEra) {
civInfo.addNotification("You have entered the [$currentEra]!", NotificationIcon.Science)
if (civInfo.isMajorCiv()) {
for (knownCiv in civInfo.getKnownCivs()) {
knownCiv.addNotification("[${civInfo.civName}] has entered the [$currentEra]!", civInfo.civName, NotificationIcon.Science)
}
}
for (policyBranch in getRuleset().policyBranches.values.filter { it.era == currentEra.name && civInfo.policies.isAdoptable(it) }) {
civInfo.addNotification("[" + policyBranch.name + "] policy branch unlocked!", NotificationIcon.Culture)
}
for (unique in currentEra.uniqueObjects) {
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
}
}
}
fun updateEra() {

View File

@ -48,6 +48,8 @@ class Nation : RulesetObject() {
var adjective = ArrayList<String>()
*/
var spyNames = ArrayList<String>()
var favoredReligion: String? = null
@Transient

View File

@ -13,6 +13,11 @@ import com.unciv.models.ruleset.unique.UniqueTarget
* @see com.unciv.models.TutorialTrigger
*/
class Tutorial : RulesetObject() {
// Why does this override RulesetObject()? The only unique it overrides is `Will not be displayed in Civilopedia`,
// so allowing it access to the full power of uniques is completely unnecessary.
// (Also, what even would it mean for this to have uniques like "[+10]% Production"? When should it even apply.)
// imo just having a flag for this (and maybe one if religion is disabled, but even then, that should be a ruleset choice) should suffice.
// -xlenstra
override var name = "" // overridden only to have the name seen first by TranslationFileWriter
/** These lines will be displayed (when the Tutorial is _triggered_) one after another,

View File

@ -477,8 +477,26 @@ object UniqueTriggerActivation {
return true
}
FreeStatBuildings, FreeSpecificBuildings ->
OneTimeGlobalSpiesWhenEnteringEra -> {
if (!civInfo.isMajorCiv()) return false
val currentEra = civInfo.getEra().name
for (otherCiv in civInfo.gameInfo.getAliveMajorCivs()) {
if (currentEra !in otherCiv.espionageManager.erasSpyEarnedFor) {
val spyName = otherCiv.espionageManager.addSpy()
otherCiv.espionageManager.erasSpyEarnedFor.add(currentEra)
if (otherCiv == civInfo || otherCiv.knows(civInfo))
otherCiv.addNotification("We have recruited [${spyName}] as a spy!", NotificationIcon.Spy)
else
otherCiv.addNotification("After an unknown civilization entered the [${currentEra}], we have recruited [${spyName}] as a spy!", NotificationIcon.Spy)
}
}
return true
}
FreeStatBuildings, FreeSpecificBuildings -> {
civInfo.civConstructions.tryAddFreeBuildings()
return true // not fully correct
}
else -> {}
}

View File

@ -18,7 +18,6 @@ enum class UniqueTarget(val inheritsFrom: UniqueTarget? = null) {
// Civilization-specific
Nation(Global),
Era(Global),
Speed(Global),
Tech(Global),
Policy(Global),
FounderBelief(Global),
@ -46,6 +45,7 @@ enum class UniqueTarget(val inheritsFrom: UniqueTarget? = null) {
Ruins(Triggerable),
// Other
Speed,
Tutorial,
CityState,
ModOptions,
@ -314,7 +314,6 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
TriggersAlertOnCompletion("Triggers a global alert upon completion", UniqueTarget.Building, UniqueTarget.Unit),
//endregion
///////////////////////////////////////// region BUILDING UNIQUES /////////////////////////////////////////
@ -351,7 +350,6 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
CreatesOneImprovement("Creates a [improvementName] improvement on a specific tile", UniqueTarget.Building),
//endregion
///////////////////////////////////////// region UNIT UNIQUES /////////////////////////////////////////
FoundCity("Founds a new city", UniqueTarget.Unit),
@ -685,7 +683,8 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
OneTimeRevealSpecificMapTiles("Reveal up to [amount/'all'] [tileFilter] within a [amount] tile radius", UniqueTarget.Ruins),
OneTimeRevealCrudeMap("From a randomly chosen tile [amount] tiles away from the ruins, reveal tiles up to [amount] tiles away with [amount]% chance", UniqueTarget.Ruins),
OneTimeTriggerVoting("Triggers voting for the Diplomatic Victory", UniqueTarget.Triggerable), // used in Building
OneTimeGlobalAlert("Triggers the following global alert: [comment]", UniqueTarget.Triggerable), // used in Policy
OneTimeGlobalAlert("Triggers the following global alert: [comment]", UniqueTarget.Policy), // used in Policy
OneTimeGlobalSpiesWhenEnteringEra("Every major Civilization gains a spy once a civilization enters this era", UniqueTarget.Era),
OneTimeUnitHeal("Heal this unit by [amount] HP", UniqueTarget.Promotion),
OneTimeUnitGainXP("This Unit gains [amount] XP", UniqueTarget.Ruins),