mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-27 08: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 {
|
.firstOrNull {
|
||||||
val tile = it.currentTile
|
val tile = it.currentTile
|
||||||
it.type == UnitType.Civilian &&
|
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)
|
&& tile.militaryUnit == null && unit.movement.canMoveTo(tile) && unit.movement.canReach(tile)
|
||||||
}
|
}
|
||||||
if (settlerOrGreatPersonToAccompany == null) return false
|
if (settlerOrGreatPersonToAccompany == null) return false
|
||||||
|
@ -235,7 +235,7 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
//region Units
|
//region Units
|
||||||
fun getCivUnits(): Sequence<MapUnit> = units.asSequence()
|
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) {
|
fun addUnit(mapUnit: MapUnit, updateCivInfo: Boolean = true) {
|
||||||
val newList = ArrayList(units)
|
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
|
//endregion
|
||||||
|
|
||||||
//region state-changing functions
|
//region state-changing functions
|
||||||
@ -559,7 +563,7 @@ class CivilizationInfo {
|
|||||||
if (!gameInfo.ruleSet.units.containsKey(unitName)) return
|
if (!gameInfo.ruleSet.units.containsKey(unitName)) return
|
||||||
val unit = getEquivalentUnit(unitName)
|
val unit = getEquivalentUnit(unitName)
|
||||||
placeUnitNearTile(cityToAddTo.location, unit.name)
|
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)
|
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
|
val greatPeople = ruleSet.units.values
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { baseUnit -> baseUnit.uniques.any { it.equalsPlaceholderText("Great Person - []") } }
|
.filter { it.isGreatPerson() }
|
||||||
.map { it.getReplacedUnit(ruleSet) }
|
.map { it.getReplacedUnit(ruleSet) }
|
||||||
.distinct()
|
.distinct()
|
||||||
.filter { !challengerGreatPeople.contains(it) && !cityStateGreatPeople.contains(it) }
|
.filter { !challengerGreatPeople.contains(it) && !cityStateGreatPeople.contains(it) }
|
||||||
|
@ -304,6 +304,8 @@ class MapUnit {
|
|||||||
|
|
||||||
fun canGarrison() = type.isMilitary() && type.isLandUnit()
|
fun canGarrison() = type.isMilitary() && type.isLandUnit()
|
||||||
|
|
||||||
|
fun isGreatPerson() = baseUnit.isGreatPerson()
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region state-changing functions
|
//region state-changing functions
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.unciv.models.ruleset
|
package com.unciv.models.ruleset
|
||||||
|
|
||||||
import com.unciv.Constants
|
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
@ -59,14 +58,21 @@ object UniqueTriggerActivation {
|
|||||||
"Free Great Person" -> {
|
"Free Great Person" -> {
|
||||||
if (civInfo.isPlayerCivilization()) civInfo.greatPeople.freeGreatPeople++
|
if (civInfo.isPlayerCivilization()) civInfo.greatPeople.freeGreatPeople++
|
||||||
else {
|
else {
|
||||||
|
val greatPeople = civInfo.getGreatPeople()
|
||||||
|
if (greatPeople.isEmpty()) return
|
||||||
|
var greatPerson = civInfo.getGreatPeople().random()
|
||||||
|
|
||||||
val preferredVictoryType = civInfo.victoryType()
|
val preferredVictoryType = civInfo.victoryType()
|
||||||
val greatPerson = when (preferredVictoryType) {
|
if (preferredVictoryType == VictoryType.Cultural) {
|
||||||
VictoryType.Cultural -> "Great Artist"
|
val culturalGP = greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
|
||||||
VictoryType.Scientific -> "Great Scientist"
|
if (culturalGP != null) greatPerson = culturalGP
|
||||||
else -> civInfo.gameInfo.ruleSet.units.values
|
|
||||||
.filter { it.uniqueObjects.any { it.placeholderText == "Great Person - []" } }.map { it.name }.random()
|
|
||||||
}
|
}
|
||||||
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" ->
|
"+1 population in each city" ->
|
||||||
|
@ -23,21 +23,21 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
var cost: Int = 0
|
var cost: Int = 0
|
||||||
var hurryCostModifier: Int = 0
|
var hurryCostModifier: Int = 0
|
||||||
var movement: Int = 0
|
var movement: Int = 0
|
||||||
var strength:Int = 0
|
var strength: Int = 0
|
||||||
var rangedStrength:Int = 0
|
var rangedStrength: Int = 0
|
||||||
var range:Int = 2
|
var range: Int = 2
|
||||||
var interceptRange = 0
|
var interceptRange = 0
|
||||||
lateinit var unitType: UnitType
|
lateinit var unitType: UnitType
|
||||||
var requiredTech:String? = null
|
var requiredTech: String? = null
|
||||||
var requiredResource:String? = null
|
var requiredResource: String? = null
|
||||||
var uniques =HashSet<String>()
|
var uniques = HashSet<String>()
|
||||||
val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } }
|
val uniqueObjects: List<Unique> by lazy { uniques.map { Unique(it) } }
|
||||||
var promotions =HashSet<String>()
|
var promotions = HashSet<String>()
|
||||||
var obsoleteTech:String?=null
|
var obsoleteTech: String? = null
|
||||||
var upgradesTo:String? = null
|
var upgradesTo: String? = null
|
||||||
var replaces:String?=null
|
var replaces: String? = null
|
||||||
var uniqueTo:String?=null
|
var uniqueTo: String? = null
|
||||||
var attackSound:String?=null
|
var attackSound: String? = null
|
||||||
|
|
||||||
fun getShortDescription(): String {
|
fun getShortDescription(): String {
|
||||||
val infoList = mutableListOf<String>()
|
val infoList = mutableListOf<String>()
|
||||||
@ -51,28 +51,28 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
return infoList.joinToString()
|
return infoList.joinToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDescription(forPickerScreen:Boolean): String {
|
fun getDescription(forPickerScreen: Boolean): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
if(requiredResource!=null) sb.appendln("Consumes 1 [{$requiredResource}]".tr())
|
if (requiredResource != null) sb.appendln("Consumes 1 [{$requiredResource}]".tr())
|
||||||
if(!forPickerScreen) {
|
if (!forPickerScreen) {
|
||||||
if(uniqueTo!=null) sb.appendln("Unique to [$uniqueTo], replaces [$replaces]".tr())
|
if (uniqueTo != null) sb.appendln("Unique to [$uniqueTo], replaces [$replaces]".tr())
|
||||||
else sb.appendln("{Cost}: $cost".tr())
|
else sb.appendln("{Cost}: $cost".tr())
|
||||||
if(requiredTech!=null) sb.appendln("Required tech: [$requiredTech]".tr())
|
if (requiredTech != null) sb.appendln("Required tech: [$requiredTech]".tr())
|
||||||
if(upgradesTo!=null) sb.appendln("Upgrades to [$upgradesTo]".tr())
|
if (upgradesTo != null) sb.appendln("Upgrades to [$upgradesTo]".tr())
|
||||||
if(obsoleteTech!=null) sb.appendln("Obsolete with [$obsoleteTech]".tr())
|
if (obsoleteTech != null) sb.appendln("Obsolete with [$obsoleteTech]".tr())
|
||||||
}
|
}
|
||||||
if(strength!=0) {
|
if (strength != 0) {
|
||||||
sb.append("$strength${Fonts.strength}, ")
|
sb.append("$strength${Fonts.strength}, ")
|
||||||
if (rangedStrength != 0) sb.append("$rangedStrength${Fonts.rangedStrength}, ")
|
if (rangedStrength != 0) sb.append("$rangedStrength${Fonts.rangedStrength}, ")
|
||||||
if (rangedStrength != 0) sb.append("$range${Fonts.range}, ")
|
if (rangedStrength != 0) sb.append("$range${Fonts.range}, ")
|
||||||
}
|
}
|
||||||
sb.appendln("$movement${Fonts.movement}")
|
sb.appendln("$movement${Fonts.movement}")
|
||||||
|
|
||||||
for(unique in uniques)
|
for (unique in uniques)
|
||||||
sb.appendln(Translations.translateBonusOrPenalty(unique))
|
sb.appendln(Translations.translateBonusOrPenalty(unique))
|
||||||
|
|
||||||
if (promotions.isNotEmpty()) {
|
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() })
|
sb.appendln(promotions.joinToString(", ", " ") { it.tr() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,11 +110,11 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
return (cost / 10).toInt() * 10 // rounded down o nearest ten
|
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 {
|
override fun shouldBeDisplayed(construction: CityConstructions): Boolean {
|
||||||
val rejectionReason = getRejectionReason(construction)
|
val rejectionReason = getRejectionReason(construction)
|
||||||
return rejectionReason==""
|
return rejectionReason == ""
|
||||||
|| rejectionReason.startsWith("Requires")
|
|| rejectionReason.startsWith("Requires")
|
||||||
|| rejectionReason.startsWith("Consumes")
|
|| rejectionReason.startsWith("Consumes")
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
fun getRejectionReason(construction: CityConstructions): String {
|
fun getRejectionReason(construction: CityConstructions): String {
|
||||||
if (unitType.isWaterUnit() && !construction.cityInfo.getCenterTile().isCoastalTile())
|
if (unitType.isWaterUnit() && !construction.cityInfo.getCenterTile().isCoastalTile())
|
||||||
return "Can only build water units in coastal cities"
|
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]
|
val filter = unique.params[0]
|
||||||
if ((filter in construction.cityInfo.civInfo.gameInfo.ruleSet.tileResources && !construction.cityInfo.civInfo.hasResource(filter))
|
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)))
|
|| (filter in construction.cityInfo.civInfo.gameInfo.ruleSet.buildings && !construction.containsBuildingOrEquivalent(filter)))
|
||||||
@ -135,10 +135,10 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
|
|
||||||
fun getRejectionReason(civInfo: CivilizationInfo): String {
|
fun getRejectionReason(civInfo: CivilizationInfo): String {
|
||||||
if (uniques.contains("Unbuildable")) return "Unbuildable"
|
if (uniques.contains("Unbuildable")) return "Unbuildable"
|
||||||
if (requiredTech!=null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched"
|
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched"
|
||||||
if (obsoleteTech!=null && civInfo.tech.isResearched(obsoleteTech!!)) return "Obsolete by $obsoleteTech"
|
if (obsoleteTech != null && civInfo.tech.isResearched(obsoleteTech!!)) return "Obsolete by $obsoleteTech"
|
||||||
if (uniqueTo!=null && uniqueTo!=civInfo.civName) return "Unique to $uniqueTo"
|
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.ruleSet.units.values.any { it.uniqueTo == civInfo.civName && it.replaces == name }) return "Our unique unit replaces this"
|
||||||
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled
|
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled
|
||||||
&& uniques.contains("Nuclear weapon")) return "Disabled by setting"
|
&& uniques.contains("Nuclear weapon")) return "Disabled by setting"
|
||||||
for (unique in uniqueObjects.filter { it.placeholderText == "Requires []" }) {
|
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
|
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"
|
} 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.isCityState()) return "No settler for city-states"
|
||||||
if (uniques.contains(Constants.settlerUnique) && civInfo.isOneCityChallenger()) return "No settler for players in One City Challenge"
|
if (uniques.contains(Constants.settlerUnique) && civInfo.isOneCityChallenger()) return "No settler for players in One City Challenge"
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isBuildable(civInfo: CivilizationInfo) = getRejectionReason(civInfo)==""
|
fun isBuildable(civInfo: CivilizationInfo) = getRejectionReason(civInfo) == ""
|
||||||
|
|
||||||
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
|
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
|
||||||
return getRejectionReason(cityConstructions) == ""
|
return getRejectionReason(cityConstructions) == ""
|
||||||
@ -192,7 +192,7 @@ 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
|
// 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)
|
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"))
|
&& 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)
|
unit.promotions.addPromotion("Drill I", isFree = true)
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
|
|
||||||
override fun getResource(): String? = requiredResource
|
override fun getResource(): String? = requiredResource
|
||||||
|
|
||||||
fun getDirectUpgradeUnit(civInfo: CivilizationInfo):BaseUnit{
|
fun getDirectUpgradeUnit(civInfo: CivilizationInfo): BaseUnit {
|
||||||
return civInfo.getEquivalentUnit(upgradesTo!!)
|
return civInfo.getEquivalentUnit(upgradesTo!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ class BaseUnit : INamed, IConstruction {
|
|||||||
else ruleset.units[replaces!!]!!
|
else ruleset.units[replaces!!]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun matchesFilter(filter:String):Boolean {
|
fun matchesFilter(filter: String): Boolean {
|
||||||
if (filter == unitType.name) return true
|
if (filter == unitType.name) return true
|
||||||
if (filter == name) return true
|
if (filter == name) return true
|
||||||
if (filter == "All") 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
|
if ((filter == "military" || filter == "Military" || filter == "military units") && unitType.isMilitary()) return true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isGreatPerson() = uniqueObjects.any { it.placeholderText == "Great Person - []" }
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,7 @@ class GreatPersonPickerScreen(val civInfo:CivilizationInfo) : PickerScreen() {
|
|||||||
closeButton.isVisible=false
|
closeButton.isVisible=false
|
||||||
rightSideButton.setText("Choose a free great person".tr())
|
rightSideButton.setText("Choose a free great person".tr())
|
||||||
|
|
||||||
val greatPersonNames = GreatPersonManager().statToGreatPersonMapping.values
|
val greatPersonUnits = civInfo.getGreatPeople()
|
||||||
.union(listOf("Great General"))
|
|
||||||
val greatPersonUnits = greatPersonNames.map { civInfo.getEquivalentUnit(it) }
|
|
||||||
for (unit in greatPersonUnits)
|
for (unit in greatPersonUnits)
|
||||||
{
|
{
|
||||||
val button = Button(skin)
|
val button = Button(skin)
|
||||||
|
Reference in New Issue
Block a user