Added a table in city screen, to show a breakdown of the ciy stats

This commit is contained in:
Yair Morgenstern 2018-04-05 13:59:22 +03:00
parent 39cc236fae
commit f662cd910d
5 changed files with 134 additions and 87 deletions

View File

@ -21,8 +21,8 @@ android {
applicationId "com.unciv.game"
minSdkVersion 14
targetSdkVersion 26
versionCode 36
versionName "1.4.3"
versionCode 37
versionName "1.4.4"
}
buildTypes {
release {

View File

@ -4,12 +4,15 @@ import com.unciv.logic.map.RoadStatus
import com.unciv.logic.map.TileInfo
import com.unciv.models.gamebasics.Building
import com.unciv.models.gamebasics.GameBasics
import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats
class CityStats {
@Transient @JvmField var currentCityStats: Stats = Stats() // This is so we won't have to calculate this multiple times - takes a lot of time, especially on phones
@Transient var baseStatList = LinkedHashMap<String,Stats>()
@Transient var statModifiers = LinkedHashMap<String,Stats>()
@Transient var currentCityStats: Stats = Stats() // This is so we won't have to calculate this multiple times - takes a lot of time, especially on phones
@Transient lateinit var cityInfo: CityInfo
private fun getStatsFromTiles(): Stats {
@ -182,19 +185,17 @@ class CityStats {
return stats
}
fun update() {
baseStatList = LinkedHashMap<String, Stats>()
val civInfo = cityInfo.civInfo
val stats = Stats()
stats.science += cityInfo.population.population.toFloat()
stats.production += cityInfo.population.freePopulation.toFloat()
stats.add(getStatsFromTiles())
stats.add(getStatsFromSpecialists(cityInfo.population.specialists, civInfo.policies.adoptedPolicies))
stats.add(getStatsFromTradeRoute())
stats.add(cityInfo.cityConstructions.getStats())
stats.add(getStatsFromPolicies(civInfo.policies.adoptedPolicies))
baseStatList["Population"] = Stats().add(Stat.Science,cityInfo.population.population.toFloat())
.add(Stat.Production,cityInfo.population.freePopulation.toFloat())
baseStatList["Tile yields"] = getStatsFromTiles()
baseStatList["Specialists"] = getStatsFromSpecialists(cityInfo.population.specialists, civInfo.policies.adoptedPolicies)
baseStatList["Trade route"] = getStatsFromTradeRoute()
baseStatList["Buildings"] = cityInfo.cityConstructions.getStats()
baseStatList["Policies"] = getStatsFromPolicies(civInfo.policies.adoptedPolicies)
val statPercentBonuses = cityInfo.cityConstructions.getStatPercentBonuses()
statPercentBonuses.add(getStatPercentBonusesFromGoldenAge(cityInfo.civInfo.goldenAges.isGoldenAge()))
@ -203,6 +204,8 @@ class CityStats {
statPercentBonuses.add(getStatPercentBonusesFromMarble())
statPercentBonuses.add(getStatPercentBonusesFromComputers())
val stats = Stats()
for (stat in baseStatList.values) stats.add(stat)
stats.production *= 1 + statPercentBonuses.production / 100 // So they get bonuses for production and gold/science
stats.add(getStatsFromProduction())
@ -214,18 +217,21 @@ class CityStats {
val isUnhappy = civInfo.happiness < 0
if (!isUnhappy) stats.food *= 1 + statPercentBonuses.food / 100 // Regular food bonus revoked when unhappy per https://forums.civfanatics.com/resources/complete-guide-to-happiness-vanilla.25584/
stats.food -= (cityInfo.population.population * 2).toFloat() // Food reduced after the bonus
val foodEaten = (cityInfo.population.population * 2).toFloat()
baseStatList["Population"]!!.food -= foodEaten // to display it to the user
stats.food -= foodEaten // Food reduced after the bonus
if (civInfo.policies.isAdopted("Civil Society"))
stats.food += cityInfo.population.numberOfSpecialists.toFloat()
if (isUnhappy) stats.food /= 4f // Reduce excess food to 1/4 per the same
stats.food *= 1 + getGrowthBonusFromPolicies()
stats.gold -= cityInfo.cityConstructions.getMaintenanceCosts().toFloat() // this is AFTER the bonus calculation!
val buildingsMaintainance = cityInfo.cityConstructions.getMaintenanceCosts().toFloat() // this is AFTER the bonus calculation!
baseStatList["Buildings"]!!.gold -= buildingsMaintainance
stats.gold -= buildingsMaintainance
this.currentCityStats = stats
}
private fun isConnectedToCapital(roadType: RoadStatus): Boolean {
if(cityInfo.civInfo.cities.count()<2) return false// first city!
val capitalTile = cityInfo.civInfo.capital.tile

View File

@ -29,10 +29,11 @@ open class Stats() {
setStats(hashMap)
}
fun add(stat:Stat, value:Float) {
fun add(stat:Stat, value:Float): Stats {
val hashMap = toHashMap()
hashMap[stat] = hashMap[stat]!!+value
setStats(hashMap)
return this
}
fun clone(): Stats {

View File

@ -8,21 +8,19 @@ import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener
import com.badlogic.gdx.utils.Align
import com.unciv.logic.city.CityInfo
import com.unciv.logic.map.TileInfo
import com.unciv.models.gamebasics.Building
import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.HexMath
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.disable
import java.util.*
class CityScreen(internal val city: CityInfo) : CameraStageBaseScreen() {
private var selectedTile: TileInfo? = null
private var buttonScale = game.settings.buttonScale
private var tileTable = Table()
private var buildingsTable = BuildingsTable(this)
private var cityStatsTable = Table()
private var cityStatsTable = CityStatsTable(this)
private var statExplainer = Table(skin)
private var cityPickerTable = Table()
private var goToWorldButton = TextButton("Exit city", CameraStageBaseScreen.skin)
private var tileGroups = ArrayList<TileGroup>()
@ -42,7 +40,6 @@ class CityScreen(internal val city: CityInfo) : CameraStageBaseScreen() {
val buildingsTableContainer = Table()
buildingsTableContainer.pad(20f)
buildingsTableContainer.background = tileTableBackground
//BuildingsTableContainer.add(new Label("Buildings",skin)).row();
buildingsTable.update()
val buildingsScroll = ScrollPane(buildingsTable)
buildingsTableContainer.add(buildingsScroll).height(stage.height / 2)
@ -55,6 +52,7 @@ class CityScreen(internal val city: CityInfo) : CameraStageBaseScreen() {
stage.addActor(cityStatsTable)
stage.addActor(goToWorldButton)
stage.addActor(cityPickerTable)
stage.addActor(statExplainer)
stage.addActor(buildingsTableContainer)
update()
displayTutorials("CityEntered")
@ -63,12 +61,39 @@ class CityScreen(internal val city: CityInfo) : CameraStageBaseScreen() {
internal fun update() {
buildingsTable.update()
updateCityPickerTable()
updateCityTable()
cityStatsTable.update()
updateStatExplainer()
updateGoToWorldButton()
updateTileTable()
updateTileGroups()
}
private fun updateStatExplainer() {
statExplainer.defaults().pad(5f)
statExplainer.clear()
statExplainer.add()
statExplainer.add("Production")
statExplainer.add("Food")
statExplainer.add("Science")
statExplainer.add("Gold")
statExplainer.add("Culture")
for (entry in city.cityStats.baseStatList){
if(entry.value.toHashMap().values.all { it==0f }) continue //irrelevant!
statExplainer.row()
statExplainer.add(entry.key)
statExplainer.add(entry.value.production.toInt().toString())
statExplainer.add(entry.value.food.toInt().toString())
statExplainer.add(entry.value.science.toInt().toString())
statExplainer.add(entry.value.gold.toInt().toString())
statExplainer.add(entry.value.culture.toInt().toString())
}
statExplainer.pack()
statExplainer.isTransform=true
statExplainer.setScale(0.8f)
statExplainer.setPosition(5f,cityStatsTable.top + 10)
}
private fun updateTileGroups() {
for (HG in tileGroups) {
@ -184,68 +209,6 @@ class CityScreen(internal val city: CityInfo) : CameraStageBaseScreen() {
stage.addActor(scrollPane)
}
private fun updateCityTable() {
val stats = city.cityStats.currentCityStats
cityStatsTable.pad(20f)
cityStatsTable.columnDefaults(0).padRight(10f)
cityStatsTable.clear()
val cityStatsHeader = Label("City Stats", CameraStageBaseScreen.skin)
cityStatsHeader.setFontScale(2f)
cityStatsTable.add(cityStatsHeader).colspan(2).pad(10f)
cityStatsTable.row()
val cityStatsValues = LinkedHashMap<String, String>()
cityStatsValues["Production"] = Math.round(stats.production).toString() + city.cityConstructions.getAmountConstructedText()
cityStatsValues["Food"] = (Math.round(stats.food).toString()
+ " (" + city.population.foodStored + "/" + city.population.foodToNextPopulation + ")")
cityStatsValues["Gold"] = Math.round(stats.gold).toString() + ""
cityStatsValues["Science"] = Math.round(stats.science).toString() + ""
cityStatsValues["Culture"] = (Math.round(stats.culture).toString()
+ " (" + city.expansion.cultureStored + "/" + city.expansion.cultureToNextTile + ")")
cityStatsValues["Population"] = city.population.freePopulation.toString() + "/" + city.population.population
for (key in cityStatsValues.keys) {
cityStatsTable.add<Image>(com.unciv.ui.utils.ImageGetter.getStatIcon(key)).align(Align.right)
cityStatsTable.add(Label(cityStatsValues[key], CameraStageBaseScreen.skin)).align(Align.left)
cityStatsTable.row()
}
val buildingText = city.cityConstructions.getCityProductionTextForCityButton()
val buildingPickButton = TextButton(buildingText, CameraStageBaseScreen.skin)
buildingPickButton.addClickListener {
game.screen = com.unciv.ui.pickerscreens.ConstructionPickerScreen(city)
dispose()
}
buildingPickButton.label.setFontScale(buttonScale)
cityStatsTable.add(buildingPickButton).colspan(2).pad(10f)
.size(buildingPickButton.width * buttonScale, buildingPickButton.height * buttonScale)
// https://forums.civfanatics.com/threads/rush-buying-formula.393892/
val construction = city.cityConstructions.getCurrentConstruction()
if (!(construction is Building && construction.isWonder)) {
cityStatsTable.row()
val buildingGoldCost = construction.getGoldCost(city.civInfo.policies.getAdoptedPolicies())
val buildingBuyButton = TextButton("Buy for \r\n$buildingGoldCost gold", CameraStageBaseScreen.skin)
buildingBuyButton.addClickListener {
city.cityConstructions.purchaseBuilding(city.cityConstructions.currentConstruction)
update()
}
if (buildingGoldCost > city.civInfo.gold) {
buildingBuyButton.disable()
}
buildingBuyButton.label.setFontScale(buttonScale)
cityStatsTable.add(buildingBuyButton).colspan(2).pad(10f)
.size(buildingBuyButton.width * buttonScale, buildingBuyButton.height * buttonScale)
}
cityStatsTable.setPosition(10f, 10f)
cityStatsTable.pack()
}
private fun updateTileTable() {
if (selectedTile == null) return
tileTable.clearChildren()
@ -268,9 +231,7 @@ class CityScreen(internal val city: CityInfo) : CameraStageBaseScreen() {
tileTable.row()
}
tileTable.pack()
tileTable.setPosition(stage.width - 10f - tileTable.width, 10f)
}

View File

@ -0,0 +1,79 @@
package com.unciv.ui.cityscreen
import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.badlogic.gdx.utils.Align
import com.unciv.models.gamebasics.Building
import com.unciv.ui.UnCivGame
import com.unciv.ui.pickerscreens.ConstructionPickerScreen
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.disable
import java.util.*
class CityStatsTable(val cityScreen: CityScreen) : Table(){
fun update() {
val city = cityScreen.city
val buttonScale = cityScreen.game.settings.buttonScale
val stats = city.cityStats.currentCityStats
pad(20f)
columnDefaults(0).padRight(10f)
clear()
val cityStatsHeader = Label("City Stats", CameraStageBaseScreen.skin)
cityStatsHeader.setFontScale(2f)
add(cityStatsHeader).colspan(2).pad(10f)
row()
val cityStatsValues = LinkedHashMap<String, String>()
cityStatsValues["Production"] = Math.round(stats.production).toString() + city.cityConstructions.getAmountConstructedText()
cityStatsValues["Food"] = (Math.round(stats.food).toString()
+ " (" + city.population.foodStored + "/" + city.population.foodToNextPopulation + ")")
cityStatsValues["Gold"] = Math.round(stats.gold).toString() + ""
cityStatsValues["Science"] = Math.round(stats.science).toString() + ""
cityStatsValues["Culture"] = (Math.round(stats.culture).toString()
+ " (" + city.expansion.cultureStored + "/" + city.expansion.cultureToNextTile + ")")
cityStatsValues["Population"] = city.population.freePopulation.toString() + "/" + city.population.population
for (key in cityStatsValues.keys) {
add(ImageGetter.getStatIcon(key)).align(Align.right)
add(Label(cityStatsValues[key], CameraStageBaseScreen.skin)).align(Align.left)
row()
}
val buildingText = city.cityConstructions.getCityProductionTextForCityButton()
val buildingPickButton = TextButton(buildingText, CameraStageBaseScreen.skin)
buildingPickButton.addClickListener {
UnCivGame.Current.screen = ConstructionPickerScreen(city)
cityScreen.dispose()
}
buildingPickButton.label.setFontScale(buttonScale)
add(buildingPickButton).colspan(2).pad(10f)
.size(buildingPickButton.width * buttonScale, buildingPickButton.height * buttonScale)
// https://forums.civfanatics.com/threads/rush-buying-formula.393892/
val construction = city.cityConstructions.getCurrentConstruction()
if (!(construction is Building && construction.isWonder)) {
row()
val buildingGoldCost = construction.getGoldCost(city.civInfo.policies.getAdoptedPolicies())
val buildingBuyButton = TextButton("Buy for \r\n$buildingGoldCost gold", CameraStageBaseScreen.skin)
buildingBuyButton.addClickListener {
city.cityConstructions.purchaseBuilding(city.cityConstructions.currentConstruction)
update()
}
if (buildingGoldCost > city.civInfo.gold) {
buildingBuyButton.disable()
}
buildingBuyButton.label.setFontScale(buttonScale)
add(buildingBuyButton).colspan(2).pad(10f)
.size(buildingBuyButton.width * buttonScale, buildingBuyButton.height * buttonScale)
}
setPosition(10f, 10f)
pack()
}
}