Diplomacy screen redesigned, to allow for more diplomacy options in the future

This commit is contained in:
Yair Morgenstern 2019-03-15 12:14:19 +02:00
parent ab2e044a09
commit 29bc82e1bd
3 changed files with 217 additions and 190 deletions

View File

@ -0,0 +1,150 @@
package com.unciv.logic.automation
import com.unciv.UnCivGame
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.GreatPersonManager
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.stats.Stats
import com.unciv.ui.worldscreen.unit.UnitActions
class SpecificUnitAutomation{
private fun hasWorkableSeaResource(tileInfo: TileInfo, civInfo: CivilizationInfo): Boolean {
return tileInfo.hasViewableResource(civInfo) && tileInfo.isWater() && tileInfo.improvement==null
}
fun automateWorkBoats(unit: MapUnit) {
val seaResourcesInCities = unit.civInfo.cities.flatMap { it.getTilesInRange() }.asSequence()
.filter { hasWorkableSeaResource(it, unit.civInfo) && (unit.canMoveTo(it) || unit.currentTile == it) }
val closestReachableResource = seaResourcesInCities.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movementAlgs().canReach(it) }
if (closestReachableResource != null) {
unit.movementAlgs().headTowards(closestReachableResource)
if (unit.currentMovement > 0 && unit.currentTile == closestReachableResource) {
val createImprovementAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.firstOrNull { it.name.startsWith("Create") } // could be either fishing boats or oil well
if (createImprovementAction != null)
return createImprovementAction.action() // unit is already gone, can't "explore"
}
}
else UnitAutomation().explore(unit, unit.getDistanceToTiles())
}
fun automateGreatGeneral(unit: MapUnit){
//try to follow nearby units. Do not garrison in city if possible
val militaryUnitTilesInDistance = unit.getDistanceToTiles().map { it.key }
.filter {val militant = it.militaryUnit
militant != null && militant.civInfo == unit.civInfo
&& (it.civilianUnit == null || it.civilianUnit == unit)
&& militant.getMaxMovement() <= 2 && !it.isCityCenter()}
if(militaryUnitTilesInDistance.isNotEmpty()) {
val tilesSortedByAffectedTroops = militaryUnitTilesInDistance
.sortedByDescending { it.getTilesInDistance(2).count {
val militaryUnit = it.militaryUnit
militaryUnit!=null && militaryUnit.civInfo==unit.civInfo
} }
unit.movementAlgs().headTowards(tilesSortedByAffectedTroops.first())
return
}
//if no unit to follow, take refuge in city.
val cityToGarrison = unit.civInfo.cities.map {it.getCenterTile()}
.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { it.civilianUnit == null && unit.canMoveTo(it) && unit.movementAlgs().canReach(it)}
if (cityToGarrison != null) {
unit.movementAlgs().headTowards(cityToGarrison)
return
}
}
fun rankTileAsCityCenter(tileInfo: TileInfo, nearbyTileRankings: Map<TileInfo, Float>): Float {
val bestTilesFromOuterLayer = tileInfo.getTilesAtDistance(2)
.asSequence()
.sortedByDescending { nearbyTileRankings[it] }.take(2)
.toList()
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
.asSequence()
.sortedByDescending { nearbyTileRankings[it] }
.take(5)
.toList()
var rank = top5Tiles.asSequence().map { nearbyTileRankings[it]!! }.sum()
if(tileInfo.neighbors.any{it.baseTerrain == "Coast"}) rank += 5
return rank
}
fun automateSettlerActions(unit: MapUnit) {
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit
val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities }
.flatMap { it.getCenterTile().getTilesInDistance(3) }.toHashSet()
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
val nearbyTileRankings = unit.getTile().getTilesInDistance(7)
.associateBy ( {it},{ Automation().rankTile(it,unit.civInfo) })
val possibleCityLocations = unit.getTile().getTilesInDistance(5)
.filter { (unit.canMoveTo(it) || unit.currentTile==it) && it !in tilesNearCities && it.isLand() }
val bestCityLocation: TileInfo? = possibleCityLocations
.asSequence()
.sortedByDescending { rankTileAsCityCenter(it, nearbyTileRankings) }
.firstOrNull { unit.movementAlgs().canReach(it) }
if(bestCityLocation==null) // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
return UnitAutomation().explore(unit, unit.getDistanceToTiles())
if(bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() })
throw Exception("City within distance")
if (unit.getTile() == bestCityLocation)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).first { it.name == "Found city" }.action()
else {
unit.movementAlgs().headTowards(bestCityLocation)
if (unit.currentMovement > 0 && unit.getTile() == bestCityLocation)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).first { it.name == "Found city" }.action()
}
}
fun automateGreatPerson(unit: MapUnit) {
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit
val relatedStat = GreatPersonManager().statToGreatPersonMapping.entries.first { it.value==unit.name }.key
val citiesByStatBoost = unit.civInfo.cities.sortedByDescending{
val stats = Stats()
for (bonus in it.cityStats.statPercentBonusList.values) stats.add(bonus)
stats.toHashMap()[relatedStat]!!
}
for(city in citiesByStatBoost){
val pathToCity =unit.movementAlgs().getShortestPath(city.getCenterTile())
if(pathToCity.isEmpty()) continue
if(pathToCity.size>2){
unit.movementAlgs().headTowards(city.getCenterTile())
return
}
// if we got here, we're pretty close, start looking!
val tiles = city.getTiles().asSequence()
.filter { (unit.canMoveTo(it) || unit.currentTile==it)
&& it.isLand()
&& !it.isCityCenter()
&& it.resource==null }
.sortedByDescending { Automation().rankTile(it,unit.civInfo) }.toList()
val chosenTile = tiles.firstOrNull { unit.movementAlgs().canReach(it) }
if(chosenTile==null) continue // to another city
unit.movementAlgs().headTowards(chosenTile)
if(unit.currentTile==chosenTile && unit.currentMovement>0)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.first { it.name.startsWith("Create") }.action()
return
}
}
}

