From fc40da11d993a4f2c47a5040609377889d6d037e Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Thu, 3 Aug 2023 11:38:36 +0300 Subject: [PATCH] Added tests to ensure that resources from buildings behave as expected (#9860) * Added tests to ensure that resources from buildings behave as expected * More tests suggested by @SeventhM * Caught an edge case - if you pillage your own tile improvement, your resources would not have updated! * Actually every time an improvement changes you could have a resource change * Update resources eon every improvement change because they could be providing resources via uniques --- .../com/unciv/logic/city/CityConstructions.kt | 16 +-- .../unciv/logic/civilization/Civilization.kt | 2 + core/src/com/unciv/logic/map/tile/Tile.kt | 7 + tests/src/com/unciv/uniques/ResourceTests.kt | 125 ++++++++++++++++++ ...edUniquesTests.kt => TimedUniquesTests.kt} | 2 +- 5 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 tests/src/com/unciv/uniques/ResourceTests.kt rename tests/src/com/unciv/uniques/{TriggeredUniquesTests.kt => TimedUniquesTests.kt} (98%) diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 30da232006..a899d2357c 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -21,8 +21,8 @@ import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.unique.LocalUniqueCache import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.UniqueMap -import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueTriggerActivation +import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stat import com.unciv.models.stats.Stats @@ -513,11 +513,11 @@ class CityConstructions : IsPartOfGameInfoSerialization { UniqueType.StatsFromTiles, UniqueType.StatsFromTilesWithout, UniqueType.StatsFromObject, UniqueType.StatPercentFromObject, UniqueType.AllStatsPercentFromObject ) - + updateUniques() - + // Happiness is global, so it could affect all cities - if(building.isStatRelated(Stat.Happiness)) { + if (building.isStatRelated(Stat.Happiness)) { for (city in civ.cities) { city.reassignPopulationDeferred() } @@ -525,8 +525,6 @@ class CityConstructions : IsPartOfGameInfoSerialization { else if(uniqueTypesModifyingYields.any { building.hasUnique(it) }) city.reassignPopulationDeferred() - city.civ.cache.updateCivResources() // this building could be a resource-requiring one - addFreeBuildings() } @@ -556,14 +554,16 @@ class CityConstructions : IsPartOfGameInfoSerialization { builtBuildings.remove(buildingName) city.civ.cache.updateCitiesConnectedToCapital(false) // could be a connecting building, like a harbor updateUniques() - city.civ.cache.updateCivResources() // this building could be a resource-requiring one } fun updateUniques(onLoadGame:Boolean = false) { builtBuildingUniqueMap.clear() for (building in getBuiltBuildings()) builtBuildingUniqueMap.addUniques(building.uniqueObjects) - if (!onLoadGame) city.cityStats.update() + if (!onLoadGame) { + city.cityStats.update() + city.civ.cache.updateCivResources() + } } fun addFreeBuildings() { diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 4720fe1f93..885a47b8ff 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -689,6 +689,8 @@ class Civilization : IsPartOfGameInfoSerialization { tacticalAI.init(this) + cache.updateCivResources() + cache.setTransients() } diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index aa40da338c..f5e8249fd4 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -311,6 +311,11 @@ open class Tile : IsPartOfGameInfoSerialization { fun changeImprovement(improvementStr: String?) { improvementIsPillaged = false improvement = improvementStr + + + if (owningCity != null){ + owningCity!!.civ.cache.updateCivResources() + } } // function handling when adding a road to the tile @@ -931,6 +936,8 @@ open class Tile : IsPartOfGameInfoSerialization { } owningCity?.reassignPopulationDeferred() + if (owningCity != null) + owningCity!!.civ.cache.updateCivResources() } fun isPillaged(): Boolean { diff --git a/tests/src/com/unciv/uniques/ResourceTests.kt b/tests/src/com/unciv/uniques/ResourceTests.kt new file mode 100644 index 0000000000..67acc78400 --- /dev/null +++ b/tests/src/com/unciv/uniques/ResourceTests.kt @@ -0,0 +1,125 @@ +package com.unciv.uniques + +import com.unciv.testing.GdxTestRunner +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith + + +@RunWith(GdxTestRunner::class) +class ResourceTests { + private val game = TestGame().apply { makeHexagonalMap(2) } + private val civInfo = game.addCiv() + private val city = game.addCity(civInfo, game.tileMap[0,0]) + + @Test + fun testConsumesResourceUnique() { + val consumesCoal = game.createBuilding("Consumes [1] [Coal]") + city.cityConstructions.addBuilding(consumesCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == -1) + } + + @Test + fun testResourceProductionModifierDoesNotAffectConsumption() { + val consumesCoal = game.createBuilding("Consumes [1] [Coal]") + val doubleCoal = game.createBuilding("Double quantity of [Coal] produced") + val doubleStrategic = game.createBuilding("Quantity of strategic resources produced by the empire +[100]%") + + city.cityConstructions.addBuilding(consumesCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == -1) + + city.cityConstructions.addBuilding(doubleCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == -1) + + city.cityConstructions.addBuilding(doubleStrategic.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == -1) + } + + @Test + fun testResourceProductionAndConsumptionModifierDoesNotAffectConsumption() { + val consumesCoal = game.createBuilding("Consumes [1] [Coal]") + val providesCoal = game.createBuilding("Provides [1] [Coal]") + val doubleCoal = game.createBuilding("Double quantity of [Coal] produced") + val doubleStrategic = game.createBuilding("Quantity of strategic resources produced by the empire +[100]%") + + city.cityConstructions.addBuilding(providesCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 1) + + city.cityConstructions.addBuilding(doubleCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 2) + + city.cityConstructions.addBuilding(doubleStrategic.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 4) + + city.cityConstructions.addBuilding(consumesCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 3) // Produce 4 (1*2*2), consume 1 + } + + @Test + fun testBuildingGrantedByUniqueGrantsResource() { + val resourceProvider = game.createBuilding("Provides [1] [Coal]") + val resourceProviderProvider = game.createBuilding("Gain a free [${resourceProvider.name}] [in this city]") + city.cityConstructions.addBuilding(resourceProviderProvider.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 1) + } + + @Test + fun testTileProvidesResourceOnlyWithRequiredTech() { + val tile = game.tileMap[1,1] + tile.resource = "Coal" + tile.improvement = "Mine" + tile.resourceAmount = 1 + + civInfo.tech.addTechnology(game.ruleset.tileImprovements["Mine"]!!.techRequired!!) + Assert.assertEquals(civInfo.getCivResourcesByName()["Coal"], 0) + + civInfo.tech.addTechnology(game.ruleset.tileResources["Coal"]!!.revealedBy!!) + + Assert.assertEquals(civInfo.getCivResourcesByName()["Coal"], 1) + } + + + @Test + fun testTileDoesNotProvideResourceWithPillagedImprovement() { + val tile = game.tileMap[1,1] + tile.resource = "Coal" + tile.improvement = "Mine" + tile.resourceAmount = 1 + + civInfo.tech.addTechnology(game.ruleset.tileImprovements["Mine"]!!.techRequired!!) + civInfo.tech.addTechnology(game.ruleset.tileResources["Coal"]!!.revealedBy!!) + Assert.assertEquals(civInfo.getCivResourcesByName()["Coal"], 1) + + tile.setPillaged() + Assert.assertEquals(civInfo.getCivResourcesByName()["Coal"], 0) + } + + @Test + /** The revealing tech should not affect whether we can get the resource from improvements */ + fun testImprovementProvidesResourceEvenWithoutTech() { + val tile = game.tileMap[1,1] + val improvement = game.createTileImprovement("Provides [1] [Coal]", "Consumes [1] [Silver]") + tile.improvement = improvement.name + civInfo.cache.updateCivResources() + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 1) + Assert.assertTrue(civInfo.getCivResourcesByName()["Silver"] == -1) + } + + @Test + /** The revealing tech should not affect whether we can get the resource from improvements */ + fun testImprovementProvidesResourceWithUniqueBonuses() { + val tile = game.tileMap[1,1] + val improvement = game.createTileImprovement("Provides [1] [Coal]") + tile.improvement = improvement.name + civInfo.cache.updateCivResources() + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 1) + + val doubleCoal = game.createBuilding("Double quantity of [Coal] produced") + city.cityConstructions.addBuilding(doubleCoal.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 2) + + val doubleStrategic = game.createBuilding("Quantity of strategic resources produced by the empire +[100]%") + city.cityConstructions.addBuilding(doubleStrategic.name) + Assert.assertTrue(civInfo.getCivResourcesByName()["Coal"] == 4) + } +} diff --git a/tests/src/com/unciv/uniques/TriggeredUniquesTests.kt b/tests/src/com/unciv/uniques/TimedUniquesTests.kt similarity index 98% rename from tests/src/com/unciv/uniques/TriggeredUniquesTests.kt rename to tests/src/com/unciv/uniques/TimedUniquesTests.kt index 986e00a411..c5746d1d4b 100644 --- a/tests/src/com/unciv/uniques/TriggeredUniquesTests.kt +++ b/tests/src/com/unciv/uniques/TimedUniquesTests.kt @@ -12,7 +12,7 @@ import org.junit.runner.RunWith @RunWith(GdxTestRunner::class) -class TriggeredUniquesTests { +class TimedUniquesTests { private val game = TestGame().apply { makeHexagonalMap(2) } private val civInfo = game.addCiv() private val policy =