mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-26 05:14:32 -04:00
Initial Civ Personality implementation (#10939)
* Barebones implementation * Personality Policy branch * Initial addtions to Victory.Focus based changes * Have no focus or personality if human * Add getter/setter for easier access to focuses * Add scaled value to various areas * Make getPersonality non null to simplify checks * Forgot to add in changes to the base class * Ugly code to add in Personality preferred victory type. Not sure how to make it cleaner * Fix typo * Early return Faith building choice * Change set concatination to listOf * fix missing equal sign * vals for getPersonality * Remove unnecessary parenthesis * Early return the other buildings as well * Some linting and renames
This commit is contained in:
parent
eb8fee8edd
commit
a7c0df8c0b
@ -649,8 +649,9 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
|
|
||||||
for (civInfo in civilizations) civInfo.setTransients()
|
for (civInfo in civilizations) civInfo.setTransients()
|
||||||
for (civInfo in civilizations) {
|
for (civInfo in civilizations) {
|
||||||
civInfo.thingsToFocusOnForVictory =
|
civInfo.thingsToFocusOnForVictory = emptySet()
|
||||||
civInfo.getPreferredVictoryTypeObject()?.getThingsToFocus(civInfo) ?: setOf()
|
for (victory in civInfo.getPreferredVictoryTypeObjects())
|
||||||
|
civInfo.thingsToFocusOnForVictory.plus(victory.getThingsToFocus(civInfo))
|
||||||
}
|
}
|
||||||
tileMap.setNeutralTransients() // has to happen after civInfo.setTransients() sets owningCity
|
tileMap.setNeutralTransients() // has to happen after civInfo.setTransients() sets owningCity
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import com.unciv.logic.map.tile.Tile
|
|||||||
import com.unciv.models.ruleset.Building
|
import com.unciv.models.ruleset.Building
|
||||||
import com.unciv.models.ruleset.INonPerpetualConstruction
|
import com.unciv.models.ruleset.INonPerpetualConstruction
|
||||||
import com.unciv.models.ruleset.Victory
|
import com.unciv.models.ruleset.Victory
|
||||||
|
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.tile.TileImprovement
|
import com.unciv.models.ruleset.tile.TileImprovement
|
||||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||||
@ -45,6 +46,7 @@ object Automation {
|
|||||||
private fun rankStatsForCityWork(stats: Stats, city: City, cityStats: Stats, specialist: Boolean, localUniqueCache: LocalUniqueCache): Float {
|
private fun rankStatsForCityWork(stats: Stats, city: City, cityStats: Stats, specialist: Boolean, localUniqueCache: LocalUniqueCache): Float {
|
||||||
val cityAIFocus = city.getCityFocus()
|
val cityAIFocus = city.getCityFocus()
|
||||||
val yieldStats = stats.clone()
|
val yieldStats = stats.clone()
|
||||||
|
val civPersonality = city.civ.getPersonality()
|
||||||
|
|
||||||
if (specialist) {
|
if (specialist) {
|
||||||
// If you have the Food Bonus, count as 1 extra food production (base is 2food)
|
// If you have the Food Bonus, count as 1 extra food production (base is 2food)
|
||||||
@ -85,15 +87,23 @@ object Automation {
|
|||||||
} else {
|
} else {
|
||||||
if (city.civ.gold < 0 && city.civ.stats.statsForNextTurn.gold <= 0)
|
if (city.civ.gold < 0 && city.civ.stats.statsForNextTurn.gold <= 0)
|
||||||
yieldStats.gold *= 2 // We have a global problem
|
yieldStats.gold *= 2 // We have a global problem
|
||||||
|
yieldStats.gold *= civPersonality.scaledFocus(PersonalityValue.Gold)
|
||||||
|
|
||||||
if (city.tiles.size < 12 || city.civ.wantsToFocusOn(Victory.Focus.Culture))
|
if (city.tiles.size < 12 || city.civ.wantsToFocusOn(Victory.Focus.Culture))
|
||||||
yieldStats.culture *= 2
|
yieldStats.culture *= 2
|
||||||
|
yieldStats.culture *= civPersonality.scaledFocus(PersonalityValue.Culture)
|
||||||
|
|
||||||
if (city.civ.getHappiness() < 0 && !specialist) // since this doesn't get updated, may overshoot
|
if (city.civ.getHappiness() < 0 && !specialist) // since this doesn't get updated, may overshoot
|
||||||
yieldStats.happiness *= 2
|
yieldStats.happiness *= 2
|
||||||
|
yieldStats.happiness *= civPersonality.scaledFocus(PersonalityValue.Happiness)
|
||||||
|
|
||||||
if (city.civ.wantsToFocusOn(Victory.Focus.Science))
|
if (city.civ.wantsToFocusOn(Victory.Focus.Science))
|
||||||
yieldStats.science *= 2
|
yieldStats.science *= 2
|
||||||
|
yieldStats.science *= civPersonality.scaledFocus(PersonalityValue.Science)
|
||||||
|
|
||||||
|
yieldStats.production *= civPersonality.scaledFocus(PersonalityValue.Production)
|
||||||
|
yieldStats.faith *= civPersonality.scaledFocus(PersonalityValue.Faith)
|
||||||
|
yieldStats.food *= civPersonality.scaledFocus(PersonalityValue.Food)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply City focus
|
// Apply City focus
|
||||||
|
@ -14,6 +14,7 @@ import com.unciv.models.ruleset.INonPerpetualConstruction
|
|||||||
import com.unciv.models.ruleset.MilestoneType
|
import com.unciv.models.ruleset.MilestoneType
|
||||||
import com.unciv.models.ruleset.PerpetualConstruction
|
import com.unciv.models.ruleset.PerpetualConstruction
|
||||||
import com.unciv.models.ruleset.Victory
|
import com.unciv.models.ruleset.Victory
|
||||||
|
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
@ -92,6 +93,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
addDefenceBuildingChoice()
|
addDefenceBuildingChoice()
|
||||||
addUnitTrainingBuildingChoice()
|
addUnitTrainingBuildingChoice()
|
||||||
addCultureBuildingChoice()
|
addCultureBuildingChoice()
|
||||||
|
addFaithBuildingChoice()
|
||||||
addOtherBuildingChoice()
|
addOtherBuildingChoice()
|
||||||
|
|
||||||
if (!city.isPuppet) {
|
if (!city.isPuppet) {
|
||||||
@ -149,6 +151,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
modifier = 5f // there's a settler just sitting here, doing nothing - BAD
|
modifier = 5f // there's a settler just sitting here, doing nothing - BAD
|
||||||
|
|
||||||
if (civInfo.playerType == PlayerType.Human) modifier /= 2 // Players prefer to make their own unit choices usually
|
if (civInfo.playerType == PlayerType.Human) modifier /= 2 // Players prefer to make their own unit choices usually
|
||||||
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Military)
|
||||||
addChoice(relativeCostEffectiveness, militaryUnit, modifier)
|
addChoice(relativeCostEffectiveness, militaryUnit, modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,34 +210,30 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
val cultureBuilding = statBuildings
|
val cultureBuilding = statBuildings
|
||||||
.filter { it.isStatRelated(Stat.Culture) }
|
.filter { it.isStatRelated(Stat.Culture) }
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (cultureBuilding != null) {
|
var modifier = 0.5f
|
||||||
var modifier = 0.5f
|
if (city.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
||||||
if (city.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
modifier = 0.8f
|
||||||
modifier = 0.8f
|
if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) modifier = 1.6f
|
||||||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) modifier = 1.6f
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Culture)
|
||||||
addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSpaceshipPartChoice() {
|
private fun addSpaceshipPartChoice() {
|
||||||
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return
|
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return
|
||||||
val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull()
|
val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull()
|
||||||
if (spaceshipPart != null) {
|
?: return
|
||||||
val modifier = 2f
|
val modifier = 2f
|
||||||
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
|
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addOtherBuildingChoice() {
|
private fun addOtherBuildingChoice() {
|
||||||
val otherBuilding = nonWonders
|
val otherBuilding = nonWonders
|
||||||
.filter { Automation.allowAutomatedConstruction(civInfo, city, it) }
|
.filter { Automation.allowAutomatedConstruction(civInfo, city, it) }
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (otherBuilding != null) {
|
val modifier = 0.6f
|
||||||
val modifier = 0.6f
|
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
|
||||||
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getWonderPriority(wonder: Building): Float {
|
private fun getWonderPriority(wonder: Building): Float {
|
||||||
@ -289,12 +288,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
&& Automation.allowAutomatedConstruction(civInfo, city, it)
|
&& Automation.allowAutomatedConstruction(civInfo, city, it)
|
||||||
}
|
}
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (unitTrainingBuilding != null && (!civInfo.wantsToFocusOn(Victory.Focus.Culture) || isAtWar)) {
|
if ((isAtWar ||
|
||||||
|
!civInfo.wantsToFocusOn(Victory.Focus.Culture) || !city.civ.getPersonality().isNeutralPersonality)) {
|
||||||
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
||||||
if (isAtWar) modifier *= 2
|
if (isAtWar) modifier *= 2
|
||||||
if (civInfo.wantsToFocusOn(Victory.Focus.Military))
|
if (civInfo.wantsToFocusOn(Victory.Focus.Military))
|
||||||
modifier *= 1.3f
|
modifier *= 1.3f
|
||||||
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Military)
|
||||||
addChoice(relativeCostEffectiveness, unitTrainingBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, unitTrainingBuilding.name, modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,19 +306,16 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
&& Automation.allowAutomatedConstruction(civInfo, city, it)
|
&& Automation.allowAutomatedConstruction(civInfo, city, it)
|
||||||
}
|
}
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (defensiveBuilding != null && (isAtWar || !civInfo.wantsToFocusOn(Victory.Focus.Culture))) {
|
var modifier = 0.2f
|
||||||
var modifier = 0.2f
|
if (isAtWar) modifier = 0.5f
|
||||||
if (isAtWar) modifier = 0.5f
|
|
||||||
|
|
||||||
// If this city is the closest city to another civ, that makes it a likely candidate for attack
|
// If this city is the closest city to another civ, that makes it a likely candidate for attack
|
||||||
if (civInfo.getKnownCivs()
|
if (civInfo.getKnownCivs()
|
||||||
.mapNotNull { NextTurnAutomation.getClosestCities(civInfo, it) }
|
.mapNotNull { NextTurnAutomation.getClosestCities(civInfo, it) }
|
||||||
.any { it.city1 == city })
|
.any { it.city1 == city })
|
||||||
modifier *= 1.5f
|
modifier *= 1.5f
|
||||||
|
addChoice(relativeCostEffectiveness, defensiveBuilding.name, modifier)
|
||||||
addChoice(relativeCostEffectiveness, defensiveBuilding.name, modifier)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addHappinessBuildingChoice() {
|
private fun addHappinessBuildingChoice() {
|
||||||
@ -325,15 +323,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
.filter { it.isStatRelated(Stat.Happiness)
|
.filter { it.isStatRelated(Stat.Happiness)
|
||||||
&& Automation.allowAutomatedConstruction(civInfo, city, it) }
|
&& Automation.allowAutomatedConstruction(civInfo, city, it) }
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (happinessBuilding != null) {
|
var modifier = 1f
|
||||||
var modifier = 1f
|
val civHappiness = civInfo.getHappiness()
|
||||||
val civHappiness = civInfo.getHappiness()
|
if (civHappiness > 5) modifier = 1 / 2f // less desperate
|
||||||
if (civHappiness > 5) modifier = 1 / 2f // less desperate
|
if (civHappiness < 0) modifier = 3f // more desperate
|
||||||
if (civHappiness < 0) modifier = 3f // more desperate
|
else if (happinessBuilding.hasUnique(UniqueType.RemoveAnnexUnhappiness)) modifier = 2f // building courthouse is always important
|
||||||
else if (happinessBuilding.hasUnique(UniqueType.RemoveAnnexUnhappiness)) modifier = 2f // building courthouse is always important
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Happiness)
|
||||||
addChoice(relativeCostEffectiveness, happinessBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, happinessBuilding.name, modifier)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addScienceBuildingChoice() {
|
private fun addScienceBuildingChoice() {
|
||||||
@ -342,33 +339,30 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
.filter { it.isStatRelated(Stat.Science)
|
.filter { it.isStatRelated(Stat.Science)
|
||||||
&& Automation.allowAutomatedConstruction(civInfo, city, it) }
|
&& Automation.allowAutomatedConstruction(civInfo, city, it) }
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (scienceBuilding != null) {
|
var modifier = 1.1f
|
||||||
var modifier = 1.1f
|
if (civInfo.wantsToFocusOn(Victory.Focus.Science))
|
||||||
if (civInfo.wantsToFocusOn(Victory.Focus.Science))
|
modifier *= 1.4f
|
||||||
modifier *= 1.4f
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Science)
|
||||||
addChoice(relativeCostEffectiveness, scienceBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, scienceBuilding.name, modifier)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addGoldBuildingChoice() {
|
private fun addGoldBuildingChoice() {
|
||||||
val goldBuilding = statBuildings.filter { it.isStatRelated(Stat.Gold) }
|
val goldBuilding = statBuildings.filter { it.isStatRelated(Stat.Gold) }
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (goldBuilding != null) {
|
var modifier = if (civInfo.stats.statsForNextTurn.gold < 0) 3f else 1.2f
|
||||||
val modifier = if (civInfo.stats.statsForNextTurn.gold < 0) 3f else 1.2f
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Gold)
|
||||||
addChoice(relativeCostEffectiveness, goldBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, goldBuilding.name, modifier)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addProductionBuildingChoice() {
|
private fun addProductionBuildingChoice() {
|
||||||
val productionBuilding = statBuildings
|
val productionBuilding = statBuildings
|
||||||
.filter { it.isStatRelated(Stat.Production) }
|
.filter { it.isStatRelated(Stat.Production) }
|
||||||
.filterBuildable()
|
.filterBuildable()
|
||||||
.minByOrNull { it.cost }
|
.minByOrNull { it.cost } ?: return
|
||||||
if (productionBuilding != null) {
|
val modifier = city.civ.getPersonality().scaledFocus(PersonalityValue.Production)
|
||||||
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f)
|
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f * modifier)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFoodBuildingChoice() {
|
private fun addFoodBuildingChoice() {
|
||||||
@ -378,11 +372,23 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
(it.isStatRelated(Stat.Food)
|
(it.isStatRelated(Stat.Food)
|
||||||
|| it.hasUnique(UniqueType.CarryOverFood, conditionalState)
|
|| it.hasUnique(UniqueType.CarryOverFood, conditionalState)
|
||||||
) && Automation.allowAutomatedConstruction(civInfo, city, it)
|
) && Automation.allowAutomatedConstruction(civInfo, city, it)
|
||||||
}.filterBuildable().minByOrNull { it.cost }
|
}.filterBuildable().minByOrNull { it.cost } ?: return
|
||||||
if (foodBuilding != null) {
|
var modifier = 1f
|
||||||
var modifier = 1f
|
if (city.population.population < 5) modifier = 1.3f
|
||||||
if (city.population.population < 5) modifier = 1.3f
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Food)
|
||||||
addChoice(relativeCostEffectiveness, foodBuilding.name, modifier)
|
addChoice(relativeCostEffectiveness, foodBuilding.name, modifier)
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addFaithBuildingChoice() {
|
||||||
|
if (civInfo.gameInfo.isReligionEnabled()) return
|
||||||
|
val faithBuilding = statBuildings
|
||||||
|
.filter { it.isStatRelated(Stat.Faith) }
|
||||||
|
.filterBuildable()
|
||||||
|
.minByOrNull { it.cost } ?: return
|
||||||
|
var modifier = 0.5f
|
||||||
|
if (civInfo.wantsToFocusOn(Victory.Focus.Faith)) modifier = 1f
|
||||||
|
modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Faith)
|
||||||
|
addChoice(relativeCostEffectiveness, faithBuilding.name, modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ object DiplomacyAutomation {
|
|||||||
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedOpenBorders)
|
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedOpenBorders)
|
||||||
&& !isTradeBeingOffered(civInfo, it, Constants.openBorders)
|
&& !isTradeBeingOffered(civInfo, it, Constants.openBorders)
|
||||||
}
|
}
|
||||||
|
|
||||||
.sortedByDescending { it.getDiplomacyManager(civInfo).relationshipLevel() }.toList()
|
.sortedByDescending { it.getDiplomacyManager(civInfo).relationshipLevel() }.toList()
|
||||||
for (otherCiv in civsThatWeCanOpenBordersWith) {
|
for (otherCiv in civsThatWeCanOpenBordersWith) {
|
||||||
// Default setting is 3, this will be changed according to different civ.
|
// Default setting is 3, this will be changed according to different civ.
|
||||||
@ -234,7 +234,9 @@ object DiplomacyAutomation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun declareWar(civInfo: Civilization) {
|
internal fun declareWar(civInfo: Civilization) {
|
||||||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) return
|
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) &&
|
||||||
|
civInfo.getPersonality().isNeutralPersonality)
|
||||||
|
return
|
||||||
if (civInfo.cities.isEmpty() || civInfo.diplomacy.isEmpty()) return
|
if (civInfo.cities.isEmpty() || civInfo.diplomacy.isEmpty()) return
|
||||||
if (civInfo.isAtWar() || civInfo.getHappiness() <= 0) return
|
if (civInfo.isAtWar() || civInfo.getHappiness() <= 0) return
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import com.unciv.models.ruleset.MilestoneType
|
|||||||
import com.unciv.models.ruleset.Policy
|
import com.unciv.models.ruleset.Policy
|
||||||
import com.unciv.models.ruleset.PolicyBranch
|
import com.unciv.models.ruleset.PolicyBranch
|
||||||
import com.unciv.models.ruleset.Victory
|
import com.unciv.models.ruleset.Victory
|
||||||
|
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||||
import com.unciv.models.ruleset.tech.Technology
|
import com.unciv.models.ruleset.tech.Technology
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||||
@ -128,15 +129,30 @@ object NextTurnAutomation {
|
|||||||
|
|
||||||
internal fun valueCityStateAlliance(civInfo: Civilization, cityState: Civilization, includeQuests: Boolean = false): Int {
|
internal fun valueCityStateAlliance(civInfo: Civilization, cityState: Civilization, includeQuests: Boolean = false): Int {
|
||||||
var value = 0
|
var value = 0
|
||||||
|
val civPersonality = civInfo.getPersonality()
|
||||||
|
|
||||||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && cityState.cityStateFunctions.canProvideStat(Stat.Culture)) {
|
if (cityState.cityStateFunctions.canProvideStat(Stat.Culture)) {
|
||||||
value += 10
|
if (civInfo.wantsToFocusOn(Victory.Focus.Culture))
|
||||||
|
value += 10
|
||||||
|
value += civPersonality[PersonalityValue.Culture].toInt() - 5
|
||||||
}
|
}
|
||||||
else if (civInfo.wantsToFocusOn(Victory.Focus.Science) && cityState.cityStateFunctions.canProvideStat(Stat.Science)) {
|
if (cityState.cityStateFunctions.canProvideStat(Stat.Faith)) {
|
||||||
|
if (civInfo.wantsToFocusOn(Victory.Focus.Faith))
|
||||||
|
value += 10
|
||||||
|
value += civPersonality[PersonalityValue.Faith].toInt() - 5
|
||||||
|
}
|
||||||
|
if (cityState.cityStateFunctions.canProvideStat(Stat.Production)) {
|
||||||
|
if (civInfo.wantsToFocusOn(Victory.Focus.Production))
|
||||||
|
value += 10
|
||||||
|
value += civPersonality[PersonalityValue.Production].toInt() - 5
|
||||||
|
}
|
||||||
|
if (cityState.cityStateFunctions.canProvideStat(Stat.Science)) {
|
||||||
// In case someone mods this in
|
// In case someone mods this in
|
||||||
value += 10
|
if (civInfo.wantsToFocusOn(Victory.Focus.Science))
|
||||||
|
value += 10
|
||||||
|
value += civPersonality[PersonalityValue.Science].toInt() - 5
|
||||||
}
|
}
|
||||||
else if (civInfo.wantsToFocusOn(Victory.Focus.Military)) {
|
if (civInfo.wantsToFocusOn(Victory.Focus.Military)) {
|
||||||
if (!cityState.isAlive())
|
if (!cityState.isAlive())
|
||||||
value -= 5
|
value -= 5
|
||||||
else {
|
else {
|
||||||
@ -146,14 +162,16 @@ object NextTurnAutomation {
|
|||||||
value -= (20 - distance) / 4
|
value -= (20 - distance) / 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) {
|
if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) {
|
||||||
value += 5 // Generally be friendly
|
value += 5 // Generally be friendly
|
||||||
}
|
}
|
||||||
if (civInfo.getHappiness() < 5 && cityState.cityStateFunctions.canProvideStat(Stat.Happiness)) {
|
if (civInfo.getHappiness() < 5 && cityState.cityStateFunctions.canProvideStat(Stat.Happiness)) {
|
||||||
value += 10 - civInfo.getHappiness()
|
value += 10 - civInfo.getHappiness()
|
||||||
|
value += civPersonality[PersonalityValue.Happiness].toInt() - 5
|
||||||
}
|
}
|
||||||
if (civInfo.getHappiness() > 5 && cityState.cityStateFunctions.canProvideStat(Stat.Food)) {
|
if (civInfo.getHappiness() > 5 && cityState.cityStateFunctions.canProvideStat(Stat.Food)) {
|
||||||
value += 5
|
value += 5
|
||||||
|
value += civPersonality[PersonalityValue.Food].toInt() - 5
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty())
|
if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty())
|
||||||
@ -442,7 +460,9 @@ object NextTurnAutomation {
|
|||||||
private fun trainSettler(civInfo: Civilization) {
|
private fun trainSettler(civInfo: Civilization) {
|
||||||
if (civInfo.isCityState()) return
|
if (civInfo.isCityState()) return
|
||||||
if (civInfo.isAtWar()) return // don't train settlers when you could be training troops.
|
if (civInfo.isAtWar()) return // don't train settlers when you could be training troops.
|
||||||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && civInfo.cities.size > 3) return
|
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && civInfo.cities.size > 3 &&
|
||||||
|
civInfo.getPersonality().isNeutralPersonality)
|
||||||
|
return
|
||||||
if (civInfo.cities.none()) return
|
if (civInfo.cities.none()) return
|
||||||
if (civInfo.getHappiness() <= civInfo.cities.size) return
|
if (civInfo.getHappiness() <= civInfo.cities.size) return
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package com.unciv.logic.civilization
|
|||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
|
import com.unciv.UncivGame
|
||||||
import com.unciv.json.HashMapVector2
|
import com.unciv.json.HashMapVector2
|
||||||
import com.unciv.logic.GameInfo
|
import com.unciv.logic.GameInfo
|
||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
@ -40,6 +41,7 @@ import com.unciv.models.ruleset.Victory
|
|||||||
import com.unciv.models.ruleset.nation.CityStateType
|
import com.unciv.models.ruleset.nation.CityStateType
|
||||||
import com.unciv.models.ruleset.nation.Difficulty
|
import com.unciv.models.ruleset.nation.Difficulty
|
||||||
import com.unciv.models.ruleset.nation.Nation
|
import com.unciv.models.ruleset.nation.Nation
|
||||||
|
import com.unciv.models.ruleset.nation.Personality
|
||||||
import com.unciv.models.ruleset.tech.Era
|
import com.unciv.models.ruleset.tech.Era
|
||||||
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
import com.unciv.models.ruleset.tile.ResourceSupplyList
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
@ -352,24 +354,31 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) }
|
fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) }
|
||||||
private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
||||||
|
|
||||||
fun getPreferredVictoryType(): String {
|
fun getPreferredVictoryTypes(): List<String> {
|
||||||
val victoryTypes = gameInfo.gameParameters.victoryTypes
|
val victoryTypes = gameInfo.gameParameters.victoryTypes
|
||||||
if (victoryTypes.size == 1)
|
if (victoryTypes.size == 1)
|
||||||
return victoryTypes.first() // That is the most relevant one
|
return listOf(victoryTypes.first()) // That is the most relevant one
|
||||||
val victoryType = nation.preferredVictoryType
|
val victoryType: List<String> = listOf(nation.preferredVictoryType, getPersonality().preferredVictoryType)
|
||||||
return if (victoryType in gameInfo.ruleset.victories) victoryType
|
.filter { it in gameInfo.gameParameters.victoryTypes }
|
||||||
else Constants.neutralVictoryType
|
return victoryType.ifEmpty { listOf(Constants.neutralVictoryType) }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
fun getPreferredVictoryTypeObject(): Victory? {
|
fun getPreferredVictoryTypeObjects(): List<Victory> {
|
||||||
val preferredVictoryType = getPreferredVictoryType()
|
val preferredVictoryTypes = getPreferredVictoryTypes()
|
||||||
return if (preferredVictoryType == Constants.neutralVictoryType) null
|
return if (preferredVictoryTypes.contains(Constants.neutralVictoryType)) emptyList()
|
||||||
else gameInfo.ruleset.victories[getPreferredVictoryType()]!!
|
else preferredVictoryTypes.map { gameInfo.ruleset.victories[it]!! }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wantsToFocusOn(focus: Victory.Focus): Boolean {
|
fun wantsToFocusOn(focus: Victory.Focus): Boolean {
|
||||||
return thingsToFocusOnForVictory.contains(focus)
|
return thingsToFocusOnForVictory.contains(focus) &&
|
||||||
|
(isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPersonality(): Personality {
|
||||||
|
return if (isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) gameInfo.ruleset.personalities[nation.personality] ?: Personality.neutralPersonality
|
||||||
|
else Personality.neutralPersonality
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
|
@ -45,7 +45,9 @@ class PolicyManager : IsPartOfGameInfoSerialization {
|
|||||||
get() {
|
get() {
|
||||||
val value = HashMap<PolicyBranch, Int>()
|
val value = HashMap<PolicyBranch, Int>()
|
||||||
for (branch in branches) {
|
for (branch in branches) {
|
||||||
value[branch] = branch.priorities[civInfo.nation.preferredVictoryType] ?: 0
|
val victoryPriority = branch.priorities[civInfo.nation.preferredVictoryType] ?: 0
|
||||||
|
val personalityPriority = civInfo.getPersonality().policy[branch.name] ?: 0
|
||||||
|
value[branch] = victoryPriority + personalityPriority
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.unciv.models.metadata.BaseRuleset
|
|||||||
import com.unciv.models.ruleset.nation.CityStateType
|
import com.unciv.models.ruleset.nation.CityStateType
|
||||||
import com.unciv.models.ruleset.nation.Difficulty
|
import com.unciv.models.ruleset.nation.Difficulty
|
||||||
import com.unciv.models.ruleset.nation.Nation
|
import com.unciv.models.ruleset.nation.Nation
|
||||||
|
import com.unciv.models.ruleset.nation.Personality
|
||||||
import com.unciv.models.ruleset.tech.Era
|
import com.unciv.models.ruleset.tech.Era
|
||||||
import com.unciv.models.ruleset.tech.TechColumn
|
import com.unciv.models.ruleset.tech.TechColumn
|
||||||
import com.unciv.models.ruleset.tech.Technology
|
import com.unciv.models.ruleset.tech.Technology
|
||||||
@ -69,6 +70,7 @@ class Ruleset {
|
|||||||
val unitTypes = LinkedHashMap<String, UnitType>()
|
val unitTypes = LinkedHashMap<String, UnitType>()
|
||||||
var victories = LinkedHashMap<String, Victory>()
|
var victories = LinkedHashMap<String, Victory>()
|
||||||
var cityStateTypes = LinkedHashMap<String, CityStateType>()
|
var cityStateTypes = LinkedHashMap<String, CityStateType>()
|
||||||
|
val personalities = LinkedHashMap<String, Personality>()
|
||||||
|
|
||||||
val greatGeneralUnits by lazy {
|
val greatGeneralUnits by lazy {
|
||||||
units.values.filter { it.hasUnique(UniqueType.GreatPersonFromCombat, StateForConditionals.IgnoreConditionals) }
|
units.values.filter { it.hasUnique(UniqueType.GreatPersonFromCombat, StateForConditionals.IgnoreConditionals) }
|
||||||
@ -159,6 +161,7 @@ class Ruleset {
|
|||||||
units.remove(it)
|
units.remove(it)
|
||||||
}
|
}
|
||||||
units.putAll(ruleset.units)
|
units.putAll(ruleset.units)
|
||||||
|
personalities.putAll(ruleset.personalities)
|
||||||
modOptions.uniques.addAll(ruleset.modOptions.uniques)
|
modOptions.uniques.addAll(ruleset.modOptions.uniques)
|
||||||
modOptions.constants.merge(ruleset.modOptions.constants)
|
modOptions.constants.merge(ruleset.modOptions.constants)
|
||||||
|
|
||||||
@ -192,6 +195,7 @@ class Ruleset {
|
|||||||
unitTypes.clear()
|
unitTypes.clear()
|
||||||
victories.clear()
|
victories.clear()
|
||||||
cityStateTypes.clear()
|
cityStateTypes.clear()
|
||||||
|
personalities.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allRulesetObjects(): Sequence<IRulesetObject> =
|
fun allRulesetObjects(): Sequence<IRulesetObject> =
|
||||||
@ -214,7 +218,8 @@ class Ruleset {
|
|||||||
tileResources.values.asSequence() +
|
tileResources.values.asSequence() +
|
||||||
unitPromotions.values.asSequence() +
|
unitPromotions.values.asSequence() +
|
||||||
units.values.asSequence() +
|
units.values.asSequence() +
|
||||||
unitTypes.values.asSequence()
|
unitTypes.values.asSequence() +
|
||||||
|
personalities.values.asSequence()
|
||||||
// Victories is only INamed
|
// Victories is only INamed
|
||||||
fun allIHasUniques(): Sequence<IHasUniques> =
|
fun allIHasUniques(): Sequence<IHasUniques> =
|
||||||
allRulesetObjects() + sequenceOf(modOptions)
|
allRulesetObjects() + sequenceOf(modOptions)
|
||||||
@ -374,6 +379,11 @@ class Ruleset {
|
|||||||
cityStateTypes += createHashmap(json().fromJsonFile(Array<CityStateType>::class.java, cityStateTypesFile))
|
cityStateTypes += createHashmap(json().fromJsonFile(Array<CityStateType>::class.java, cityStateTypesFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val personalitiesFile = folderHandle.child("Personalities.json")
|
||||||
|
if (personalitiesFile.exists()) {
|
||||||
|
personalities += createHashmap(json().fromJsonFile(Array<Personality>::class.java, personalitiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add objects that might not be present in base ruleset mods, but are required
|
// Add objects that might not be present in base ruleset mods, but are required
|
||||||
|
@ -51,6 +51,7 @@ class Nation : RulesetObject() {
|
|||||||
var uniqueText = ""
|
var uniqueText = ""
|
||||||
var innerColor: List<Int>? = null
|
var innerColor: List<Int>? = null
|
||||||
var startBias = ArrayList<String>()
|
var startBias = ArrayList<String>()
|
||||||
|
var personality: String? = null
|
||||||
|
|
||||||
var startIntroPart1 = ""
|
var startIntroPart1 = ""
|
||||||
var startIntroPart2 = ""
|
var startIntroPart2 = ""
|
||||||
|
77
core/src/com/unciv/models/ruleset/nation/Personality.kt
Normal file
77
core/src/com/unciv/models/ruleset/nation/Personality.kt
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package com.unciv.models.ruleset.nation
|
||||||
|
|
||||||
|
import com.unciv.Constants
|
||||||
|
import com.unciv.models.ruleset.RulesetObject
|
||||||
|
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||||
|
import kotlin.reflect.KMutableProperty0
|
||||||
|
|
||||||
|
enum class PersonalityValue {
|
||||||
|
Production,
|
||||||
|
Food,
|
||||||
|
Gold,
|
||||||
|
Science,
|
||||||
|
Culture,
|
||||||
|
Happiness,
|
||||||
|
Faith,
|
||||||
|
Military,
|
||||||
|
WarMongering,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Personality: RulesetObject() {
|
||||||
|
var production: Float = 5f
|
||||||
|
var food: Float = 5f
|
||||||
|
var gold: Float = 5f
|
||||||
|
var science: Float = 5f
|
||||||
|
var culture: Float = 5f
|
||||||
|
var happiness: Float = 5f
|
||||||
|
var faith: Float = 5f
|
||||||
|
var military: Float = 5f
|
||||||
|
var warMongering: Float = 5f // Todo: Look into where this should be inserted
|
||||||
|
var policy = LinkedHashMap<String, Int>()
|
||||||
|
var preferredVictoryType: String = Constants.neutralVictoryType
|
||||||
|
var isNeutralPersonality: Boolean = false
|
||||||
|
|
||||||
|
private fun nameToVariable(value: PersonalityValue):KMutableProperty0<Float> {
|
||||||
|
return when(value) {
|
||||||
|
PersonalityValue.Production -> ::production
|
||||||
|
PersonalityValue.Food -> ::food
|
||||||
|
PersonalityValue.Gold -> ::gold
|
||||||
|
PersonalityValue.Science -> ::science
|
||||||
|
PersonalityValue.Culture -> ::culture
|
||||||
|
PersonalityValue.Happiness -> ::happiness
|
||||||
|
PersonalityValue.Faith -> ::faith
|
||||||
|
PersonalityValue.Military -> ::military
|
||||||
|
PersonalityValue.WarMongering -> ::warMongering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val neutralPersonality: Personality by lazy {
|
||||||
|
val base = Personality()
|
||||||
|
base.isNeutralPersonality = true
|
||||||
|
base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the value to a more meaningful range, where 10 is 2, and 5 is 1
|
||||||
|
*/
|
||||||
|
fun scaledFocus(value: PersonalityValue): Float {
|
||||||
|
return nameToVariable(value).get() / 5
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(value: PersonalityValue): Float {
|
||||||
|
return nameToVariable(value).get()
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun set(personalityValue: PersonalityValue, value: Float){
|
||||||
|
nameToVariable(personalityValue).set(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUniqueTarget() = UniqueTarget.Personality
|
||||||
|
|
||||||
|
override fun makeLink(): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -57,6 +57,7 @@ enum class UniqueTarget(
|
|||||||
Tutorial,
|
Tutorial,
|
||||||
CityState(inheritsFrom = Global),
|
CityState(inheritsFrom = Global),
|
||||||
ModOptions,
|
ModOptions,
|
||||||
|
Personality,
|
||||||
|
|
||||||
// Modifiers
|
// Modifiers
|
||||||
Conditional("Modifiers that can be added to other uniques to limit when they will be active", modifierType = ModifierType.Conditional),
|
Conditional("Modifiers that can be added to other uniques to limit when they will be active", modifierType = ModifierType.Conditional),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user