View File

@ -3,13 +3,11 @@ package com.unciv.logic.automation
import com.unciv.UnCivGame
import com.unciv.logic.battle.*
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.GreatPersonManager
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.models.gamebasics.GameBasics
import com.unciv.models.stats.Stats
import com.unciv.ui.utils.getRandom
import com.unciv.ui.worldscreen.unit.UnitAction
import com.unciv.ui.worldscreen.unit.UnitActions
@ -215,9 +213,8 @@ class UnitAutomation{
if (unit.baseUnit().upgradesTo != null) {
val upgradedUnit = GameBasics.Units[unit.baseUnit().upgradesTo!!]!!
if (upgradedUnit.isBuildable(unit.civInfo)) {
val goldCostOfUpgrade = (upgradedUnit.cost - unit.baseUnit().cost) * 2 + 10
val upgradeAction = unitActions.firstOrNull { it.name.startsWith("Upgrade to") }
if (upgradeAction != null && unit.civInfo.gold > goldCostOfUpgrade) {
if (upgradeAction != null && upgradeAction.canAct) {
upgradeAction.action()
return true
}
@ -445,144 +442,3 @@ class UnitAutomation{
}
}
class SpecificUnitAutomation{
private fun hasWorkableSeaResource(tileInfo: TileInfo, civInfo: CivilizationInfo): Boolean {
return tileInfo.hasViewableResource(civInfo) && tileInfo.isWater() && tileInfo.improvement==null
}
fun automateWorkBoats(unit: MapUnit) {
val seaResourcesInCities = unit.civInfo.cities.flatMap { it.getTilesInRange() }.asSequence()
.filter { hasWorkableSeaResource(it, unit.civInfo) && (unit.canMoveTo(it) || unit.currentTile == it) }
val closestReachableResource = seaResourcesInCities.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movementAlgs().canReach(it) }
if (closestReachableResource != null) {
unit.movementAlgs().headTowards(closestReachableResource)
if (unit.currentMovement > 0 && unit.currentTile == closestReachableResource) {
val createImprovementAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen)
.firstOrNull { it.name.startsWith("Create") } // could be either fishing boats or oil well
if (createImprovementAction != null)
return createImprovementAction.action() // unit is already gone, can't "explore"
}
}
else UnitAutomation().explore(unit, unit.getDistanceToTiles())
}
fun automateGreatGeneral(unit: MapUnit){
//try to follow nearby units. Do not garrison in city if possible
val militaryUnitTilesInDistance = unit.getDistanceToTiles().map { it.key }
.filter {val militant = it.militaryUnit
militant != null && militant.civInfo == unit.civInfo
&& (it.civilianUnit == null || it.civilianUnit == unit)
&& militant.getMaxMovement() <= 2 && !it.isCityCenter()}
if(militaryUnitTilesInDistance.isNotEmpty()) {
val tilesSortedByAffectedTroops = militaryUnitTilesInDistance
.sortedByDescending { it.getTilesInDistance(2).count {
val militaryUnit = it.militaryUnit
militaryUnit!=null && militaryUnit.civInfo==unit.civInfo
} }
unit.movementAlgs().headTowards(tilesSortedByAffectedTroops.first())
return
}
//if no unit to follow, take refuge in city.
val cityToGarrison = unit.civInfo.cities.map {it.getCenterTile()}
.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { it.civilianUnit == null && unit.canMoveTo(it) && unit.movementAlgs().canReach(it)}
if (cityToGarrison != null) {
unit.movementAlgs().headTowards(cityToGarrison)
return
}
}
fun rankTileAsCityCenter(tileInfo: TileInfo, nearbyTileRankings: Map<TileInfo, Float>): Float {
val bestTilesFromOuterLayer = tileInfo.getTilesAtDistance(2)
.asSequence()
.sortedByDescending { nearbyTileRankings[it] }.take(2)
.toList()
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
.asSequence()
.sortedByDescending { nearbyTileRankings[it] }
.take(5)
.toList()
var rank = top5Tiles.asSequence().map { nearbyTileRankings[it]!! }.sum()
if(tileInfo.neighbors.any{it.baseTerrain == "Coast"}) rank += 5
return rank
}
fun automateSettlerActions(unit: MapUnit) {
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit
val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities }
.flatMap { it.getCenterTile().getTilesInDistance(3) }.toHashSet()
// This is to improve performance - instead of ranking each tile in the area up to 19 times, do it once.
val nearbyTileRankings = unit.getTile().getTilesInDistance(7)
.associateBy ( {it},{ Automation().rankTile(it,unit.civInfo) })
val possibleCityLocations = unit.getTile().getTilesInDistance(5)
.filter { (unit.canMoveTo(it) || unit.currentTile==it) && it !in tilesNearCities && it.isLand() }
val bestCityLocation: TileInfo? = possibleCityLocations
.asSequence()
.sortedByDescending { rankTileAsCityCenter(it, nearbyTileRankings) }
.firstOrNull { unit.movementAlgs().canReach(it) }
if(bestCityLocation==null) // We got a badass over here, all tiles within 5 are taken? Screw it, random walk.
return UnitAutomation().explore(unit, unit.getDistanceToTiles())
if(bestCityLocation.getTilesInDistance(3).any { it.isCityCenter() })
throw Exception("City within distance")
if (unit.getTile() == bestCityLocation)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).first { it.name == "Found city" }.action()
else {
unit.movementAlgs().headTowards(bestCityLocation)
if (unit.currentMovement > 0 && unit.getTile() == bestCityLocation)
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).first { it.name == "Found city" }.action()
}
}
fun automateGreatPerson(unit: MapUnit) {
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit
val relatedStat = GreatPersonManager().statToGreatPersonMapping.entries.first { it.value==unit.name }.key
val citiesByStatBoost = unit.civInfo.cities.sortedByDescending{
val stats = Stats()
for (bonus in it.cityStats.statPercentBonusList.values) stats.add(bonus)
stats.toHashMap()[relatedStat]!!
}
for(city in citiesByStatBoost){
val pathToCity =unit.movementAlgs().getShortestPath(city.getCenterTile())
if(pathToCity.isEmpty()) continue
if(pathToCity.size>2){
unit.movementAlgs().headTowards(city.getCenterTile())
return
}
// if we got here, we're pretty close, start looking!
val tiles = city.getTiles().asSequence()
.filter { (unit.canMoveTo(it) || unit.currentTile==it)
&& it.isLand()
&& !it.isCityCenter()
&& it.resource==null }
.sortedByDescending { Automation().rankTile(it,unit.civInfo) }.toList()
val chosenTile = tiles.firstOrNull { unit.movementAlgs().canReach(it) }
if(chosenTile==null) continue // to another city
unit.movementAlgs().headTowards(chosenTile)
if(unit.currentTile==chosenTile && unit.currentMovement>0)
UnitActions().getUnitActions(unit,UnCivGame.Current.worldScreen)
.first { it.name.startsWith("Create") }.action()
return
}
}
}

