mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 03:53:12 -04:00
chore(purity): BattleHelper
This commit is contained in:
parent
4a82d07ae0
commit
74b32e137a
@ -38,7 +38,7 @@ plugins {
|
||||
// This is *with* gradle 8.2 downloaded according the project specs, no idea what that's about
|
||||
kotlin("multiplatform") version "1.9.24"
|
||||
kotlin("plugin.serialization") version "1.9.24"
|
||||
id("io.github.yairm210.purity-plugin") version "0.0.51" apply(false)
|
||||
id("io.github.yairm210.purity-plugin") version "1.1.0" apply(false)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -17,7 +17,6 @@ import com.unciv.models.ruleset.IConstruction
|
||||
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.LocalUniqueCache
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
@ -26,6 +25,7 @@ import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.ui.screens.cityscreen.CityScreen
|
||||
import com.unciv.ui.screens.victoryscreen.RankingType
|
||||
import yairm210.purity.annotations.Readonly
|
||||
import kotlin.math.max
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -42,6 +42,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
|
||||
private val constructionsToAvoid = personality.getMatchingUniques(UniqueType.WillNotBuild, cityState)
|
||||
.map{ it.params[0] }
|
||||
|
||||
@Readonly
|
||||
private fun shouldAvoidConstruction (construction: IConstruction): Boolean {
|
||||
val stateForConditionals = cityState
|
||||
for (toAvoid in constructionsToAvoid) {
|
||||
@ -109,7 +111,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
|
||||
fun chooseNextConstruction() {
|
||||
if (cityConstructions.getCurrentConstruction() !is PerpetualConstruction) return // don't want to be stuck on these forever
|
||||
|
||||
|
||||
addBuildingChoices()
|
||||
|
||||
if (!city.isPuppet) {
|
||||
@ -197,6 +199,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
|
||||
// Define what makes a tile worth sending a Workboat to
|
||||
// todo Prepare for mods that allow improving water tiles without a resource?
|
||||
@Readonly
|
||||
fun Tile.isWorthImproving(): Boolean {
|
||||
if (getOwner() != civInfo) return false
|
||||
if (!WorkerAutomation.hasWorkableSeaResource(this, civInfo)) return false
|
||||
@ -205,6 +208,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
|
||||
// Search for a tile justifying producing a Workboat
|
||||
// todo should workboatAutomationSearchMaxTiles depend on game state?
|
||||
@Readonly
|
||||
fun findTileWorthImproving(): Boolean {
|
||||
val searchMaxTiles = civInfo.gameInfo.ruleset.modOptions.constants.workboatAutomationSearchMaxTiles
|
||||
val bfs = BFS(city.getCenterTile()) {
|
||||
@ -263,13 +267,14 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
private fun getValueOfBuilding(building: Building, localUniqueCache: LocalUniqueCache): Float {
|
||||
var value = 0f
|
||||
value += applyBuildingStats(building, localUniqueCache)
|
||||
value += applyMilitaryBuildingValue(building)
|
||||
value += applyVictoryBuildingValue(building)
|
||||
value += applyOnetimeUniqueBonuses(building)
|
||||
value += getMilitaryBuildingValue(building)
|
||||
value += getVictoryBuildingValue(building)
|
||||
value += getOnetimeUniqueBonuses(building)
|
||||
return value
|
||||
}
|
||||
|
||||
private fun applyOnetimeUniqueBonuses(building: Building): Float {
|
||||
@Readonly
|
||||
private fun getOnetimeUniqueBonuses(building: Building): Float {
|
||||
var value = 0f
|
||||
if (building.isWonder) {
|
||||
// Buildings generally don't have these uniques, and Wonders generally only one of these, so we can save some time by not checking every building for every unique
|
||||
@ -295,8 +300,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
private fun applyVictoryBuildingValue(building: Building): Float {
|
||||
|
||||
@Readonly
|
||||
private fun getVictoryBuildingValue(building: Building): Float {
|
||||
var value = 0f
|
||||
if (!cityIsOverAverageProduction) return value
|
||||
if (building.hasUnique(UniqueType.TriggersCulturalVictory)
|
||||
@ -305,7 +311,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
return value
|
||||
}
|
||||
|
||||
private fun applyMilitaryBuildingValue(building: Building): Float {
|
||||
@Readonly
|
||||
private fun getMilitaryBuildingValue(building: Building): Float {
|
||||
var value = 0f
|
||||
var warModifier = if (isAtWar) 1f else .5f
|
||||
// If this city is the closest city to another civ, that makes it a likely candidate for attack
|
||||
@ -330,7 +337,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
|
||||
private fun applyBuildingStats(building: Building, localUniqueCache: LocalUniqueCache): Float {
|
||||
val buildingStats = getStatDifferenceFromBuilding(building.name, localUniqueCache)
|
||||
getBuildingStatsFromUniques(building, buildingStats)
|
||||
buildingStats.add(getBuildingStatsFromUniques(building, buildingStats))
|
||||
|
||||
buildingStats.food *= 3
|
||||
|
||||
@ -352,6 +359,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
return Automation.rankStatsValue(buildingStats.clone(), civInfo)
|
||||
}
|
||||
|
||||
// NOT readonly safe, since it alters the tile ownership of real tiles
|
||||
private fun getStatDifferenceFromBuilding(building: String, localUniqueCache: LocalUniqueCache): Stats {
|
||||
val newCity = city.clone()
|
||||
newCity.setTransients(city.civ) // Will break the owned tiles. Needs to be reverted before leaving this function
|
||||
@ -365,20 +373,23 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
return newCity.cityStats.currentCityStats - oldStats
|
||||
}
|
||||
|
||||
private fun getBuildingStatsFromUniques(building: Building, buildingStats: Stats) {
|
||||
@Readonly
|
||||
private fun getBuildingStatsFromUniques(building: Building, buildingStats: Stats) : Stats {
|
||||
val stats = Stats()
|
||||
for (unique in building.getMatchingUniques(UniqueType.StatPercentBonusCities, cityState)) {
|
||||
val statType = Stat.valueOf(unique.params[1])
|
||||
val relativeAmount = unique.params[0].toFloat() / 100f
|
||||
val amount = civInfo.stats.statsForNextTurn[statType] * relativeAmount
|
||||
buildingStats[statType] += amount
|
||||
stats[statType] += amount
|
||||
}
|
||||
|
||||
for (unique in building.getMatchingUniques(UniqueType.CarryOverFood, cityState)) {
|
||||
if (city.matchesFilter(unique.params[1]) && unique.params[0].toInt() != 0) {
|
||||
val foodGain = cityStats.currentCityStats.food + buildingStats.food
|
||||
val relativeAmount = unique.params[0].toFloat() / 100f
|
||||
buildingStats[Stat.Food] += foodGain * relativeAmount // Essentialy gives us the food per turn this unique saves us
|
||||
stats[Stat.Food] += foodGain * relativeAmount // Essentialy gives us the food per turn this unique saves us
|
||||
}
|
||||
}
|
||||
return stats
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import com.unciv.logic.city.City
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.models.ruleset.unique.GameContext
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
object BattleHelper {
|
||||
|
||||
@ -71,6 +72,7 @@ object BattleHelper {
|
||||
/**
|
||||
* Choses the best target in attackableEnemies, this could be a city or a unit.
|
||||
*/
|
||||
@Readonly
|
||||
private fun chooseAttackTarget(unit: MapUnit, attackableEnemies: List<AttackableTile>): AttackableTile? {
|
||||
// Get the highest valued attackableEnemy
|
||||
var highestAttackValue = 0
|
||||
@ -95,6 +97,7 @@ object BattleHelper {
|
||||
* Siege units will almost always attack cities.
|
||||
* Base value is 100(Mele) 110(Ranged) standard deviation is around 80 to 130
|
||||
*/
|
||||
@Readonly
|
||||
private fun getCityAttackValue(attacker: MapUnit, city: City): Int {
|
||||
val attackerUnit = MapUnitCombatant(attacker)
|
||||
val cityUnit = CityCombatant(city)
|
||||
@ -144,6 +147,7 @@ object BattleHelper {
|
||||
* Returns a value which represents the attacker's motivation to attack a unit.
|
||||
* Base value is 100 and standard deviation is around 80 to 130
|
||||
*/
|
||||
@Readonly
|
||||
private fun getUnitAttackValue(attacker: MapUnit, attackTile: AttackableTile): Int {
|
||||
// Base attack value, there is nothing there...
|
||||
var attackValue = Int.MIN_VALUE
|
||||
|
@ -10,6 +10,7 @@ import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.models.UnitActionType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
object ReligiousUnitAutomation {
|
||||
|
||||
@ -21,6 +22,7 @@ object ReligiousUnitAutomation {
|
||||
it.religion.getMajorityReligion() != unit.civ.religionManager.religion
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun isValidSpreadReligionTarget(city: City): Boolean {
|
||||
val diplomacyManager = unit.civ.getDiplomacyManager(city.civ)
|
||||
if (diplomacyManager?.hasFlag(DiplomacyFlags.AgreedToNotSpreadReligion) == true){
|
||||
@ -33,6 +35,7 @@ object ReligiousUnitAutomation {
|
||||
}
|
||||
|
||||
/** Lowest value will be chosen */
|
||||
@Readonly
|
||||
fun rankCityForReligionSpread(city: City): Int {
|
||||
var rank = city.getCenterTile().aerialDistanceTo(unit.getTile())
|
||||
|
||||
@ -44,6 +47,7 @@ object ReligiousUnitAutomation {
|
||||
return rank
|
||||
}
|
||||
|
||||
|
||||
val city =
|
||||
if (ourCitiesWithoutReligion.any())
|
||||
ourCitiesWithoutReligion.minByOrNull { it.getCenterTile().aerialDistanceTo(unit.getTile()) }
|
||||
@ -118,6 +122,7 @@ object ReligiousUnitAutomation {
|
||||
}
|
||||
}
|
||||
|
||||
@Readonly
|
||||
private fun determineBestInquisitorCityToConvert(
|
||||
unit: MapUnit,
|
||||
): City? {
|
||||
|
@ -497,6 +497,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
val isWaterUnit by lazy { type.isWaterUnit() }
|
||||
@Readonly fun isAirUnit() = type.isAirUnit()
|
||||
|
||||
@Readonly
|
||||
fun isProbablySiegeUnit() = isRanged()
|
||||
&& getMatchingUniques(UniqueType.Strength, GameContext.IgnoreConditionals)
|
||||
.any { it.params[0].toInt() > 0 && it.hasModifier(UniqueType.ConditionalVsCity) }
|
||||
|
@ -59,7 +59,7 @@ object Log {
|
||||
*
|
||||
* The [params] can contain value-producing lambdas, which will be called and their value used as parameter for the message instead.
|
||||
*/
|
||||
@Pure @Suppress("purity") // log considered pure everywhere
|
||||
@Pure @Suppress("purity") // good suppression - log considered pure everywhere
|
||||
fun debug(msg: String, vararg params: Any?) {
|
||||
if (backend.isRelease()) return
|
||||
debug(getTag(), msg, *params)
|
||||
|
Loading…
x
Reference in New Issue
Block a user