"Free great person" can no longer grant great people that are unique to another civ

Unified Great Person recognition
Better Great Person AI detection for unique units
This commit is contained in:
Yair Morgenstern
2020-12-27 11:05:11 +02:00
parent 713086849a
commit 87c6819462
7 changed files with 61 additions and 49 deletions

View File

@ -279,7 +279,7 @@ object UnitAutomation {
.firstOrNull {
val tile = it.currentTile
it.type == UnitType.Civilian &&
(it.hasUnique(Constants.settlerUnique) || unit.name in GreatPersonManager().statToGreatPersonMapping.values)
(it.hasUnique(Constants.settlerUnique) || unit.isGreatPerson())
&& tile.militaryUnit == null && unit.movement.canMoveTo(tile) && unit.movement.canReach(tile)
}
if (settlerOrGreatPersonToAccompany == null) return false

View File

@ -235,7 +235,7 @@ class CivilizationInfo {
//region Units
fun getCivUnits(): Sequence<MapUnit> = units.asSequence()
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.hasUnique("Great Person - []") }
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() }
fun addUnit(mapUnit: MapUnit, updateCivInfo: Boolean = true) {
val newList = ArrayList(units)
@ -402,6 +402,10 @@ class CivilizationInfo {
}
}
fun getGreatPeople() = gameInfo.ruleSet.units.values.asSequence()
.filter { it.isGreatPerson() }.map { getEquivalentUnit(it.name) }.toHashSet()
//endregion
//region state-changing functions
@ -559,7 +563,7 @@ class CivilizationInfo {
if (!gameInfo.ruleSet.units.containsKey(unitName)) return
val unit = getEquivalentUnit(unitName)
placeUnitNearTile(cityToAddTo.location, unit.name)
if (unit.uniques.any { it.equalsPlaceholderText("Great Person - []") })
if (unit.isGreatPerson())
addNotification("A [${unit.name}] has been born in [${cityToAddTo.name}]!", cityToAddTo.location, Color.GOLD)
}

View File

