diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 3f9afe62ad..9bbfdfe0fc 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -649,8 +649,9 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion for (civInfo in civilizations) civInfo.setTransients() for (civInfo in civilizations) { - civInfo.thingsToFocusOnForVictory = - civInfo.getPreferredVictoryTypeObject()?.getThingsToFocus(civInfo) ?: setOf() + civInfo.thingsToFocusOnForVictory = emptySet() + for (victory in civInfo.getPreferredVictoryTypeObjects()) + civInfo.thingsToFocusOnForVictory.plus(victory.getThingsToFocus(civInfo)) } tileMap.setNeutralTransients() // has to happen after civInfo.setTransients() sets owningCity diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 7dbcb2da37..2916eb40c5 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -10,6 +10,7 @@ import com.unciv.logic.map.tile.Tile import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.INonPerpetualConstruction 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.TileImprovement 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 { val cityAIFocus = city.getCityFocus() val yieldStats = stats.clone() + val civPersonality = city.civ.getPersonality() if (specialist) { // If you have the Food Bonus, count as 1 extra food production (base is 2food) @@ -85,15 +87,23 @@ object Automation { } else { if (city.civ.gold < 0 && city.civ.stats.statsForNextTurn.gold <= 0) 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)) yieldStats.culture *= 2 + yieldStats.culture *= civPersonality.scaledFocus(PersonalityValue.Culture) if (city.civ.getHappiness() < 0 && !specialist) // since this doesn't get updated, may overshoot yieldStats.happiness *= 2 + yieldStats.happiness *= civPersonality.scaledFocus(PersonalityValue.Happiness) if (city.civ.wantsToFocusOn(Victory.Focus.Science)) 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 diff --git a/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt b/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt index 6732c3d91f..550dc24edd 100644 --- a/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt +++ b/core/src/com/unciv/logic/automation/city/ConstructionAutomation.kt @@ -14,6 +14,7 @@ import com.unciv.models.ruleset.INonPerpetualConstruction import com.unciv.models.ruleset.MilestoneType import com.unciv.models.ruleset.PerpetualConstruction 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.UniqueType import com.unciv.models.stats.Stat @@ -92,6 +93,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { addDefenceBuildingChoice() addUnitTrainingBuildingChoice() addCultureBuildingChoice() + addFaithBuildingChoice() addOtherBuildingChoice() if (!city.isPuppet) { @@ -149,6 +151,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { 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 + modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Military) addChoice(relativeCostEffectiveness, militaryUnit, modifier) } @@ -207,34 +210,30 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { val cultureBuilding = statBuildings .filter { it.isStatRelated(Stat.Culture) } .filterBuildable() - .minByOrNull { it.cost } - if (cultureBuilding != null) { - var modifier = 0.5f - if (city.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it - modifier = 0.8f - if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) modifier = 1.6f - addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier) - } + .minByOrNull { it.cost } ?: return + var modifier = 0.5f + if (city.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it + modifier = 0.8f + if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) modifier = 1.6f + modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Culture) + addChoice(relativeCostEffectiveness, cultureBuilding.name, modifier) } private fun addSpaceshipPartChoice() { if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull() - if (spaceshipPart != null) { - val modifier = 2f - addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier) - } + ?: return + val modifier = 2f + addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier) } private fun addOtherBuildingChoice() { val otherBuilding = nonWonders .filter { Automation.allowAutomatedConstruction(civInfo, city, it) } .filterBuildable() - .minByOrNull { it.cost } - if (otherBuilding != null) { - val modifier = 0.6f - addChoice(relativeCostEffectiveness, otherBuilding.name, modifier) - } + .minByOrNull { it.cost } ?: return + val modifier = 0.6f + addChoice(relativeCostEffectiveness, otherBuilding.name, modifier) } private fun getWonderPriority(wonder: Building): Float { @@ -289,12 +288,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { && Automation.allowAutomatedConstruction(civInfo, city, it) } .filterBuildable() - .minByOrNull { it.cost } - if (unitTrainingBuilding != null && (!civInfo.wantsToFocusOn(Victory.Focus.Culture) || isAtWar)) { + .minByOrNull { it.cost } ?: return + 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 if (isAtWar) modifier *= 2 if (civInfo.wantsToFocusOn(Victory.Focus.Military)) modifier *= 1.3f + modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Military) addChoice(relativeCostEffectiveness, unitTrainingBuilding.name, modifier) } } @@ -305,19 +306,16 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { && Automation.allowAutomatedConstruction(civInfo, city, it) } .filterBuildable() - .minByOrNull { it.cost } - if (defensiveBuilding != null && (isAtWar || !civInfo.wantsToFocusOn(Victory.Focus.Culture))) { - var modifier = 0.2f - if (isAtWar) modifier = 0.5f + .minByOrNull { it.cost } ?: return + var modifier = 0.2f + if (isAtWar) modifier = 0.5f - // If this city is the closest city to another civ, that makes it a likely candidate for attack - if (civInfo.getKnownCivs() - .mapNotNull { NextTurnAutomation.getClosestCities(civInfo, it) } - .any { it.city1 == city }) - modifier *= 1.5f - - addChoice(relativeCostEffectiveness, defensiveBuilding.name, modifier) - } + // If this city is the closest city to another civ, that makes it a likely candidate for attack + if (civInfo.getKnownCivs() + .mapNotNull { NextTurnAutomation.getClosestCities(civInfo, it) } + .any { it.city1 == city }) + modifier *= 1.5f + addChoice(relativeCostEffectiveness, defensiveBuilding.name, modifier) } private fun addHappinessBuildingChoice() { @@ -325,15 +323,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { .filter { it.isStatRelated(Stat.Happiness) && Automation.allowAutomatedConstruction(civInfo, city, it) } .filterBuildable() - .minByOrNull { it.cost } - if (happinessBuilding != null) { - var modifier = 1f - val civHappiness = civInfo.getHappiness() - if (civHappiness > 5) modifier = 1 / 2f // less desperate - if (civHappiness < 0) modifier = 3f // more desperate - else if (happinessBuilding.hasUnique(UniqueType.RemoveAnnexUnhappiness)) modifier = 2f // building courthouse is always important - addChoice(relativeCostEffectiveness, happinessBuilding.name, modifier) - } + .minByOrNull { it.cost } ?: return + var modifier = 1f + val civHappiness = civInfo.getHappiness() + if (civHappiness > 5) modifier = 1 / 2f // less desperate + if (civHappiness < 0) modifier = 3f // more desperate + 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) } private fun addScienceBuildingChoice() { @@ -342,33 +339,30 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { .filter { it.isStatRelated(Stat.Science) && Automation.allowAutomatedConstruction(civInfo, city, it) } .filterBuildable() - .minByOrNull { it.cost } - if (scienceBuilding != null) { - var modifier = 1.1f - if (civInfo.wantsToFocusOn(Victory.Focus.Science)) - modifier *= 1.4f - addChoice(relativeCostEffectiveness, scienceBuilding.name, modifier) - } + .minByOrNull { it.cost } ?: return + var modifier = 1.1f + if (civInfo.wantsToFocusOn(Victory.Focus.Science)) + modifier *= 1.4f + modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Science) + addChoice(relativeCostEffectiveness, scienceBuilding.name, modifier) } private fun addGoldBuildingChoice() { val goldBuilding = statBuildings.filter { it.isStatRelated(Stat.Gold) } .filterBuildable() - .minByOrNull { it.cost } - if (goldBuilding != null) { - val modifier = if (civInfo.stats.statsForNextTurn.gold < 0) 3f else 1.2f - addChoice(relativeCostEffectiveness, goldBuilding.name, modifier) - } + .minByOrNull { it.cost } ?: return + var modifier = if (civInfo.stats.statsForNextTurn.gold < 0) 3f else 1.2f + modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Gold) + addChoice(relativeCostEffectiveness, goldBuilding.name, modifier) } private fun addProductionBuildingChoice() { val productionBuilding = statBuildings .filter { it.isStatRelated(Stat.Production) } .filterBuildable() - .minByOrNull { it.cost } - if (productionBuilding != null) { - addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f) - } + .minByOrNull { it.cost } ?: return + val modifier = city.civ.getPersonality().scaledFocus(PersonalityValue.Production) + addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f * modifier) } private fun addFoodBuildingChoice() { @@ -378,11 +372,23 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) { (it.isStatRelated(Stat.Food) || it.hasUnique(UniqueType.CarryOverFood, conditionalState) ) && Automation.allowAutomatedConstruction(civInfo, city, it) - }.filterBuildable().minByOrNull { it.cost } - if (foodBuilding != null) { - var modifier = 1f - if (city.population.population < 5) modifier = 1.3f - addChoice(relativeCostEffectiveness, foodBuilding.name, modifier) - } + }.filterBuildable().minByOrNull { it.cost } ?: return + var modifier = 1f + if (city.population.population < 5) modifier = 1.3f + modifier *= city.civ.getPersonality().scaledFocus(PersonalityValue.Food) + 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) } } diff --git a/core/src/com/unciv/logic/automation/civilization/DiplomacyAutomation.kt b/core/src/com/unciv/logic/automation/civilization/DiplomacyAutomation.kt index f636d14130..eb6bc8bd3c 100644 --- a/core/src/com/unciv/logic/automation/civilization/DiplomacyAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/DiplomacyAutomation.kt @@ -100,7 +100,7 @@ object DiplomacyAutomation { && !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedOpenBorders) && !isTradeBeingOffered(civInfo, it, Constants.openBorders) } - + .sortedByDescending { it.getDiplomacyManager(civInfo).relationshipLevel() }.toList() for (otherCiv in civsThatWeCanOpenBordersWith) { // Default setting is 3, this will be changed according to different civ. @@ -234,7 +234,9 @@ object DiplomacyAutomation { } 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.isAtWar() || civInfo.getHappiness() <= 0) return diff --git a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt index 6b1ce39e9a..3be4dab015 100644 --- a/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/NextTurnAutomation.kt @@ -20,6 +20,7 @@ import com.unciv.models.ruleset.MilestoneType import com.unciv.models.ruleset.Policy import com.unciv.models.ruleset.PolicyBranch 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.tile.ResourceType import com.unciv.models.ruleset.unique.StateForConditionals @@ -128,15 +129,30 @@ object NextTurnAutomation { internal fun valueCityStateAlliance(civInfo: Civilization, cityState: Civilization, includeQuests: Boolean = false): Int { var value = 0 + val civPersonality = civInfo.getPersonality() - if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && cityState.cityStateFunctions.canProvideStat(Stat.Culture)) { - value += 10 + if (cityState.cityStateFunctions.canProvideStat(Stat.Culture)) { + 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 - 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()) value -= 5 else { @@ -146,14 +162,16 @@ object NextTurnAutomation { value -= (20 - distance) / 4 } } - else if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) { + if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) { value += 5 // Generally be friendly } if (civInfo.getHappiness() < 5 && cityState.cityStateFunctions.canProvideStat(Stat.Happiness)) { value += 10 - civInfo.getHappiness() + value += civPersonality[PersonalityValue.Happiness].toInt() - 5 } if (civInfo.getHappiness() > 5 && cityState.cityStateFunctions.canProvideStat(Stat.Food)) { value += 5 + value += civPersonality[PersonalityValue.Food].toInt() - 5 } if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty()) @@ -442,7 +460,9 @@ object NextTurnAutomation { private fun trainSettler(civInfo: Civilization) { if (civInfo.isCityState()) return 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.getHappiness() <= civInfo.cities.size) return diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 97a148cf78..1e5a4193ef 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -2,6 +2,7 @@ package com.unciv.logic.civilization import com.badlogic.gdx.math.Vector2 import com.unciv.Constants +import com.unciv.UncivGame import com.unciv.json.HashMapVector2 import com.unciv.logic.GameInfo 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.Difficulty 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.tile.ResourceSupplyList import com.unciv.models.ruleset.tile.ResourceType @@ -352,24 +354,31 @@ class Civilization : IsPartOfGameInfoSerialization { fun getCompletedPolicyBranchesCount(): Int = policies.adoptedPolicies.count { Policy.isBranchCompleteByName(it) } private fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() } - fun getPreferredVictoryType(): String { + fun getPreferredVictoryTypes(): List { val victoryTypes = gameInfo.gameParameters.victoryTypes if (victoryTypes.size == 1) - return victoryTypes.first() // That is the most relevant one - val victoryType = nation.preferredVictoryType - return if (victoryType in gameInfo.ruleset.victories) victoryType - else Constants.neutralVictoryType + return listOf(victoryTypes.first()) // That is the most relevant one + val victoryType: List = listOf(nation.preferredVictoryType, getPersonality().preferredVictoryType) + .filter { it in gameInfo.gameParameters.victoryTypes } + return victoryType.ifEmpty { listOf(Constants.neutralVictoryType) } + } @Suppress("MemberVisibilityCanBePrivate") - fun getPreferredVictoryTypeObject(): Victory? { - val preferredVictoryType = getPreferredVictoryType() - return if (preferredVictoryType == Constants.neutralVictoryType) null - else gameInfo.ruleset.victories[getPreferredVictoryType()]!! + fun getPreferredVictoryTypeObjects(): List { + val preferredVictoryTypes = getPreferredVictoryTypes() + return if (preferredVictoryTypes.contains(Constants.neutralVictoryType)) emptyList() + else preferredVictoryTypes.map { gameInfo.ruleset.victories[it]!! } } 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 diff --git a/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt b/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt index 3bbce33b3b..9431f88bbe 100644 --- a/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt @@ -45,7 +45,9 @@ class PolicyManager : IsPartOfGameInfoSerialization { get() { val value = HashMap() 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 } diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index c2b378c9ca..4b7529c65f 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -8,6 +8,7 @@ import com.unciv.models.metadata.BaseRuleset import com.unciv.models.ruleset.nation.CityStateType import com.unciv.models.ruleset.nation.Difficulty 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.TechColumn import com.unciv.models.ruleset.tech.Technology @@ -69,6 +70,7 @@ class Ruleset { val unitTypes = LinkedHashMap() var victories = LinkedHashMap() var cityStateTypes = LinkedHashMap() + val personalities = LinkedHashMap() val greatGeneralUnits by lazy { units.values.filter { it.hasUnique(UniqueType.GreatPersonFromCombat, StateForConditionals.IgnoreConditionals) } @@ -159,6 +161,7 @@ class Ruleset { units.remove(it) } units.putAll(ruleset.units) + personalities.putAll(ruleset.personalities) modOptions.uniques.addAll(ruleset.modOptions.uniques) modOptions.constants.merge(ruleset.modOptions.constants) @@ -192,6 +195,7 @@ class Ruleset { unitTypes.clear() victories.clear() cityStateTypes.clear() + personalities.clear() } fun allRulesetObjects(): Sequence = @@ -214,7 +218,8 @@ class Ruleset { tileResources.values.asSequence() + unitPromotions.values.asSequence() + units.values.asSequence() + - unitTypes.values.asSequence() + unitTypes.values.asSequence() + + personalities.values.asSequence() // Victories is only INamed fun allIHasUniques(): Sequence = allRulesetObjects() + sequenceOf(modOptions) @@ -374,6 +379,11 @@ class Ruleset { cityStateTypes += createHashmap(json().fromJsonFile(Array::class.java, cityStateTypesFile)) } + val personalitiesFile = folderHandle.child("Personalities.json") + if (personalitiesFile.exists()) { + personalities += createHashmap(json().fromJsonFile(Array::class.java, personalitiesFile)) + } + // Add objects that might not be present in base ruleset mods, but are required diff --git a/core/src/com/unciv/models/ruleset/nation/Nation.kt b/core/src/com/unciv/models/ruleset/nation/Nation.kt index 379076507e..e25cfd6ef4 100644 --- a/core/src/com/unciv/models/ruleset/nation/Nation.kt +++ b/core/src/com/unciv/models/ruleset/nation/Nation.kt @@ -51,6 +51,7 @@ class Nation : RulesetObject() { var uniqueText = "" var innerColor: List? = null var startBias = ArrayList() + var personality: String? = null var startIntroPart1 = "" var startIntroPart2 = "" diff --git a/core/src/com/unciv/models/ruleset/nation/Personality.kt b/core/src/com/unciv/models/ruleset/nation/Personality.kt new file mode 100644 index 0000000000..ff181bc5b6 --- /dev/null +++ b/core/src/com/unciv/models/ruleset/nation/Personality.kt @@ -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() + var preferredVictoryType: String = Constants.neutralVictoryType + var isNeutralPersonality: Boolean = false + + private fun nameToVariable(value: PersonalityValue):KMutableProperty0 { + 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 "" + } + +} diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueTarget.kt b/core/src/com/unciv/models/ruleset/unique/UniqueTarget.kt index 525e2ca40f..380d2bdf49 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueTarget.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueTarget.kt @@ -57,6 +57,7 @@ enum class UniqueTarget( Tutorial, CityState(inheritsFrom = Global), ModOptions, + Personality, // Modifiers Conditional("Modifiers that can be added to other uniques to limit when they will be active", modifierType = ModifierType.Conditional),