From 9eb9176b2a3bddd9c4c464d9c847027acd855ec7 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Fri, 29 Mar 2019 18:03:18 +0300 Subject: [PATCH] Units & buildings requiring resources or other needs are now displayed, but not selectable, from the city screen as constructions --- android/build.gradle | 4 +- .../src/com/unciv/logic/city/IConstruction.kt | 4 + .../com/unciv/models/gamebasics/Building.kt | 75 ++++++++++++------- .../unciv/models/gamebasics/unit/BaseUnit.kt | 35 ++++++--- .../unciv/ui/cityscreen/ConstructionsTable.kt | 32 +++++--- 5 files changed, 101 insertions(+), 49 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 3f07d413f5..0288c64b2c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.app" minSdkVersion 14 targetSdkVersion 28 - versionCode 220 - versionName "2.14.1.patch1" + versionCode 221 + versionName "2.14.2" } // Had to add this crap for Travis to build, it wanted to sign the app diff --git a/core/src/com/unciv/logic/city/IConstruction.kt b/core/src/com/unciv/logic/city/IConstruction.kt index bf18d9dd5c..289409a11d 100644 --- a/core/src/com/unciv/logic/city/IConstruction.kt +++ b/core/src/com/unciv/logic/city/IConstruction.kt @@ -7,6 +7,7 @@ interface IConstruction : INamed, ICivilopedia { fun getProductionCost(adoptedPolicies: HashSet): Int fun getGoldCost(adoptedPolicies: HashSet): Int fun isBuildable(construction: CityConstructions): Boolean + fun shouldBeDisplayed(construction: CityConstructions): Boolean fun postBuildEvent(construction: CityConstructions) // Yes I'm hilarious. fun canBePurchased(): Boolean } @@ -14,6 +15,9 @@ interface IConstruction : INamed, ICivilopedia { open class SpecialConstruction(override var name: String, override val description: String) : IConstruction{ + override fun shouldBeDisplayed(construction: CityConstructions): Boolean { + return isBuildable(construction) + } companion object { fun getSpecialConstructions(): List { diff --git a/core/src/com/unciv/models/gamebasics/Building.kt b/core/src/com/unciv/models/gamebasics/Building.kt index de86f63000..aab30f297f 100644 --- a/core/src/com/unciv/models/gamebasics/Building.kt +++ b/core/src/com/unciv/models/gamebasics/Building.kt @@ -8,6 +8,7 @@ import com.unciv.models.stats.Stats import com.unciv.ui.utils.getRandom class Building : NamedStats(), IConstruction{ + override val description: String get() = getDescription(false, hashSetOf()) @@ -167,43 +168,63 @@ class Building : NamedStats(), IConstruction{ return (cost / 10).toInt() * 10 } - override fun isBuildable(construction: CityConstructions): Boolean { - if (construction.isBuilt(name)) return false + + override fun shouldBeDisplayed(construction: CityConstructions): Boolean { + val rejectionReason = getRejectionReason(construction) + return rejectionReason=="" + || rejectionReason.startsWith("Requires") + || rejectionReason == "Wonder is being built elsewhere" + } + + fun getRejectionReason(construction: CityConstructions):String{ + if (construction.isBuilt(name)) return "Already built" val civInfo = construction.cityInfo.civInfo - if (uniqueTo!=null && uniqueTo!=civInfo.civName) return false - if (GameBasics.Buildings.values.any { it.uniqueTo==civInfo.civName && it.replaces==name }) return false - if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return false + if (uniqueTo!=null && uniqueTo!=civInfo.civName) return "Unique to $uniqueTo" + if (GameBasics.Buildings.values.any { it.uniqueTo==civInfo.civName && it.replaces==name }) return "Our unique building replaces this" + if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched" + + // Regular wonders if (isWonder && requiredBuildingInAllCities==null){ if(civInfo.gameInfo.civilizations.flatMap { it.cities } .any {it.cityConstructions.isBuilt(name)}) - return false - + return "Wonder is already built" + if(civInfo.cities.any { it!=construction.cityInfo && it.cityConstructions.isBeingConstructed(name) }) - return false + return "Wonder is being built elsewhere" } - if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) return false - if (requiredBuildingInAllCities != null && civInfo.cities.any { !it.cityConstructions.containsBuildingOrEquivalent(requiredBuildingInAllCities!!) }) - return false - if(requiredBuildingInAllCities!=null && civInfo.cities.any { - it.cityConstructions.isBeingConstructed(name) || it.cityConstructions.isBuilt(name) - }) - return false - if (cannotBeBuiltWith != null && construction.isBuilt(cannotBeBuiltWith!!)) return false + // National wonders + if(requiredBuildingInAllCities!=null) { + if (civInfo.cities.any { !it.cityConstructions.containsBuildingOrEquivalent(requiredBuildingInAllCities!!) }) + return "Requires a $requiredBuildingInAllCities in all cities" + + if (civInfo.cities.any {it.cityConstructions.isBuilt(name) }) + return "Wonder is already built" + if (civInfo.cities.any {it.cityConstructions.isBeingConstructed(name) }) + return "Wonder is being built elsewhere" + } + + if (requiredBuilding != null && !construction.containsBuildingOrEquivalent(requiredBuilding!!)) + return "Requires a $requiredBuilding in this city" + if (cannotBeBuiltWith != null && construction.isBuilt(cannotBeBuiltWith!!)) + return "Cannot be built with $cannotBeBuiltWith" + if ("Must be next to desert" in uniques && !construction.cityInfo.getCenterTile().getTilesInDistance(1).any { it.baseTerrain == "Desert" }) - return false + return "Must be next to desert" + if ("Must be next to mountain" in uniques && !construction.cityInfo.getCenterTile().getTilesInDistance(1).any { it.baseTerrain == "Mountain" }) - return false + return "Must be next to mountain" + if("Can only be built in coastal cities" in uniques && construction.cityInfo.getCenterTile().neighbors.none { it.baseTerrain=="Coast" }) - return false - if (requiredResource != null && !civInfo.hasResource(requiredResource!!)) - return false + return "Can only be built in coastal cities" + if (requiredResource != null && !civInfo.hasResource(requiredResource!!)) + return "Requires $requiredResource" if (requiredNearbyImprovedResources != null) { val containsResourceWithImprovement = construction.cityInfo.getTilesInRange() @@ -213,14 +234,18 @@ class Building : NamedStats(), IConstruction{ && it.getTileResource().improvement == it.improvement && it.getOwner() == civInfo } - if (!containsResourceWithImprovement) return false + if (!containsResourceWithImprovement) return "Nearby $requiredNearbyImprovedResources required" } if ("Spaceship part" in uniques) { - if (!civInfo.getBuildingUniques().contains("Enables construction of Spaceship parts")) return false - if (civInfo.victoryManager.unconstructedSpaceshipParts()[name] == 0) return false // Don't need to build any more of these! + if (!civInfo.getBuildingUniques().contains("Enables construction of Spaceship parts")) return "Apollo project not built!" + if (civInfo.victoryManager.unconstructedSpaceshipParts()[name] == 0) return "Don't need to build any more of these!" } - return true + return "" + } + + override fun isBuildable(construction: CityConstructions): Boolean { + return getRejectionReason(construction)=="" } override fun postBuildEvent(construction: CityConstructions) { diff --git a/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt b/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt index 76b6550155..b0f45f7b0b 100644 --- a/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/gamebasics/unit/BaseUnit.kt @@ -108,22 +108,33 @@ class BaseUnit : INamed, IConstruction, ICivilopedia { fun getDisbandGold() = getBaseGoldCost().toInt()/20 - fun isBuildable(civInfo:CivilizationInfo): Boolean { - if (unbuildable) return false - if (requiredTech!=null && !civInfo.tech.isResearched(requiredTech!!)) return false - if (obsoleteTech!=null && civInfo.tech.isResearched(obsoleteTech!!)) return false - if (uniqueTo!=null && uniqueTo!=civInfo.civName) return false - if (GameBasics.Units.values.any { it.uniqueTo==civInfo.civName && it.replaces==name }) return false - if (requiredResource!=null && !civInfo.hasResource(requiredResource!!)) return false - return true + override fun shouldBeDisplayed(construction: CityConstructions): Boolean { + val rejectionReason = getRejectionReason(construction) + return rejectionReason=="" || rejectionReason.startsWith("Requires") } - override fun isBuildable(construction: CityConstructions): Boolean { - if(!isBuildable(construction.cityInfo.civInfo)) return false + fun getRejectionReason(construction: CityConstructions): String { + val civRejectionReason = getRejectionReason(construction.cityInfo.civInfo) + if(civRejectionReason!="") return civRejectionReason if(unitType.isWaterUnit() && construction.cityInfo.getCenterTile().neighbors.none { it.baseTerrain=="Coast" }) - return false - return true + return "Can't build water units by the coast" + return "" + } + fun getRejectionReason(civInfo: CivilizationInfo): String { + if (unbuildable) return "Unbuildable" + if (requiredTech!=null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched" + if (obsoleteTech!=null && civInfo.tech.isResearched(obsoleteTech!!)) return "Obsolete by $obsoleteTech" + if (uniqueTo!=null && uniqueTo!=civInfo.civName) return "Unique to $uniqueTo" + if (GameBasics.Units.values.any { it.uniqueTo==civInfo.civName && it.replaces==name }) return "Our unique unit replaces this" + if (requiredResource!=null && !civInfo.hasResource(requiredResource!!)) return "Requires $requiredResource" + return "" + } + + fun isBuildable(civInfo: CivilizationInfo) = getRejectionReason(civInfo)=="" + + override fun isBuildable(construction: CityConstructions): Boolean { + return getRejectionReason(construction) == "" } override fun postBuildEvent(construction: CityConstructions) { diff --git a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt index 72489d5a99..686a4e7287 100644 --- a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt @@ -19,7 +19,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre var constructionScrollPane:ScrollPane?=null - private fun getProductionButton(construction: String, buttonText: String): Table { + private fun getProductionButton(construction: String, buttonText: String, rejectionReason: String=""): Table { val pickProductionButton = Table() pickProductionButton.touchable = Touchable.enabled pickProductionButton.align(Align.left) @@ -32,11 +32,20 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre pickProductionButton.add(ImageGetter.getConstructionImage(construction).surroundWithCircle(40f)).padRight(10f) pickProductionButton.add(buttonText.toLabel().setFontColor(Color.WHITE)) - pickProductionButton.onClick { - cityScreen.city.cityConstructions.currentConstruction = construction - cityScreen.city.cityStats.update() - cityScreen.update() + + if(rejectionReason=="") { + pickProductionButton.onClick { + cityScreen.city.cityConstructions.currentConstruction = construction + cityScreen.city.cityStats.update() + cityScreen.update() + } } + else { + pickProductionButton.color = Color.GRAY + pickProductionButton.row() + pickProductionButton.add(rejectionReason.toLabel().setFontColor(Color.RED)) + } + if(construction==cityScreen.city.cityConstructions.currentConstruction) pickProductionButton.color= Color.GREEN return pickProductionButton @@ -77,18 +86,21 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre constructionPickerTable.background = ImageGetter.getBackground(Color.BLACK) val units = ArrayList() - for (unit in GameBasics.Units.values.filter { it.isBuildable(cityConstructions) }) + for (unit in GameBasics.Units.values.filter { it.shouldBeDisplayed(cityConstructions) }) units += getProductionButton(unit.name, - unit.name.tr() + "\r\n" + cityConstructions.turnsToConstruction(unit.name) + " {turns}".tr()) + unit.name.tr() + "\r\n" + cityConstructions.turnsToConstruction(unit.name) + " {turns}".tr(), + unit.getRejectionReason(cityConstructions)) constructionPickerTable.addCategory("Units",units) val buildableWonders = ArrayList
() val buildableBuildings = ArrayList
() for (building in GameBasics.Buildings.values) { - if (!building.isBuildable(cityConstructions) && building.name != cityConstructions.currentConstruction) continue + if (!building.shouldBeDisplayed(cityConstructions) && building.name != cityConstructions.currentConstruction) continue val productionTextButton = getProductionButton(building.name, - building.name + "\r\n" + cityConstructions.turnsToConstruction(building.name) + " {turns}".tr()) + building.name + "\r\n" + cityConstructions.turnsToConstruction(building.name) + " {turns}".tr(), + building.getRejectionReason(cityConstructions) + ) if (building.isWonder) buildableWonders += productionTextButton else @@ -99,7 +111,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre constructionPickerTable.addCategory("Buildings",buildableBuildings) val specialConstructions = ArrayList
() - for (specialConstruction in SpecialConstruction.getSpecialConstructions().filter { it.isBuildable(cityConstructions) }) { + for (specialConstruction in SpecialConstruction.getSpecialConstructions().filter { it.shouldBeDisplayed(cityConstructions) }) { specialConstructions += getProductionButton(specialConstruction.name, "Produce [${specialConstruction.name}]".tr()) }