chore: moved statsForNextTurn and happiness transients into civ stats

This commit is contained in:
Yair Morgenstern 2023-01-18 14:49:25 +02:00
parent 1ae75f22be
commit bdc239fd34
17 changed files with 53 additions and 53 deletions

View File

@ -86,7 +86,7 @@ object Automation {
// Food already handled above. Science/Culture have low weights in Stats already
yieldStats.gold /= 2 // it's barely worth anything at this point
} else {
if (city.civInfo.gold < 0 && city.civInfo.statsForNextTurn.gold <= 0)
if (city.civInfo.gold < 0 && city.civInfo.stats.statsForNextTurn.gold <= 0)
yieldStats.gold *= 2 // We have a global problem
if (city.tiles.size < 12 || city.civInfo.wantsToFocusOn(Victory.Focus.Culture))
@ -433,7 +433,7 @@ object Automation {
else
(2.4f + (stats.food - 2) / 2) // 1.2 point for each food up to 2, from there on half a point
rank += if (civInfo.gold < 0 && civInfo.statsForNextTurn.gold <= 0)
rank += if (civInfo.gold < 0 && civInfo.stats.statsForNextTurn.gold <= 0)
stats.gold
else
stats.gold / 3 // 3 gold is much worse than 2 production

View File

@ -112,7 +112,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
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))) return
if (!isAtWar && (civInfo.stats.statsForNextTurn.gold < 0 || militaryUnits > max(5, cities * 2))) return
if (civInfo.gold < -50) return
val militaryUnit = Automation.chooseMilitaryUnit(cityInfo) ?: return
@ -332,7 +332,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
.isBuildable().minByOrNull { it.cost }
if (goldBuilding != null) {
val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f
val modifier = if (civInfo.stats.statsForNextTurn.gold < 0) 3f else 1.2f
addChoice(relativeCostEffectiveness, goldBuilding.name, modifier)
}
}

View File

@ -96,7 +96,7 @@ object NextTurnAutomation {
fun automateGoldToSciencePercentage(civInfo: CivilizationInfo) {
// Don't let the AI run blindly with the default convert-gold-to-science ratio if that option is enabled
val estimatedIncome = civInfo.statsForNextTurn.gold.toInt()
val estimatedIncome = civInfo.stats.statsForNextTurn.gold.toInt()
val projectedGold = civInfo.gold + estimatedIncome
// TODO: some cleverness, this is just wild guessing.
val pissPoor = civInfo.tech.era.baseUnitBuyCost
@ -691,7 +691,7 @@ object NextTurnAutomation {
civInfo.canSignResearchAgreementsWith(it)
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedResearchAgreement)
}
.sortedByDescending { it.statsForNextTurn.science }
.sortedByDescending { it.stats.statsForNextTurn.science }
for (otherCiv in canSignResearchAgreementCiv) {
// Default setting is 5, this will be changed according to different civ.

View File

@ -346,7 +346,7 @@ object ReligionAutomation {
) 0f
// This is something completely different from the original, but I have no idea
// what happens over there
else civInfo.statsForNextTurn[Stat.valueOf(unique.params[2])] * 5f / unique.params[1].toFloat()
else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[2])] * 5f / unique.params[1].toFloat()
UniqueType.BuyUnitsWithStat, UniqueType.BuyBuildingsWithStat ->
if (civInfo.religionManager.religion != null
&& civInfo.religionManager.religion!!.getFollowerUniques()
@ -354,7 +354,7 @@ object ReligionAutomation {
) 0f
// This is something completely different from the original, but I have no idea
// what happens over there
else civInfo.statsForNextTurn[Stat.valueOf(unique.params[1])] * 10f / civInfo.getEra().baseUnitBuyCost
else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[1])] * 10f / civInfo.getEra().baseUnitBuyCost
UniqueType.BuyUnitsByProductionCost ->
15f * if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f else 1f
UniqueType.StatsWhenSpreading ->

View File

