mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-09 20:29:50 +07:00
Diplomacy screen redesigned, to allow for more diplomacy options in the future
This commit is contained in:
parent
ab2e044a09
commit
29bc82e1bd
150
core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt
Normal file
150
core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user