From 3b8362738628a120fb3e1c076e6aed1116a511a1 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Tue, 7 Aug 2018 07:39:44 +0300 Subject: [PATCH 1/6] Left side of diplomacy screen now scrolls --- android/assets/jsons/Translations.json | 39 ++++++++++++------- android/build.gradle | 4 +- .../com/unciv/logic/city/CityConstructions.kt | 2 +- .../src/com/unciv/ui/trade/DiplomacyScreen.kt | 8 +--- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/android/assets/jsons/Translations.json b/android/assets/jsons/Translations.json index 177aa94bb5..37524692aa 100644 --- a/android/assets/jsons/Translations.json +++ b/android/assets/jsons/Translations.json @@ -822,6 +822,8 @@ French:"Un [greatPerson] est né!" Romanian:"Sa născut un [greatPerson]!" } + + "We have encountered [civName]!":{} // Save and load game @@ -2387,11 +2389,12 @@ } // EG Rationalism policy branch unlocked! // Trade - "Trade with [otherCiv]":{ - Italian:"Scambia con [otherCiv]" - Russian:"Торговля с [otherCiv]" - French:"Échanger avec [otherCiv]" - Romanian:"Comerțul cu [otherCiv]" + + "Trade":{ + Italian:"Scambia" + Russian:"Торговля" + French:"Échanger" + Romanian:"Comerțul" } "Offer trade":{ Italian:"Offerta commerciale" @@ -2412,16 +2415,16 @@ Romanian:"Articolele noastre" } "Our trade offer":{ - Italian:"La nostra offerta commerciale" - Russian:"Наше торговое предложение" - French:"Notre offre commerciale" - Romanian:"Oferta noastră comercială" + Italian:"La nostra \n offerta commerciale" + Russian:"Наше торговое\n предложение" + French:"Notre offre \n commerciale" + Romanian:"Oferta noastră \n comercială" } "[otherCiv]'s trade offer":{ - Italian:"Offerta commerciale di [otherCiv]" - Russian:"Коммерческое предложение [otherCiv]" - French:"L'offre commerciale de [otherCiv]" - Romanian:"ofertă de schimb a [otherCiv]" + Italian:"Offerta commerciale \n di [otherCiv]" + Russian:"Коммерческое \n предложение [otherCiv]" + French:"L'offre commerciale \n de [otherCiv]" + Romanian:"ofertă de \n schimb a [otherCiv]" } "[otherCiv]'s items":{ Italian:"Gli articoli di [otherCiv]" @@ -2453,6 +2456,11 @@ French:"Accept" Romanian:"Accepta" } + "There's nothing on the table":{} + + + "Peace treaty":{} + "Gold per turn":{} //civilisations @@ -2597,6 +2605,11 @@ Romanian:"Diplomaţie" } + "War":{} + "Peace":{} + + "Declare war":{} + // Overview screen "Overview":{ Italian:"Panoramica" diff --git a/android/build.gradle b/android/build.gradle index 2ef77471e1..4d570f7454 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.game" minSdkVersion 14 targetSdkVersion 26 - versionCode 115 - versionName "2.7.3.2" + versionCode 116 + versionName "2.7.4" } buildTypes { release { diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 436c8e6300..19f2d39333 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -50,7 +50,7 @@ class CityConstructions { } fun getProductionForTileInfo(): String { - var result = currentConstruction + var result = currentConstruction.tr() if (SpecialConstruction.getSpecialConstructions().none { it.name==result }) result += "\r\n{in} ".tr() + turnsToConstruction(currentConstruction) + " {turns}".tr() return result diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index ac86496632..8a59c43883 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -1,10 +1,7 @@ package com.unciv.ui.trade import com.badlogic.gdx.graphics.Color -import com.badlogic.gdx.scenes.scene2d.ui.Label -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.badlogic.gdx.scenes.scene2d.ui.* import com.unciv.UnCivGame import com.unciv.ui.utils.* @@ -14,7 +11,7 @@ class DiplomacyScreen():CameraStageBaseScreen(){ val rightSideTable = Table() init{ - val splitPane = SplitPane(leftSideTable,rightSideTable,false, skin) + val splitPane = SplitPane(ScrollPane(leftSideTable),rightSideTable,false, skin) splitPane.setSplitAmount(0.2f) updateLeftSideTable() @@ -64,7 +61,6 @@ class DiplomacyScreen():CameraStageBaseScreen(){ } civTable.add(declareWarButton).row() } - leftSideTable.add(civTable).row() } } From c35f7d68f2b418fbeff5a6ce69e6ca2d5095491f Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Tue, 7 Aug 2018 09:04:35 +0300 Subject: [PATCH 2/6] No longer get notifications for units of other friendly civs around your territory --- core/src/com/unciv/logic/GameInfo.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 68675d7089..3163ade2d8 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -51,7 +51,9 @@ class GameInfo { player.startTurn() - val enemyUnitsCloseToTerritory = player.getViewableTiles().filter { it.militaryUnit!=null && it.militaryUnit!!.civInfo!=player + val enemyUnitsCloseToTerritory = player.getViewableTiles() + .filter { it.militaryUnit!=null && it.militaryUnit!!.civInfo!=player + && player.isAtWarWith(it.militaryUnit!!.civInfo) && (it.getOwner()==player || it.neighbors.any { neighbor -> neighbor.getOwner()==player }) } for(enemyUnitTile in enemyUnitsCloseToTerritory) { val inOrNear = if(enemyUnitTile.getOwner()==player) "in" else "near" From 9d0035a8a6eb3c7acc5c389a1f216ac991d850eb Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 8 Aug 2018 08:23:20 +0300 Subject: [PATCH 3/6] AI now considers war on multiple civs, starting with the closest --- android/assets/jsons/Translations.json | 2 ++ core/src/com/unciv/logic/automation/Automation.kt | 1 + 2 files changed, 3 insertions(+) diff --git a/android/assets/jsons/Translations.json b/android/assets/jsons/Translations.json index 37524692aa..7a0dfe7eb0 100644 --- a/android/assets/jsons/Translations.json +++ b/android/assets/jsons/Translations.json @@ -824,6 +824,8 @@ } "We have encountered [civName]!":{} + + "Cannot provide upkeep for [unitName] - unit has been disbanded!":{} // Save and load game diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index b8461533df..a2a41a4d33 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -81,6 +81,7 @@ class Automation { val enemyCivsByDistanceToOurs = civInfo.diplomacy.values.map { it.otherCiv() } .filterNot { it == civInfo || it.cities.isEmpty() || !civInfo.diplomacy[it.civName]!!.canDeclareWar() } .groupBy { getMinDistanceBetweenCities(civInfo,it) } + .toSortedMap() val ourCombatStrength = evaluteCombatStrength(civInfo) for (group in enemyCivsByDistanceToOurs){ if(group.key>7) break From 0ac87fa8c47bf14b42574d64e0c29d8c3e2a653b Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 8 Aug 2018 08:25:19 +0300 Subject: [PATCH 4/6] AI Performance improvement (added transient currentTile to unit) --- core/src/com/unciv/logic/map/MapUnit.kt | 7 ++++--- core/src/com/unciv/logic/map/TileMap.kt | 2 ++ .../com/unciv/ui/worldscreen/unit/UnitTable.kt | 18 ++++++------------ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index b7166da7f6..f33d4e388d 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -27,9 +27,9 @@ class MapUnit { fun getBaseUnit(): BaseUnit = GameBasics.Units[name]!! fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement - fun getTile(): TileInfo { - return civInfo.gameInfo.tileMap.values.first{it.militaryUnit==this || it.civilianUnit==this} - } + + @Transient private lateinit var currentTile :TileInfo + fun getTile(): TileInfo = currentTile fun getDistanceToTiles(): HashMap { val tile = getTile() @@ -187,6 +187,7 @@ class MapUnit { if(getBaseUnit().unitType== UnitType.Civilian) tile.civilianUnit=this else tile.militaryUnit=this + currentTile = tile } /** diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 7ef0a52fe8..4edfb5b799 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -65,6 +65,8 @@ class TileMap { fun setTransients() { for (tileInfo in values){ tileInfo.tileMap = this + if(tileInfo.militaryUnit!=null) tileInfo.militaryUnit!!.currentTile = tileInfo + if(tileInfo.civilianUnit!=null) tileInfo.civilianUnit!!.currentTile = tileInfo } } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 9c88a29bb9..90dbf7c361 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -34,21 +34,15 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ } fun update() { - if(selectedUnit!=null) - { - if(selectedUnit!!.civInfo != worldScreen.civInfo) { // The unit that was selected, was captured. It exists but is no longer ours. + if(selectedUnit!=null) { + if (selectedUnit!!.civInfo != worldScreen.civInfo) { // The unit that was selected, was captured. It exists but is no longer ours. + selectedUnit = null + currentlyExecutingAction = null + selectedUnitHasChanged = true + } else if (selectedUnit!! !in selectedUnit!!.getTile().getUnits()) { // The unit that was there no longer exists} selectedUnit = null currentlyExecutingAction = null selectedUnitHasChanged = true - } - else { - try { - selectedUnit!!.getTile() - } catch (ex: Exception) { // The unit that was there no longer exists} - selectedUnit = null - currentlyExecutingAction = null - selectedUnitHasChanged=true - } } } From 376ae64e771b25169a85a502e372af2247c2d261 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 8 Aug 2018 08:26:23 +0300 Subject: [PATCH 5/6] AI Ranged units no longer head towards defeated cities --- .../unciv/logic/automation/UnitAutomation.kt | 152 ++++++++++-------- .../com/unciv/logic/city/CityConstructions.kt | 2 +- 2 files changed, 88 insertions(+), 66 deletions(-) diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index a4871e429e..d4508ddccd 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -12,13 +12,64 @@ import com.unciv.logic.map.MapUnit import com.unciv.logic.map.TileInfo import com.unciv.models.gamebasics.GameBasics import com.unciv.ui.utils.getRandom +import com.unciv.ui.worldscreen.unit.UnitAction import com.unciv.ui.worldscreen.unit.UnitActions class UnitAutomation{ + fun automateUnitMoves(unit: MapUnit) { + + if (unit.name == "Settler") { + automateSettlerActions(unit) + return + } + + if (unit.name == "Worker") { + WorkerAutomation(unit).automateWorkerAction() + return + } + + if(unit.name.startsWith("Great")) return // I don't know what to do with you yet. + + val unitActions = UnitActions().getUnitActions(unit,UnCivGame.Current.worldScreen) + + if (tryUpgradeUnit(unit, unitActions)) return + + // Accompany settlers + if (tryAccompanySettler(unit)) return + + if (unit.health < 50) { + healUnit(unit) + return + } // do nothing but heal + + // if there is an attackable unit in the vicinity, attack! + if (tryAttackNearbyEnemy(unit)) return + + if (tryGarrisoningUnit(unit)) return + + if (unit.health < 80) { + healUnit(unit) + return + } // do nothing but heal until 80 health + + // find the closest enemy unit that we know of within 5 spaces and advance towards it + if (tryAdvanceTowardsCloseEnemy(unit)) return + + if (unit.health < 100) { + healUnit(unit) + return + } + + // Focus all units without a specific target on the enemy city closest to one of our cities + if (tryHeadTowardsEnemyCity(unit)) return + + // else, go to a random space + randomWalk(unit) + // if both failed, then... there aren't any reachable tiles. Which is possible. + } + fun healUnit(unit:MapUnit) { - // If we're low on health then heal - // todo: go to a more defensible place if there is one val tilesInDistance = unit.getDistanceToTiles().keys val unitTile = unit.getTile() @@ -70,78 +121,53 @@ class UnitAutomation{ return attackableTiles } - fun automateUnitMoves(unit: MapUnit) { + private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean { + var closeEnemies = unit.getTile().getTilesInDistance(5) + .filter{ containsAttackableEnemy(it, unit.civInfo) && unit.movementAlgs().canReach(it)} + if(unit.getBaseUnit().unitType.isRanged()) + closeEnemies = closeEnemies.filterNot { it.isCityCenter() && it.getCity()!!.health==1 } - if (unit.name == "Settler") { - automateSettlerActions(unit) - return + val closestEnemy = closeEnemies.minBy { it.arialDistanceTo(unit.getTile()) } + + if (closestEnemy != null) { + unit.movementAlgs().headTowards(closestEnemy) + return true } + return false + } - if (unit.name == "Worker") { - WorkerAutomation(unit).automateWorkerAction() - return + private fun tryAccompanySettler(unit: MapUnit): Boolean { + val closeTileWithSettler = unit.getDistanceToTiles().keys.firstOrNull { + it.civilianUnit != null && it.civilianUnit!!.name == "Settler" } + if (closeTileWithSettler != null && unit.canMoveTo(closeTileWithSettler)) { + unit.movementAlgs().headTowards(closeTileWithSettler) + return true + } + return false + } - if(unit.name.startsWith("Great")) return // I don't know what to do with you yet. - - val unitActions = UnitActions().getUnitActions(unit,UnCivGame.Current.worldScreen) - - if(unit.getBaseUnit().upgradesTo!=null) { + private fun tryUpgradeUnit(unit: MapUnit, unitActions: List): Boolean { + if (unit.getBaseUnit().upgradesTo != null) { val upgradedUnit = GameBasics.Units[unit.getBaseUnit().upgradesTo!!]!! if (upgradedUnit.isBuildable(unit.civInfo)) { val goldCostOfUpgrade = (upgradedUnit.cost - unit.getBaseUnit().cost) * 2 + 10 val upgradeAction = unitActions.firstOrNull { it.name.startsWith("Upgrade to") } if (upgradeAction != null && unit.civInfo.gold > goldCostOfUpgrade) { upgradeAction.action() - return + return true } } } + return false + } - // Accompany settlers - val closeTileWithSettler = unit.getDistanceToTiles().keys.firstOrNull{ - it.civilianUnit!=null && it.civilianUnit!!.name=="Settler"} - if(closeTileWithSettler != null && unit.canMoveTo(closeTileWithSettler)){ - unit.movementAlgs().headTowards(closeTileWithSettler) - return - } - - if (unit.health < 50) { - healUnit(unit) - return - } // do nothing but heal - - // if there is an attackable unit in the vicinity, attack! - if (tryAttackNearbyEnemy(unit)) return - - if (tryGarrisoningUnit(unit)) return - - - - if (unit.health < 80) { - healUnit(unit) - return - } // do nothing but heal until 80 health - - - // find the closest enemy unit that we know of within 5 spaces and advance towards it - val closestEnemy = unit.getTile().getTilesInDistance(5) - .firstOrNull { containsAttackableEnemy(it,unit.civInfo) && unit.movementAlgs().canReach(it) } - - if (closestEnemy != null) { - unit.movementAlgs().headTowards(closestEnemy) - return - } - - if (unit.health < 100) { - healUnit(unit) - return - } - - // Focus all units without a specific target on the enemy city closest to one of our cities - val enemyCities = unit.civInfo.exploredTiles.map { unit.civInfo.gameInfo.tileMap[it] } - .filter { it.isCityCenter() && it.getOwner()!=unit.civInfo } - if(enemyCities.isNotEmpty() && unit.civInfo.cities.isNotEmpty()) { + private fun tryHeadTowardsEnemyCity(unit: MapUnit): Boolean { + var enemyCities = unit.civInfo.exploredTiles.map { unit.civInfo.gameInfo.tileMap[it] } + .filter { it.isCityCenter() && it.getOwner() != unit.civInfo } + if(unit.getBaseUnit().unitType.isRanged()) + enemyCities = enemyCities.filterNot { it.getCity()!!.health==1 } + if (enemyCities.isNotEmpty() && unit.civInfo.cities.isNotEmpty()) { val closestReachableEnemyCity = enemyCities .filter { unit.movementAlgs().canReach(it) } .minBy { city -> @@ -149,13 +175,10 @@ class UnitAutomation{ } if (closestReachableEnemyCity != null) { unit.movementAlgs().headTowards(closestReachableEnemyCity) - return + return true } } - - // else, go to a random space - randomWalk(unit) - // if both failed, then... there aren't any reachable tiles. Which is possible. + return false } private fun tryAttackNearbyEnemy(unit: MapUnit): Boolean { @@ -177,7 +200,6 @@ class UnitAutomation{ if (unit.getBaseUnit().unitType.isMelee() && capturableCity!=null) enemyTileToAttack = capturableCity // enter it quickly, top priority! - else if (nonCityTilesToAttack.isNotEmpty()) // second priority, units enemyTileToAttack = nonCityTilesToAttack.minBy { Battle().getMapCombatantOfTile(it.tileToAttack)!!.getHealth() } else if (cityWithHealthLeft!=null) enemyTileToAttack = cityWithHealthLeft// third priority, city diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 19f2d39333..c63c76384c 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -43,7 +43,7 @@ class CityConstructions { } fun getCityProductionTextForCityButton(): String { - var result = currentConstruction + var result = currentConstruction.tr() if (SpecialConstruction.getSpecialConstructions().none { it.name==result }) result += "\r\n" + turnsToConstruction(currentConstruction) + " {turns}".tr() return result From 9b38d0c33e497d9ce4afe328bd8a47bc78b05bc7 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 8 Aug 2018 18:34:23 +0300 Subject: [PATCH 6/6] Cities conquered in battle lose population --- core/src/com/unciv/logic/automation/UnitAutomation.kt | 1 + core/src/com/unciv/logic/battle/Battle.kt | 2 ++ core/src/com/unciv/logic/map/MapUnit.kt | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index d4508ddccd..eed898d6e7 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -218,6 +218,7 @@ class UnitAutomation{ } private fun tryGarrisoningUnit(unit: MapUnit): Boolean { + if(unit.getBaseUnit().unitType.isMelee()) return false // don't garrison melee units, they're not that good at it val reachableCitiesWithoutUnits = unit.civInfo.cities.filter { val centerTile = it.getCenterTile() unit.canMoveTo(centerTile) diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 86efd6a81a..485d1c1eb2 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -106,6 +106,8 @@ class Battle(val gameInfo:GameInfo=UnCivGame.Current.gameInfo) { private fun conquerCity(city: CityInfo, attacker: ICombatant) { val enemyCiv = city.civInfo attacker.getCivilization().addNotification("We have conquered the city of [${city.name}]!",city.location, Color.RED) + val currentPopulation = city.population.population + if(currentPopulation>1) city.population.population -= 1 + currentPopulation/4 // so from 2-4 population, remove 1, from 5-8, remove 2, etc. city.moveToCiv(attacker.getCivilization()) city.health = city.getMaxHealth() / 2 // I think that cities recover to half health when conquered? city.getCenterTile().apply { diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index f33d4e388d..2c843d09b2 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -28,7 +28,8 @@ class MapUnit { fun getBaseUnit(): BaseUnit = GameBasics.Units[name]!! fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement - @Transient private lateinit var currentTile :TileInfo + @Transient + internal lateinit var currentTile :TileInfo fun getTile(): TileInfo = currentTile fun getDistanceToTiles(): HashMap {