@ -502,7 +502,7 @@ class QuestManager {
val greatPeople = ruleSet.units.values
.asSequence()
.filter { baseUnit -> baseUnit.uniques.any { it.equalsPlaceholderText("Great Person - []") } }
.filter { it.isGreatPerson() }
.map { it.getReplacedUnit(ruleSet) }
.distinct()
.filter { !challengerGreatPeople.contains(it) && !cityStateGreatPeople.contains(it) }

View File

@ -304,6 +304,8 @@ class MapUnit {
fun canGarrison() = type.isMilitary() && type.isLandUnit()
fun isGreatPerson() = baseUnit.isGreatPerson()
//endregion
//region state-changing functions

View File

@ -1,6 +1,5 @@
package com.unciv.models.ruleset
import com.unciv.Constants
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.stats.Stats
@ -59,14 +58,21 @@ object UniqueTriggerActivation {
"Free Great Person" -> {
if (civInfo.isPlayerCivilization()) civInfo.greatPeople.freeGreatPeople++
else {
val greatPeople = civInfo.getGreatPeople()
if (greatPeople.isEmpty()) return
var greatPerson = civInfo.getGreatPeople().random()
val preferredVictoryType = civInfo.victoryType()
val greatPerson = when (preferredVictoryType) {
VictoryType.Cultural -> "Great Artist"
VictoryType.Scientific -> "Great Scientist"
else -> civInfo.gameInfo.ruleSet.units.values
.filter { it.uniqueObjects.any { it.placeholderText == "Great Person - []" } }.map { it.name }.random()
if (preferredVictoryType == VictoryType.Cultural) {
val culturalGP = greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
if (culturalGP != null) greatPerson = culturalGP
}
civInfo.addUnit(greatPerson, chosenCity)
if (preferredVictoryType == VictoryType.Scientific) {
val scientificGP = greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
if (scientificGP != null) greatPerson = scientificGP
}
civInfo.addUnit(greatPerson.name, chosenCity)
}
}
"+1 population in each city" ->

View File

@ -23,21 +23,21 @@ class BaseUnit : INamed, IConstruction {
var cost: Int = 0
var hurryCostModifier: Int = 0
var movement: Int = 0
var strength:Int = 0
var rangedStrength:Int = 0
var range:Int = 2
var strength: Int = 0
var rangedStrength: Int = 0
var range: Int = 2
var interceptRange = 0
lateinit var unitType: UnitType
var requiredTech:String? = null
var requiredResource:String? = null
var uniques =HashSet<String>()
var requiredTech: String? = null
var requiredResource: String? = null
var uniques = HashSet<String>()
val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } }
var promotions =HashSet<String>()
var obsoleteTech:String?=null
var upgradesTo:String? = null
var replaces:String?=null
var uniqueTo:String?=null
var attackSound:String?=null
var promotions = HashSet<String>()
var obsoleteTech: String? = null
var upgradesTo: String? = null
var replaces: String? = null
var uniqueTo: String? = null
var attackSound: String? = null
fun getShortDescription(): String {
val infoList = mutableListOf<String>()
@ -51,28 +51,28 @@ class BaseUnit : INamed, IConstruction {
return infoList.joinToString()
}
fun getDescription(forPickerScreen:Boolean): String {
fun getDescription(forPickerScreen: Boolean): String {
val sb = StringBuilder()
if(requiredResource!=null) sb.appendln("Consumes 1 [{$requiredResource}]".tr())
if(!forPickerScreen) {
if(uniqueTo!=null) sb.appendln("Unique to [$uniqueTo], replaces [$replaces]".tr())
if (requiredResource != null) sb.appendln("Consumes 1 [{$requiredResource}]".tr())
if (!forPickerScreen) {
if (uniqueTo != null) sb.appendln("Unique to [$uniqueTo], replaces [$replaces]".tr())
else sb.appendln("{Cost}: $cost".tr())
if(requiredTech!=null) sb.appendln("Required tech: [$requiredTech]".tr())
if(upgradesTo!=null) sb.appendln("Upgrades to [$upgradesTo]".tr())
if(obsoleteTech!=null) sb.appendln("Obsolete with [$obsoleteTech]".tr())
if (requiredTech != null) sb.appendln("Required tech: [$requiredTech]".tr())
if (upgradesTo != null) sb.appendln("Upgrades to [$upgradesTo]".tr())
if (obsoleteTech != null) sb.appendln("Obsolete with [$obsoleteTech]".tr())
}
if(strength!=0) {
if (strength != 0) {
sb.append("$strength${Fonts.strength}, ")
if (rangedStrength != 0) sb.append("$rangedStrength${Fonts.rangedStrength}, ")
if (rangedStrength != 0) sb.append("$range${Fonts.range}, ")
}
sb.appendln("$movement${Fonts.movement}")
for(unique in uniques)
for (unique in uniques)
sb.appendln(Translations.translateBonusOrPenalty(unique))
if (promotions.isNotEmpty()) {
sb.append((if (promotions.size==1) "Free promotion:" else "Free promotions:").tr())
sb.append((if (promotions.size == 1) "Free promotion:" else "Free promotions:").tr())
sb.appendln(promotions.joinToString(", ", " ") { it.tr() })
}
@ -110,11 +110,11 @@ class BaseUnit : INamed, IConstruction {
return (cost / 10).toInt() * 10 // rounded down o nearest ten
}
fun getDisbandGold() = getBaseGoldCost().toInt()/20
fun getDisbandGold() = getBaseGoldCost().toInt() / 20
override fun shouldBeDisplayed(construction: CityConstructions): Boolean {
val rejectionReason = getRejectionReason(construction)
return rejectionReason==""
return rejectionReason == ""
|| rejectionReason.startsWith("Requires")
|| rejectionReason.startsWith("Consumes")
}
@ -122,7 +122,7 @@ class BaseUnit : INamed, IConstruction {
fun getRejectionReason(construction: CityConstructions): String {
if (unitType.isWaterUnit() && !construction.cityInfo.getCenterTile().isCoastalTile())
return "Can only build water units in coastal cities"
for (unique in uniqueObjects.filter { it.placeholderText == "Not displayed as an available construction without []"}) {
for (unique in uniqueObjects.filter { it.placeholderText == "Not displayed as an available construction without []" }) {
val filter = unique.params[0]
if ((filter in construction.cityInfo.civInfo.gameInfo.ruleSet.tileResources && !construction.cityInfo.civInfo.hasResource(filter))
|| (filter in construction.cityInfo.civInfo.gameInfo.ruleSet.buildings && !construction.containsBuildingOrEquivalent(filter)))
@ -135,10 +135,10 @@ class BaseUnit : INamed, IConstruction {
fun getRejectionReason(civInfo: CivilizationInfo): String {
if (uniques.contains("Unbuildable")) return "Unbuildable"
if (requiredTech!=null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched"
if (obsoleteTech!=null && civInfo.tech.isResearched(obsoleteTech!!)) return "Obsolete by $obsoleteTech"
if (uniqueTo!=null && uniqueTo!=civInfo.civName) return "Unique to $uniqueTo"
if (civInfo.gameInfo.ruleSet.units.values.any { it.uniqueTo==civInfo.civName && it.replaces==name }) return "Our unique unit replaces this"
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched"
if (obsoleteTech != null && civInfo.tech.isResearched(obsoleteTech!!)) return "Obsolete by $obsoleteTech"
if (uniqueTo != null && uniqueTo != civInfo.civName) return "Unique to $uniqueTo"
if (civInfo.gameInfo.ruleSet.units.values.any { it.uniqueTo == civInfo.civName && it.replaces == name }) return "Our unique unit replaces this"
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled
&& uniques.contains("Nuclear weapon")) return "Disabled by setting"
for (unique in uniqueObjects.filter { it.placeholderText == "Requires []" }) {
@ -147,13 +147,13 @@ class BaseUnit : INamed, IConstruction {
if (civInfo.cities.none { it.cityConstructions.containsBuildingOrEquivalent(filter) }) return unique.text // Wonder is not built
} else if (!civInfo.policies.adoptedPolicies.contains(filter)) return "Policy is not adopted"
}
if (requiredResource!=null && !civInfo.hasResource(requiredResource!!) && !civInfo.gameInfo.gameParameters.godMode) return "Consumes 1 [$requiredResource]"
if (requiredResource != null && !civInfo.hasResource(requiredResource!!) && !civInfo.gameInfo.gameParameters.godMode) return "Consumes 1 [$requiredResource]"
if (uniques.contains(Constants.settlerUnique) && civInfo.isCityState()) return "No settler for city-states"
if (uniques.contains(Constants.settlerUnique) && civInfo.isOneCityChallenger()) return "No settler for players in One City Challenge"
return ""
}
fun isBuildable(civInfo: CivilizationInfo) = getRejectionReason(civInfo)==""
fun isBuildable(civInfo: CivilizationInfo) = getRejectionReason(civInfo) == ""
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
return getRejectionReason(cityConstructions) == ""
@ -192,8 +192,8 @@ class BaseUnit : INamed, IConstruction {
}
// This is to be deprecated and converted to "All newly-trained [] in this city receive the [] promotion" - keeping it here to that mods with this can still work for now
if (unit.type in listOf(UnitType.Melee,UnitType.Mounted,UnitType.Armor)
&& construction.cityInfo.containsBuildingUnique("All newly-trained melee, mounted, and armored units in this city receive the Drill I promotion"))
if (unit.type in listOf(UnitType.Melee, UnitType.Mounted, UnitType.Armor)
&& construction.cityInfo.containsBuildingUnique("All newly-trained melee, mounted, and armored units in this city receive the Drill I promotion"))
unit.promotions.addPromotion("Drill I", isFree = true)
return true
@ -201,7 +201,7 @@ class BaseUnit : INamed, IConstruction {
override fun getResource(): String? = requiredResource
fun getDirectUpgradeUnit(civInfo: CivilizationInfo):BaseUnit{
fun getDirectUpgradeUnit(civInfo: CivilizationInfo): BaseUnit {
return civInfo.getEquivalentUnit(upgradesTo!!)
}
@ -212,7 +212,7 @@ class BaseUnit : INamed, IConstruction {
else ruleset.units[replaces!!]!!
}
fun matchesFilter(filter:String):Boolean {
fun matchesFilter(filter: String): Boolean {
if (filter == unitType.name) return true
if (filter == name) return true
if (filter == "All") return true
@ -223,4 +223,6 @@ class BaseUnit : INamed, IConstruction {
if ((filter == "military" || filter == "Military" || filter == "military units") && unitType.isMilitary()) return true
return false
}
fun isGreatPerson() = uniqueObjects.any { it.placeholderText == "Great Person - []" }
}

View File

@ -18,9 +18,7 @@ class GreatPersonPickerScreen(val civInfo:CivilizationInfo) : PickerScreen() {
closeButton.isVisible=false
rightSideButton.setText("Choose a free great person".tr())
val greatPersonNames = GreatPersonManager().statToGreatPersonMapping.values
.union(listOf("Great General"))
val greatPersonUnits = greatPersonNames.map { civInfo.getEquivalentUnit(it) }
val greatPersonUnits = civInfo.getGreatPeople()
for (unit in greatPersonUnits)
{
val button = Button(skin)