mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
AI can now offer you trades - this is a big step towards proper diplomacy relations!
As of now they only offer luxury-to-luxury trades
This commit is contained in:
Binary file not shown.
Before Width: | Height: | Size: 968 KiB After Width: | Height: | Size: 966 KiB |
@ -21,8 +21,8 @@ android {
|
|||||||
applicationId "com.unciv.app"
|
applicationId "com.unciv.app"
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 229
|
versionCode 230
|
||||||
versionName "2.14.9"
|
versionName "2.14.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Had to add this crap for Travis to build, it wanted to sign the app
|
// Had to add this crap for Travis to build, it wanted to sign the app
|
||||||
|
@ -2,12 +2,10 @@ package com.unciv.logic.automation
|
|||||||
|
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.logic.civilization.PlayerType
|
import com.unciv.logic.civilization.PlayerType
|
||||||
|
import com.unciv.logic.civilization.TradeRequest
|
||||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||||
import com.unciv.logic.map.MapUnit
|
import com.unciv.logic.map.MapUnit
|
||||||
import com.unciv.logic.trade.TradeEvaluation
|
import com.unciv.logic.trade.*
|
||||||
import com.unciv.logic.trade.TradeLogic
|
|
||||||
import com.unciv.logic.trade.TradeOffer
|
|
||||||
import com.unciv.logic.trade.TradeType
|
|
||||||
import com.unciv.models.gamebasics.GameBasics
|
import com.unciv.models.gamebasics.GameBasics
|
||||||
import com.unciv.models.gamebasics.tech.Technology
|
import com.unciv.models.gamebasics.tech.Technology
|
||||||
import com.unciv.models.gamebasics.tr
|
import com.unciv.models.gamebasics.tr
|
||||||
@ -115,30 +113,62 @@ class NextTurnAutomation{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun potentialLuxuryTrades(civInfo:CivilizationInfo, otherCivInfo:CivilizationInfo): ArrayList<Trade> {
|
||||||
|
val tradeLogic = TradeLogic(civInfo, otherCivInfo)
|
||||||
|
val ourTradableLuxuryResources = tradeLogic.ourAvailableOffers
|
||||||
|
.filter { it.type == TradeType.Luxury_Resource && it.amount > 1 }
|
||||||
|
val theirTradableLuxuryResources = tradeLogic.theirAvailableOffers
|
||||||
|
.filter { it.type == TradeType.Luxury_Resource && it.amount > 1 }
|
||||||
|
val weHaveTheyDont = ourTradableLuxuryResources
|
||||||
|
.filter { resource ->
|
||||||
|
tradeLogic.theirAvailableOffers
|
||||||
|
.none { it.name == resource.name && it.type == TradeType.Luxury_Resource }
|
||||||
|
}
|
||||||
|
val theyHaveWeDont = theirTradableLuxuryResources
|
||||||
|
.filter { resource ->
|
||||||
|
tradeLogic.ourAvailableOffers
|
||||||
|
.none { it.name == resource.name && it.type == TradeType.Luxury_Resource }
|
||||||
|
}
|
||||||
|
val trades = ArrayList<Trade>()
|
||||||
|
for(i in 0..min(weHaveTheyDont.lastIndex, theyHaveWeDont.lastIndex)){
|
||||||
|
val trade = Trade()
|
||||||
|
trade.ourOffers.add(weHaveTheyDont[i].copy(amount = 1))
|
||||||
|
trade.theirOffers.add(theyHaveWeDont[i].copy(amount = 1))
|
||||||
|
trades.add(trade)
|
||||||
|
}
|
||||||
|
return trades
|
||||||
|
}
|
||||||
|
|
||||||
private fun exchangeLuxuries(civInfo: CivilizationInfo) {
|
private fun exchangeLuxuries(civInfo: CivilizationInfo) {
|
||||||
for (otherCiv in civInfo.diplomacy.values.map { it.otherCiv() }.filterNot { it.isPlayerCivilization() }) {
|
val knownCivs = civInfo.diplomacy.values.map { it.otherCiv() }
|
||||||
val tradeLogic = TradeLogic(civInfo, otherCiv)
|
|
||||||
val ourTradableLuxuryResources = tradeLogic.ourAvailableOffers
|
// Player trades are... more complicated.
|
||||||
.filter { it.type == TradeType.Luxury_Resource && it.amount > 1 }
|
// When the AI offers a trade, it's not immediately accepted,
|
||||||
val theirTradableLuxuryResources = tradeLogic.theirAvailableOffers
|
// so what if it thinks that it has a spare luxury and offers it to two human players?
|
||||||
.filter { it.type == TradeType.Luxury_Resource && it.amount > 1 }
|
// What's to stop the AI "nagging" the player to accept a luxury trade?
|
||||||
val weHaveTheyDont = ourTradableLuxuryResources
|
// We should A. add some sort of timer (20? 30 turns?) between luxury trade requests if they're denied
|
||||||
.filter { resource ->
|
// B. have a way for the AI to keep track of the "pending offers" - see DiplomacyManager.resourcesFromTrade
|
||||||
tradeLogic.theirAvailableOffers
|
|
||||||
.none { it.name == resource.name && it.type == TradeType.Luxury_Resource }
|
for (otherCiv in knownCivs.filter { it.isPlayerCivilization() }) {
|
||||||
}
|
val trades = potentialLuxuryTrades(civInfo,otherCiv)
|
||||||
val theyHaveWeDont = theirTradableLuxuryResources
|
for(trade in trades){
|
||||||
.filter { resource ->
|
val tradeRequest = TradeRequest(civInfo.civName, trade.reverse())
|
||||||
tradeLogic.ourAvailableOffers
|
otherCiv.tradeRequests.add(tradeRequest)
|
||||||
.none { it.name == resource.name && it.type == TradeType.Luxury_Resource }
|
}
|
||||||
}
|
}
|
||||||
val numberOfTrades = min(weHaveTheyDont.size, theyHaveWeDont.size)
|
|
||||||
if (numberOfTrades > 0) {
|
// AI trades are automatically accepted
|
||||||
tradeLogic.currentTrade.ourOffers.addAll(weHaveTheyDont.take(numberOfTrades).map { it.copy(amount = 1) })
|
for (otherCiv in knownCivs.filterNot { it.isPlayerCivilization() }) {
|
||||||
tradeLogic.currentTrade.theirOffers.addAll(theyHaveWeDont.take(numberOfTrades).map { it.copy(amount = 1) })
|
val trades = potentialLuxuryTrades(civInfo,otherCiv)
|
||||||
|
for(trade in trades){
|
||||||
|
val tradeLogic = TradeLogic(civInfo,otherCiv)
|
||||||
|
tradeLogic.currentTrade.ourOffers.addAll(trade.ourOffers)
|
||||||
|
tradeLogic.currentTrade.theirOffers.addAll(trade.theirOffers)
|
||||||
tradeLogic.acceptTrade()
|
tradeLogic.acceptTrade()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMinDistanceBetweenCities(civ1: CivilizationInfo, civ2: CivilizationInfo): Int {
|
fun getMinDistanceBetweenCities(civ1: CivilizationInfo, civ2: CivilizationInfo): Int {
|
||||||
|
@ -12,6 +12,7 @@ import com.unciv.logic.map.BFS
|
|||||||
import com.unciv.logic.map.MapUnit
|
import com.unciv.logic.map.MapUnit
|
||||||
import com.unciv.logic.map.RoadStatus
|
import com.unciv.logic.map.RoadStatus
|
||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
|
import com.unciv.logic.trade.Trade
|
||||||
import com.unciv.models.Counter
|
import com.unciv.models.Counter
|
||||||
import com.unciv.models.gamebasics.Difficulty
|
import com.unciv.models.gamebasics.Difficulty
|
||||||
import com.unciv.models.gamebasics.GameBasics
|
import com.unciv.models.gamebasics.GameBasics
|
||||||
@ -33,18 +34,23 @@ enum class PlayerType{
|
|||||||
Human
|
Human
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TradeRequest(val requestingCiv:String,
|
||||||
|
/** Their offers are what they offer us, and our offers are what they want in return */
|
||||||
|
val trade: Trade){
|
||||||
|
}
|
||||||
|
|
||||||
class CivilizationInfo {
|
class CivilizationInfo {
|
||||||
@Transient lateinit var gameInfo: GameInfo
|
@Transient lateinit var gameInfo: GameInfo
|
||||||
/**
|
/**
|
||||||
* never add or remove from here directly, could cause comodification problems.
|
* We never add or remove from here directly, could cause comodification problems.
|
||||||
* Instead, create a copy list with the change, and replace this list.
|
* Instead, we create a copy list with the change, and replace this list.
|
||||||
* The other solution, casting toList() every "get", has a performance cost
|
* The other solution, casting toList() every "get", has a performance cost
|
||||||
*/
|
*/
|
||||||
@Transient private var units=ArrayList<MapUnit>()
|
@Transient private var units=listOf<MapUnit>()
|
||||||
@Transient var viewableTiles = HashSet<TileInfo>()
|
@Transient var viewableTiles = setOf<TileInfo>()
|
||||||
@Transient var viewableInvisibleUnitsTiles = HashSet<TileInfo>()
|
@Transient var viewableInvisibleUnitsTiles = setOf<TileInfo>()
|
||||||
|
|
||||||
// This is for performance since every movement calculation depends on this, see MapUnit comment
|
/** This is for performance since every movement calculation depends on this, see MapUnit comment */
|
||||||
@Transient var hasActiveGreatWall = false
|
@Transient var hasActiveGreatWall = false
|
||||||
|
|
||||||
var gold = 0
|
var gold = 0
|
||||||
@ -61,6 +67,7 @@ class CivilizationInfo {
|
|||||||
var diplomacy = HashMap<String, DiplomacyManager>()
|
var diplomacy = HashMap<String, DiplomacyManager>()
|
||||||
var notifications = ArrayList<Notification>()
|
var notifications = ArrayList<Notification>()
|
||||||
val popupAlerts = ArrayList<PopupAlert>()
|
val popupAlerts = ArrayList<PopupAlert>()
|
||||||
|
val tradeRequests = ArrayList<TradeRequest>()
|
||||||
|
|
||||||
// if we only use lists, and change the list each time the cities are changed,
|
// if we only use lists, and change the list each time the cities are changed,
|
||||||
// we won't get concurrent modification exceptions.
|
// we won't get concurrent modification exceptions.
|
||||||
@ -79,14 +86,14 @@ class CivilizationInfo {
|
|||||||
fun clone(): CivilizationInfo {
|
fun clone(): CivilizationInfo {
|
||||||
val toReturn = CivilizationInfo()
|
val toReturn = CivilizationInfo()
|
||||||
toReturn.gold = gold
|
toReturn.gold = gold
|
||||||
toReturn.happiness=happiness
|
toReturn.happiness = happiness
|
||||||
toReturn.playerType=playerType
|
toReturn.playerType = playerType
|
||||||
toReturn.civName=civName
|
toReturn.civName = civName
|
||||||
toReturn.tech = tech.clone()
|
toReturn.tech = tech.clone()
|
||||||
toReturn.policies = policies.clone()
|
toReturn.policies = policies.clone()
|
||||||
toReturn.goldenAges = goldenAges.clone()
|
toReturn.goldenAges = goldenAges.clone()
|
||||||
toReturn.greatPeople=greatPeople.clone()
|
toReturn.greatPeople = greatPeople.clone()
|
||||||
toReturn.victoryManager=victoryManager.clone()
|
toReturn.victoryManager = victoryManager.clone()
|
||||||
toReturn.diplomacy.putAll(diplomacy.values.map { it.clone() }.associateBy { it.otherCivName })
|
toReturn.diplomacy.putAll(diplomacy.values.map { it.clone() }.associateBy { it.otherCivName })
|
||||||
toReturn.cities = cities.map { it.clone() }
|
toReturn.cities = cities.map { it.clone() }
|
||||||
toReturn.exploredTiles.addAll(exploredTiles)
|
toReturn.exploredTiles.addAll(exploredTiles)
|
||||||
@ -116,10 +123,7 @@ class CivilizationInfo {
|
|||||||
fun isPlayerCivilization() = playerType==PlayerType.Human
|
fun isPlayerCivilization() = playerType==PlayerType.Human
|
||||||
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
|
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
|
||||||
fun isBarbarianCivilization() = gameInfo.getBarbarianCivilization()==this
|
fun isBarbarianCivilization() = gameInfo.getBarbarianCivilization()==this
|
||||||
|
fun getStatsForNextTurn():Stats = getStatMapForNextTurn().values.toList().reduce{a,b->a+b}
|
||||||
fun getStatsForNextTurn():Stats{
|
|
||||||
return getStatMapForNextTurn().values.toList().reduce{a,b->a+b}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getStatMapForNextTurn(): HashMap<String, Stats> {
|
fun getStatMapForNextTurn(): HashMap<String, Stats> {
|
||||||
val statMap = HashMap<String,Stats>()
|
val statMap = HashMap<String,Stats>()
|
||||||
|
@ -16,6 +16,8 @@ class DiplomacyManager() {
|
|||||||
lateinit var otherCivName:String
|
lateinit var otherCivName:String
|
||||||
var trades = ArrayList<Trade>()
|
var trades = ArrayList<Trade>()
|
||||||
var diplomaticStatus = DiplomaticStatus.War
|
var diplomaticStatus = DiplomaticStatus.War
|
||||||
|
/** Contains various flags (declared war, promised to not settle, declined luxury trade) and the number of turns in which they will expire */
|
||||||
|
var flagsCountdown = HashMap<String,Int>()
|
||||||
|
|
||||||
fun clone(): DiplomacyManager {
|
fun clone(): DiplomacyManager {
|
||||||
val toReturn = DiplomacyManager()
|
val toReturn = DiplomacyManager()
|
||||||
@ -63,6 +65,11 @@ class DiplomacyManager() {
|
|||||||
if(offer.type== TradeType.Strategic_Resource || offer.type== TradeType.Luxury_Resource)
|
if(offer.type== TradeType.Strategic_Resource || offer.type== TradeType.Luxury_Resource)
|
||||||
counter.add(GameBasics.TileResources[offer.name]!!,offer.amount)
|
counter.add(GameBasics.TileResources[offer.name]!!,offer.amount)
|
||||||
}
|
}
|
||||||
|
for(tradeRequest in otherCiv().tradeRequests.filter { it.requestingCiv==civInfo.civName }){
|
||||||
|
for(offer in tradeRequest.trade.theirOffers) // "theirOffers" in the other civ's trade request, is actually out civ's offers
|
||||||
|
if(offer.type== TradeType.Strategic_Resource || offer.type== TradeType.Luxury_Resource)
|
||||||
|
counter.add(GameBasics.TileResources[offer.name]!!,-offer.amount)
|
||||||
|
}
|
||||||
return counter
|
return counter
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
@ -95,6 +102,12 @@ class DiplomacyManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeUntenebleTrades()
|
removeUntenebleTrades()
|
||||||
|
|
||||||
|
for(flag in flagsCountdown.keys.toList()) {
|
||||||
|
flagsCountdown[flag] = flagsCountdown[flag]!! - 1
|
||||||
|
if(flagsCountdown[flag]==0) flagsCountdown.remove(flag)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun declareWar(){
|
fun declareWar(){
|
||||||
|
@ -31,4 +31,11 @@ class Trade{
|
|||||||
toReturn.ourOffers.addAll(ourOffers)
|
toReturn.ourOffers.addAll(ourOffers)
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun set(trade: Trade) {
|
||||||
|
ourOffers.clear()
|
||||||
|
ourOffers.addAll(trade.ourOffers)
|
||||||
|
theirOffers.clear()
|
||||||
|
theirOffers.addAll(trade.theirOffers)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package com.unciv.logic.trade
|
package com.unciv.logic.trade
|
||||||
|
|
||||||
|
import com.unciv.models.gamebasics.tr
|
||||||
|
|
||||||
data class TradeOffer(var name:String, var type: TradeType, var duration:Int, var amount:Int=1) {
|
data class TradeOffer(var name:String, var type: TradeType, var duration:Int, var amount:Int=1) {
|
||||||
|
|
||||||
constructor() : this("", TradeType.Gold,0,0) // so that the json deserializer can work
|
constructor() : this("", TradeType.Gold,0,0) // so that the json deserializer can work
|
||||||
@ -9,4 +11,18 @@ data class TradeOffer(var name:String, var type: TradeType, var duration:Int, va
|
|||||||
&& offer.type==type
|
&& offer.type==type
|
||||||
&& offer.amount==amount
|
&& offer.amount==amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getOfferText(): String {
|
||||||
|
var offerText = name.tr()
|
||||||
|
if (type !in tradesToNotHaveNumbers) offerText += " (" + amount + ")"
|
||||||
|
if (duration > 1) offerText += "\n" + duration + " {turns}".tr()
|
||||||
|
return offerText
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object{
|
||||||
|
val tradesToNotHaveNumbers = listOf(TradeType.Technology, TradeType.City,
|
||||||
|
TradeType.Introduction, TradeType.Treaty, TradeType.WarDeclaration)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -12,12 +12,13 @@ class Nation : INamed {
|
|||||||
else return name
|
else return name
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var leaderName: String
|
var leaderName=""
|
||||||
|
|
||||||
lateinit var declaringWar:String
|
var declaringWar=""
|
||||||
lateinit var attacked:String
|
var attacked=""
|
||||||
lateinit var defeated:String
|
var defeated=""
|
||||||
lateinit var introduction:String
|
var introduction=""
|
||||||
|
var tradeRequest=""
|
||||||
|
|
||||||
var neutralLetsHearIt = ArrayList<String>()
|
var neutralLetsHearIt = ArrayList<String>()
|
||||||
var neutralYes = ArrayList<String>()
|
var neutralYes = ArrayList<String>()
|
||||||
|
@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
import com.unciv.UnCivGame
|
import com.unciv.UnCivGame
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
|
import com.unciv.logic.trade.TradeLogic
|
||||||
import com.unciv.models.gamebasics.tr
|
import com.unciv.models.gamebasics.tr
|
||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
import com.unciv.ui.worldscreen.optionstable.PopupTable
|
import com.unciv.ui.worldscreen.optionstable.PopupTable
|
||||||
@ -59,6 +60,13 @@ class DiplomacyScreen:CameraStageBaseScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setTrade(civ: CivilizationInfo): TradeTable {
|
||||||
|
rightSideTable.clear()
|
||||||
|
val tradeTable =TradeTable(civ, stage) { updateLeftSideTable() }
|
||||||
|
rightSideTable.add(tradeTable)
|
||||||
|
return tradeTable
|
||||||
|
}
|
||||||
|
|
||||||
private fun getDiplomacyTable(civ: CivilizationInfo): Table {
|
private fun getDiplomacyTable(civ: CivilizationInfo): Table {
|
||||||
val diplomacyTable = Table()
|
val diplomacyTable = Table()
|
||||||
diplomacyTable.defaults().pad(10f)
|
diplomacyTable.defaults().pad(10f)
|
||||||
@ -67,10 +75,7 @@ class DiplomacyScreen:CameraStageBaseScreen() {
|
|||||||
diplomacyTable.addSeparator()
|
diplomacyTable.addSeparator()
|
||||||
|
|
||||||
val tradeButton = TextButton("Trade".tr(), skin)
|
val tradeButton = TextButton("Trade".tr(), skin)
|
||||||
tradeButton.onClick {
|
tradeButton.onClick { setTrade(civ) }
|
||||||
rightSideTable.clear()
|
|
||||||
rightSideTable.add(TradeTable(civ, stage) { updateLeftSideTable() })
|
|
||||||
}
|
|
||||||
diplomacyTable.add(tradeButton).row()
|
diplomacyTable.add(tradeButton).row()
|
||||||
|
|
||||||
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
|
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
|
||||||
|
@ -7,7 +7,6 @@ import com.unciv.logic.trade.TradeOffer
|
|||||||
import com.unciv.logic.trade.TradeOffersList
|
import com.unciv.logic.trade.TradeOffersList
|
||||||
import com.unciv.logic.trade.TradeType
|
import com.unciv.logic.trade.TradeType
|
||||||
import com.unciv.logic.trade.TradeType.*
|
import com.unciv.logic.trade.TradeType.*
|
||||||
import com.unciv.models.gamebasics.tr
|
|
||||||
import com.unciv.ui.cityscreen.ExpanderTab
|
import com.unciv.ui.cityscreen.ExpanderTab
|
||||||
import com.unciv.ui.utils.CameraStageBaseScreen
|
import com.unciv.ui.utils.CameraStageBaseScreen
|
||||||
import com.unciv.ui.utils.disable
|
import com.unciv.ui.utils.disable
|
||||||
@ -16,8 +15,7 @@ import kotlin.math.min
|
|||||||
|
|
||||||
class OffersListScroll(val onOfferClicked: (TradeOffer) -> Unit) : ScrollPane(null) {
|
class OffersListScroll(val onOfferClicked: (TradeOffer) -> Unit) : ScrollPane(null) {
|
||||||
val table = Table(CameraStageBaseScreen.skin).apply { defaults().pad(5f) }
|
val table = Table(CameraStageBaseScreen.skin).apply { defaults().pad(5f) }
|
||||||
val tradesToNotHaveNumbers = listOf(Technology, City,
|
|
||||||
Introduction, Treaty, WarDeclaration)
|
|
||||||
|
|
||||||
val expanderTabs = HashMap<TradeType, ExpanderTab>()
|
val expanderTabs = HashMap<TradeType, ExpanderTab>()
|
||||||
|
|
||||||
@ -50,10 +48,7 @@ class OffersListScroll(val onOfferClicked: (TradeOffer) -> Unit) : ScrollPane(nu
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (offer in offersOfType) {
|
for (offer in offersOfType) {
|
||||||
var buttonText = offer.name.tr()
|
val tradeButton = TextButton(offer.getOfferText(), CameraStageBaseScreen.skin)
|
||||||
if (offer.type !in tradesToNotHaveNumbers) buttonText += " (" + offer.amount + ")"
|
|
||||||
if (offer.duration > 1) buttonText += "\n" + offer.duration + " {turns}".tr()
|
|
||||||
val tradeButton = TextButton(buttonText, CameraStageBaseScreen.skin)
|
|
||||||
val amountPerClick =
|
val amountPerClick =
|
||||||
if (offer.type == Gold) 50
|
if (offer.type == Gold) 50
|
||||||
else 1
|
else 1
|
||||||
|
74
core/src/com/unciv/ui/worldscreen/AlertPopup.kt
Normal file
74
core/src/com/unciv/ui/worldscreen/AlertPopup.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.unciv.ui.worldscreen
|
||||||
|
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
|
import com.unciv.logic.civilization.AlertType
|
||||||
|
import com.unciv.logic.civilization.PopupAlert
|
||||||
|
import com.unciv.models.gamebasics.Nation
|
||||||
|
import com.unciv.models.gamebasics.tr
|
||||||
|
import com.unciv.ui.utils.addSeparator
|
||||||
|
import com.unciv.ui.utils.onClick
|
||||||
|
import com.unciv.ui.utils.toLabel
|
||||||
|
import com.unciv.ui.worldscreen.optionstable.PopupTable
|
||||||
|
|
||||||
|
class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): PopupTable(worldScreen){
|
||||||
|
fun getCloseButton(text:String): TextButton {
|
||||||
|
val button = TextButton(text.tr(), skin)
|
||||||
|
button.onClick { close() }
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addLeaderName(translatedNation: Nation){
|
||||||
|
val otherCivLeaderName = "[${translatedNation.leaderName}] of [${translatedNation.getNameTranslation()}]".tr()
|
||||||
|
add(otherCivLeaderName.toLabel())
|
||||||
|
addSeparator()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
when(popupAlert.type){
|
||||||
|
AlertType.WarDeclaration -> {
|
||||||
|
val translatedNation = worldScreen.gameInfo.getCivilization(popupAlert.value).getTranslatedNation()
|
||||||
|
addLeaderName(translatedNation)
|
||||||
|
addGoodSizedLabel(translatedNation.declaringWar).row()
|
||||||
|
val responseTable = Table()
|
||||||
|
responseTable.add(getCloseButton("You'll pay for this!"))
|
||||||
|
responseTable.add(getCloseButton("Very well."))
|
||||||
|
add(responseTable)
|
||||||
|
}
|
||||||
|
AlertType.Defeated -> {
|
||||||
|
val translatedNation = worldScreen.gameInfo.getCivilization(popupAlert.value).getTranslatedNation()
|
||||||
|
addLeaderName(translatedNation)
|
||||||
|
addGoodSizedLabel(translatedNation.defeated).row()
|
||||||
|
add(getCloseButton("Farewell."))
|
||||||
|
}
|
||||||
|
AlertType.FirstContact -> {
|
||||||
|
val translatedNation = worldScreen.gameInfo.getCivilization(popupAlert.value).getTranslatedNation()
|
||||||
|
addLeaderName(translatedNation)
|
||||||
|
addGoodSizedLabel(translatedNation.introduction).row()
|
||||||
|
add(getCloseButton("A pleasure to meet you."))
|
||||||
|
}
|
||||||
|
AlertType.CityConquered -> {
|
||||||
|
addGoodSizedLabel("What would you like to do with the city?").row()
|
||||||
|
add(getCloseButton("Annex")).row()
|
||||||
|
add(TextButton("Raze", skin).onClick {
|
||||||
|
worldScreen.currentPlayerCiv.cities.first { it.name==popupAlert.value }.isBeingRazed=true
|
||||||
|
worldScreen.shouldUpdate=true
|
||||||
|
close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open()
|
||||||
|
isOpen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close(){
|
||||||
|
worldScreen.currentPlayerCiv.popupAlerts.remove(popupAlert)
|
||||||
|
isOpen = false
|
||||||
|
remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var isOpen = false
|
||||||
|
}
|
||||||
|
}
|
74
core/src/com/unciv/ui/worldscreen/TradePopup.kt
Normal file
74
core/src/com/unciv/ui/worldscreen/TradePopup.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.unciv.ui.worldscreen
|
||||||
|
|
||||||
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
|
import com.unciv.logic.trade.TradeLogic
|
||||||
|
import com.unciv.models.gamebasics.tr
|
||||||
|
import com.unciv.ui.trade.DiplomacyScreen
|
||||||
|
import com.unciv.ui.utils.addSeparator
|
||||||
|
import com.unciv.ui.utils.toLabel
|
||||||
|
import com.unciv.ui.worldscreen.optionstable.PopupTable
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class TradePopup(worldScreen: WorldScreen): PopupTable(worldScreen){
|
||||||
|
init{
|
||||||
|
val currentPlayerCiv = worldScreen.currentPlayerCiv
|
||||||
|
val tradeRequest = currentPlayerCiv.tradeRequests.first()
|
||||||
|
|
||||||
|
val requestingCiv = worldScreen.gameInfo.getCivilization(tradeRequest.requestingCiv)
|
||||||
|
val translatedNation = requestingCiv.getTranslatedNation()
|
||||||
|
val otherCivLeaderName = "[${translatedNation.leaderName}] of [${translatedNation.getNameTranslation()}]".tr()
|
||||||
|
|
||||||
|
add(otherCivLeaderName.toLabel())
|
||||||
|
addSeparator()
|
||||||
|
|
||||||
|
val trade = tradeRequest.trade
|
||||||
|
val tradeOffersTable = Table().apply { defaults().pad(10f) }
|
||||||
|
for(i in 0..max(trade.theirOffers.lastIndex, trade.ourOffers.lastIndex)){
|
||||||
|
if(trade.theirOffers.lastIndex>=i) tradeOffersTable.add(trade.theirOffers[i].getOfferText().toLabel())
|
||||||
|
else tradeOffersTable.add()
|
||||||
|
if(trade.ourOffers.lastIndex>=i) tradeOffersTable.add(trade.ourOffers[i].getOfferText().toLabel())
|
||||||
|
else tradeOffersTable.add()
|
||||||
|
tradeOffersTable.row()
|
||||||
|
}
|
||||||
|
add(tradeOffersTable).row()
|
||||||
|
|
||||||
|
addGoodSizedLabel(translatedNation.tradeRequest).colspan(columns).row()
|
||||||
|
|
||||||
|
addButton("Sounds good!".tr()){
|
||||||
|
val tradeLogic = TradeLogic(currentPlayerCiv, requestingCiv)
|
||||||
|
tradeLogic.currentTrade.set(trade)
|
||||||
|
tradeLogic.acceptTrade()
|
||||||
|
currentPlayerCiv.tradeRequests.remove(tradeRequest)
|
||||||
|
remove()
|
||||||
|
PopupTable(worldScreen).apply {
|
||||||
|
add(otherCivLeaderName.toLabel()).colspan(2)
|
||||||
|
addSeparator()
|
||||||
|
addGoodSizedLabel("Excellent!").row()
|
||||||
|
addButton("Goodbye."){
|
||||||
|
this.remove()
|
||||||
|
worldScreen.shouldUpdate=true
|
||||||
|
// in all cases, worldScreen.shouldUpdate should be set to true when we remove the last of the popups
|
||||||
|
// in order for the next trade to appear immediately
|
||||||
|
}
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addButton("Not this time.".tr()){
|
||||||
|
currentPlayerCiv.tradeRequests.remove(tradeRequest)
|
||||||
|
remove()
|
||||||
|
worldScreen.shouldUpdate=true
|
||||||
|
}
|
||||||
|
addButton("How about something else...".tr()){
|
||||||
|
currentPlayerCiv.tradeRequests.remove(tradeRequest)
|
||||||
|
remove()
|
||||||
|
|
||||||
|
val diplomacyScreen= DiplomacyScreen()
|
||||||
|
val tradeTable = diplomacyScreen.setTrade(requestingCiv)
|
||||||
|
tradeTable.tradeLogic.currentTrade.set(trade)
|
||||||
|
tradeTable.offerColumnsTable.update()
|
||||||
|
worldScreen.game.screen=diplomacyScreen
|
||||||
|
worldScreen.shouldUpdate=true
|
||||||
|
}
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,9 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||||
import com.unciv.UnCivGame
|
import com.unciv.UnCivGame
|
||||||
import com.unciv.logic.GameSaver
|
import com.unciv.logic.GameSaver
|
||||||
import com.unciv.logic.civilization.AlertType
|
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.logic.civilization.PopupAlert
|
|
||||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||||
import com.unciv.models.gamebasics.GameBasics
|
import com.unciv.models.gamebasics.GameBasics
|
||||||
import com.unciv.models.gamebasics.Nation
|
|
||||||
import com.unciv.models.gamebasics.tile.ResourceType
|
import com.unciv.models.gamebasics.tile.ResourceType
|
||||||
import com.unciv.models.gamebasics.tr
|
import com.unciv.models.gamebasics.tr
|
||||||
import com.unciv.models.gamebasics.unit.UnitType
|
import com.unciv.models.gamebasics.unit.UnitType
|
||||||
@ -26,7 +23,6 @@ import com.unciv.ui.trade.DiplomacyScreen
|
|||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
import com.unciv.ui.worldscreen.bottombar.BattleTable
|
import com.unciv.ui.worldscreen.bottombar.BattleTable
|
||||||
import com.unciv.ui.worldscreen.bottombar.WorldScreenBottomBar
|
import com.unciv.ui.worldscreen.bottombar.WorldScreenBottomBar
|
||||||
import com.unciv.ui.worldscreen.optionstable.PopupTable
|
|
||||||
import com.unciv.ui.worldscreen.unit.UnitActionsTable
|
import com.unciv.ui.worldscreen.unit.UnitActionsTable
|
||||||
|
|
||||||
class WorldScreen : CameraStageBaseScreen() {
|
class WorldScreen : CameraStageBaseScreen() {
|
||||||
@ -167,13 +163,16 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||||||
notificationsScroll.setPosition(stage.width - notificationsScroll.width - 5f,
|
notificationsScroll.setPosition(stage.width - notificationsScroll.width - 5f,
|
||||||
nextTurnButton.y - notificationsScroll.height - 5f)
|
nextTurnButton.y - notificationsScroll.height - 5f)
|
||||||
|
|
||||||
if(!gameInfo.oneMoreTurnMode && currentPlayerCiv.victoryManager.hasWon()) game.screen = VictoryScreen()
|
when {
|
||||||
else if(currentPlayerCiv.policies.freePolicies>0) game.screen = PolicyPickerScreen(currentPlayerCiv)
|
!gameInfo.oneMoreTurnMode && currentPlayerCiv.victoryManager.hasWon() -> game.screen = VictoryScreen()
|
||||||
else if(currentPlayerCiv.greatPeople.freeGreatPeople>0) game.screen = GreatPersonPickerScreen()
|
currentPlayerCiv.policies.freePolicies>0 -> game.screen = PolicyPickerScreen(currentPlayerCiv)
|
||||||
|
currentPlayerCiv.greatPeople.freeGreatPeople>0 -> game.screen = GreatPersonPickerScreen()
|
||||||
if(game.screen==this && !tutorials.isTutorialShowing
|
currentPlayerCiv.tradeRequests.isNotEmpty() ->{
|
||||||
&& currentPlayerCiv.popupAlerts.any() && !AlertPopup.isOpen){
|
TradePopup(this)
|
||||||
AlertPopup(this,currentPlayerCiv.popupAlerts.first())
|
}
|
||||||
|
!tutorials.isTutorialShowing
|
||||||
|
&& currentPlayerCiv.popupAlerts.any() && !AlertPopup.isOpen ->
|
||||||
|
AlertPopup(this,currentPlayerCiv.popupAlerts.first())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,10 +221,10 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createNextTurnButton(): TextButton {
|
private fun createNextTurnButton(): TextButton {
|
||||||
val nextTurnButton = TextButton("Next turn".tr(), CameraStageBaseScreen.skin)
|
val nextTurnButton = TextButton("Next turn".tr(), skin)
|
||||||
nextTurnButton.onClick {
|
nextTurnButton.onClick {
|
||||||
if(currentPlayerCiv.policies.shouldOpenPolicyPicker && !currentPlayerCiv.policies.canAdoptPolicy())
|
if(currentPlayerCiv.policies.shouldOpenPolicyPicker && !currentPlayerCiv.policies.canAdoptPolicy())
|
||||||
currentPlayerCiv.policies.shouldOpenPolicyPicker = false // something has chanhed and we can no longer adopt the policy, e.g. we conquered another city
|
currentPlayerCiv.policies.shouldOpenPolicyPicker = false // something has changed and we can no longer adopt the policy, e.g. we conquered another city
|
||||||
|
|
||||||
if (currentPlayerCiv.tech.freeTechs != 0) {
|
if (currentPlayerCiv.tech.freeTechs != 0) {
|
||||||
game.screen = TechPickerScreen(true, currentPlayerCiv)
|
game.screen = TechPickerScreen(true, currentPlayerCiv)
|
||||||
@ -281,7 +280,6 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun resize(width: Int, height: Int) {
|
override fun resize(width: Int, height: Int) {
|
||||||
|
|
||||||
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
|
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
|
||||||
super.resize(width, height)
|
super.resize(width, height)
|
||||||
game.worldScreen = WorldScreen() // start over.
|
game.worldScreen = WorldScreen() // start over.
|
||||||
@ -299,95 +297,35 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||||||
|
|
||||||
// otherwise images will not load properly!
|
// otherwise images will not load properly!
|
||||||
update()
|
update()
|
||||||
|
showTutorialsOnNextTurn()
|
||||||
val shownTutorials = UnCivGame.Current.settings.tutorialsShown
|
|
||||||
displayTutorials("NextTurn")
|
|
||||||
if("BarbarianEncountered" !in shownTutorials
|
|
||||||
&& currentPlayerCiv.viewableTiles.any { it.getUnits().any { unit -> unit.civInfo.isBarbarianCivilization() } })
|
|
||||||
displayTutorials("BarbarianEncountered")
|
|
||||||
if(currentPlayerCiv.cities.size > 2) displayTutorials("SecondCity")
|
|
||||||
if(currentPlayerCiv.happiness < 0) displayTutorials("Unhappiness")
|
|
||||||
if(currentPlayerCiv.goldenAges.isGoldenAge()) displayTutorials("GoldenAge")
|
|
||||||
if(gameInfo.turns >= 100) displayTutorials("ContactMe")
|
|
||||||
val resources = currentPlayerCiv.getCivResources()
|
|
||||||
if(resources.keys.any { it.resourceType==ResourceType.Luxury }) displayTutorials("LuxuryResource")
|
|
||||||
if(resources.keys.any { it.resourceType==ResourceType.Strategic}) displayTutorials("StrategicResource")
|
|
||||||
if("EnemyCity" !in shownTutorials
|
|
||||||
&& currentPlayerCiv.exploredTiles.asSequence().map { gameInfo.tileMap[it] }
|
|
||||||
.any { it.isCityCenter() && it.getOwner()!=currentPlayerCiv })
|
|
||||||
displayTutorials("EnemyCity")
|
|
||||||
if("Enables construction of Spaceship parts" in currentPlayerCiv.getBuildingUniques())
|
|
||||||
displayTutorials("ApolloProgram")
|
|
||||||
if(currentPlayerCiv.getCivUnits().any { it.type == UnitType.Siege })
|
|
||||||
displayTutorials("SiegeUnitTrained")
|
|
||||||
if(currentPlayerCiv.tech.getUniques().contains("Enables embarkation for land units"))
|
|
||||||
displayTutorials("CanEmbark")
|
|
||||||
|
|
||||||
shouldUpdate=false
|
shouldUpdate=false
|
||||||
}
|
}
|
||||||
super.render(delta)
|
super.render(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private fun showTutorialsOnNextTurn(){
|
||||||
|
val shownTutorials = UnCivGame.Current.settings.tutorialsShown
|
||||||
class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert):PopupTable(worldScreen){
|
displayTutorials("NextTurn")
|
||||||
fun getCloseButton(text:String): TextButton {
|
if("BarbarianEncountered" !in shownTutorials
|
||||||
val button = TextButton(text.tr(), skin)
|
&& currentPlayerCiv.viewableTiles.any { it.getUnits().any { unit -> unit.civInfo.isBarbarianCivilization() } })
|
||||||
button.onClick { close() }
|
displayTutorials("BarbarianEncountered")
|
||||||
return button
|
if(currentPlayerCiv.cities.size > 2) displayTutorials("SecondCity")
|
||||||
|
if(currentPlayerCiv.happiness < 0) displayTutorials("Unhappiness")
|
||||||
|
if(currentPlayerCiv.goldenAges.isGoldenAge()) displayTutorials("GoldenAge")
|
||||||
|
if(gameInfo.turns >= 100) displayTutorials("ContactMe")
|
||||||
|
val resources = currentPlayerCiv.getCivResources()
|
||||||
|
if(resources.keys.any { it.resourceType==ResourceType.Luxury }) displayTutorials("LuxuryResource")
|
||||||
|
if(resources.keys.any { it.resourceType==ResourceType.Strategic}) displayTutorials("StrategicResource")
|
||||||
|
if("EnemyCity" !in shownTutorials
|
||||||
|
&& currentPlayerCiv.exploredTiles.asSequence().map { gameInfo.tileMap[it] }
|
||||||
|
.any { it.isCityCenter() && it.getOwner()!=currentPlayerCiv })
|
||||||
|
displayTutorials("EnemyCity")
|
||||||
|
if("Enables construction of Spaceship parts" in currentPlayerCiv.getBuildingUniques())
|
||||||
|
displayTutorials("ApolloProgram")
|
||||||
|
if(currentPlayerCiv.getCivUnits().any { it.type == UnitType.Siege })
|
||||||
|
displayTutorials("SiegeUnitTrained")
|
||||||
|
if(currentPlayerCiv.tech.getUniques().contains("Enables embarkation for land units"))
|
||||||
|
displayTutorials("CanEmbark")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addLeaderName(translatedNation:Nation){
|
|
||||||
val otherCivLeaderName = "[${translatedNation.leaderName}] of [${translatedNation.getNameTranslation()}]".tr()
|
|
||||||
add(otherCivLeaderName.toLabel())
|
|
||||||
addSeparator()
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
|
|
||||||
when(popupAlert.type){
|
|
||||||
AlertType.WarDeclaration -> {
|
|
||||||
val translatedNation = worldScreen.gameInfo.getCivilization(popupAlert.value).getTranslatedNation()
|
|
||||||
addLeaderName(translatedNation)
|
|
||||||
addGoodSizedLabel(translatedNation.declaringWar).row()
|
|
||||||
val responseTable = Table()
|
|
||||||
responseTable.add(getCloseButton("You'll pay for this!"))
|
|
||||||
responseTable.add(getCloseButton("Very well."))
|
|
||||||
add(responseTable)
|
|
||||||
}
|
|
||||||
AlertType.Defeated -> {
|
|
||||||
val translatedNation = worldScreen.gameInfo.getCivilization(popupAlert.value).getTranslatedNation()
|
|
||||||
addLeaderName(translatedNation)
|
|
||||||
addGoodSizedLabel(translatedNation.defeated).row()
|
|
||||||
add(getCloseButton("Farewell."))
|
|
||||||
}
|
|
||||||
AlertType.FirstContact -> {
|
|
||||||
val translatedNation = worldScreen.gameInfo.getCivilization(popupAlert.value).getTranslatedNation()
|
|
||||||
addLeaderName(translatedNation)
|
|
||||||
addGoodSizedLabel(translatedNation.introduction).row()
|
|
||||||
add(getCloseButton("A pleasure to meet you."))
|
|
||||||
}
|
|
||||||
AlertType.CityConquered -> {
|
|
||||||
addGoodSizedLabel("What would you like to do with the city?").row()
|
|
||||||
add(getCloseButton("Annex")).row()
|
|
||||||
add(TextButton("Raze",skin).onClick {
|
|
||||||
worldScreen.currentPlayerCiv.cities.first { it.name==popupAlert.value }.isBeingRazed=true
|
|
||||||
worldScreen.shouldUpdate=true
|
|
||||||
close()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
open()
|
|
||||||
isOpen = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun close(){
|
|
||||||
worldScreen.currentPlayerCiv.popupAlerts.remove(popupAlert)
|
|
||||||
isOpen = false
|
|
||||||
remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
var isOpen = false
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -32,10 +32,10 @@ open class PopupTable(val screen: CameraStageBaseScreen): Table(CameraStageBaseS
|
|||||||
return add(label).width(screen.stage.width/2)
|
return add(label).width(screen.stage.width/2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addButton(text:String, action:()->Unit){
|
fun addButton(text:String, action:()->Unit): Cell<TextButton> {
|
||||||
val button = TextButton(text.tr(), skin).apply { color= ImageGetter.getBlue() }
|
val button = TextButton(text.tr(), skin).apply { color= ImageGetter.getBlue() }
|
||||||
button.onClick(action)
|
button.onClick(action)
|
||||||
add(button).row()
|
return add(button).apply { row() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user