diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index 5275d444a2..bc58600a92 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -262,7 +262,7 @@ class CityStats { currentCityStats = Stats() for (stat in baseStatList.values) currentCityStats.add(stat) - if(currentCityStats.production<1) currentCityStats.production=1 + if(currentCityStats.production<1) currentCityStats.production=1f } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index f8da61bc5a..bc889097bc 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -15,7 +15,10 @@ import com.unciv.models.gamebasics.tile.ResourceType import com.unciv.models.gamebasics.tile.TileResource import com.unciv.models.gamebasics.unit.UnitType import com.unciv.models.stats.Stats +import com.unciv.ui.Trade +import com.unciv.ui.TradeType import com.unciv.ui.utils.getRandom +import com.unciv.ui.utils.tr import kotlin.math.max import kotlin.math.pow @@ -34,7 +37,7 @@ class CivilizationInfo { var goldenAges = GoldenAgeManager() var greatPeople = GreatPersonManager() var scienceVictory = ScienceVictoryManager() -// @Transient var diplomacy = HashMap() + var diplomacy = HashMap() var cities = ArrayList() var exploredTiles = HashSet() @@ -134,6 +137,7 @@ class CivilizationInfo { fun getCivResources(): Counter { val civResources = Counter() for (city in cities) civResources.add(city.getCityResources()) + for (dip in diplomacy.values) civResources.add(dip.resourcesFromTrade()) return civResources } @@ -155,6 +159,7 @@ class CivilizationInfo { goldenAges.civInfo = this policies.civInfo = this tech.civInfo = this + diplomacy.values.forEach { it.civInfo=this} for (unit in getCivUnits()) { unit.civInfo=this @@ -207,6 +212,7 @@ class CivilizationInfo { goldenAges.endTurn(happiness) getCivUnits().forEach { it.endTurn() } + diplomacy.values.forEach{it.nextTurn()} gameInfo.updateTilesToCities() } @@ -239,6 +245,18 @@ class CivilizationInfo { viewablePositions += getCivUnits() .flatMap { it.getViewableTiles()} // Tiles within 2 tiles of units viewablePositions.map { it.position }.filterNot { exploredTiles.contains(it) }.toCollection(exploredTiles) + + val viewedCivs = viewablePositions + .flatMap { it.getUnits().map { u->u.civInfo }.union(listOf(it.getOwner())) } + .filterNotNull().filterNot { it==this } + + for(otherCiv in viewedCivs) + if(!diplomacy.containsKey(otherCiv.civName)){ + diplomacy[otherCiv.civName] = DiplomacyManager().apply { otherCivName=otherCiv.civName } + otherCiv.diplomacy[civName] = DiplomacyManager().apply { otherCivName=civName } + addNotification("We have encountered ["+otherCiv.civName+"]!".tr(),null, Color.GOLD) + } + return viewablePositions.distinct() } @@ -265,14 +283,40 @@ enum class DiplomaticStatus{ War } -//class DiplomacyManager { -// @Transient lateinit var civInfo:CivilizationInfo -// lateinit var otherCivName:String -// var status:DiplomaticStatus = DiplomaticStatus.Peace -// -// fun otherCiv() = civInfo.gameInfo.civilizations.first{it.civName==otherCivName} +class DiplomacyManager { + @Transient lateinit var civInfo:CivilizationInfo + lateinit var otherCivName:String +// var status:DiplomaticStatus = DiplomaticStatus.War + var trades = ArrayList() + + fun resourcesFromTrade(): Counter { + val counter = Counter() + for(trade in trades){ + for(offer in trade.ourOffers) + if(offer.type==TradeType.Strategic_Resource || offer.type==TradeType.Luxury_Resource) + counter.add(GameBasics.TileResources[offer.name]!!,-offer.amount) + for(offer in trade.theirOffers) + if(offer.type==TradeType.Strategic_Resource || offer.type==TradeType.Luxury_Resource) + counter.add(GameBasics.TileResources[offer.name]!!,offer.amount) + } + return counter + } + + fun nextTurn(){ + for(trade in trades.toList()){ // Each civ lowers their own offers by 1. If we were to lower the enemies as well, the offers would date twice as fast! + for(offer in trade.ourOffers.union(trade.theirOffers).filter { it.duration>0 }) + offer.duration-- + + if(trade.ourOffers.all { it.duration<=0 } && trade.theirOffers.all { it.duration<=0 }) { + trades.remove(trade) + civInfo.addNotification("One of our trades with [$otherCivName] has ended!".tr(),null, Color.YELLOW) + } + } + } + + fun otherCiv() = civInfo.gameInfo.civilizations.first{it.civName==otherCivName} // fun declareWar(){ // status = DiplomaticStatus.War // otherCiv().diplomacy[civInfo.civName]!!.status = DiplomaticStatus.War // } -//} +} diff --git a/core/src/com/unciv/ui/EmpireOverviewScreen.kt b/core/src/com/unciv/ui/EmpireOverviewScreen.kt index 249947f6ac..fd7f14fce9 100644 --- a/core/src/com/unciv/ui/EmpireOverviewScreen.kt +++ b/core/src/com/unciv/ui/EmpireOverviewScreen.kt @@ -1,32 +1,94 @@ package com.unciv.ui -import com.badlogic.gdx.scenes.scene2d.ui.* +import com.badlogic.gdx.scenes.scene2d.ui.Label +import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane +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.UnCivGame -import com.unciv.logic.civilization.CivilizationInfo import com.unciv.ui.utils.* +import kotlin.math.max import kotlin.math.roundToInt class EmpireOverviewScreen : CameraStageBaseScreen(){ - init { - val civInfo = UnCivGame.Current.gameInfo.getPlayerCivilization() - val closeButton = TextButton("Close".tr(), skin) + val civInfo = UnCivGame.Current.gameInfo.getPlayerCivilization() + init { + val topTable = Table().apply { defaults().pad(10f) } + val centerTable=Table().apply { defaults().pad(20f) } + + val closeButton = TextButton("Close".tr(), skin) closeButton.addClickListener { UnCivGame.Current.setWorldScreen() } closeButton.y = stage.height - closeButton.height - 5 - stage.addActor(closeButton) + topTable.add(closeButton) - val table=Table() - table.defaults().pad(20f) + val setCityInfoButton = TextButton("Cities",skin) + val setCities = { + centerTable.clear() + centerTable.add(getCityInfoTable()) + centerTable.pack() + centerTable.center(stage) + } + setCities() + setCityInfoButton.addClickListener(setCities) + topTable.add(setCityInfoButton) - table.add(getCityInfoTable(civInfo)) - table.add(getHappinessTable(civInfo)) - table.add(getGoldTable(civInfo)) - table.center(stage) - stage.addActor(table) + val setStatsInfoButton = TextButton("Stats",skin) + setStatsInfoButton.addClickListener { + centerTable.clear() + centerTable.add(getHappinessTable()) + centerTable.add(getGoldTable()) + centerTable.pack() + centerTable.center(stage) + } + topTable.add(setStatsInfoButton) + + val setCurrentTradesButton = TextButton("Trades",skin) + setCurrentTradesButton.addClickListener { + centerTable.clear() + centerTable.add(getTradesTable()) + centerTable.pack() + centerTable.center(stage) + } + topTable.add(setCurrentTradesButton) + + topTable.pack() + topTable.width = stage.width + topTable.y = stage.height-topTable.height + + stage.addActor(topTable) + stage.addActor(centerTable) } - private fun getHappinessTable(civInfo: CivilizationInfo): Table { + private fun getTradesTable(): Table { + val tradesTable = Table() + for(diplomacy in civInfo.diplomacy.values) + for(trade in diplomacy.trades) + tradesTable.add(createTradeTable(trade,diplomacy.otherCivName)).row() + + return tradesTable + } + + private fun createTradeTable(trade:Trade, civName:String): Table { + val table = Table(skin) + table.defaults().pad(10f) + table.add(civInfo.civName) + table.add(civName).row() + val ourOffersStrings = trade.ourOffers.map { it.amount.toString()+" "+it.name + + (if (it.duration==0) "" else " ("+it.duration+" turns)") } + val theirOffersStrings = trade.theirOffers.map { it.amount.toString()+" "+it.name + + (if (it.duration==0) "" else " ("+it.duration+" turns)") } + for(i in 0 until max(trade.ourOffers.size,trade.theirOffers.size)){ + if(ourOffersStrings.size>i) table.add(ourOffersStrings[i]) + else table.add() + if(theirOffersStrings.size>i) table.add(theirOffersStrings[i]) + else table.add() + table.row() + } + return table + } + + private fun getHappinessTable(): Table { val happinessTable = Table(skin) happinessTable.defaults().pad(5f) happinessTable.add(Label("Happiness", skin).setFont(24)).colspan(2).row() @@ -40,7 +102,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){ return happinessTable } - private fun getGoldTable(civInfo: CivilizationInfo): Table { + private fun getGoldTable(): Table { val goldTable = Table(skin) goldTable.defaults().pad(5f) goldTable.add(Label("Gold", skin).setFont(24)).colspan(2).row() @@ -57,7 +119,7 @@ class EmpireOverviewScreen : CameraStageBaseScreen(){ return goldTable } - private fun getCityInfoTable(civInfo: CivilizationInfo): Table { + private fun getCityInfoTable(): Table { val iconSize = 20f//if you set this too low, there is a chance that the tables will be misaligned val padding = 5f diff --git a/core/src/com/unciv/ui/TradeScreen.kt b/core/src/com/unciv/ui/TradeScreen.kt index c9e00a348c..8c26543ae6 100644 --- a/core/src/com/unciv/ui/TradeScreen.kt +++ b/core/src/com/unciv/ui/TradeScreen.kt @@ -6,7 +6,6 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.unciv.UnCivGame import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.models.Counter import com.unciv.models.gamebasics.tile.ResourceType import com.unciv.ui.utils.* import kotlin.math.min @@ -20,7 +19,10 @@ enum class TradeType{ City } -data class TradeOffer(val name:String, val type:TradeType, val duration:Int) { +data class TradeOffer(var name:String, var type:TradeType, var duration:Int, var amount:Int) { + + constructor() : this("",TradeType.Gold,0,0) // so that the json deserializer can work + fun getText(): String { var text = "{$name}" if(duration>0) text += " ($duration {turns})" @@ -28,9 +30,27 @@ data class TradeOffer(val name:String, val type:TradeType, val duration:Int) { } } -class TradeOffersList():Counter(){} +class TradeOffersList:ArrayList(){ + override fun add(element: TradeOffer): Boolean { + val equivalentOffer = firstOrNull { it.name==element.name&&it.type==element.type } + if(equivalentOffer==null){ + super.add(element) + return true + } + equivalentOffer.amount += element.amount + if(equivalentOffer.amount==0) remove(equivalentOffer) + return true + } +} + +class Trade{ + fun reverse(): Trade { + val newTrade = Trade() + newTrade.theirOffers+=ourOffers.map { it.copy() } + newTrade.ourOffers+=theirOffers.map { it.copy() } + return newTrade + } -class Trade(){ val theirOffers = TradeOffersList() val ourOffers = TradeOffersList() } @@ -66,6 +86,13 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre } else tradeText.setText("I think not.") } + else if(offerButton.text.toString() == "Accept"){ + civInfo.diplomacy[otherCivilization.civName]!!.trades.add(currentTrade) + otherCivilization.diplomacy[civInfo.civName]!!.trades.add(currentTrade.reverse()) + val newTradeScreen = TradeScreen(otherCivilization) + UnCivGame.Current.screen = newTradeScreen + newTradeScreen.tradeText.setText("Pleasure doing business with you!") + } } lowerTable.add(offerButton) @@ -81,34 +108,34 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre fun update(){ table.clear() - val ourAvailableOffersTable = ScrollPane(getTableOfOffers(ourAvailableOffers, currentTrade.ourOffers)) - val ourOffersTable = ScrollPane(getTableOfOffers(currentTrade.ourOffers, ourAvailableOffers)) - val theirOffersTable = ScrollPane(getTableOfOffers(currentTrade.theirOffers, theirAvailableOffers)) - val theirAvailableOffersTable = ScrollPane(getTableOfOffers(theirAvailableOffers, currentTrade.theirOffers)) + val ourAvailableOffersTable = getTableOfOffers(ourAvailableOffers, currentTrade.ourOffers) + val ourOffersTable = getTableOfOffers(currentTrade.ourOffers, ourAvailableOffers) + val theirOffersTable = getTableOfOffers(currentTrade.theirOffers, theirAvailableOffers) + val theirAvailableOffersTable = getTableOfOffers(theirAvailableOffers, currentTrade.theirOffers) table.add("Our items") table.add("Our trade offer") table.add(otherCivilization.civName+"'s trade offer") table.add(otherCivilization.civName+"'s items").row() - table.add(ourAvailableOffersTable).width(stage.width/4) - table.add(ourOffersTable).width(stage.width/4) - table.add(theirOffersTable).width(stage.width/4) - table.add(theirAvailableOffersTable).width(stage.width/4) + table.add(ourAvailableOffersTable).size(stage.width/4,stage.width/2) + table.add(ourOffersTable).size(stage.width/4,stage.width/2) + table.add(theirOffersTable).size(stage.width/4,stage.width/2) + table.add(theirAvailableOffersTable).size(stage.width/4,stage.width/2) table.pack() table.center(stage) } - fun getTableOfOffers(offers: TradeOffersList, correspondingOffers: TradeOffersList): Table { + fun getTableOfOffers(offers: TradeOffersList, correspondingOffers: TradeOffersList): ScrollPane { val table= Table(skin).apply { defaults().pad(5f) } for(offer in offers) { - val tb = TextButton(offer.key.name+" ("+offer.value+")",skin) + val tb = TextButton(offer.name+" ("+offer.amount+")",skin) val amountPerClick = - if(offer.key.type==TradeType.Gold) 50 + if(offer.type==TradeType.Gold) 50 else 1 - if(offer.value>0) + if(offer.amount>0) tb.addClickListener { - val amountTransfered = min(amountPerClick, offer.value) - offers.add(offer.key,-amountTransfered) - correspondingOffers.add(offer.key,amountTransfered) + val amountTransferred = min(amountPerClick, offer.amount) + offers += offer.copy(amount = -amountTransferred) + correspondingOffers += offer.copy(amount = amountTransferred) offerButton.setText("Offer trade") tradeText.setText("What do you have in mind?") update() @@ -116,7 +143,7 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre else tb.disable() // for instance we have negative gold table.add(tb).row() } - return table + return ScrollPane(table) } fun getAvailableOffers(civInfo: CivilizationInfo): TradeOffersList { @@ -124,28 +151,30 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre for(entry in civInfo.getCivResources().filterNot { it.key.resourceType == ResourceType.Bonus }) { val resourceTradeType = if(entry.key.resourceType==ResourceType.Luxury) TradeType.Luxury_Resource else TradeType.Strategic_Resource - offers.add(TradeOffer(entry.key.name, resourceTradeType, 30), entry.value) + offers.add(TradeOffer(entry.key.name, resourceTradeType, 30, entry.value)) } - offers.add(TradeOffer("Gold",TradeType.Gold,0),civInfo.gold) + offers.add(TradeOffer("Gold",TradeType.Gold,0,civInfo.gold)) return offers } fun isTradeAcceptable(trade:Trade): Boolean { - val sumOfTheirOffers = trade.theirOffers.map { evaluateOffer(it.key,false)*it.value }.sum() - val sumOfOurOffers = trade.ourOffers.map { evaluateOffer(it.key,true)*it.value }.sum() + val sumOfTheirOffers = trade.theirOffers.map { evaluateOffer(it,false) }.sum() + val sumOfOurOffers = trade.ourOffers.map { evaluateOffer(it,true)}.sum() return sumOfOurOffers >= sumOfTheirOffers } fun evaluateOffer(offer:TradeOffer, otherCivIsRecieving:Boolean): Int { if(offer.type==TradeType.Gold) return 1 if(offer.type == TradeType.Luxury_Resource){ - if(!theirAvailableOffers.containsKey(offer)) // We want to take away their last luxury or give them one they don't have - return 250 - if(!otherCivIsRecieving && !ourAvailableOffers.containsKey(offer)) return 250 // they're giving us a luxury we don't have yet - return 100 // this is useful only as a barter trade to other civs + var value = 100*offer.amount + if(!theirAvailableOffers.any { it.name==offer.name }) // We want to take away their last luxury or give them one they don't have + value += 250 + else if(!otherCivIsRecieving && !ourAvailableOffers.any { it.name==offer.name }) + value += 250 // they're giving us a luxury we don't have yet + return value // this is useful only as a barter trade to other civs } if(offer.type == TradeType.Strategic_Resource){ - return 50 + return 50 * offer.amount } return 1000 // Dunno what this is? } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index ba4ec0672f..27755eb3ee 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -1,6 +1,7 @@ package com.unciv.ui.worldscreen import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton @@ -27,7 +28,7 @@ class WorldScreen : CameraStageBaseScreen() { val bottomBar = WorldScreenBottomBar(this) val unitActionsTable = UnitActionsTable(this) - private val techButton = TextButton("", CameraStageBaseScreen.skin) + private val techButton = TextButton("", CameraStageBaseScreen.skin).apply { color= Color.BLUE } val tradeButtons = Table() private val nextTurnButton = createNextTurnButton() @@ -59,14 +60,8 @@ class WorldScreen : CameraStageBaseScreen() { tradeButtons.defaults().pad(5f) - for(civ in gameInfo.civilizations.filterNot { it.isPlayerCivilization() || it.isBarbarianCivilization() }){ - val tb = TextButton(civ.civName,skin) - tb.addClickListener { UnCivGame.Current.screen = TradeScreen(civ) } - tradeButtons.add(tb) - } - tradeButtons.pack() stage.addActor(tradeButtons) - tradeButtons.isVisible=false +// tradeButtons.isVisible=false bottomBar.width = stage.width stage.addActor(bottomBar) @@ -86,6 +81,8 @@ class WorldScreen : CameraStageBaseScreen() { } updateTechButton() + updateTradeButtons() + bottomBar.update(tileMapHolder.selectedTile) // has to come before tilemapholder update because the tilemapholder actions depend on the selected unit! minimap.update() minimap.y = bottomBar.height @@ -104,6 +101,19 @@ class WorldScreen : CameraStageBaseScreen() { else if(civInfo.greatPeople.freeGreatPeople>0) game.screen = GreatPersonPickerScreen() } + private fun updateTradeButtons() { + tradeButtons.clear() + for(civ in gameInfo.civilizations.filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() }){ + if(!civInfo.diplomacy.containsKey(civ.civName)) continue + val tb = TextButton("Trade with [${civ.civName}]".tr(),skin) + tb.addClickListener { UnCivGame.Current.screen = TradeScreen(civ) } + tradeButtons.add(tb).row() + } + + tradeButtons.pack() + tradeButtons.y = techButton.y -20 - tradeButtons.height + } + private fun updateTechButton() { techButton.isVisible = civInfo.cities.isNotEmpty() @@ -115,8 +125,6 @@ class WorldScreen : CameraStageBaseScreen() { techButton.setSize(techButton.prefWidth, techButton.prefHeight) techButton.setPosition(10f, topBar.y - techButton.height - 5f) - - tradeButtons.y = techButton.y - tradeButtons.height } private fun createNextTurnButton(): TextButton {