mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-22 05:41:11 +07:00
AI for Inquisitor and Missionary (#5590)
* First pull request to add missionaries and inquisitors * First pull request to add missionaries and inquisitors * First pull request to add missionaries and inquisitors * First pull request to add missionaries and inquisitors * Ai choice * some fixes * some fixes * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * adding a system for the ai to buy with faith * finally done * finally done * some fixes * some fixes * some fixes * some fixes * some fixes * some fixes * some fixes
This commit is contained in:
@ -2,6 +2,7 @@ package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
import com.unciv.logic.civilization.CityAction
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
@ -10,6 +11,7 @@ import com.unciv.logic.map.BFS
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.VictoryType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
@ -39,12 +41,15 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
val relativeCostEffectiveness = ArrayList<ConstructionChoice>()
|
||||
|
||||
private val faithConstruction = arrayListOf<BaseUnit>()
|
||||
|
||||
data class ConstructionChoice(val choice:String, var choiceModifier:Float,val remainingWork:Int)
|
||||
|
||||
fun addChoice(choices:ArrayList<ConstructionChoice>, choice:String, choiceModifier: Float){
|
||||
choices.add(ConstructionChoice(choice,choiceModifier,cityConstructions.getRemainingWork(choice)))
|
||||
}
|
||||
|
||||
|
||||
fun chooseNextConstruction() {
|
||||
if (!UncivGame.Current.settings.autoAssignCityProduction
|
||||
&& civInfo.playerType == PlayerType.Human && !cityInfo.isPuppet
|
||||
@ -62,6 +67,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
addCultureBuildingChoice()
|
||||
addSpaceshipPartChoice()
|
||||
addOtherBuildingChoice()
|
||||
addReligousUnit()
|
||||
|
||||
if (!cityInfo.isPuppet) {
|
||||
addWondersChoice()
|
||||
@ -94,14 +100,24 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
NotificationIcon.Construction
|
||||
)
|
||||
cityConstructions.currentConstructionFromQueue = chosenConstruction
|
||||
|
||||
if (civInfo.isPlayerCivilization()) return // don't want the ai to control what a player uses faith for
|
||||
|
||||
val chosenItem = faithConstruction.asSequence()
|
||||
.filterNotNull()
|
||||
.filter { it.getStatBuyCost(cityInfo, stat = Stat.Faith)!! <= civInfo.religionManager.storedFaith }
|
||||
.firstOrNull() ?: return
|
||||
|
||||
|
||||
cityConstructions.purchaseConstruction(chosenItem.name, -1, false, stat=Stat.Faith)
|
||||
|
||||
}
|
||||
|
||||
private fun addMilitaryUnitChoice() {
|
||||
if (!isAtWar && !cityIsOverAverageProduction) return // don't make any military units here. Infrastructure first!
|
||||
if ((!isAtWar && civInfo.statsForNextTurn.gold > 0 && militaryUnits < max(5, cities * 2))
|
||||
|| (isAtWar && civInfo.gold > -50)) {
|
||||
val militaryUnit = Automation.chooseMilitaryUnit(cityInfo)
|
||||
if (militaryUnit == null) return
|
||||
val militaryUnit = Automation.chooseMilitaryUnit(cityInfo) ?: return
|
||||
val unitsToCitiesRatio = cities.toFloat() / (militaryUnits + 1)
|
||||
// most buildings and civ units contribute the the civ's growth, military units are anti-growth
|
||||
var modifier = sqrt(unitsToCitiesRatio) / 2
|
||||
@ -322,4 +338,48 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
}
|
||||
}
|
||||
|
||||
private fun addReligousUnit(){
|
||||
|
||||
var modifier = 0f
|
||||
|
||||
val missionary = cityInfo.getRuleset().units.values.asSequence()
|
||||
.firstOrNull { it -> it.canBePurchasedWithStat(cityInfo, Stat.Faith)
|
||||
&& it.getMatchingUniques("Can [] [] times").any { it.params[0] == "Spread Religion"} }
|
||||
|
||||
|
||||
val inquisitor = cityInfo.getRuleset().units.values.asSequence()
|
||||
.firstOrNull { it.canBePurchasedWithStat(cityInfo, Stat.Faith)
|
||||
&& it.hasUnique("Prevents spreading of religion to the city it is next to") }
|
||||
|
||||
|
||||
|
||||
// these 4 if conditions are used to determine if an AI should buy units to spread religion, or spend faith to buy things like new military units or new buildings.
|
||||
// currently this AI can only buy inquisitors and missionaries with faith
|
||||
// this system will have to be reengineered to support buying other stuff with faith
|
||||
if (preferredVictoryType == VictoryType.Domination) return
|
||||
if (civInfo.religionManager.religion?.name == null) return
|
||||
if (preferredVictoryType == VictoryType.Cultural) modifier += 1
|
||||
if (isAtWar) modifier -= 0.5f
|
||||
if (cityInfo.religion.getMajorityReligion()?.name != civInfo.religionManager.religion?.name)
|
||||
return // you don't want to build units of opposing religions.
|
||||
|
||||
|
||||
val citiesNotFollowingOurReligion = civInfo.cities.asSequence()
|
||||
.filterNot { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion!!.name }
|
||||
|
||||
val buildInqusitor = citiesNotFollowingOurReligion
|
||||
.filter { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion?.name }
|
||||
.toList().size.toFloat() / 10 + modifier
|
||||
|
||||
val possibleSpreadReligionTargets = civInfo.gameInfo.getCities()
|
||||
.filter { it.getCenterTile().aerialDistanceTo(cityInfo.getCenterTile()) < 30 }
|
||||
|
||||
val buildMissionary = possibleSpreadReligionTargets.toList().size.toFloat() / 15 + modifier
|
||||
|
||||
if (buildMissionary > buildInqusitor && missionary != null) faithConstruction.add(missionary)
|
||||
else if(inquisitor != null) faithConstruction.add(inquisitor)
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,19 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.ReligionManager
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.UnitAction
|
||||
import com.unciv.models.UnitActionType
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.TileResource
|
||||
import com.unciv.models.stats.Stat
|
||||
@ -280,6 +287,81 @@ object SpecificUnitAutomation {
|
||||
}
|
||||
}
|
||||
|
||||
fun automateMissionary(unit: MapUnit){
|
||||
if (unit.religion != unit.civInfo.religionManager.religion?.name)
|
||||
return unit.destroy()
|
||||
|
||||
val cities = unit.civInfo.gameInfo.getCities().asSequence()
|
||||
.filter { it.religion.getMajorityReligion()?.name != unit.getReligionDisplayName() }
|
||||
.filterNot { it.civInfo.isAtWarWith(unit.civInfo) }
|
||||
.minByOrNull { it.getCenterTile().aerialDistanceTo(unit.currentTile) } ?: return
|
||||
|
||||
|
||||
val destination = cities.getTiles().asSequence()
|
||||
.filterNot { unit.getTile().owningCity == it.owningCity } // to prevent the ai from moving around randomly
|
||||
.filter { unit.movement.canMoveTo(it) }
|
||||
.minByOrNull { it.aerialDistanceTo(unit.currentTile) }
|
||||
|
||||
|
||||
if (destination != null) {
|
||||
unit.movement.headTowards(destination)
|
||||
}
|
||||
|
||||
if (unit.currentTile.owningCity?.religion?.getMajorityReligion()?.name != unit.religion)
|
||||
doReligiousAction(unit, unit.getTile())
|
||||
}
|
||||
|
||||
fun automateInquisitor(unit: MapUnit){
|
||||
val cityToConvert = unit.civInfo.cities.asSequence()
|
||||
.filterNot { it.religion.getMajorityReligion()?.name == null }
|
||||
.filterNot { it.religion.getMajorityReligion()?.name == unit.religion }
|
||||
.minByOrNull { it.getCenterTile().aerialDistanceTo(unit.currentTile) }
|
||||
|
||||
val cityToProtect = unit.civInfo.cities.asSequence()
|
||||
.filter { it.religion.getMajorityReligion()?.name == unit.religion }
|
||||
.filter { isInquisitorInTheCity(it, unit)}
|
||||
.maxByOrNull { it.population.population } // cities with most populations will be prioritized by the AI
|
||||
|
||||
var destination: TileInfo? = null
|
||||
|
||||
if (cityToProtect != null){
|
||||
destination = cityToProtect.getCenterTile().neighbors.asSequence()
|
||||
.filterNot { unit.getTile().owningCity == it.owningCity } // to prevent the ai from moving around randomly
|
||||
.filter { unit.movement.canMoveTo(it) }
|
||||
.minByOrNull { it.aerialDistanceTo(unit.currentTile) }
|
||||
}
|
||||
if (destination == null){
|
||||
if (cityToConvert == null) return
|
||||
destination = cityToConvert.getCenterTile().neighbors.asSequence()
|
||||
.filterNot { unit.getTile().owningCity == it.owningCity } // to prevent the ai from moving around randomly
|
||||
.filter { unit.movement.canMoveTo(it) }
|
||||
.minByOrNull { it.aerialDistanceTo(unit.currentTile) }
|
||||
}
|
||||
|
||||
if (destination != null)
|
||||
unit.movement.headTowards(destination)
|
||||
|
||||
|
||||
if (cityToConvert != null && unit.currentTile.getCity() == destination!!.getCity()){
|
||||
doReligiousAction(unit, destination)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun isInquisitorInTheCity(city: CityInfo, unit: MapUnit): Boolean {
|
||||
if (!city.religion.isProtectedByInquisitor())
|
||||
return false
|
||||
|
||||
for (tile in city.getCenterTile().neighbors)
|
||||
if (unit.currentTile == tile)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fun automateFighter(unit: MapUnit) {
|
||||
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange())
|
||||
val enemyAirUnitsInRange = tilesInRange
|
||||
@ -420,4 +502,11 @@ object SpecificUnitAutomation {
|
||||
|
||||
UnitActions.getEnhanceReligionAction(unit)()
|
||||
}
|
||||
|
||||
private fun doReligiousAction(unit: MapUnit, destination: TileInfo){
|
||||
val actionList: java.util.ArrayList<UnitAction> = ArrayList()
|
||||
UnitActions.addActionsWithLimitedUses(unit, actionList, destination)
|
||||
if (actionList.firstOrNull()?.action == null) return
|
||||
actionList.first().action!!.invoke()
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +159,14 @@ object UnitAutomation {
|
||||
if (unit.hasUnique("Can construct []"))
|
||||
return SpecificUnitAutomation.automateImprovementPlacer(unit) // includes great people plus moddable units
|
||||
|
||||
if (unit.getMatchingUniques("Can [] [] times").any{ it.params[0] == "Spread Religion" })
|
||||
return SpecificUnitAutomation.automateMissionary(unit)
|
||||
|
||||
if (unit.hasUnique("Prevents spreading of religion to the city it is next to"))
|
||||
return SpecificUnitAutomation.automateInquisitor(unit)
|
||||
|
||||
|
||||
|
||||
// ToDo: automation of great people skills (may speed up construction, provides a science boost, etc.)
|
||||
|
||||
return // The AI doesn't know how to handle unknown civilian units
|
||||
|
@ -284,6 +284,14 @@ class CityInfoReligionManager {
|
||||
}
|
||||
return addedPressure
|
||||
}
|
||||
|
||||
fun isProtectedByInquisitor(): Boolean {
|
||||
for (tile in cityInfo.getCenterTile().neighbors)
|
||||
if (tile.civilianUnit?.hasUnique("Prevents spreading of religion to the city it is next to") == true)
|
||||
return true
|
||||
if (cityInfo.getCenterTile().civilianUnit?.name == "Inquisitor") return true
|
||||
return false
|
||||
}
|
||||
|
||||
private fun pressureAmountToAdjacentCities(pressuredCity: CityInfo): Int {
|
||||
var pressure = pressureFromAdjacentCities.toFloat()
|
||||
|
@ -528,7 +528,8 @@ object UnitActions {
|
||||
}
|
||||
}
|
||||
|
||||
private fun addActionsWithLimitedUses(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
|
||||
fun addActionsWithLimitedUses(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
|
||||
|
||||
val actionsToAdd = unit.religiousActionsUnitCanDo()
|
||||
if (actionsToAdd.none()) return
|
||||
if (unit.religion == null || unit.civInfo.gameInfo.religions[unit.religion]!!.isPantheon()) return
|
||||
@ -551,7 +552,7 @@ object UnitActions {
|
||||
}
|
||||
}
|
||||
|
||||
private fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, city: CityInfo) {
|
||||
fun addSpreadReligionActions(unit: MapUnit, actionList: ArrayList<UnitAction>, city: CityInfo) {
|
||||
if (!unit.civInfo.gameInfo.isReligionEnabled()) return
|
||||
val blockedByInquisitor =
|
||||
city.getCenterTile()
|
||||
|
Reference in New Issue
Block a user