From da7786a2cb15d91dcd819befa499a176d03512ad Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Thu, 23 Mar 2023 22:57:23 +0100 Subject: [PATCH] More Spectator/Barbarians fixes related to income (#9011) * Prevent Barbarians and Spectator having income or Allies * Prevent Barbarians and Spectator having income or Allies - fix test --- .../unciv/logic/civilization/Civilization.kt | 2 +- .../diplomacy/CityStateFunctions.kt | 2 +- .../civilization/managers/TurnManager.kt | 45 ++++++++++--------- .../EmpireOverviewCategories.kt | 1 + .../unciv/uniques/TriggeredUniquesTests.kt | 3 ++ 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 7289ba1050..f5cf38749e 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -505,7 +505,7 @@ class Civilization : IsPartOfGameInfoSerialization { */ fun isDefeated() = when { isBarbarian() || isSpectator() -> false // Barbarians and voyeurs can't lose - hasEverOwnedOriginalCapital == true -> cities.isEmpty() + hasEverOwnedOriginalCapital -> cities.isEmpty() else -> units.getCivUnits().none() } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt index 4405452b28..bfe9975410 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt @@ -222,7 +222,7 @@ class CityStateFunctions(val civInfo: Civilization) { var newAllyName: String? = null if (!civInfo.isCityState()) return val maxInfluence = civInfo.diplomacy - .filter { !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() } + .filter { it.value.otherCiv().isMajorCiv() && !it.value.otherCiv().isDefeated() } .maxByOrNull { it.value.getInfluence() } if (maxInfluence != null && maxInfluence.value.getInfluence() >= 60) { newAllyName = maxInfluence.key diff --git a/core/src/com/unciv/logic/civilization/managers/TurnManager.kt b/core/src/com/unciv/logic/civilization/managers/TurnManager.kt index d2c066404d..3f4ce0cff7 100644 --- a/core/src/com/unciv/logic/civilization/managers/TurnManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/TurnManager.kt @@ -17,6 +17,7 @@ import com.unciv.logic.trade.TradeEvaluation import com.unciv.models.ruleset.ModOptionsConstants import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.endTurn +import com.unciv.models.stats.Stats import com.unciv.ui.components.MayaCalendar import java.util.* import kotlin.math.max @@ -213,8 +214,6 @@ class TurnManager(val civInfo: Civilization) { fun endTurn() { - if (civInfo.isSpectator()) return - val notificationsLog = civInfo.notificationsLog val notificationsThisTurn = Civilization.NotificationsLog(civInfo.gameInfo.turns) notificationsThisTurn.notifications.addAll(civInfo.notifications) @@ -228,9 +227,15 @@ class TurnManager(val civInfo: Civilization) { civInfo.notifications.clear() - civInfo.updateStatsForNextTurn() + if (civInfo.isDefeated() || civInfo.isSpectator()) return // yes they do call this, best not update any further stuff - val nextTurnStats = civInfo.stats.statsForNextTurn + var nextTurnStats = + if (civInfo.isBarbarian()) + Stats() + else { + civInfo.updateStatsForNextTurn() + civInfo.stats.statsForNextTurn + } civInfo.policies.endTurn(nextTurnStats.culture.toInt()) civInfo.totalCultureForContests += nextTurnStats.culture.toInt() @@ -239,17 +244,18 @@ class TurnManager(val civInfo: Civilization) { civInfo.questManager.endTurn() // disband units until there are none left OR the gold values are normal - if (!civInfo.isBarbarian() && civInfo.gold < -100 && nextTurnStats.gold.toInt() < 0) { - for (i in 1 until (civInfo.gold / -100)) { - var civMilitaryUnits = civInfo.units.getCivUnits().filter { it.baseUnit.isMilitary() } - if (civMilitaryUnits.any()) { - val unitToDisband = civMilitaryUnits.first() - unitToDisband.disband() - civMilitaryUnits -= unitToDisband - val unitName = unitToDisband.shortDisplayName() - civInfo.addNotification("Cannot provide unit upkeep for $unitName - unit has been disbanded!", NotificationCategory.Units, unitName, NotificationIcon.Death) - } - } + if (!civInfo.isBarbarian() && civInfo.gold <= -200 && nextTurnStats.gold.toInt() < 0) { + val militaryUnits = civInfo.units.getCivUnits().filter { it.isMilitary() } + do { + val unitToDisband = militaryUnits.minByOrNull { it.baseUnit.cost } + // or .firstOrNull()? + ?: break + unitToDisband.disband() + val unitName = unitToDisband.shortDisplayName() + civInfo.addNotification("Cannot provide unit upkeep for $unitName - unit has been disbanded!", NotificationCategory.Units, unitName, NotificationIcon.Death) + civInfo.updateStatsForNextTurn() // recalculate unit upkeep + nextTurnStats = civInfo.stats.statsForNextTurn + } while (civInfo.gold <= -200 && nextTurnStats.gold.toInt() < 0) } civInfo.addGold( nextTurnStats.gold.toInt() ) @@ -265,13 +271,10 @@ class TurnManager(val civInfo: Civilization) { if (civInfo.isMajorCiv()) // City-states don't get great people! civInfo.greatPeople.addGreatPersonPoints() - // To handle tile's owner issue (#8246), we need to run being razed city first. - for (city in sequence { - yieldAll(civInfo.cities.filter { it.isBeingRazed }) - yieldAll(civInfo.cities.filterNot { it.isBeingRazed }) - }.toList()) { // a city can be removed while iterating (if it's being razed) so we need to iterate over a copy + // To handle tile's owner issue (#8246), we need to run cities being razed first. + // a city can be removed while iterating (if it's being razed) so we need to iterate over a copy - sorting does one + for (city in civInfo.cities.sortedByDescending { it.isBeingRazed }) CityTurnManager(city).endTurn() - } civInfo.temporaryUniques.endTurn() diff --git a/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewCategories.kt b/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewCategories.kt index e24f90efda..9b2ead91b0 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewCategories.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/EmpireOverviewCategories.kt @@ -25,6 +25,7 @@ enum class EmpireOverviewCategories( Stats("StatIcons/Gold", 'S', Align.top) { override fun createTab(viewingPlayer: Civilization, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?) = StatsOverviewTab(viewingPlayer, overviewScreen) + override fun showDisabled(viewingPlayer: Civilization) = viewingPlayer.isSpectator() }, Trades("StatIcons/Acquire", 'T', Align.top) { override fun createTab(viewingPlayer: Civilization, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?) = diff --git a/tests/src/com/unciv/uniques/TriggeredUniquesTests.kt b/tests/src/com/unciv/uniques/TriggeredUniquesTests.kt index c2e9eb0d94..723999d268 100644 --- a/tests/src/com/unciv/uniques/TriggeredUniquesTests.kt +++ b/tests/src/com/unciv/uniques/TriggeredUniquesTests.kt @@ -39,6 +39,9 @@ class TriggeredUniquesTests { @Test fun testConditionalTimedUniqueExpires() { civInfo.policies.adopt(policy, true) + // For endTurn to do the part we need, the civ must be alive - have a city or unit, + // and right now that attacker is not in the civ's unit list + civInfo.units.addUnit(attacker.unit, false) TurnManager(civInfo).endTurn() val modifiers = BattleDamage.getAttackModifiers(attacker, defender) Assert.assertTrue("Timed Strength should no longer work after endTurn", modifiers.sumValues() == 0)