mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-15 02:09:21 +07:00
"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:
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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) }
|
||||
|
@ -304,6 +304,8 @@ class MapUnit {
|
||||
|
||||
fun canGarrison() = type.isMilitary() && type.isLandUnit()
|
||||
|
||||
fun isGreatPerson() = baseUnit.isGreatPerson()
|
||||
|
||||
//endregion
|
||||
|
||||
//region state-changing functions
|
||||
|
@ -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" ->
|
||||
|
@ -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 - []" }
|
||||
}
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user