@ -310,9 +310,9 @@ class CityStats(val cityInfo: CityInfo) {
private fun getStatPercentBonusesFromUnitSupply(): Stats {
val stats = Stats()
val supplyDeficit = cityInfo.civInfo.stats().getUnitSupplyDeficit()
val supplyDeficit = cityInfo.civInfo.stats.getUnitSupplyDeficit()
if (supplyDeficit > 0)
stats.production = cityInfo.civInfo.stats().getUnitSupplyProductionPenalty()
stats.production = cityInfo.civInfo.stats.getUnitSupplyProductionPenalty()
return stats
}

View File

@ -19,6 +19,13 @@ import kotlin.math.pow
/** CivInfo class was getting too crowded */
class CivInfoStats(val civInfo: CivilizationInfo) {
@Transient
/** Happiness for next turn */
var happiness = 0
@Transient
var statsForNextTurn = Stats()
private fun getUnitMaintenance(): Int {
val baseUnitCost = 0.5f
var freeUnits = 3
@ -174,7 +181,7 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
Constants.cityStates,
Stats().add(
Stat.valueOf(unique.params[0]),
otherCiv.statsForNextTurn[Stat.valueOf(unique.params[0])] * unique.params[1].toFloat() / 100f
statsForNextTurn[Stat.valueOf(unique.params[0])] * unique.params[1].toFloat() / 100f
)
)
}

View File

@ -114,12 +114,6 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
@Transient
var enemyMovementPenaltyUniques: Sequence<Unique>? = null
@Transient
var statsForNextTurn = Stats()
@Transient
var happinessForNextTurn = 0
@Transient
var detailedCivResources = ResourceSupplyList()
@ -397,18 +391,17 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
}
@Transient
private val civInfoStats = CivInfoStats(this)
fun stats() = civInfoStats
val stats = CivInfoStats(this)
@Transient
val cache = CivInfoTransientCache(this)
fun updateStatsForNextTurn() {
happinessForNextTurn = stats().getHappinessBreakdown().values.sum().roundToInt()
statsForNextTurn = stats().getStatMapForNextTurn().values.reduce { a, b -> a + b }
stats.happiness = stats.getHappinessBreakdown().values.sum().roundToInt()
stats.statsForNextTurn = stats.getStatMapForNextTurn().values.reduce { a, b -> a + b }
}
fun getHappiness() = happinessForNextTurn
fun getHappiness() = stats.happiness
fun getCivResources(): ResourceSupplyList = summarizedCivResources
@ -755,8 +748,8 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
else when (category) {
RankingType.Score -> calculateTotalScore().toInt()
RankingType.Population -> cities.sumOf { it.population.population }
RankingType.Crop_Yield -> statsForNextTurn.food.roundToInt()
RankingType.Production -> statsForNextTurn.production.roundToInt()
RankingType.Crop_Yield -> stats.statsForNextTurn.food.roundToInt()
RankingType.Production -> stats.statsForNextTurn.production.roundToInt()
RankingType.Gold -> gold
RankingType.Territory -> cities.sumOf { it.tiles.size }
RankingType.Force -> getMilitaryMight()
@ -1010,7 +1003,7 @@ class CivilizationInfo : IsPartOfGameInfoSerialization {
notifications.clear()
updateStatsForNextTurn()
val nextTurnStats = statsForNextTurn
val nextTurnStats = stats.statsForNextTurn
policies.endTurn(nextTurnStats.culture.toInt())
totalCultureForContests += nextTurnStats.culture.toInt()

View File

@ -124,8 +124,8 @@ class TechManager : IsPartOfGameInfoSerialization {
fun remainingScienceToTech(techName: String) = costOfTech(techName) - researchOfTech(techName)
fun turnsToTech(techName: String) = when {
civInfo.statsForNextTurn.science <= 0f -> ""
else -> max(1, ceil(remainingScienceToTech(techName).toDouble() / civInfo.statsForNextTurn.science).toInt()).toString()
civInfo.stats.statsForNextTurn.science <= 0f -> ""
else -> max(1, ceil(remainingScienceToTech(techName).toDouble() / civInfo.stats.statsForNextTurn.science).toInt()).toString()
}
fun isResearched(techName: String): Boolean = techsResearched.contains(techName)
@ -185,7 +185,7 @@ class TechManager : IsPartOfGameInfoSerialization {
// http://www.civclub.net/bbs/forum.php?mod=viewthread&tid=123976
// Apparently yes, we care about the absolute tech cost, not the actual calculated-for-this-player tech cost,
// so don't change to costOfTech()
return min(overflowScience, max(civInfo.statsForNextTurn.science.toInt() * 5,
return min(overflowScience, max(civInfo.stats.statsForNextTurn.science.toInt() * 5,
getRuleset().technologies[currentTechnologyName()]!!.cost))
}

View File

@ -502,7 +502,7 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
// At the end of every turn
if (flag == DiplomacyFlags.ResearchAgreement.name)
totalOfScienceDuringRA += civInfo.statsForNextTurn.science.toInt()
totalOfScienceDuringRA += civInfo.stats.statsForNextTurn.science.toInt()
// These modifiers decrease slightly @ 50
if (flagsCountdown[flag] == 50) {

View File

@ -39,7 +39,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
}
offers.add(TradeOffer("Gold", TradeType.Gold, civInfo.gold))
offers.add(TradeOffer("Gold per turn", TradeType.Gold_Per_Turn, civInfo.statsForNextTurn.gold.toInt()))
offers.add(TradeOffer("Gold per turn", TradeType.Gold_Per_Turn, civInfo.stats.statsForNextTurn.gold.toInt()))
if (!civInfo.isOneCityChallenger() && !otherCivilization.isOneCityChallenger()
&& !civInfo.isCityState() && !otherCivilization.isCityState()) {

View File

@ -158,13 +158,13 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
UniqueType.ConditionalWithResource -> state.civInfo?.hasResource(condition.params[0]) == true
UniqueType.ConditionalWithoutResource -> state.civInfo?.hasResource(condition.params[0]) == false
UniqueType.ConditionalHappy ->
state.civInfo != null && state.civInfo.statsForNextTurn.happiness >= 0
state.civInfo != null && state.civInfo.stats.statsForNextTurn.happiness >= 0
UniqueType.ConditionalBetweenHappiness ->
state.civInfo != null
&& condition.params[0].toInt() <= state.civInfo.happinessForNextTurn
&& state.civInfo.happinessForNextTurn < condition.params[1].toInt()
&& condition.params[0].toInt() <= state.civInfo.stats.happiness
&& state.civInfo.stats.happiness < condition.params[1].toInt()
UniqueType.ConditionalBelowHappiness ->
state.civInfo != null && state.civInfo.happinessForNextTurn < condition.params[0].toInt()
state.civInfo != null && state.civInfo.stats.happiness < condition.params[0].toInt()
UniqueType.ConditionalGoldenAge ->
state.civInfo != null && state.civInfo.goldenAges.isGoldenAge()
UniqueType.ConditionalWLTKD ->

View File

@ -80,7 +80,7 @@ class StatsOverviewTab(
}
fun update() {
val statMap = viewingPlayer.stats().getStatMapForNextTurn()
val statMap = viewingPlayer.stats.getStatMapForNextTurn()
updateHappinessTable()
goldTable.updateStatTable(Stat.Gold, statMap)
scienceTable.updateStatTable(Stat.Science, statMap)
@ -109,7 +109,7 @@ class StatsOverviewTab(
private fun updateHappinessTable() = happinessTable.apply {
addHeading("Happiness")
val happinessBreakdown = viewingPlayer.stats().getHappinessBreakdown()
val happinessBreakdown = viewingPlayer.stats.getHappinessBreakdown()
for ((key, value) in happinessBreakdown)
addLabeledValue(key, value)
addTotal(happinessBreakdown.values.sum())

View File

@ -87,7 +87,7 @@ class UnitOverviewTab(
private fun showWorldScreenAt(tile: TileInfo) = showWorldScreenAt(tile.position, null)
private fun getUnitSupplyTable(): ExpanderTab {
val stats = viewingPlayer.stats()
val stats = viewingPlayer.stats
val deficit = stats.getUnitSupplyDeficit()
val icon = if (deficit <= 0) null else Group().apply {
isTransform = false

View File

@ -53,7 +53,7 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS
if (isWorldScreen) {
val techCost = techManager.costOfTech(techName)
val remainingTech = techManager.remainingScienceToTech(techName)
val techThisTurn = techManager.civInfo.statsForNextTurn.science
val techThisTurn = techManager.civInfo.stats.statsForNextTurn.science
val percentComplete = (techCost - remainingTech) / techCost.toFloat()
val percentWillBeComplete = (techCost - (remainingTech-techThisTurn)) / techCost.toFloat()

View File

@ -25,7 +25,7 @@ class OfferColumnsTable(private val tradeLogic: TradeLogic, val screen: Diplomac
private val ourAvailableOffersTable = OffersListScroll("OurAvail") {
when (it.type) {
TradeType.Gold -> openGoldSelectionPopup(it, tradeLogic.currentTrade.ourOffers, tradeLogic.ourCivilization.gold)
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.ourOffers, tradeLogic.ourCivilization.statsForNextTurn.gold.toInt())
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.ourOffers, tradeLogic.ourCivilization.stats.statsForNextTurn.gold.toInt())
else -> addOffer(it, tradeLogic.currentTrade.ourOffers, tradeLogic.currentTrade.theirOffers)
}
}
@ -33,21 +33,21 @@ class OfferColumnsTable(private val tradeLogic: TradeLogic, val screen: Diplomac
private val ourOffersTable = OffersListScroll("OurTrade") {
when (it.type) {
TradeType.Gold -> openGoldSelectionPopup(it, tradeLogic.currentTrade.ourOffers, tradeLogic.ourCivilization.gold)
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.ourOffers, tradeLogic.ourCivilization.statsForNextTurn.gold.toInt())
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.ourOffers, tradeLogic.ourCivilization.stats.statsForNextTurn.gold.toInt())
else -> addOffer(it.copy(amount = -it.amount), tradeLogic.currentTrade.ourOffers, tradeLogic.currentTrade.theirOffers)
}
}
private val theirOffersTable = OffersListScroll("TheirTrade") {
when (it.type) {
TradeType.Gold -> openGoldSelectionPopup(it, tradeLogic.currentTrade.theirOffers, tradeLogic.otherCivilization.gold)
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.theirOffers, tradeLogic.otherCivilization.statsForNextTurn.gold.toInt())
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.theirOffers, tradeLogic.otherCivilization.stats.statsForNextTurn.gold.toInt())
else -> addOffer(it.copy(amount = -it.amount), tradeLogic.currentTrade.theirOffers, tradeLogic.currentTrade.ourOffers)
}
}
private val theirAvailableOffersTable = OffersListScroll("TheirAvail") {
when (it.type) {
TradeType.Gold -> openGoldSelectionPopup(it, tradeLogic.currentTrade.theirOffers, tradeLogic.otherCivilization.gold)
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.theirOffers, tradeLogic.otherCivilization.statsForNextTurn.gold.toInt())
TradeType.Gold_Per_Turn -> openGoldSelectionPopup(it, tradeLogic.currentTrade.theirOffers, tradeLogic.otherCivilization.stats.statsForNextTurn.gold.toInt())
else -> addOffer(it, tradeLogic.currentTrade.theirOffers, tradeLogic.currentTrade.ourOffers)
}
}

View File

@ -187,7 +187,7 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
}
fun update(worldScreen: WorldScreen) {
val newVisible = worldScreen.selectedCiv.stats().getUnitSupplyDeficit() > 0
val newVisible = worldScreen.selectedCiv.stats.getUnitSupplyDeficit() > 0
if (newVisible == unitSupplyCell.hasActor()) return
if (newVisible) unitSupplyCell.setActor(unitSupplyImage)
.size(50f).padLeft(10f)
@ -316,7 +316,7 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
}
private fun updateStatsTable(civInfo: CivilizationInfo) {
val nextTurnStats = civInfo.statsForNextTurn
val nextTurnStats = civInfo.stats.statsForNextTurn
val goldPerTurn = " (" + rateLabel(nextTurnStats.gold) + ")"
goldLabel.setText(civInfo.gold.toString() + goldPerTurn)

View File

@ -42,14 +42,14 @@ class GlobalUniquesTests {
fun statsNotOnBuilding() {
val civInfo = game.addCiv("[+2 Gold]")
civInfo.updateStatsForNextTurn()
Assert.assertTrue(civInfo.statsForNextTurn.equals(Stats(gold=2f)))
Assert.assertTrue(civInfo.stats.statsForNextTurn.equals(Stats(gold=2f)))
}
@Test
fun statsHappinessNotOnBuilding() {
val civInfo = game.addCiv("[+7 Happiness]")
civInfo.updateStatsForNextTurn()
Assert.assertTrue(civInfo.happinessForNextTurn == civInfo.getDifficulty().baseHappiness + 7)
Assert.assertTrue(civInfo.stats.happiness == civInfo.getDifficulty().baseHappiness + 7)
}
@ -201,7 +201,7 @@ class GlobalUniquesTests {
val city2 = game.addCity(civInfo, tile2)
val inBetweenTile = game.setTileFeatures(Vector2(0f, 1f), Constants.desert)
inBetweenTile.roadStatus = RoadStatus.Road
civInfo.cache().updateCitiesConnectedToCapital()
civInfo.cache.updateCitiesConnectedToCapital()
city2.cityStats.update()
Assert.assertTrue(city2.cityStats.finalStatList["Trade routes"]!!.science == 30f)
@ -215,9 +215,9 @@ class GlobalUniquesTests {
civInfo.policies.freePolicies = 3
for (policyName in policiesToAdopt){
val policy = game.ruleset.policies[policyName]!!
civInfo.policies.adopt(policy, )
civInfo.policies.adopt(policy)
}
Assert.assertTrue(civInfo.stats().getStatMapForNextTurn()["Policies"]!!.science == 30f)
Assert.assertTrue(civInfo.stats.getStatMapForNextTurn()["Policies"]!!.science == 30f)
}
@Test
@ -235,7 +235,7 @@ class GlobalUniquesTests {
civ1.updateStatsForNextTurn()
Assert.assertTrue(civ1.statsForNextTurn.science == 30f)
Assert.assertTrue(civ1.stats.statsForNextTurn.science == 30f)
}
@Test
@ -253,7 +253,7 @@ class GlobalUniquesTests {
val baseHappiness = civ1.getDifficulty().baseHappiness
// Since civ1 has no cities, there are no other happiness sources
Assert.assertTrue(civ1.happinessForNextTurn == baseHappiness + 42)
Assert.assertTrue(civ1.stats.happiness == baseHappiness + 42)
}
@Test
@ -269,7 +269,7 @@ class GlobalUniquesTests {
civ1.updateStatsForNextTurn()
Assert.assertTrue(civ1.statsForNextTurn.science == 90f)
Assert.assertTrue(civ1.stats.statsForNextTurn.science == 90f)
}
// endregion
@ -410,7 +410,7 @@ class GlobalUniquesTests {
val inBetweenTile = game.setTileFeatures(Vector2(0f, 1f), Constants.desert)
inBetweenTile.roadStatus = RoadStatus.Road
civInfo.cache().updateCitiesConnectedToCapital()
civInfo.cache.updateCitiesConnectedToCapital()
Assert.assertTrue(city2.cityStats.isConnectedToCapital(RoadStatus.Road))
city2.cityStats.update()