View File

@ -1,21 +1,25 @@
package com.unciv.ui.trade
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.ui.SplitPane
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.gamebasics.tr
import com.unciv.ui.utils.*
import com.unciv.ui.worldscreen.optionstable.PopupTable
import com.unciv.ui.worldscreen.optionstable.YesNoPopupTable
class DiplomacyScreen:CameraStageBaseScreen(){
class DiplomacyScreen:CameraStageBaseScreen() {
val leftSideTable = Table().apply { defaults().pad(10f) }
val rightSideTable = Table()
init{
init {
onBackButtonClicked { UnCivGame.Current.setWorldScreen() }
val splitPane = SplitPane(ScrollPane(leftSideTable),rightSideTable,false, skin)
val splitPane = SplitPane(ScrollPane(leftSideTable), rightSideTable, false, skin)
splitPane.splitAmount = 0.2f
updateLeftSideTable()
@ -37,28 +41,46 @@ class DiplomacyScreen:CameraStageBaseScreen(){
for (civ in UnCivGame.Current.gameInfo.civilizations
.filterNot { it.isDefeated() || it.isPlayerCivilization() || it.isBarbarianCivilization() }) {
if (!currentPlayerCiv.diplomacy.containsKey(civ.civName)) continue
val civDiplomacy = currentPlayerCiv.diplomacy[civ.civName]!!
val civTable = Table().apply { background = ImageGetter.getBackground(civ.getNation().getColor()) }
civTable.pad(10f)
civTable.defaults().pad(10f)
val peaceWarStatus = civDiplomacy.diplomaticStatus.toString()
civTable.add(Label(civ.civName.tr() + " ({$peaceWarStatus})".tr(), skin)
.setFontSize(22).setFontColor(civ.getNation().getSecondaryColor())).row()
civTable.addSeparator()
val civIndicator = ImageGetter.getCircle().apply { color = civ.getNation().getSecondaryColor() }
.surroundWithCircle(100f).apply { circle.color = civ.getNation().getColor() }
val relationship = ImageGetter.getCircle()
if(currentPlayerCiv.isAtWarWith(civ)) relationship.color = Color.RED
else relationship.color = Color.GREEN
relationship.setSize(30f,30f)
civIndicator.addActor(relationship)
leftSideTable.add(civIndicator).row()
civIndicator.onClick {
rightSideTable.clear()
rightSideTable.add(getDiplomacyTable(civ))
}
}
}
private fun getDiplomacyTable(civ: CivilizationInfo): Table {
val diplomacyTable = Table()
diplomacyTable.defaults().pad(10f)
val leaderName = "[" + civ.getNation().leaderName + "] of [" + civ.civName + "]"
diplomacyTable.add(leaderName.toLabel())
diplomacyTable.addSeparator()
val tradeButton = TextButton("Trade".tr(), skin)
tradeButton.onClick {
rightSideTable.clear()
rightSideTable.add(TradeTable(civ, stage){updateLeftSideTable()})
rightSideTable.add(TradeTable(civ, stage) { updateLeftSideTable() })
}
civTable.add(tradeButton).row()
diplomacyTable.add(tradeButton).row()
val currentPlayerCiv = UnCivGame.Current.gameInfo.getCurrentPlayerCivilization()
val civDiplomacy = currentPlayerCiv.diplomacy[civ.civName]!!
if (!currentPlayerCiv.isAtWarWith(civ)) {
val declareWarButton = TextButton("Declare war".tr(), skin)
declareWarButton.color = Color.RED
val turnsToPeaceTreaty = civDiplomacy.turnsToPeaceTreaty()
if(turnsToPeaceTreaty>0){
if (turnsToPeaceTreaty > 0) {
declareWarButton.disable()
declareWarButton.setText(declareWarButton.text.toString() + " ($turnsToPeaceTreaty)")
}
@ -77,9 +99,8 @@ class DiplomacyScreen:CameraStageBaseScreen(){
updateLeftSideTable()
}, this)
}
civTable.add(declareWarButton).row()
}
leftSideTable.add(civTable).row()
diplomacyTable.add(declareWarButton).row()
}
return diplomacyTable
}
}