mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 09:48:12 +07:00
Add personality uses (#11807)
* Reduced max motivationToAttack from relative combat strength * Refactored MotivationToAttackAutomation to use a float instead of an int * Added personality values to MotivationToAttackAutomation * Fixed minor build errors * Added DiplomacyAutomation personality modifiers * Improved ConstructionAutomation personality values * Added some more personality implementations * Tweaked trainSettler * Fixed civilizations asking for help against city-states * Adjusted DiplomacyAutomation to account for war mongering * Fixed typo
This commit is contained in:
@ -233,8 +233,8 @@ object Automation {
|
||||
if (civInfo.gameInfo.turns > 120 * speed.barbarianModifier * multiplier)
|
||||
multiplier /= 2
|
||||
|
||||
// If we have a lot of, or no cities we are not afraid
|
||||
if (civInfo.cities.isEmpty() || civInfo.cities.size >= 4 * multiplier)
|
||||
// If we have no cities or a lot of units we are not afraid
|
||||
if (civInfo.cities.isEmpty() || civInfo.units.getCivUnits().count() >= 4 * multiplier)
|
||||
return false
|
||||
|
||||
// If we have vision of our entire starting continent (ish) we are not afraid
|
||||
|
@ -183,7 +183,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
modifier = 5f // there's a settler just sitting here, doing nothing - BAD
|
||||
|
||||
if (!civInfo.isAIOrAutoPlaying()) modifier /= 2 // Players prefer to make their own unit choices usually
|
||||
modifier *= personality.scaledFocus(PersonalityValue.Aggressive)
|
||||
modifier *= personality.modifierFocus(PersonalityValue.Military, .3f)
|
||||
addChoice(relativeCostEffectiveness, militaryUnit, modifier)
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return
|
||||
val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull()
|
||||
?: return
|
||||
val modifier = 2f
|
||||
val modifier = 3f * personality.modifierFocus(PersonalityValue.Science, .4f)
|
||||
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
|
||||
}
|
||||
|
||||
@ -287,8 +287,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
var value = 0f
|
||||
if (!cityIsOverAverageProduction) return value
|
||||
if (building.isWonder) value += 2f
|
||||
if (building.hasUnique(UniqueType.TriggersCulturalVictory)) value += 10f
|
||||
if (building.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) value += 10f
|
||||
if (building.hasUnique(UniqueType.TriggersCulturalVictory)) value += 10f * personality.modifierFocus(PersonalityValue.Culture, .3f)
|
||||
if (building.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) value += 10f * personality.modifierFocus(PersonalityValue.Science, .3f)
|
||||
return value
|
||||
}
|
||||
|
||||
@ -300,8 +300,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
.mapNotNull { NextTurnAutomation.getClosestCities(civInfo, it) }
|
||||
.any { it.city1 == city })
|
||||
warModifier *= 2f
|
||||
value += warModifier * building.cityHealth.toFloat() / city.getMaxHealth()
|
||||
value += warModifier * building.cityStrength.toFloat() / (city.getStrength() + 3) // The + 3 here is to reduce the priority of building walls immedietly
|
||||
value += warModifier * building.cityHealth.toFloat() / city.getMaxHealth() * personality.inverseModifierFocus(PersonalityValue.Aggressive, .3f)
|
||||
value += warModifier * building.cityStrength.toFloat() / (city.getStrength() + 3) * personality.inverseModifierFocus(PersonalityValue.Aggressive, .3f) // The + 3 here is to reduce the priority of building walls immedietly
|
||||
|
||||
for (experienceUnique in building.getMatchingUniques(UniqueType.UnitStartingExperience, cityState)) {
|
||||
var modifier = experienceUnique.params[1].toFloat() / 5
|
||||
@ -346,7 +346,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
if (civInfo.wantsToFocusOn(stat))
|
||||
buildingStats[stat] *= 2f
|
||||
|
||||
buildingStats[stat] *= personality.scaledFocus(PersonalityValue[stat])
|
||||
buildingStats[stat] *= personality.modifierFocus(PersonalityValue[stat], .5f)
|
||||
}
|
||||
|
||||
return Automation.rankStatsValue(civInfo.getPersonality().scaleStats(buildingStats.clone(), .3f), civInfo)
|
||||
|
@ -18,18 +18,18 @@ object DeclareWarPlanEvaluator {
|
||||
* This style of declaring war favors fighting stronger civilizations.
|
||||
* @return The movtivation of the plan. If it is > 0 then we can declare the war.
|
||||
*/
|
||||
fun evaluateTeamWarPlan(civInfo: Civilization, target: Civilization, teamCiv: Civilization, givenMotivation: Int?): Int {
|
||||
fun evaluateTeamWarPlan(civInfo: Civilization, target: Civilization, teamCiv: Civilization, givenMotivation: Float?): Float {
|
||||
val teamCivDiplo = civInfo.getDiplomacyManager(teamCiv)!!
|
||||
if (civInfo.getPersonality()[PersonalityValue.DeclareWar] == 0f) return -1000
|
||||
if (teamCivDiplo.isRelationshipLevelLT(RelationshipLevel.Neutral)) return -1000
|
||||
if (civInfo.getPersonality()[PersonalityValue.DeclareWar] == 0f) return -1000f
|
||||
if (teamCivDiplo.isRelationshipLevelLT(RelationshipLevel.Neutral)) return -1000f
|
||||
|
||||
var motivation = givenMotivation
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0)
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0f)
|
||||
|
||||
if (teamCivDiplo.isRelationshipLevelEQ(RelationshipLevel.Neutral)) motivation -= 5
|
||||
if (teamCivDiplo.isRelationshipLevelEQ(RelationshipLevel.Neutral)) motivation -= 5f
|
||||
// Make sure that they can actually help us with the target
|
||||
if (!teamCiv.threatManager.getNeighboringCivilizations().contains(target)) {
|
||||
motivation -= 40
|
||||
motivation -= 40f
|
||||
}
|
||||
|
||||
val civForce = civInfo.getStatForRanking(RankingType.Force)
|
||||
@ -46,11 +46,11 @@ object DeclareWarPlanEvaluator {
|
||||
if (civForce + teamCivForce < targetForce * multiplier) {
|
||||
// We are weaker then them even with our combined forces
|
||||
// If they have twice our combined force we will have -30 motivation
|
||||
motivation -= (30 * ((targetForce * multiplier) / (teamCivForce + civForce) - 1)).toInt()
|
||||
motivation -= 30 * ((targetForce * multiplier) / (teamCivForce + civForce) - 1)
|
||||
} else if (civForce + teamCivForce > targetForce * 2) {
|
||||
// Why gang up on such a weaker enemy when we can declare war ourselves?
|
||||
// If our combined force is twice their force we will have -20 motivation
|
||||
motivation -= (20 * ((civForce + teamCivForce) / targetForce * 2) - 1).toInt()
|
||||
motivation -= 20 * ((civForce + teamCivForce) / targetForce * 2) - 1
|
||||
}
|
||||
|
||||
val civScore = civInfo.getStatForRanking(RankingType.Score)
|
||||
@ -59,9 +59,9 @@ object DeclareWarPlanEvaluator {
|
||||
|
||||
if (teamCivScore > civScore * 1.4f && teamCivScore >= targetCivScore) {
|
||||
// If teamCiv has more score than us and the target they are likely in a good position already
|
||||
motivation -= (20 * ((teamCivScore / (civScore * 1.4f)) - 1)).toInt()
|
||||
motivation -= 20 * ((teamCivScore / (civScore * 1.4f)) - 1)
|
||||
}
|
||||
return motivation - 20
|
||||
return motivation - 20f
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,20 +70,20 @@ object DeclareWarPlanEvaluator {
|
||||
* Favors protecting allies.
|
||||
* @return The movtivation of the plan. If it is > 0 then we can declare the war.
|
||||
*/
|
||||
fun evaluateJoinWarPlan(civInfo: Civilization, target: Civilization, civToJoin: Civilization, givenMotivation: Int?): Int {
|
||||
fun evaluateJoinWarPlan(civInfo: Civilization, target: Civilization, civToJoin: Civilization, givenMotivation: Float?): Float {
|
||||
val thirdCivDiplo = civInfo.getDiplomacyManager(civToJoin)!!
|
||||
if (civInfo.getPersonality()[PersonalityValue.DeclareWar] == 0f) return -1000
|
||||
if (thirdCivDiplo.isRelationshipLevelLE(RelationshipLevel.Favorable)) return -1000
|
||||
if (civInfo.getPersonality()[PersonalityValue.DeclareWar] == 0f) return -1000f
|
||||
if (thirdCivDiplo.isRelationshipLevelLE(RelationshipLevel.Favorable)) return -1000f
|
||||
|
||||
var motivation = givenMotivation
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0)
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0f)
|
||||
// We need to be able to trust the thirdCiv at least somewhat
|
||||
if (thirdCivDiplo.diplomaticStatus != DiplomaticStatus.DefensivePact &&
|
||||
thirdCivDiplo.opinionOfOtherCiv() + motivation * 2 < 80) {
|
||||
motivation -= 80 - (thirdCivDiplo.opinionOfOtherCiv() + motivation * 2).toInt()
|
||||
motivation -= 80f - thirdCivDiplo.opinionOfOtherCiv() + motivation * 2
|
||||
}
|
||||
if (!civToJoin.threatManager.getNeighboringCivilizations().contains(target)) {
|
||||
motivation -= 20
|
||||
motivation -= 20f
|
||||
}
|
||||
|
||||
val targetForce = target.getStatForRanking(RankingType.Force) - 0.8f * target.getCivsAtWarWith().sumOf { it.getStatForRanking(RankingType.Force) }.coerceAtLeast(100)
|
||||
@ -93,7 +93,7 @@ object DeclareWarPlanEvaluator {
|
||||
val civToJoinForce = (civToJoin.getStatForRanking(RankingType.Force) - 0.8f * civToJoin.getCivsAtWarWith().sumOf { it.getStatForRanking(RankingType.Force) }).coerceAtLeast(100f)
|
||||
if (civToJoinForce < targetForce / 2) {
|
||||
// Make sure that there is no wrap around
|
||||
motivation -= (10 * (targetForce / civToJoinForce)).toInt().coerceIn(-1000, 1000)
|
||||
motivation -= 10 * (targetForce / civToJoinForce).coerceIn(-1000f, 1000f)
|
||||
}
|
||||
|
||||
// A higher motivation means that we can be riskier
|
||||
@ -105,7 +105,7 @@ object DeclareWarPlanEvaluator {
|
||||
else -> 0.8f
|
||||
}
|
||||
if (civToJoinForce + civForce < targetForce * multiplier) {
|
||||
motivation -= (20 * (targetForce * multiplier) / (civToJoinForce + civForce)).toInt().coerceIn(-1000, 1000)
|
||||
motivation -= 20 * (targetForce * multiplier) / (civToJoinForce + civForce).coerceIn(-1000f, 1000f)
|
||||
}
|
||||
|
||||
return motivation - 15
|
||||
@ -116,11 +116,11 @@ object DeclareWarPlanEvaluator {
|
||||
*
|
||||
* @return The movtivation of the plan. If it is > 0 then we can declare the war.
|
||||
*/
|
||||
fun evaluateJoinOurWarPlan(civInfo: Civilization, target: Civilization, civToJoin: Civilization, givenMotivation: Int?): Int {
|
||||
if (civInfo.getDiplomacyManager(civToJoin)!!.isRelationshipLevelLT(RelationshipLevel.Favorable)) return -1000
|
||||
var motivation = givenMotivation ?: 0
|
||||
fun evaluateJoinOurWarPlan(civInfo: Civilization, target: Civilization, civToJoin: Civilization, givenMotivation: Float?): Float {
|
||||
if (civInfo.getDiplomacyManager(civToJoin)!!.isRelationshipLevelLT(RelationshipLevel.Favorable)) return -1000f
|
||||
var motivation = givenMotivation ?: 0f
|
||||
if (!civToJoin.threatManager.getNeighboringCivilizations().contains(target)) {
|
||||
motivation -= 50
|
||||
motivation -= 50f
|
||||
}
|
||||
|
||||
val targetForce = target.getStatForRanking(RankingType.Force)
|
||||
@ -128,10 +128,10 @@ object DeclareWarPlanEvaluator {
|
||||
|
||||
// They need to be at least half the targets size
|
||||
val thirdCivForce = (civToJoin.getStatForRanking(RankingType.Force) - 0.8f * civToJoin.getCivsAtWarWith().sumOf { it.getStatForRanking(RankingType.Force) }).coerceAtLeast(100f)
|
||||
motivation += (20 * thirdCivForce / targetForce.toFloat()).toInt().coerceAtMost(40)
|
||||
motivation += 20 * thirdCivForce / targetForce.toFloat().coerceAtMost(40f)
|
||||
|
||||
// If we have less relative force then the target then we have more motivation to accept
|
||||
motivation += (30 * (1 - (civForce / targetForce.toFloat()))).toInt().coerceIn(-30, 30)
|
||||
motivation += 30 * (1 - (civForce / targetForce.toFloat())).coerceIn(-30f, 30f)
|
||||
|
||||
return motivation - 20
|
||||
}
|
||||
@ -142,15 +142,15 @@ object DeclareWarPlanEvaluator {
|
||||
*
|
||||
* @return The movtivation of the plan. If it is > 0 then we can declare the war.
|
||||
*/
|
||||
fun evaluateDeclareWarPlan(civInfo: Civilization, target: Civilization, givenMotivation: Int?): Int {
|
||||
if (civInfo.getPersonality()[PersonalityValue.DeclareWar] == 0f) return -1000
|
||||
fun evaluateDeclareWarPlan(civInfo: Civilization, target: Civilization, givenMotivation: Float?): Float {
|
||||
if (civInfo.getPersonality()[PersonalityValue.DeclareWar] == 0f) return -1000f
|
||||
val motivation = givenMotivation
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0)
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0f)
|
||||
|
||||
val diploManager = civInfo.getDiplomacyManager(target)!!
|
||||
|
||||
if (diploManager.hasFlag(DiplomacyFlags.WaryOf) && diploManager.getFlag(DiplomacyFlags.WaryOf) < 0) {
|
||||
val turnsToPlan = (10 - (motivation / 10)).coerceAtLeast(3)
|
||||
val turnsToPlan = (10 - (motivation / 10)).coerceAtLeast(3f)
|
||||
val turnsToWait = turnsToPlan + diploManager.getFlag(DiplomacyFlags.WaryOf)
|
||||
return motivation - turnsToWait * 3
|
||||
}
|
||||
@ -163,14 +163,14 @@ object DeclareWarPlanEvaluator {
|
||||
*
|
||||
* @return The motivation of the plan. If it is > 0 then we can start planning the war.
|
||||
*/
|
||||
fun evaluateStartPreparingWarPlan(civInfo: Civilization, target: Civilization, givenMotivation: Int?): Int {
|
||||
fun evaluateStartPreparingWarPlan(civInfo: Civilization, target: Civilization, givenMotivation: Float?): Float {
|
||||
val motivation = givenMotivation
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0)
|
||||
?: MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civInfo, target, 0f)
|
||||
|
||||
// TODO: We use negative values in WaryOf for now so that we aren't adding any extra fields to the save file
|
||||
// This will very likely change in the future and we will want to build upon it
|
||||
val diploManager = civInfo.getDiplomacyManager(target)!!
|
||||
if (diploManager.hasFlag(DiplomacyFlags.WaryOf)) return 0
|
||||
if (diploManager.hasFlag(DiplomacyFlags.WaryOf)) return 0f
|
||||
|
||||
return motivation - 15
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ object DeclareWarTargetAutomation {
|
||||
* Chooses a target civilization along with a plan of attack.
|
||||
* Note that this doesn't guarantee that we will declare war on them immediatly, or that we will end up declaring war at all.
|
||||
*/
|
||||
fun chooseDeclareWarTarget(civInfo: Civilization, civAttackMotivations: List<Pair<Civilization, Int>>) {
|
||||
fun chooseDeclareWarTarget(civInfo: Civilization, civAttackMotivations: List<Pair<Civilization, Float>>) {
|
||||
val highestValueTargets = civAttackMotivations.sortedByDescending { it.first.getStatForRanking(RankingType.Score) }
|
||||
|
||||
for (target in highestValueTargets) {
|
||||
@ -27,7 +27,7 @@ object DeclareWarTargetAutomation {
|
||||
/**
|
||||
* Determines a war plan against this [target] and executes it if able.
|
||||
*/
|
||||
private fun tryDeclareWarWithPlan(civInfo: Civilization, target: Civilization, motivation: Int): Boolean {
|
||||
private fun tryDeclareWarWithPlan(civInfo: Civilization, target: Civilization, motivation: Float): Boolean {
|
||||
|
||||
if (!target.isCityState()) {
|
||||
if (motivation > 5 && tryTeamWar(civInfo, target, motivation)) return true
|
||||
@ -46,7 +46,7 @@ object DeclareWarTargetAutomation {
|
||||
* The safest option for war is to invite a new ally to join the war with us.
|
||||
* Together we are stronger and are more likely to take down bigger threats.
|
||||
*/
|
||||
private fun tryTeamWar(civInfo: Civilization, target: Civilization, motivation: Int): Boolean {
|
||||
private fun tryTeamWar(civInfo: Civilization, target: Civilization, motivation: Float): Boolean {
|
||||
val potentialAllies = civInfo.getDiplomacyManager(target)!!.getCommonKnownCivs()
|
||||
.filter {
|
||||
it.isMajorCiv()
|
||||
@ -74,7 +74,7 @@ object DeclareWarTargetAutomation {
|
||||
/**
|
||||
* The next safest aproach is to join an existing war on the side of an ally that is already at war with [target].
|
||||
*/
|
||||
private fun tryJoinWar(civInfo: Civilization, target: Civilization, motivation: Int): Boolean {
|
||||
private fun tryJoinWar(civInfo: Civilization, target: Civilization, motivation: Float): Boolean {
|
||||
val potentialAllies = civInfo.getDiplomacyManager(target)!!.getCommonKnownCivs()
|
||||
.filter {
|
||||
it.isMajorCiv()
|
||||
@ -102,7 +102,7 @@ object DeclareWarTargetAutomation {
|
||||
/**
|
||||
* Lastly, if our motivation is high enough and we don't have any better plans then lets just declare war.
|
||||
*/
|
||||
private fun declareWar(civInfo: Civilization, target: Civilization, motivation: Int): Boolean {
|
||||
private fun declareWar(civInfo: Civilization, target: Civilization, motivation: Float): Boolean {
|
||||
if (DeclareWarPlanEvaluator.evaluateDeclareWarPlan(civInfo, target, motivation) > 0) {
|
||||
civInfo.getDiplomacyManager(target)!!.declareWar()
|
||||
return true
|
||||
@ -113,7 +113,7 @@ object DeclareWarTargetAutomation {
|
||||
/**
|
||||
* Slightly safter is to silently plan an invasion and declare war later.
|
||||
*/
|
||||
private fun prepareWar(civInfo: Civilization, target: Civilization, motivation: Int): Boolean {
|
||||
private fun prepareWar(civInfo: Civilization, target: Civilization, motivation: Float): Boolean {
|
||||
// TODO: We use negative values in WaryOf for now so that we aren't adding any extra fields to the save file
|
||||
// This will very likely change in the future and we will want to build upon it
|
||||
val diploManager = civInfo.getDiplomacyManager(target)!!
|
||||
|
@ -8,6 +8,7 @@ import com.unciv.logic.civilization.AlertType
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.PopupAlert
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
|
||||
import com.unciv.logic.trade.TradeEvaluation
|
||||
@ -33,7 +34,8 @@ object DiplomacyAutomation {
|
||||
.sortedByDescending { it.getDiplomacyManager(civInfo)!!.relationshipLevel() }.toList()
|
||||
for (otherCiv in civsThatWeCanDeclareFriendshipWith) {
|
||||
// Default setting is 2, this will be changed according to different civ.
|
||||
if ((1..10).random() <= 2 && wantsToSignDeclarationOfFrienship(civInfo, otherCiv)) {
|
||||
if ((1..10).random() <= 2 * civInfo.getPersonality().modifierFocus(PersonalityValue.Diplomacy, .5f)
|
||||
&& wantsToSignDeclarationOfFrienship(civInfo, otherCiv)) {
|
||||
otherCiv.popupAlerts.add(PopupAlert(AlertType.DeclarationOfFriendship, civInfo.civName))
|
||||
}
|
||||
}
|
||||
@ -53,7 +55,12 @@ object DiplomacyAutomation {
|
||||
val allAliveCivs = allCivs - deadCivs
|
||||
|
||||
// Motivation should be constant as the number of civs changes
|
||||
var motivation = diploManager.opinionOfOtherCiv().toInt() - 40
|
||||
var motivation = diploManager.opinionOfOtherCiv() - 40f
|
||||
|
||||
// Warmongerers don't make good allies
|
||||
if (diploManager.hasModifier(DiplomaticModifiers.WarMongerer)) {
|
||||
motivation -= diploManager.getModifier(DiplomaticModifiers.WarMongerer) * civInfo.getPersonality().modifierFocus(PersonalityValue.Diplomacy, .5f)
|
||||
}
|
||||
|
||||
// If the other civ is stronger than we are compelled to be nice to them
|
||||
// If they are too weak, then thier friendship doesn't mean much to us
|
||||
@ -65,13 +72,13 @@ object DiplomacyAutomation {
|
||||
}
|
||||
|
||||
// Try to ally with a fourth of the civs in play
|
||||
val civsToAllyWith = 0.25f * allAliveCivs
|
||||
val civsToAllyWith = 0.25f * allAliveCivs * civInfo.getPersonality().modifierFocus(PersonalityValue.Diplomacy, .3f)
|
||||
if (numOfFriends < civsToAllyWith) {
|
||||
// Goes from 10 to 0 once the civ gets 1/4 of all alive civs as friends
|
||||
motivation += (10 - 10 * (numOfFriends / civsToAllyWith)).toInt()
|
||||
motivation += (10 - 10 * numOfFriends / civsToAllyWith)
|
||||
} else {
|
||||
// Goes from 0 to -120 as the civ gets more friends, offset by civsToAllyWith
|
||||
motivation -= (120f * (numOfFriends - civsToAllyWith) / (knownCivs - civsToAllyWith)).toInt()
|
||||
motivation -= (120f * (numOfFriends - civsToAllyWith) / (knownCivs - civsToAllyWith))
|
||||
}
|
||||
|
||||
// The more friends they have the less we should want to sign friendship (To promote teams)
|
||||
@ -88,7 +95,7 @@ object DiplomacyAutomation {
|
||||
// Wait to declare frienships until more civs
|
||||
// Goes from -30 to 0 when we know 75% of allCivs
|
||||
val civsToKnow = 0.75f * allAliveCivs
|
||||
motivation -= ((civsToKnow - knownCivs) / civsToKnow * 30f).toInt().coerceAtLeast(0)
|
||||
motivation -= ((civsToKnow - knownCivs) / civsToKnow * 30f).coerceAtLeast(0f)
|
||||
|
||||
// If they are the only non-friendly civ near us then they are the only civ to attack and expand into
|
||||
if (civInfo.threatManager.getNeighboringCivilizations().none {
|
||||
@ -97,7 +104,7 @@ object DiplomacyAutomation {
|
||||
})
|
||||
motivation -= 20
|
||||
|
||||
motivation -= hasAtLeastMotivationToAttack(civInfo, otherCiv, motivation / 2) * 2
|
||||
motivation -= hasAtLeastMotivationToAttack(civInfo, otherCiv, motivation / 2f) * 2
|
||||
|
||||
return motivation > 0
|
||||
}
|
||||
@ -139,7 +146,8 @@ object DiplomacyAutomation {
|
||||
// Being able to see their cities can give us an advantage later on, especially with espionage enabled
|
||||
if (otherCiv.cities.count { !it.getCenterTile().isVisible(civInfo) } < otherCiv.cities.count() * .8f)
|
||||
return true
|
||||
if (hasAtLeastMotivationToAttack(civInfo, otherCiv, (diploManager.opinionOfOtherCiv() / 2).toInt()) > 0)
|
||||
if (hasAtLeastMotivationToAttack(civInfo, otherCiv,
|
||||
diploManager.opinionOfOtherCiv() * civInfo.getPersonality().modifierFocus(PersonalityValue.Commerce, .3f) / 2) > 0)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
@ -157,7 +165,7 @@ object DiplomacyAutomation {
|
||||
|
||||
for (otherCiv in canSignResearchAgreementCiv) {
|
||||
// Default setting is 5, this will be changed according to different civ.
|
||||
if ((1..10).random() <= 5) continue
|
||||
if ((1..10).random() <= 5 * civInfo.getPersonality().modifierFocus(PersonalityValue.Science, .3f)) continue
|
||||
val tradeLogic = TradeLogic(civInfo, otherCiv)
|
||||
val cost = civInfo.diplomacyFunctions.getResearchAgreementCost(otherCiv)
|
||||
tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.researchAgreement, TradeType.Treaty, cost))
|
||||
@ -174,13 +182,13 @@ object DiplomacyAutomation {
|
||||
.filter {
|
||||
civInfo.diplomacyFunctions.canSignDefensivePactWith(it)
|
||||
&& !civInfo.getDiplomacyManager(it)!!.hasFlag(DiplomacyFlags.DeclinedDefensivePact)
|
||||
&& civInfo.getDiplomacyManager(it)!!.opinionOfOtherCiv() < 70f
|
||||
&& civInfo.getDiplomacyManager(it)!!.opinionOfOtherCiv() < 70f * civInfo.getPersonality().inverseModifierFocus(PersonalityValue.Aggressive, .2f)
|
||||
&& !isTradeBeingOffered(civInfo, it, Constants.defensivePact)
|
||||
}
|
||||
|
||||
for (otherCiv in canSignDefensivePactCiv) {
|
||||
// Default setting is 3, this will be changed according to different civ.
|
||||
if ((1..10).random() <= 7) continue
|
||||
if ((1..10).random() <= 7 * civInfo.getPersonality().inverseModifierFocus(PersonalityValue.Loyal, .3f)) continue
|
||||
if (wantsToSignDefensivePact(civInfo, otherCiv)) {
|
||||
//todo: Add more in depth evaluation here
|
||||
val tradeLogic = TradeLogic(civInfo, otherCiv)
|
||||
@ -198,7 +206,7 @@ object DiplomacyAutomation {
|
||||
fun wantsToSignDefensivePact(civInfo: Civilization, otherCiv: Civilization): Boolean {
|
||||
val diploManager = civInfo.getDiplomacyManager(otherCiv)!!
|
||||
if (diploManager.hasFlag(DiplomacyFlags.DeclinedDefensivePact)) return false
|
||||
if (diploManager.opinionOfOtherCiv() < 60f) return false
|
||||
if (diploManager.opinionOfOtherCiv() < 65f * civInfo.getPersonality().inverseModifierFocus(PersonalityValue.Aggressive, .3f)) return false
|
||||
val commonknownCivs = diploManager.getCommonKnownCivs()
|
||||
for (thirdCiv in commonknownCivs) {
|
||||
// If they have bad relations with any of our friends, don't consider it
|
||||
@ -222,28 +230,33 @@ object DiplomacyAutomation {
|
||||
val allAliveCivs = allCivs - deadCivs
|
||||
|
||||
// We have to already be at RelationshipLevel.Ally, so we must have 80 oppinion of them
|
||||
var motivation = diploManager.opinionOfOtherCiv().toInt() - 80
|
||||
var motivation = diploManager.opinionOfOtherCiv() - 80
|
||||
|
||||
// Warmongerers don't make good allies
|
||||
if (diploManager.hasModifier(DiplomaticModifiers.WarMongerer)) {
|
||||
motivation -= diploManager.getModifier(DiplomaticModifiers.WarMongerer) * civInfo.getPersonality().modifierFocus(PersonalityValue.Diplomacy, .5f)
|
||||
}
|
||||
|
||||
// If they are stronger than us, then we value it a lot more
|
||||
// If they are weaker than us, then we don't value it
|
||||
motivation += when (Automation.threatAssessment(civInfo, otherCiv)) {
|
||||
ThreatLevel.VeryHigh -> 10
|
||||
ThreatLevel.High -> 5
|
||||
ThreatLevel.Low -> -5
|
||||
ThreatLevel.VeryLow -> -10
|
||||
ThreatLevel.Low -> -3
|
||||
ThreatLevel.VeryLow -> -7
|
||||
else -> 0
|
||||
}
|
||||
|
||||
// If they have a defensive pact with another civ then we would get drawn into thier battles as well
|
||||
// If they have a defensive pact with another civ then we would get drawn into their battles as well
|
||||
motivation -= 15 * otherCivNonOverlappingDefensivePacts
|
||||
|
||||
// Becomre more desperate as we have more wars
|
||||
motivation += civInfo.diplomacy.values.count { it.otherCiv().isMajorCiv() && it.diplomaticStatus == DiplomaticStatus.War } * 5
|
||||
|
||||
// Try to have a defensive pact with 1/5 of all civs
|
||||
val civsToAllyWith = 0.20f * allAliveCivs
|
||||
// Goes from 0 to -50 as the civ gets more allies, offset by civsToAllyWith
|
||||
motivation -= (50f * (defensivePacts - civsToAllyWith) / (allAliveCivs - civsToAllyWith)).coerceAtMost(0f).toInt()
|
||||
val civsToAllyWith = 0.20f * allAliveCivs * civInfo.getPersonality().modifierFocus(PersonalityValue.Diplomacy, .5f)
|
||||
// Goes from 0 to -40 as the civ gets more allies, offset by civsToAllyWith
|
||||
motivation -= (40f * (defensivePacts - civsToAllyWith) / (allAliveCivs - civsToAllyWith)).coerceAtMost(0f)
|
||||
|
||||
return motivation > 0
|
||||
}
|
||||
@ -270,8 +283,8 @@ object DiplomacyAutomation {
|
||||
|
||||
if (targetCivs.none()) return
|
||||
|
||||
val targetCivsWithMotivation: List<Pair<Civilization, Int>> = targetCivs
|
||||
.map { Pair(it, hasAtLeastMotivationToAttack(civInfo, it, 0)) }
|
||||
val targetCivsWithMotivation: List<Pair<Civilization, Float>> = targetCivs
|
||||
.map { Pair(it, hasAtLeastMotivationToAttack(civInfo, it, 0f)) }
|
||||
.filter { it.second > 0 }.toList()
|
||||
|
||||
DeclareWarTargetAutomation.chooseDeclareWarTarget(civInfo, targetCivsWithMotivation)
|
||||
@ -293,7 +306,7 @@ object DiplomacyAutomation {
|
||||
.filter { it.tradeRequests.none { tradeRequest -> tradeRequest.requestingCiv == civInfo.civName && tradeRequest.trade.isPeaceTreaty() } }
|
||||
|
||||
for (enemy in enemiesCiv) {
|
||||
if (hasAtLeastMotivationToAttack(civInfo, enemy, 10) >= 10) {
|
||||
if (hasAtLeastMotivationToAttack(civInfo, enemy, 10f) >= 10) {
|
||||
// We can still fight. Refuse peace.
|
||||
continue
|
||||
}
|
||||
@ -336,7 +349,8 @@ object DiplomacyAutomation {
|
||||
internal fun askForHelp(civInfo: Civilization) {
|
||||
if (!civInfo.isAtWar() || civInfo.cities.isEmpty() || civInfo.diplomacy.isEmpty()) return
|
||||
|
||||
for (enemyCiv in civInfo.getCivsAtWarWith().sortedByDescending { it.getStatForRanking(RankingType.Force) }) {
|
||||
val enemyCivs = civInfo.getCivsAtWarWith().filter { it.isMajorCiv() }.sortedByDescending { it.getStatForRanking(RankingType.Force) }
|
||||
for (enemyCiv in enemyCivs) {
|
||||
val potentialAllies = enemyCiv.threatManager.getNeighboringCivilizations()
|
||||
.filter {
|
||||
civInfo.knows(it) && !it.isAtWarWith(enemyCiv)
|
||||
|
@ -12,6 +12,7 @@ import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.MapPathing
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.ui.screens.victoryscreen.RankingType
|
||||
@ -20,33 +21,38 @@ object MotivationToAttackAutomation {
|
||||
|
||||
/** Will return the motivation to attack, but might short circuit if the value is guaranteed to
|
||||
* be lower than `atLeast`. So any values below `atLeast` should not be used for comparison. */
|
||||
fun hasAtLeastMotivationToAttack(civInfo: Civilization, targetCiv: Civilization, atLeast: Int): Int {
|
||||
fun hasAtLeastMotivationToAttack(civInfo: Civilization, targetCiv: Civilization, atLeast: Float): Float {
|
||||
val diplomacyManager = civInfo.getDiplomacyManager(targetCiv)!!
|
||||
val personality = civInfo.getPersonality()
|
||||
|
||||
val targetCitiesWithOurCity = civInfo.threatManager.getNeighboringCitiesOfOtherCivs().filter { it.second.civ == targetCiv }.toList()
|
||||
val targetCities = targetCitiesWithOurCity.map { it.second }
|
||||
|
||||
if (targetCitiesWithOurCity.isEmpty()) return 0
|
||||
if (targetCitiesWithOurCity.isEmpty()) return 0f
|
||||
|
||||
if (targetCities.all { hasNoUnitsThatCanAttackCityWithoutDying(civInfo, it) })
|
||||
return 0
|
||||
return 0f
|
||||
|
||||
val baseForce = 100f
|
||||
|
||||
val ourCombatStrength = calculateSelfCombatStrength(civInfo, baseForce)
|
||||
val theirCombatStrength = calculateCombatStrengthWithProtectors(targetCiv, baseForce, civInfo)
|
||||
|
||||
val modifiers:MutableList<Pair<String, Int>> = mutableListOf()
|
||||
modifiers.add(Pair("Base motivation", -15))
|
||||
val modifiers: MutableList<Pair<String, Float>> = mutableListOf()
|
||||
|
||||
modifiers.add(Pair("Relative combat strength", getCombatStrengthModifier(ourCombatStrength, theirCombatStrength + 0.8f * civInfo.threatManager.getCombinedForceOfWarringCivs())))
|
||||
// If our personality is to declare war more then we should have a higher base motivation (a negative number closer to 0)
|
||||
modifiers.add(Pair("Base motivation", -(15f * personality.inverseModifierFocus(PersonalityValue.DeclareWar, 0.5f))))
|
||||
|
||||
modifiers.add(Pair("Relative combat strength", getCombatStrengthModifier(civInfo, ourCombatStrength, theirCombatStrength + 0.8f * civInfo.threatManager.getCombinedForceOfWarringCivs())))
|
||||
// TODO: For now this will be a very high value because the AI can't handle multiple fronts, this should be changed later though
|
||||
modifiers.add(Pair("Concurrent wars", -civInfo.getCivsAtWarWith().count { it.isMajorCiv() && it != targetCiv } * 20))
|
||||
modifiers.add(Pair("Their concurrent wars", targetCiv.getCivsAtWarWith().count { it.isMajorCiv() } * 3))
|
||||
modifiers.add(Pair("Concurrent wars", -civInfo.getCivsAtWarWith().count { it.isMajorCiv() && it != targetCiv } * 20f))
|
||||
modifiers.add(Pair("Their concurrent wars", targetCiv.getCivsAtWarWith().count { it.isMajorCiv() } * 3f))
|
||||
|
||||
modifiers.add(Pair("Their allies", getDefensivePactAlliesScore(targetCiv, civInfo, baseForce, ourCombatStrength)))
|
||||
|
||||
if (civInfo.threatManager.getNeighboringCivilizations().none { it != targetCiv && it.isMajorCiv()
|
||||
&& civInfo.getDiplomacyManager(it)!!.isRelationshipLevelLT(RelationshipLevel.Friend) })
|
||||
modifiers.add(Pair("No other threats", 10))
|
||||
modifiers.add(Pair("No other threats", 10f))
|
||||
|
||||
if (targetCiv.isMajorCiv()) {
|
||||
val scoreRatioModifier = getScoreRatioModifier(targetCiv, civInfo)
|
||||
@ -55,61 +61,65 @@ object MotivationToAttackAutomation {
|
||||
modifiers.add(Pair("Relative technologies", getRelativeTechModifier(civInfo, targetCiv)))
|
||||
|
||||
if (civInfo.stats.getUnitSupplyDeficit() != 0) {
|
||||
modifiers.add(Pair("Over unit supply", (civInfo.stats.getUnitSupplyDeficit() * 2).coerceAtMost(20)))
|
||||
modifiers.add(Pair("Over unit supply", (civInfo.stats.getUnitSupplyDeficit() * 2f).coerceAtMost(20f)))
|
||||
} else if (targetCiv.stats.getUnitSupplyDeficit() == 0 && !targetCiv.isCityState()) {
|
||||
modifiers.add(Pair("Relative production", getProductionRatioModifier(civInfo, targetCiv)))
|
||||
}
|
||||
}
|
||||
|
||||
val minTargetCityDistance = targetCitiesWithOurCity.minOf { it.second.getCenterTile().aerialDistanceTo(it.first.getCenterTile()) }
|
||||
// Defensive civs should avoid fighting civilizations that are farther away and don't pose a threat
|
||||
modifiers.add(Pair("Far away cities", when {
|
||||
minTargetCityDistance > 20 -> -10
|
||||
minTargetCityDistance > 14 -> -8
|
||||
minTargetCityDistance > 10 -> -3
|
||||
else -> 0
|
||||
}))
|
||||
if (minTargetCityDistance < 6) modifiers.add(Pair("Close cities", 5))
|
||||
minTargetCityDistance > 20 -> -10f
|
||||
minTargetCityDistance > 14 -> -8f
|
||||
minTargetCityDistance > 10 -> -3f
|
||||
else -> 0f
|
||||
} * personality.inverseModifierFocus(PersonalityValue.Aggressive, 0.2f)))
|
||||
|
||||
val diplomacyManager = civInfo.getDiplomacyManager(targetCiv)!!
|
||||
// Defensive civs want to deal with potential nearby cities to protect themselves
|
||||
if (minTargetCityDistance < 6)
|
||||
modifiers.add(Pair("Close cities", 5f * personality.inverseModifierFocus(PersonalityValue.Aggressive, 1f)))
|
||||
|
||||
if (diplomacyManager.hasFlag(DiplomacyFlags.ResearchAgreement))
|
||||
modifiers.add(Pair("Research Agreement", -5))
|
||||
modifiers.add(Pair("Research Agreement", -5f * personality.modifierFocus(PersonalityValue.Loyal, .2f)
|
||||
* personality.scaledFocus(PersonalityValue.Science) * personality.modifierFocus(PersonalityValue.Commerce, .3f)))
|
||||
|
||||
if (diplomacyManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship))
|
||||
modifiers.add(Pair("Declaration of Friendship", -10))
|
||||
modifiers.add(Pair("Declaration of Friendship", -10f * personality.modifierFocus(PersonalityValue.Loyal, .5f)))
|
||||
|
||||
if (diplomacyManager.hasFlag(DiplomacyFlags.DefensivePact))
|
||||
modifiers.add(Pair("Defensive Pact", -15))
|
||||
modifiers.add(Pair("Defensive Pact", -15f * personality.modifierFocus(PersonalityValue.Loyal, .3f)))
|
||||
|
||||
modifiers.add(Pair("Relationship", getRelationshipModifier(diplomacyManager)))
|
||||
|
||||
if (diplomacyManager.hasFlag(DiplomacyFlags.Denunciation)) {
|
||||
modifiers.add(Pair("Denunciation", 5))
|
||||
modifiers.add(Pair("Denunciation", 5f * personality.inverseModifierFocus(PersonalityValue.Diplomacy, .5f)))
|
||||
}
|
||||
|
||||
if (diplomacyManager.hasFlag(DiplomacyFlags.WaryOf) && diplomacyManager.getFlag(DiplomacyFlags.WaryOf) < 0) {
|
||||
modifiers.add(Pair("PlanningAttack", -diplomacyManager.getFlag(DiplomacyFlags.WaryOf)))
|
||||
// Completely defensive civs will plan defensively and have a 0 here
|
||||
modifiers.add(Pair("PlanningAttack", -diplomacyManager.getFlag(DiplomacyFlags.WaryOf) * personality.scaledFocus(PersonalityValue.Aggressive) / 2))
|
||||
} else {
|
||||
val attacksPlanned = civInfo.diplomacy.values.count { it.hasFlag(DiplomacyFlags.WaryOf) && it.getFlag(DiplomacyFlags.WaryOf) < 0 }
|
||||
modifiers.add(Pair("PlanningAttackAgainstOtherCivs", -attacksPlanned * 5))
|
||||
modifiers.add(Pair("PlanningAttackAgainstOtherCivs", -attacksPlanned * 5f * personality.inverseModifierFocus(PersonalityValue.Aggressive, .5f)))
|
||||
}
|
||||
|
||||
if (diplomacyManager.resourcesFromTrade().any { it.amount > 0 })
|
||||
modifiers.add(Pair("Receiving trade resources", -5))
|
||||
modifiers.add(Pair("Receiving trade resources", -8f * personality.modifierFocus(PersonalityValue.Commerce, .5f) * personality.modifierFocus(PersonalityValue.Loyal, .2f)))
|
||||
|
||||
// If their cities don't have any nearby cities that are also targets to us and it doesn't include their capital
|
||||
// Then there cities are likely isolated and a good target.
|
||||
if (targetCiv.getCapital(true) !in targetCities
|
||||
&& targetCities.all { theirCity -> !theirCity.neighboringCities.any { it !in targetCities } }) {
|
||||
modifiers.add(Pair("Isolated city", 15))
|
||||
modifiers.add(Pair("Isolated city", 10f * personality.modifierFocus(PersonalityValue.Aggressive, .8f)))
|
||||
}
|
||||
|
||||
if (targetCiv.isCityState()) {
|
||||
modifiers.add(Pair("Protectors", -targetCiv.cityStateFunctions.getProtectorCivs().size * 3))
|
||||
modifiers.add(Pair("Protectors", -targetCiv.cityStateFunctions.getProtectorCivs().size * 3f))
|
||||
if (targetCiv.cityStateFunctions.getProtectorCivs().contains(civInfo))
|
||||
modifiers.add(Pair("Under our protection", -15))
|
||||
modifiers.add(Pair("Under our protection", -15 * personality.modifierFocus(PersonalityValue.Loyal, .8f)))
|
||||
if (targetCiv.getAllyCiv() == civInfo.civName)
|
||||
modifiers.add(Pair("Allied City-state", -20)) // There had better be a DAMN good reason
|
||||
modifiers.add(Pair("Allied City-state", -20 * personality.modifierFocus(PersonalityValue.Loyal, .8f))) // There had better be a DAMN good reason
|
||||
}
|
||||
|
||||
addWonderBasedMotivations(targetCiv, modifiers)
|
||||
@ -117,8 +127,8 @@ object MotivationToAttackAutomation {
|
||||
modifiers.add(Pair("War with allies", getAlliedWarMotivation(civInfo, targetCiv)))
|
||||
|
||||
// Purely for debugging, remove modifiers that don't have an effect
|
||||
modifiers.removeAll { it.second == 0 }
|
||||
var motivationSoFar = modifiers.sumOf { it.second }
|
||||
modifiers.removeAll { it.second == 0f }
|
||||
var motivationSoFar = modifiers.map { it.second }.sum()
|
||||
|
||||
// Short-circuit to avoid A-star
|
||||
if (motivationSoFar < atLeast) return motivationSoFar
|
||||
@ -145,69 +155,70 @@ object MotivationToAttackAutomation {
|
||||
return ourCombatStrength
|
||||
}
|
||||
|
||||
private fun addWonderBasedMotivations(otherCiv: Civilization, modifiers: MutableList<Pair<String, Int>>) {
|
||||
private fun addWonderBasedMotivations(otherCiv: Civilization, modifiers: MutableList<Pair<String, Float>>) {
|
||||
var wonderCount = 0
|
||||
for (city in otherCiv.cities) {
|
||||
val construction = city.cityConstructions.getCurrentConstruction()
|
||||
if (construction is Building && construction.hasUnique(UniqueType.TriggersCulturalVictory))
|
||||
modifiers.add(Pair("About to win", 15))
|
||||
modifiers.add(Pair("About to win", 15f))
|
||||
if (construction is BaseUnit && construction.hasUnique(UniqueType.AddInCapital))
|
||||
modifiers.add(Pair("About to win", 15))
|
||||
modifiers.add(Pair("About to win", 15f))
|
||||
wonderCount += city.cityConstructions.getBuiltBuildings().count { it.isWonder }
|
||||
}
|
||||
|
||||
// The more wonders they have, the more beneficial it is to conquer them
|
||||
// Civs need an army to protect thier wonders which give the most score
|
||||
if (wonderCount > 0)
|
||||
modifiers.add(Pair("Owned Wonders", wonderCount))
|
||||
modifiers.add(Pair("Owned Wonders", wonderCount.toFloat()))
|
||||
}
|
||||
|
||||
/** If they are at war with our allies, then we should join in */
|
||||
private fun getAlliedWarMotivation(civInfo: Civilization, otherCiv: Civilization): Int {
|
||||
var alliedWarMotivation = 0
|
||||
private fun getAlliedWarMotivation(civInfo: Civilization, otherCiv: Civilization): Float {
|
||||
var alliedWarMotivation = 0f
|
||||
for (thirdCiv in civInfo.getDiplomacyManager(otherCiv)!!.getCommonKnownCivs()) {
|
||||
val thirdCivDiploManager = civInfo.getDiplomacyManager(thirdCiv)
|
||||
if (thirdCivDiploManager!!.isRelationshipLevelGE(RelationshipLevel.Friend)
|
||||
&& thirdCiv.isAtWarWith(otherCiv)
|
||||
) {
|
||||
if (thirdCiv.getDiplomacyManager(otherCiv)!!.hasFlag(DiplomacyFlags.Denunciation))
|
||||
alliedWarMotivation += 2
|
||||
alliedWarMotivation += if (thirdCivDiploManager.hasFlag(DiplomacyFlags.DefensivePact)) 15
|
||||
else if (thirdCivDiploManager.isRelationshipLevelGT(RelationshipLevel.Friend)) 5
|
||||
else 2
|
||||
if (thirdCivDiploManager!!.isRelationshipLevelLT(RelationshipLevel.Friend)) continue
|
||||
|
||||
if (thirdCiv.getDiplomacyManager(otherCiv)!!.hasFlag(DiplomacyFlags.Denunciation))
|
||||
alliedWarMotivation += 2f
|
||||
|
||||
if (thirdCiv.isAtWarWith(otherCiv)) {
|
||||
alliedWarMotivation += if (thirdCivDiploManager.hasFlag(DiplomacyFlags.DefensivePact)) 15f
|
||||
else if (thirdCivDiploManager.hasFlag(DiplomacyFlags.DeclarationOfFriendship)) 5f
|
||||
else 2f
|
||||
}
|
||||
}
|
||||
return alliedWarMotivation
|
||||
return alliedWarMotivation * civInfo.getPersonality().modifierFocus(PersonalityValue.Loyal, .5f)
|
||||
}
|
||||
|
||||
private fun getRelationshipModifier(diplomacyManager: DiplomacyManager): Int {
|
||||
private fun getRelationshipModifier(diplomacyManager: DiplomacyManager): Float {
|
||||
val relationshipModifier = when (diplomacyManager.relationshipIgnoreAfraid()) {
|
||||
RelationshipLevel.Unforgivable -> 15
|
||||
RelationshipLevel.Enemy -> 10
|
||||
RelationshipLevel.Competitor -> 5
|
||||
RelationshipLevel.Favorable -> -2
|
||||
RelationshipLevel.Friend -> -5
|
||||
RelationshipLevel.Ally -> -10 // this is so that ally + DoF is not too unbalanced -
|
||||
RelationshipLevel.Unforgivable -> 15f
|
||||
RelationshipLevel.Enemy -> 10f
|
||||
RelationshipLevel.Competitor -> 5f
|
||||
RelationshipLevel.Favorable -> -2f
|
||||
RelationshipLevel.Friend -> -5f
|
||||
RelationshipLevel.Ally -> -10f // this is so that ally + DoF is not too unbalanced -
|
||||
// still possible for AI to declare war for isolated city
|
||||
else -> 0
|
||||
else -> 0f
|
||||
}
|
||||
return relationshipModifier
|
||||
return relationshipModifier * diplomacyManager.civInfo.getPersonality().modifierFocus(PersonalityValue.Diplomacy, .3f)
|
||||
}
|
||||
|
||||
private fun getRelativeTechModifier(civInfo: Civilization, otherCiv: Civilization): Int {
|
||||
private fun getRelativeTechModifier(civInfo: Civilization, otherCiv: Civilization): Float {
|
||||
val relativeTech = civInfo.getStatForRanking(RankingType.Technologies) - otherCiv.getStatForRanking(RankingType.Technologies)
|
||||
val relativeTechModifier = when {
|
||||
relativeTech > 6 -> 10
|
||||
relativeTech > 3 -> 5
|
||||
relativeTech > -3 -> 0
|
||||
relativeTech > -6 -> -2
|
||||
relativeTech > -9 -> -5
|
||||
else -> -10
|
||||
relativeTech > 6 -> 10f
|
||||
relativeTech > 3 -> 5f
|
||||
relativeTech > -3 -> 0f
|
||||
relativeTech > -6 -> -2f
|
||||
relativeTech > -9 -> -5f
|
||||
else -> -10f
|
||||
}
|
||||
return relativeTechModifier
|
||||
return relativeTechModifier * civInfo.getPersonality().modifierFocus(PersonalityValue.Science, .2f)
|
||||
}
|
||||
|
||||
private fun getProductionRatioModifier(civInfo: Civilization, otherCiv: Civilization): Int {
|
||||
private fun getProductionRatioModifier(civInfo: Civilization, otherCiv: Civilization): Float {
|
||||
// If either of our Civs are suffering from a supply deficit, our army must be too large
|
||||
// There is no easy way to check the raw production if a civ has a supply deficit
|
||||
// We might try to divide the current production by the getUnitSupplyProductionPenalty()
|
||||
@ -215,67 +226,69 @@ object MotivationToAttackAutomation {
|
||||
|
||||
val productionRatio = civInfo.getStatForRanking(RankingType.Production).toFloat() / otherCiv.getStatForRanking(RankingType.Production).toFloat()
|
||||
val productionRatioModifier = when {
|
||||
productionRatio > 2f -> 15
|
||||
productionRatio > 1.5f -> 7
|
||||
productionRatio > 1.2 -> 3
|
||||
productionRatio > .8f -> 0
|
||||
productionRatio > .5f -> -5
|
||||
productionRatio > .25f -> -10
|
||||
else -> -10
|
||||
productionRatio > 2f -> 15f
|
||||
productionRatio > 1.5f -> 7f
|
||||
productionRatio > 1.2 -> 3f
|
||||
productionRatio > .8f -> 0f
|
||||
productionRatio > .5f -> -5f
|
||||
productionRatio > .25f -> -10f
|
||||
else -> -10f
|
||||
}
|
||||
return productionRatioModifier
|
||||
return productionRatioModifier * civInfo.getPersonality().modifierFocus(PersonalityValue.Production, .2f)
|
||||
}
|
||||
|
||||
private fun getScoreRatioModifier(otherCiv: Civilization, civInfo: Civilization): Int {
|
||||
private fun getScoreRatioModifier(otherCiv: Civilization, civInfo: Civilization): Float {
|
||||
// Civs with more score are more threatening to our victory
|
||||
// Bias towards attacking civs with a high score and low military
|
||||
// Bias against attacking civs with a low score and a high military
|
||||
// Designed to mitigate AIs declaring war on weaker civs instead of their rivals
|
||||
val scoreRatio = otherCiv.getStatForRanking(RankingType.Score).toFloat() / civInfo.getStatForRanking(RankingType.Score).toFloat()
|
||||
val scoreRatioModifier = when {
|
||||
scoreRatio > 2f -> 20
|
||||
scoreRatio > 1.5f -> 15
|
||||
scoreRatio > 1.25f -> 10
|
||||
scoreRatio > 1f -> 2
|
||||
scoreRatio > .8f -> 0
|
||||
scoreRatio > .5f -> -2
|
||||
scoreRatio > .25f -> -5
|
||||
else -> -10
|
||||
scoreRatio > 2f -> 15f
|
||||
scoreRatio > 1.5f -> 10f
|
||||
scoreRatio > 1.25f -> 5f
|
||||
scoreRatio > 1f -> 2f
|
||||
scoreRatio > .8f -> 0f
|
||||
scoreRatio > .5f -> -2f
|
||||
scoreRatio > .25f -> -5f
|
||||
else -> -10f
|
||||
}
|
||||
return scoreRatioModifier
|
||||
}
|
||||
|
||||
private fun getDefensivePactAlliesScore(otherCiv: Civilization, civInfo: Civilization, baseForce: Float, ourCombatStrength: Float): Int {
|
||||
var theirAlliesValue = 0
|
||||
private fun getDefensivePactAlliesScore(otherCiv: Civilization, civInfo: Civilization, baseForce: Float, ourCombatStrength: Float): Float {
|
||||
var theirAlliesValue = 0f
|
||||
for (thirdCiv in otherCiv.diplomacy.values.filter { it.hasFlag(DiplomacyFlags.DefensivePact) && it.otherCiv() != civInfo }) {
|
||||
val thirdCivCombatStrengthRatio = otherCiv.getStatForRanking(RankingType.Force).toFloat() + baseForce / ourCombatStrength
|
||||
theirAlliesValue += when {
|
||||
thirdCivCombatStrengthRatio > 5 -> -15
|
||||
thirdCivCombatStrengthRatio > 2.5 -> -10
|
||||
thirdCivCombatStrengthRatio > 2 -> -8
|
||||
thirdCivCombatStrengthRatio > 1.5 -> -5
|
||||
thirdCivCombatStrengthRatio > .8 -> -2
|
||||
else -> 0
|
||||
thirdCivCombatStrengthRatio > 5 -> -15f
|
||||
thirdCivCombatStrengthRatio > 2.5 -> -10f
|
||||
thirdCivCombatStrengthRatio > 2 -> -8f
|
||||
thirdCivCombatStrengthRatio > 1.5 -> -5f
|
||||
thirdCivCombatStrengthRatio > .8 -> -2f
|
||||
else -> 0f
|
||||
}
|
||||
}
|
||||
return theirAlliesValue
|
||||
}
|
||||
|
||||
private fun getCombatStrengthModifier(ourCombatStrength: Float, theirCombatStrength: Float): Int {
|
||||
private fun getCombatStrengthModifier(civInfo: Civilization, ourCombatStrength: Float, theirCombatStrength: Float): Float {
|
||||
val combatStrengthRatio = ourCombatStrength / theirCombatStrength
|
||||
val combatStrengthModifier = when {
|
||||
combatStrengthRatio > 5f -> 30
|
||||
combatStrengthRatio > 4f -> 20
|
||||
combatStrengthRatio > 3f -> 15
|
||||
combatStrengthRatio > 2f -> 10
|
||||
combatStrengthRatio > 1.5f -> 5
|
||||
combatStrengthRatio > .8f -> 0
|
||||
combatStrengthRatio > .6f -> -5
|
||||
combatStrengthRatio > .4f -> -15
|
||||
combatStrengthRatio > .2f -> -20
|
||||
else -> -20
|
||||
combatStrengthRatio > 5f -> 20f
|
||||
combatStrengthRatio > 4f -> 15f
|
||||
combatStrengthRatio > 3f -> 12f
|
||||
combatStrengthRatio > 2f -> 10f
|
||||
combatStrengthRatio > 1.8f -> 8f
|
||||
combatStrengthRatio > 1.6f -> 6f
|
||||
combatStrengthRatio > 1.4f -> 4f
|
||||
combatStrengthRatio > 1.2f -> 2f
|
||||
combatStrengthRatio > .8f -> 0f
|
||||
combatStrengthRatio > .6f -> -5f
|
||||
combatStrengthRatio > .4f -> -15f
|
||||
else -> -20f
|
||||
}
|
||||
return combatStrengthModifier
|
||||
return combatStrengthModifier * civInfo.getPersonality().modifierFocus(PersonalityValue.Military, .2f)
|
||||
}
|
||||
|
||||
private fun hasNoUnitsThatCanAttackCityWithoutDying(civInfo: Civilization, theirCity: City) = civInfo.units.getCivUnits().filter { it.isMilitary() }.none {
|
||||
@ -295,7 +308,7 @@ object MotivationToAttackAutomation {
|
||||
*
|
||||
* @return The motivation ranging from -30 to around +10
|
||||
*/
|
||||
private fun getAttackPathsModifier(civInfo: Civilization, otherCiv: Civilization, targetCitiesWithOurCity: List<Pair<City, City>>): Int {
|
||||
private fun getAttackPathsModifier(civInfo: Civilization, otherCiv: Civilization, targetCitiesWithOurCity: List<Pair<City, City>>): Float {
|
||||
|
||||
fun isTileCanMoveThrough(civInfo: Civilization, tile: Tile): Boolean {
|
||||
val owner = tile.getOwner()
|
||||
@ -308,12 +321,12 @@ object MotivationToAttackAutomation {
|
||||
}
|
||||
|
||||
val attackPaths: MutableList<List<Tile>> = mutableListOf()
|
||||
var attackPathModifiers: Int = -3
|
||||
var attackPathModifiers: Float = -3f
|
||||
|
||||
// For each city, we want to calculate if there is an attack path to the enemy
|
||||
for (attacksGroupedByCity in targetCitiesWithOurCity.groupBy { it.first }) {
|
||||
val cityToAttackFrom = attacksGroupedByCity.key
|
||||
var cityAttackValue = 0
|
||||
var cityAttackValue = 0f
|
||||
|
||||
// We only want to calculate the best attack path and use it's value
|
||||
// Land routes are clearly better than sea routes
|
||||
@ -321,7 +334,7 @@ object MotivationToAttackAutomation {
|
||||
val landAttackPath = MapPathing.getConnection(civInfo, cityToAttackFrom.getCenterTile(), cityToAttack.getCenterTile(), ::isLandTileCanMoveThrough)
|
||||
if (landAttackPath != null && landAttackPath.size < 16) {
|
||||
attackPaths.add(landAttackPath)
|
||||
cityAttackValue = 3
|
||||
cityAttackValue = 3f
|
||||
break
|
||||
}
|
||||
|
||||
@ -341,7 +354,7 @@ object MotivationToAttackAutomation {
|
||||
val reachableEnemyCitiesBfs = BFS(civInfo.getCapital(true)!!.getCenterTile()) { isTileCanMoveThrough(civInfo, it) }
|
||||
reachableEnemyCitiesBfs.stepToEnd()
|
||||
val reachableEnemyCities = otherCiv.cities.filter { reachableEnemyCitiesBfs.hasReachedTile(it.getCenterTile()) }
|
||||
if (reachableEnemyCities.isEmpty()) return -50 // Can't even reach the enemy city, no point in war.
|
||||
if (reachableEnemyCities.isEmpty()) return -50f // Can't even reach the enemy city, no point in war.
|
||||
val minAttackDistance = reachableEnemyCities.minOf { reachableEnemyCitiesBfs.getPathTo(it.getCenterTile()).count() }
|
||||
|
||||
// Longer attack paths are worse, but if the attack path is too far away we shouldn't completely discard the possibility
|
||||
|
@ -468,19 +468,18 @@ object NextTurnAutomation {
|
||||
if (civInfo.cities.none()) return
|
||||
if (civInfo.getHappiness() <= civInfo.cities.size) return
|
||||
|
||||
if (civInfo.units.getCivUnits().any { it.hasUnique(UniqueType.FoundCity) }) return
|
||||
if (civInfo.cities.any {
|
||||
val currentConstruction = it.cityConstructions.getCurrentConstruction()
|
||||
currentConstruction is BaseUnit && currentConstruction.isCityFounder()
|
||||
}) return
|
||||
val settlerUnits = civInfo.gameInfo.ruleset.units.values
|
||||
.filter { it.isCityFounder() && it.isBuildable(civInfo) &&
|
||||
personality.getMatchingUniques(UniqueType.WillNotBuild, StateForConditionals(civInfo))
|
||||
.none { unique -> it.matchesFilter(unique.params[0]) } }
|
||||
if (settlerUnits.isEmpty()) return
|
||||
if (!civInfo.units.getCivUnits().none { it.hasUnique(UniqueType.FoundCity) }) return
|
||||
|
||||
if (civInfo.cities.any {
|
||||
val currentConstruction = it.cityConstructions.getCurrentConstruction()
|
||||
currentConstruction is BaseUnit && currentConstruction.isCityFounder()
|
||||
}) return
|
||||
|
||||
if (civInfo.units.getCivUnits().none { it.isMilitary() }) return // We need someone to defend him first
|
||||
if (civInfo.units.getCivUnits().count { it.isMilitary() } < civInfo.cities.size) return // We need someone to defend them first
|
||||
|
||||
val workersBuildableForThisCiv = civInfo.gameInfo.ruleset.units.values.any {
|
||||
it.hasUnique(UniqueType.BuildImprovements)
|
||||
|
@ -37,7 +37,7 @@ object UseGoldAutomation {
|
||||
private fun useGoldForCityStates(civ: Civilization) {
|
||||
// RARE EDGE CASE: If you ally with a city-state, you may reveal more map that includes ANOTHER civ!
|
||||
// So if we don't lock this list, we may later discover that there are more known civs, concurrent modification exception!
|
||||
val knownCityStates = civ.getKnownCivs().filter { it.isCityState() && MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civ, it, 0) <= 0 }.toList()
|
||||
val knownCityStates = civ.getKnownCivs().filter { it.isCityState() && MotivationToAttackAutomation.hasAtLeastMotivationToAttack(civ, it, 0f) <= 0 }.toList()
|
||||
|
||||
// canBeMarriedBy checks actual cost, but it can't be below 500*speedmodifier, and the later check is expensive
|
||||
if (civ.gold >= 330 && civ.getHappiness() > 0 && civ.hasUnique(UniqueType.CityStateCanBeBoughtForGold)) {
|
||||
|
@ -7,6 +7,7 @@ import com.unciv.logic.civilization.DiplomacyAction
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.PopupAlert
|
||||
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
|
||||
@ -186,11 +187,11 @@ object DeclareWar {
|
||||
if (thirdCiv.isAtWarWith(otherCiv) && !thirdCiv.isAtWarWith(civInfo)) {
|
||||
// Improve our relations
|
||||
if (thirdCiv.isCityState()) thirdCiv.getDiplomacyManager(civInfo)!!.addInfluence(10f)
|
||||
else thirdCiv.getDiplomacyManager(civInfo)!!.addModifier(DiplomaticModifiers.SharedEnemy, 5f)
|
||||
else thirdCiv.getDiplomacyManager(civInfo)!!.addModifier(DiplomaticModifiers.SharedEnemy, 5f * civInfo.getPersonality().modifierFocus(PersonalityValue.Loyal, .3f))
|
||||
} else if (thirdCiv.isAtWarWith(civInfo)) {
|
||||
// Improve their relations
|
||||
if (thirdCiv.isCityState()) thirdCiv.getDiplomacyManager(otherCiv)!!.addInfluence(10f)
|
||||
else thirdCiv.getDiplomacyManager(otherCiv)!!.addModifier(DiplomaticModifiers.SharedEnemy, 5f)
|
||||
else thirdCiv.getDiplomacyManager(otherCiv)!!.addModifier(DiplomaticModifiers.SharedEnemy, 5f * civInfo.getPersonality().modifierFocus(PersonalityValue.Loyal, .3f))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,12 +221,12 @@ object DeclareWar {
|
||||
val diploManager = knownCiv.getDiplomacyManager(diplomacyManager.civInfo)!!
|
||||
if (betrayedFriendship) {
|
||||
val amount = if (knownCiv == otherCiv) -40f else -20f
|
||||
diploManager.addModifier(DiplomaticModifiers.BetrayedDeclarationOfFriendship, amount)
|
||||
diploManager.addModifier(DiplomaticModifiers.BetrayedDeclarationOfFriendship, amount * knownCiv.getPersonality().modifierFocus(PersonalityValue.Loyal, .3f))
|
||||
}
|
||||
if (betrayedDefensivePact) {
|
||||
//Note: this stacks with Declaration of Friendship
|
||||
val amount = if (knownCiv == otherCiv) -20f else -10f
|
||||
diploManager.addModifier(DiplomaticModifiers.BetrayedDefensivePact, amount)
|
||||
diploManager.addModifier(DiplomaticModifiers.BetrayedDefensivePact, amount * knownCiv.getPersonality().modifierFocus(PersonalityValue.Loyal, .3f))
|
||||
}
|
||||
diploManager.removeModifier(DiplomaticModifiers.DeclaredFriendshipWithOurAllies) // obviously this guy's declarations of friendship aren't worth much.
|
||||
diploManager.removeModifier(DiplomaticModifiers.SignedDefensivePactWithOurAllies)
|
||||
|
@ -6,6 +6,7 @@ import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.models.ruleset.nation.PersonalityValue
|
||||
|
||||
class Trade : IsPartOfGameInfoSerialization {
|
||||
|
||||
@ -57,9 +58,9 @@ class TradeRequest : IsPartOfGameInfoSerialization {
|
||||
// the numbers of the flags (20,5) are the amount of turns to wait until offering again
|
||||
if (trade.ourOffers.all { it.type == TradeType.Luxury_Resource }
|
||||
&& trade.theirOffers.all { it.type == TradeType.Luxury_Resource })
|
||||
requestingCivDiploManager.setFlag(DiplomacyFlags.DeclinedLuxExchange,5)
|
||||
requestingCivDiploManager.setFlag(DiplomacyFlags.DeclinedLuxExchange,5 - (requestingCivInfo.getPersonality()[PersonalityValue.Commerce] / 2).toInt())
|
||||
if (trade.ourOffers.any { it.name == Constants.researchAgreement })
|
||||
requestingCivDiploManager.setFlag(DiplomacyFlags.DeclinedResearchAgreement,10)
|
||||
requestingCivDiploManager.setFlag(DiplomacyFlags.DeclinedResearchAgreement,15 - requestingCivInfo.getPersonality()[PersonalityValue.Science].toInt())
|
||||
if (trade.ourOffers.any { it.name == Constants.defensivePact })
|
||||
requestingCivDiploManager.setFlag(DiplomacyFlags.DeclinedDefensivePact,10)
|
||||
if (trade.ourOffers.any { it.name == Constants.openBorders })
|
||||
|
@ -163,7 +163,7 @@ class TradeEvaluation {
|
||||
return 0
|
||||
} else if (civInfo.isAtWarWith(civToDeclareWarOn)) {
|
||||
// We shouldn't require them to pay us to join our war (no negative values)
|
||||
return (20 * DeclareWarPlanEvaluator.evaluateJoinOurWarPlan(civInfo, civToDeclareWarOn, tradePartner, null)).coerceAtLeast(0)
|
||||
return (20 * DeclareWarPlanEvaluator.evaluateJoinOurWarPlan(civInfo, civToDeclareWarOn, tradePartner, null)).toInt().coerceAtLeast(0)
|
||||
} else {
|
||||
// Why should we pay you to go fight someone else?
|
||||
return 0
|
||||
@ -281,13 +281,13 @@ class TradeEvaluation {
|
||||
&& trade.ourOffers.any {it.type == TradeType.WarDeclaration && it.name == offer.name}) {
|
||||
// Only accept if the war will benefit us, or if they pay us enough
|
||||
// We shouldn't want to pay them for us to declare war (no negative values)
|
||||
return (-20 * DeclareWarPlanEvaluator.evaluateTeamWarPlan(civInfo, civToDeclareWarOn, tradePartner, null)).coerceAtLeast(0)
|
||||
return (-20 * DeclareWarPlanEvaluator.evaluateTeamWarPlan(civInfo, civToDeclareWarOn, tradePartner, null)).toInt().coerceAtLeast(0)
|
||||
} else if (tradePartner.isAtWarWith(civToDeclareWarOn)) {
|
||||
// We might want them to pay us to join them in war (no negative values)
|
||||
return (-20 * DeclareWarPlanEvaluator.evaluateJoinWarPlan(civInfo, civToDeclareWarOn, tradePartner, null)).coerceAtLeast(0)
|
||||
return (-20 * DeclareWarPlanEvaluator.evaluateJoinWarPlan(civInfo, civToDeclareWarOn, tradePartner, null)).toInt().coerceAtLeast(0)
|
||||
} else {
|
||||
// We might want them to pay us to declare war (no negative values)
|
||||
return (-25 * DeclareWarPlanEvaluator.evaluateDeclareWarPlan(civInfo, civToDeclareWarOn, null)).coerceAtLeast(0)
|
||||
return (-25 * DeclareWarPlanEvaluator.evaluateDeclareWarPlan(civInfo, civToDeclareWarOn, null)).toInt().coerceAtLeast(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +347,7 @@ class TradeEvaluation {
|
||||
if (ourCombatStrength * 1.5f >= theirCombatStrength && theirCombatStrength * 1.5f >= ourCombatStrength)
|
||||
return 0 // we're roughly equal, there's no huge power imbalance
|
||||
if (ourCombatStrength > theirCombatStrength) {
|
||||
if (MotivationToAttackAutomation.hasAtLeastMotivationToAttack(ourCiv, otherCiv, 0) <= 0) return 0
|
||||
if (MotivationToAttackAutomation.hasAtLeastMotivationToAttack(ourCiv, otherCiv, 0f) <= 0) return 0
|
||||
val absoluteAdvantage = ourCombatStrength - theirCombatStrength
|
||||
val percentageAdvantage = absoluteAdvantage / theirCombatStrength.toFloat()
|
||||
// We don't add the same constraint here. We should not make peace easily if we're
|
||||
|
@ -93,12 +93,19 @@ class Personality: RulesetObject() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the value to a more meaningful range, where 10 is 2, and 5 is 1
|
||||
* Scales the value to a more meaningful range, where 10 is 2, and 5 is 1, and 0 is 0
|
||||
*/
|
||||
fun scaledFocus(value: PersonalityValue): Float {
|
||||
return nameToVariable(value).get() / 5
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse scales the value to a more meaningful range, where 0 is 2, and 5 is 1 and 10 is 0
|
||||
*/
|
||||
fun inverseScaledFocus(value: PersonalityValue): Float {
|
||||
return (10 - nameToVariable(value).get()) / 5
|
||||
}
|
||||
|
||||
/**
|
||||
* @param weight a value between 0 and 1 that determines how much the modifier deviates from 1
|
||||
* @return a modifier between 0 and 2 centered around 1 based off of the personality value and the weight given
|
||||
@ -107,6 +114,15 @@ class Personality: RulesetObject() {
|
||||
return 1f + (scaledFocus(value) - 1) * weight
|
||||
}
|
||||
|
||||
/**
|
||||
* An inverted version of [modifierFocus], a personality value of 0 becomes a 10, 8 becomes a 2, etc.
|
||||
* @param weight a value between 0 and 1 that determines how much the modifier deviates from 1
|
||||
* @return a modifier between 0 and 2 centered around 1 based off of the personality value and the weight given
|
||||
*/
|
||||
fun inverseModifierFocus(value: PersonalityValue, weight: Float): Float {
|
||||
return 1f - (inverseScaledFocus(value) - 2) * weight
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the stats based on the personality and the weight given
|
||||
* @param weight a positive value that determines how much the personality should impact the stats given
|
||||
|
Reference in New Issue
Block a user