From cf6543bc2984e9119711a41577b931af2d5cadaa Mon Sep 17 00:00:00 2001 From: Timo T Date: Thu, 5 May 2022 08:09:15 +0200 Subject: [PATCH] Fix Sweden not being able to gift great people to city states (#6690) --- .../unciv/ui/worldscreen/unit/UnitActions.kt | 6 +- tests/build.gradle.kts | 4 +- .../com/unciv/uniques/GlobalUniquesTests.kt | 209 +++++------------- tests/src/com/unciv/uniques/TestGame.kt | 122 ++++++++++ .../src/com/unciv/uniques/UnitUniquesTests.kt | 48 ++++ 5 files changed, 228 insertions(+), 161 deletions(-) create mode 100644 tests/src/com/unciv/uniques/TestGame.kt create mode 100644 tests/src/com/unciv/uniques/UnitUniquesTests.kt diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 9ea2bc46b5..aa89f3cded 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -817,7 +817,7 @@ object UnitActions { if (getGiftAction != null) actionList += getGiftAction } - private fun getGiftAction(unit: MapUnit, tile: TileInfo): UnitAction? { + fun getGiftAction(unit: MapUnit, tile: TileInfo): UnitAction? { val recipient = tile.getOwner() // We need to be in another civs territory. if (recipient == null || recipient.isCurrentPlayer()) return null @@ -825,7 +825,7 @@ object UnitActions { // City States only take military units (and units specifically allowed by uniques) if (recipient.isCityState()) { if (!unit.matchesFilter("Military") - && unit.getMatchingUniques("Gain [] Influence with a [] gift to a City-State") + && unit.getMatchingUniques(UniqueType.GainInfluenceWithUnitGiftToCityState, checkCivInfoUniques = true) .none { unit.matchesFilter(it.params[1]) } ) return null } @@ -837,7 +837,7 @@ object UnitActions { val giftAction = { if (recipient.isCityState()) { - for (unique in unit.civInfo.getMatchingUniques(UniqueType.GainInfluenceWithUnitGiftToCityState)) { + for (unique in unit.getMatchingUniques(UniqueType.GainInfluenceWithUnitGiftToCityState, checkCivInfoUniques = true)) { if (unit.matchesFilter(unique.params[1])) { recipient.getDiplomacyManager(unit.civInfo) .addInfluence(unique.params[0].toFloat() - 5f) diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index a2c61de3f7..7a815548ab 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -6,8 +6,8 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_1_7 - targetCompatibility = JavaVersion.VERSION_1_7 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } tasks { diff --git a/tests/src/com/unciv/uniques/GlobalUniquesTests.kt b/tests/src/com/unciv/uniques/GlobalUniquesTests.kt index 79c1ad8319..2ce39c7a19 100644 --- a/tests/src/com/unciv/uniques/GlobalUniquesTests.kt +++ b/tests/src/com/unciv/uniques/GlobalUniquesTests.kt @@ -3,15 +3,6 @@ package com.unciv.uniques import com.badlogic.gdx.math.Vector2 import com.unciv.Constants -import com.unciv.UncivGame -import com.unciv.logic.GameInfo -import com.unciv.logic.city.CityInfo -import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.logic.map.MapUnit -import com.unciv.logic.map.TileInfo -import com.unciv.logic.map.TileMap -import com.unciv.models.metadata.GameSettings -import com.unciv.models.ruleset.* import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.stats.Stats import com.unciv.testing.GdxTestRunner @@ -24,115 +15,21 @@ import org.junit.runner.RunWith @RunWith(GdxTestRunner::class) class GlobalUniquesTests { - private var ruleSet = Ruleset() - private var gameInfo = GameInfo() - private var tileMap = TileMap() + private lateinit var game: TestGame - companion object { - var objectsCreated = 0 - } - @Before fun initTheWorld() { - // Set UncivGame.Current so that debug variables are initialized - UncivGame.Current = UncivGame("Test") - // And the settings can be reached for the locale used in .tr() - UncivGame.Current.settings = GameSettings() - - // Create a new ruleset we can easily edit, and set the important variables of gameInfo - RulesetCache.loadRulesets() - ruleSet = RulesetCache.getVanillaRuleset() - gameInfo.ruleSet = ruleSet - gameInfo.difficultyObject = ruleSet.difficulties["Prince"]!! - - // Create a tilemap, needed for city centers - gameInfo.tileMap = tileMap + game = TestGame() } - private fun addTile(terrain: String, position: Vector2 = Vector2.Zero, features: List = listOf()): TileInfo { - val tile = TileInfo() - tile.ruleset = ruleSet - tile.baseTerrain = terrain - for (feature in features) { - tile.addTerrainFeature(feature) - } - tile.tileMap = tileMap - tile.position = position - tile.setTransients() - while (tileMap.tileMatrix.size < position.x + 1) - tileMap.tileMatrix.add(ArrayList()) - while (tileMap.tileMatrix[position.x.toInt()].size < position.y + 1) - tileMap.tileMatrix[position.x.toInt()].add(null) - tileMap.tileMatrix[position.x.toInt()][position.y.toInt()] = tile - return tile - } - - private fun addCiv(): CivilizationInfo { - val nationName = "Nation-$objectsCreated" - objectsCreated++ - ruleSet.nations[nationName] = Nation().apply { - name = nationName - cities = arrayListOf("The Capital") - } - val civInfo = CivilizationInfo() - civInfo.nation = ruleSet.nations[nationName]!! - civInfo.gameInfo = gameInfo - civInfo.civName = nationName - civInfo.setTransients() - gameInfo.civilizations.add(civInfo) - return civInfo - } - - private fun addCity(civInfo: CivilizationInfo, tile: TileInfo, replacePalace: Boolean = false): CityInfo { - val cityInfo = CityInfo(civInfo, tile.position) - cityInfo.population.addPopulation(-1) // Remove population - - if (replacePalace && civInfo.cities.size == 1) { - // Add a capital indicator without any other stats - val palaceWithoutStats = createBuildingWithUnique(UniqueType.IndicatesCapital.text) - cityInfo.cityConstructions.removeBuilding("Palace") - cityInfo.cityConstructions.addBuilding(palaceWithoutStats.name) - } - return cityInfo - } - - private fun addUnit(name: String, civInfo: CivilizationInfo, tile: TileInfo): MapUnit { - val baseUnit = ruleSet.units[name]!! - baseUnit.ruleset = ruleSet - val mapUnit = baseUnit.getMapUnit(civInfo) - mapUnit.putInTile(tile) - return mapUnit - } - - private fun addEmptySpecialist(): String { - val name = "specialist-${objectsCreated}" - objectsCreated++ - ruleSet.specialists[name] = Specialist() - return name - } - - private fun createBuildingWithUnique(unique: String): Building { - return createBuildingWithUniques(arrayListOf(unique)) - } - - private fun createBuildingWithUniques(uniques: ArrayList = arrayListOf()): Building { - val building = Building() - building.uniques = uniques - building.name = "Building-${objectsCreated}" - objectsCreated++ - ruleSet.buildings[building.name] = building - return building - } - - - + @Test /** tests [UniqueType.Stats] */ fun stats() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val buildingName = createBuildingWithUnique("[+1 Food]").name + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val buildingName = game.createBuildingWithUnique("[+1 Food]").name cityInfo.cityConstructions.addBuilding(buildingName) cityInfo.cityStats.update() @@ -141,10 +38,10 @@ class GlobalUniquesTests { @Test fun statsPerCity() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val buildingName = createBuildingWithUnique("[+1 Production] [in this city]").name + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val buildingName = game.createBuildingWithUnique("[+1 Production] [in this city]").name cityInfo.cityConstructions.addBuilding(buildingName) cityInfo.cityStats.update() @@ -153,11 +50,11 @@ class GlobalUniquesTests { @Test fun statsPerSpecialist() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val building = createBuildingWithUnique("[+3 Gold] from every specialist [in this city]") - val specialistName = addEmptySpecialist() + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val building = game.createBuildingWithUnique("[+3 Gold] from every specialist [in this city]") + val specialistName = game.addEmptySpecialist() building.specialistSlots.add(specialistName,2) cityInfo.population.addPopulation(2) cityInfo.population.specialistAllocations[specialistName] = 2 @@ -169,10 +66,10 @@ class GlobalUniquesTests { @Test fun statsPerPopulation() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val building = createBuildingWithUnique("[+3 Gold] per [2] population [in this city]") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val building = game.createBuildingWithUnique("[+3 Gold] per [2] population [in this city]") cityInfo.population.addPopulation(4) cityInfo.cityConstructions.addBuilding(building.name) @@ -182,10 +79,10 @@ class GlobalUniquesTests { @Test fun statsPerXPopulation() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val building = createBuildingWithUnique("[+3 Gold] in cities with [3] or more population") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val building = game.createBuildingWithUnique("[+3 Gold] in cities with [3] or more population") cityInfo.population.addPopulation(2) cityInfo.cityConstructions.addBuilding(building.name) @@ -199,10 +96,10 @@ class GlobalUniquesTests { @Test fun statsFromCitiesOnSpecificTiles() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val building = createBuildingWithUnique("[+3 Gold] in cities on [${Constants.desert}] tiles") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val building = game.createBuildingWithUnique("[+3 Gold] in cities on [${Constants.desert}] tiles") cityInfo.cityConstructions.addBuilding(building.name) cityInfo.cityStats.update() @@ -214,11 +111,11 @@ class GlobalUniquesTests { @Test fun statsSpendingGreatPeople() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val unit = addUnit("Great Prophet", civInfo, tile) - val building = createBuildingWithUnique("[+250 Gold] whenever a Great Person is expended") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val unit = game.addUnit("Great Prophet", civInfo, tile) + val building = game.createBuildingWithUnique("[+250 Gold] whenever a Great Person is expended") cityInfo.cityConstructions.addBuilding(building.name) civInfo.addGold(-civInfo.gold) @@ -228,38 +125,38 @@ class GlobalUniquesTests { @Test fun statsFromTiles() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val building = createBuildingWithUnique("[+4 Gold] from [${Constants.grassland}] tiles [in all cities]") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val building = game.createBuildingWithUnique("[+4 Gold] from [${Constants.grassland}] tiles [in all cities]") cityInfo.cityConstructions.addBuilding(building.name) - val tile2 = addTile(Constants.grassland, Vector2(0f,1f)) + val tile2 = game.addTile(Constants.grassland, Vector2(0f,1f)) Assert.assertTrue(tile2.getTileStats(cityInfo, civInfo).gold == 4f) } @Test fun statsFromTilesWithout() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val building = createBuildingWithUnique("[+4 Gold] from [${Constants.grassland}] tiles without [${Constants.forest}] [in all cities]") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val building = game.createBuildingWithUnique("[+4 Gold] from [${Constants.grassland}] tiles without [${Constants.forest}] [in all cities]") cityInfo.cityConstructions.addBuilding(building.name) - val tile2 = addTile(Constants.grassland, Vector2(0f,1f)) + val tile2 = game.addTile(Constants.grassland, Vector2(0f,1f)) Assert.assertTrue(tile2.getTileStats(cityInfo, civInfo).gold == 4f) - val tile3 = addTile(Constants.grassland, Vector2(0f, 2f), listOf(Constants.forest)) + val tile3 = game.addTile(Constants.grassland, Vector2(0f, 2f), listOf(Constants.forest)) Assert.assertFalse(tile3.getTileStats(cityInfo, civInfo).gold == 4f) } @Test fun statsFromObject() { - val civInfo = addCiv() - val tile = addTile(Constants.desert) - val cityInfo = addCity(civInfo, tile, true) - val specialist = addEmptySpecialist() - val building = createBuildingWithUnique("[+3 Faith] from every [${specialist}]") + val civInfo = game.addCiv() + val tile = game.addTile(Constants.desert) + val cityInfo = game.addCity(civInfo, tile, true) + val specialist = game.addEmptySpecialist() + val building = game.createBuildingWithUnique("[+3 Faith] from every [${specialist}]") cityInfo.cityConstructions.addBuilding(building.name) cityInfo.population.addPopulation(2) @@ -269,17 +166,17 @@ class GlobalUniquesTests { Assert.assertTrue(cityInfo.cityStats.finalStatList["Specialists"]!!.faith == 6f) cityInfo.cityConstructions.removeBuilding(building.name) - val building2 = createBuildingWithUnique("[+3 Faith] from every [${Constants.grassland}]") + val building2 = game.createBuildingWithUnique("[+3 Faith] from every [${Constants.grassland}]") cityInfo.cityConstructions.addBuilding(building2.name) - val tile2 = addTile(Constants.grassland, Vector2(1f, 0f)) + val tile2 = game.addTile(Constants.grassland, Vector2(1f, 0f)) Assert.assertTrue(tile2.getTileStats(cityInfo, civInfo).faith == 3f) cityInfo.cityConstructions.removeBuilding(building2.name) - val emptyBuilding = createBuildingWithUniques() + val emptyBuilding = game.createBuildingWithUniques() - val building3 = createBuildingWithUnique("[+3 Faith] from every [${emptyBuilding.name}]") + val building3 = game.createBuildingWithUnique("[+3 Faith] from every [${emptyBuilding.name}]") cityInfo.cityConstructions.addBuilding(emptyBuilding.name) cityInfo.cityConstructions.addBuilding(building3.name) cityInfo.cityStats.update() diff --git a/tests/src/com/unciv/uniques/TestGame.kt b/tests/src/com/unciv/uniques/TestGame.kt new file mode 100644 index 0000000000..d067a9f25f --- /dev/null +++ b/tests/src/com/unciv/uniques/TestGame.kt @@ -0,0 +1,122 @@ +package com.unciv.uniques + +import com.badlogic.gdx.math.Vector2 +import com.unciv.UncivGame +import com.unciv.logic.GameInfo +import com.unciv.logic.city.CityInfo +import com.unciv.logic.civilization.CityStateType +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.civilization.PlayerType +import com.unciv.logic.map.MapUnit +import com.unciv.logic.map.TileInfo +import com.unciv.logic.map.TileMap +import com.unciv.models.metadata.BaseRuleset +import com.unciv.models.metadata.GameSettings +import com.unciv.models.ruleset.* +import com.unciv.models.ruleset.unique.UniqueType + +class TestGame { + + private var objectsCreated = 0 + val ruleset: Ruleset + val gameInfo = GameInfo() + val tileMap = TileMap() + + init { + // Set UncivGame.Current so that debug variables are initialized + UncivGame.Current = UncivGame("Test") + // And the settings can be reached for the locale used in .tr() + UncivGame.Current.settings = GameSettings() + + // Create a new ruleset we can easily edit, and set the important variables of gameInfo + RulesetCache.loadRulesets() + ruleset = RulesetCache[BaseRuleset.Civ_V_GnK.fullName]!! + gameInfo.ruleSet = ruleset + gameInfo.difficultyObject = ruleset.difficulties["Prince"]!! + + // Create a tilemap, needed for city centers + gameInfo.tileMap = tileMap + } + + fun addTile(terrain: String, position: Vector2 = Vector2.Zero, features: List = listOf()): TileInfo { + val tile = TileInfo() + tile.ruleset = ruleset + tile.baseTerrain = terrain + for (feature in features) { + tile.addTerrainFeature(feature) + } + tile.tileMap = tileMap + tile.position = position + tile.setTransients() + while (tileMap.tileMatrix.size < position.x + 1) + tileMap.tileMatrix.add(ArrayList()) + while (tileMap.tileMatrix[position.x.toInt()].size < position.y + 1) + tileMap.tileMatrix[position.x.toInt()].add(null) + tileMap.tileMatrix[position.x.toInt()][position.y.toInt()] = tile + return tile + } + + fun addCiv(uniques: List = emptyList(), isPlayer: Boolean = false, cityState: CityStateType? = null): CivilizationInfo { + + val nationName = "Nation-${objectsCreated++}" + ruleset.nations[nationName] = Nation().apply { + name = nationName + cities = arrayListOf("The Capital") + if (cityState != null) { + cityStateType = cityState + } + this.uniques = ArrayList(uniques) + } + val civInfo = CivilizationInfo() + civInfo.nation = ruleset.nations[nationName]!! + civInfo.gameInfo = gameInfo + civInfo.civName = nationName + if (isPlayer) civInfo.playerType = PlayerType.Human + civInfo.setTransients() + if (cityState != null) { + civInfo.cityStateFunctions.initCityState(ruleset, "Ancient era", emptyList()) + } + gameInfo.civilizations.add(civInfo) + return civInfo + } + + fun addCity(civInfo: CivilizationInfo, tile: TileInfo, replacePalace: Boolean = false): CityInfo { + val cityInfo = CityInfo(civInfo, tile.position) + cityInfo.population.addPopulation(-1) // Remove population + + if (replacePalace && civInfo.cities.size == 1) { + // Add a capital indicator without any other stats + val palaceWithoutStats = createBuildingWithUnique(UniqueType.IndicatesCapital.text) + cityInfo.cityConstructions.removeBuilding("Palace") + cityInfo.cityConstructions.addBuilding(palaceWithoutStats.name) + } + return cityInfo + } + + fun addUnit(name: String, civInfo: CivilizationInfo, tile: TileInfo): MapUnit { + val baseUnit = ruleset.units[name]!! + baseUnit.ruleset = ruleset + val mapUnit = baseUnit.getMapUnit(civInfo) + mapUnit.putInTile(tile) + return mapUnit + } + + fun addEmptySpecialist(): String { + val name = "specialist-${objectsCreated++}" + ruleset.specialists[name] = Specialist() + return name + } + + fun createBuildingWithUnique(unique: String): Building { + return createBuildingWithUniques(arrayListOf(unique)) + } + + fun createBuildingWithUniques(uniques: ArrayList = arrayListOf()): Building { + val building = Building() + building.uniques = uniques + building.name = "Building-${objectsCreated++}" + ruleset.buildings[building.name] = building + return building + } + +} \ No newline at end of file diff --git a/tests/src/com/unciv/uniques/UnitUniquesTests.kt b/tests/src/com/unciv/uniques/UnitUniquesTests.kt new file mode 100644 index 0000000000..5ef189b1ac --- /dev/null +++ b/tests/src/com/unciv/uniques/UnitUniquesTests.kt @@ -0,0 +1,48 @@ +package com.unciv.uniques + +import com.badlogic.gdx.math.Vector2 +import com.unciv.Constants +import com.unciv.logic.civilization.CityStateType +import com.unciv.testing.GdxTestRunner +import com.unciv.ui.worldscreen.unit.UnitActions +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Assert +import org.junit.Assert.assertNotNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(GdxTestRunner::class) +class UnitUniquesTests { + private lateinit var game: TestGame + + @Before + fun initTheWorld() { + game = TestGame() + } + + @Test + fun `Sweden can gift Great Persons to City States`() { + // when + val cityState = game.addCiv(cityState = CityStateType.Cultured) + val cityStateCapitalTile = game.addTile(Constants.grassland, Vector2(0f, 0f)) + val cityStateCapital = game.addCity(cityState, cityStateCapitalTile) + + val mainCiv = game.addCiv( + uniques = listOf("Gain [90] Influence with a [Great Person] gift to a City-State"), + isPlayer = true + ) + game.gameInfo.currentPlayerCiv = mainCiv + + val unitTile = game.addTile(Constants.grassland, Vector2(1f, 0f)) + cityStateCapital.expansion.takeOwnership(unitTile) + + val greatPerson = game.addUnit("Great Scientist", mainCiv, unitTile) + + // then + val giftAction = UnitActions.getGiftAction(greatPerson, unitTile) + + assertNotNull("Great Person should have a gift action", giftAction) + } +} \ No newline at end of file