From 17307f16f93109d6a0f1d4f578d35a3ca0df34bd Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Fri, 20 Apr 2018 11:33:56 +0300 Subject: [PATCH] Can now conquer cities! =D --- core/src/com/unciv/UnCivGame.kt | 4 +- core/src/com/unciv/logic/GameInfo.kt | 1 + core/src/com/unciv/logic/battle/Battle.kt | 42 ++++- .../com/unciv/logic/battle/CityCombatant.kt | 8 +- .../com/unciv/logic/battle/CombatantType.kt | 1 + .../unciv/logic/battle/MapUnitCombatant.kt | 7 +- .../unciv/logic/city/CityExpansionManager.kt | 32 ++-- core/src/com/unciv/logic/city/CityInfo.kt | 34 ++-- .../logic/civilization/CivilizationInfo.kt | 2 +- core/src/com/unciv/logic/map/TileInfo.kt | 5 + .../unciv/logic/map/UnitMovementAlgorithms.kt | 8 +- .../com/unciv/ui/cityscreen/CityStatsTable.kt | 2 +- core/src/com/unciv/ui/tilegroups/TileGroup.kt | 164 ++++++++++-------- .../com/unciv/ui/worldscreen/BattleTable.kt | 37 ++-- 14 files changed, 220 insertions(+), 127 deletions(-) diff --git a/core/src/com/unciv/UnCivGame.kt b/core/src/com/unciv/UnCivGame.kt index 24505d5d13..5dc13ae0d4 100644 --- a/core/src/com/unciv/UnCivGame.kt +++ b/core/src/com/unciv/UnCivGame.kt @@ -58,9 +58,11 @@ class UnCivGame : Game() { gameInfo.civilizations.add(CivilizationInfo("Greece", Vector2(3f,5f), gameInfo)) // all the rest whatever barbarianCivilization.civName = "Barbarians" + + gameInfo.setTransients() // needs to be before placeBarbarianUnit because it depends on the tilemap having its gameinfo set + (1..5).forEach { gameInfo.placeBarbarianUnit() } - gameInfo.setTransients() worldScreen = WorldScreen() setWorldScreen() diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index e0cb43084c..8ea903d78b 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -137,6 +137,7 @@ class GameInfo { } val damageToAttacker = Battle(this).calculateDamageToAttacker(MapUnitCombatant(unit), MapUnitCombatant(unitToAttack)) + if(damageToAttacker < unit.health) { // don't attack if we'll die from the attack unit.headTowards(unitTileToAttack.position) Battle(this).attack(MapUnitCombatant(unit), MapUnitCombatant(unitToAttack)) diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 1163b3ac69..18cb909063 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -1,6 +1,8 @@ package com.unciv.logic.battle import com.unciv.logic.GameInfo +import com.unciv.logic.city.CityInfo +import com.unciv.logic.map.TileInfo import com.unciv.logic.map.UnitType import java.util.* import kotlin.collections.HashMap @@ -67,13 +69,15 @@ class Battle(val gameInfo:GameInfo) { var damageToDefender = calculateDamageToDefender(attacker,defender) var damageToAttacker = calculateDamageToAttacker(attacker,defender) - if (attacker.getCombatantType() == CombatantType.Ranged) { + if(defender.getCombatantType() == CombatantType.Civilian){ + defender.takeDamage(100) // kill + } + else if (attacker.getCombatantType() == CombatantType.Ranged) { defender.takeDamage(damageToDefender) // straight up } else { //melee attack is complicated, because either side may defeat the other midway //so...for each round, we randomize who gets the attack in. Seems to be a good way to work for now. - while (damageToDefender + damageToAttacker > 0) { if (Random().nextInt(damageToDefender + damageToAttacker) < damageToDefender) { damageToDefender-- @@ -87,7 +91,11 @@ class Battle(val gameInfo:GameInfo) { } } - // After dust as settled + postBattleAction(attacker,defender,attackedTile) + + } + + fun postBattleAction(attacker: ICombatant, defender: ICombatant, attackedTile:TileInfo){ if (defender.getCivilization().isPlayerCivilization()) { val whatHappenedString = @@ -100,10 +108,38 @@ class Battle(val gameInfo:GameInfo) { gameInfo.getPlayerCivilization().addNotification(notificationString, attackedTile.position) } + if(defender.isDefeated() + && defender.getCombatantType() == CombatantType.City + && attacker.getCombatantType() == CombatantType.Melee){ + conquerCity((defender as CityCombatant).city, attacker) + } + if (defender.isDefeated() && attacker.getCombatantType() == CombatantType.Melee) (attacker as MapUnitCombatant).unit.moveToTile(attackedTile) if(attacker is MapUnitCombatant) attacker.unit.currentMovement = 0f } + private fun conquerCity(city: CityInfo, attacker: ICombatant) { + val enemyCiv = city.civInfo + attacker.getCivilization().addNotification("We have conquered the city of ${city.name}!",city.cityLocation) + enemyCiv.cities.remove(city) + attacker.getCivilization().cities.add(city) + city.civInfo = attacker.getCivilization() + city.health = city.getMaxHealth() / 2 // I think that cities recover to half health? + city.getTile().unit = null + city.expansion.cultureStored = 0; + city.expansion.reset() + if(city.cityConstructions.isBuilt("Palace")){ + city.cityConstructions.builtBuildings.remove("Palace") + if(enemyCiv.cities.isEmpty()) { + gameInfo.getPlayerCivilization() + .addNotification("The ${enemyCiv.civName} civilization has been destroyed!", null) + } + else{ + enemyCiv.cities.first().cityConstructions.builtBuildings.add("Palace") // relocate palace + } + } + } + } \ No newline at end of file diff --git a/core/src/com/unciv/logic/battle/CityCombatant.kt b/core/src/com/unciv/logic/battle/CityCombatant.kt index 0efa6e4389..9977a6cdcf 100644 --- a/core/src/com/unciv/logic/battle/CityCombatant.kt +++ b/core/src/com/unciv/logic/battle/CityCombatant.kt @@ -4,7 +4,6 @@ import com.unciv.logic.city.CityInfo import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.TileInfo import com.unciv.models.gamebasics.GameBasics -import kotlin.math.max class CityCombatant(val city: CityInfo) : ICombatant { override fun getHealth(): Int = city.health @@ -14,7 +13,8 @@ class CityCombatant(val city: CityInfo) : ICombatant { override fun isDefeated(): Boolean = city.health==1 override fun takeDamage(damage: Int) { - city.health = max(1, city.health - damage) // min health is 1 + city.health -= damage + if(city.health<1) city.health=1 // min health is 1 } override fun getCombatantType(): CombatantType = CombatantType.City @@ -37,7 +37,9 @@ class CityCombatant(val city: CityInfo) : ICombatant { // 10% bonus foreach pop val strengthWithPop = (baseStrength + strengthFromTechs) * (1 + 0.1*city.population.population) - return strengthWithPop.toInt() + + + return strengthWithPop.toInt() * 100 // *100 because a city is always at 100% strength } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/battle/CombatantType.kt b/core/src/com/unciv/logic/battle/CombatantType.kt index ec0c1feab9..375e7ba4fc 100644 --- a/core/src/com/unciv/logic/battle/CombatantType.kt +++ b/core/src/com/unciv/logic/battle/CombatantType.kt @@ -3,5 +3,6 @@ package com.unciv.logic.battle enum class CombatantType{ Melee, Ranged, + Civilian, City } \ No newline at end of file diff --git a/core/src/com/unciv/logic/battle/MapUnitCombatant.kt b/core/src/com/unciv/logic/battle/MapUnitCombatant.kt index 78a9fcac50..f07ea8f6ac 100644 --- a/core/src/com/unciv/logic/battle/MapUnitCombatant.kt +++ b/core/src/com/unciv/logic/battle/MapUnitCombatant.kt @@ -25,13 +25,16 @@ class MapUnitCombatant(val unit: MapUnit) : ICombatant { return attackerStrength*unit.health } - override fun getDefendingStrength(attacker: ICombatant): Int = - unit.getBaseUnit().strength*unit.health + override fun getDefendingStrength(attacker: ICombatant): Int { + // too: if ranged units get ranged attacked, they use their ranged str to defend! + return unit.getBaseUnit().strength*unit.health + } override fun getCombatantType(): CombatantType { when(unit.getBaseUnit().unitType){ UnitType.Melee -> return CombatantType.Melee UnitType.Ranged -> return CombatantType.Ranged + UnitType.Civilian -> return CombatantType.Civilian else -> throw Exception("Should never get here!") } } diff --git a/core/src/com/unciv/logic/city/CityExpansionManager.kt b/core/src/com/unciv/logic/city/CityExpansionManager.kt index 6cc6244d79..1811578289 100644 --- a/core/src/com/unciv/logic/city/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/CityExpansionManager.kt @@ -1,11 +1,18 @@ package com.unciv.logic.city +import com.badlogic.gdx.math.Vector2 + class CityExpansionManager { @Transient lateinit var cityInfo: CityInfo + var cityTiles = ArrayList() var cultureStored: Int = 0 - private var tilesClaimed: Int = 0 + + fun reset(){ + cityTiles = ArrayList(cityInfo.civInfo.gameInfo.tileMap + .getTilesInDistance(cityInfo.cityLocation, 1).map { it.position }) + } // This one has conflicting sources - // http://civilization.wikia.com/wiki/Mathematics_of_Civilization_V says it's 20+(10(t-1))^1.1 @@ -13,31 +20,30 @@ class CityExpansionManager { // (per game XML files) at 6*(t+0.4813)^1.3 // The second seems to be more based, so I'll go with that //Speciality of Angkor Wat - val cultureToNextTile: Int - get() { - var cultureToNextTile = 6 * Math.pow(tilesClaimed + 1.4813, 1.3) - if (cityInfo.civInfo.buildingUniques.contains("NewTileCostReduction")) cultureToNextTile *= 0.75 - if (cityInfo.civInfo.policies.isAdopted("Tradition")) cultureToNextTile *= 0.75 - return Math.round(cultureToNextTile).toInt() - } + fun getCultureToNextTile(): Int { + val numTilesClaimed = cityTiles.size - 7 + var cultureToNextTile = 6 * Math.pow(numTilesClaimed + 1.4813, 1.3) + if (cityInfo.civInfo.buildingUniques.contains("NewTileCostReduction")) cultureToNextTile *= 0.75 + if (cityInfo.civInfo.policies.isAdopted("Tradition")) cultureToNextTile *= 0.75 + return Math.round(cultureToNextTile).toInt() + } private fun addNewTileWithCulture() { - cultureStored -= cultureToNextTile + cultureStored -= getCultureToNextTile() for (i in 2..3) { val tiles = cityInfo.civInfo.gameInfo.tileMap.getTilesInDistance(cityInfo.cityLocation, i).filter { it.owner == null } if (tiles.isEmpty()) continue val chosenTile = tiles.maxBy { cityInfo.rankTile(it) } - chosenTile!!.owner = cityInfo.civInfo.civName - tilesClaimed++ + cityTiles.add(chosenTile!!.position) + chosenTile.owner = cityInfo.civInfo.civName return } } fun nextTurn(culture: Float) { - cultureStored += culture.toInt() - if (cultureStored >= cultureToNextTile) { + if (cultureStored >= getCultureToNextTile()) { addNewTileWithCulture() cityInfo.civInfo.addNotification(cityInfo.name + " has expanded its borders!", cityInfo.cityLocation) } diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index c93935649e..c179875bf0 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -23,6 +23,8 @@ class CityInfo { var expansion = CityExpansionManager() var cityStats = CityStats() + + internal val tileMap: TileMap get() = civInfo.gameInfo.tileMap @@ -51,23 +53,22 @@ class CityInfo { val buildingUniques: List get() = cityConstructions.getBuiltBuildings().filter { it.unique!=null }.map { it.unique } - val greatPersonPoints: Stats - get() { - var greatPersonPoints = population.getSpecialists().times(3f) + fun getGreatPersonPoints(): Stats { + var greatPersonPoints = population.getSpecialists().times(3f) - for (building in cityConstructions.getBuiltBuildings()) - if (building.greatPersonPoints != null) - greatPersonPoints.add(building.greatPersonPoints!!) + for (building in cityConstructions.getBuiltBuildings()) + if (building.greatPersonPoints != null) + greatPersonPoints.add(building.greatPersonPoints!!) - if (civInfo.buildingUniques.contains("GreatPersonGenerationIncrease")) - greatPersonPoints = greatPersonPoints.times(1.33f) - if (civInfo.policies.isAdopted("Entrepreneurship")) - greatPersonPoints.gold *= 1.25f - if (civInfo.policies.isAdopted("Freedom")) - greatPersonPoints = greatPersonPoints.times(1.25f) + if (civInfo.buildingUniques.contains("GreatPersonGenerationIncrease")) + greatPersonPoints = greatPersonPoints.times(1.33f) + if (civInfo.policies.isAdopted("Entrepreneurship")) + greatPersonPoints.gold *= 1.25f + if (civInfo.policies.isAdopted("Freedom")) + greatPersonPoints = greatPersonPoints.times(1.25f) - return greatPersonPoints - } + return greatPersonPoints + } constructor() // for json parsing, we need to have a default constructor @@ -88,9 +89,8 @@ class CityInfo { cityConstructions.currentConstruction = "Worker" // Default for first city only! } - for (tileInfo in civInfo.gameInfo.tileMap.getTilesInDistance(cityLocation, 1)) { - tileInfo.owner = civInfo.civName - } + expansion.reset() + val tile = getTile() tile.workingCity = this.name diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index b1979fc612..d07334b968 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -141,7 +141,7 @@ class CivilizationInfo { for (city in cities) { city.nextTurn() - greatPeople.addGreatPersonPoints(city.greatPersonPoints) + greatPeople.addGreatPersonPoints(city.getGreatPersonPoints()) } val greatPerson = greatPeople.getNewGreatPerson() diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index fe4a65f856..de1460726f 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -21,6 +21,11 @@ class TileInfo { var improvement: String? = null var improvementInProgress: String? = null var owner: String? = null // owning civ name + get() { + val containingCity = tileMap.gameInfo.civilizations.flatMap { it.cities }.firstOrNull{it.expansion.cityTiles.contains(position)} + if(containingCity==null) return null + return containingCity.civInfo.civName + } var workingCity: String? = null // Working City name var roadStatus = RoadStatus.None var explored = false diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 6c72307dfa..f9152bddf9 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -17,7 +17,8 @@ class UnitMovementAlgorithms(val tileMap: TileMap){ for (tileToCheck in tilesToCheck) for (maybeUpdatedTile in tileToCheck.neighbors) { if(maybeUpdatedTile.owner != null && maybeUpdatedTile.owner != civInfo.civName && maybeUpdatedTile.isCityCenter) - continue + continue // Enemy city, can't move through it! + // if(maybeUpdatedTile.unit!=null && maybeUpdatedTile.unit!!.civInfo != civInfo) continue // Enemy unit! var distanceBetweenTiles = maybeUpdatedTile.lastTerrain.movementCost.toFloat() // no road if (tileToCheck.roadStatus !== RoadStatus.None && maybeUpdatedTile.roadStatus !== RoadStatus.None) //Road @@ -39,6 +40,11 @@ class UnitMovementAlgorithms(val tileMap: TileMap){ tilesToCheck = updatedTiles } + + // now that we've used the tiles that have friendly units to "pass through" them, + // we have to remove them from the final result since we can't actually get there. +// distanceToTiles.filterKeys { it.unit!=null }.forEach { distanceToTiles.remove(it.key) } + return distanceToTiles } diff --git a/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt b/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt index 873f6feb50..ef62dceb86 100644 --- a/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt @@ -34,7 +34,7 @@ class CityStatsTable(val cityScreen: CityScreen) : Table(){ cityStatsValues["Gold"] = Math.round(stats.gold).toString() + "" cityStatsValues["Science"] = Math.round(stats.science).toString() + "" cityStatsValues["Culture"] = (Math.round(stats.culture).toString() - + " (" + city.expansion.cultureStored + "/" + city.expansion.cultureToNextTile + ")") + + " (" + city.expansion.cultureStored + "/" + city.expansion.getCultureToNextTile() + ")") cityStatsValues["Population"] = city.population.getFreePopulation().toString() + "/" + city.population.population for (key in cityStatsValues.keys) { diff --git a/core/src/com/unciv/ui/tilegroups/TileGroup.kt b/core/src/com/unciv/ui/tilegroups/TileGroup.kt index bb340e85cd..aa1a41a353 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroup.kt @@ -57,50 +57,10 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { return } - if(terrainFeatureImage==null && tileInfo.terrainFeature!=null){ - terrainFeatureImage = ImageGetter.getImage("TerrainIcons/${tileInfo.terrainFeature}.png") - addActor(terrainFeatureImage) - terrainFeatureImage!!.run { - setSize(30f,30f) - setColor(1f,1f,1f,0.5f) - setPosition(this@TileGroup.width /2-width/2, - this@TileGroup.height/2-height/2) - } - } - - if(terrainFeatureImage!=null && tileInfo.terrainFeature==null){ - terrainFeatureImage!!.remove() - terrainFeatureImage=null - } - - - val RGB= tileInfo.getBaseTerrain().RGB!! - hexagon.color = Color(RGB[0]/255f,RGB[1]/255f,RGB[2]/255f,1f) - if(!isViewable) hexagon.color = hexagon.color.lerp(Color.BLACK,0.6f) - - - if (tileInfo.hasViewableResource(tileInfo.tileMap.gameInfo.getPlayerCivilization()) && resourceImage == null) { // Need to add the resource image! - val fileName = "ResourceIcons/" + tileInfo.resource + "_(Civ5).png" - resourceImage = ImageGetter.getImage(fileName) - resourceImage!!.setSize(20f, 20f) - resourceImage!!.setPosition(width/2 - resourceImage!!.width/2-20f, - height/2 - resourceImage!!.height/2) // left - addActor(resourceImage!!) - } - - - - if (tileInfo.improvement != null && tileInfo.improvement != improvementType) { - improvementImage = ImageGetter.getImage("ImprovementIcons/" + tileInfo.improvement!!.replace(' ', '_') + "_(Civ5).png") - addActor(improvementImage) - improvementImage!!.run { - setSize(20f, 20f) - - setPosition(this@TileGroup.width/2 - width/2+20f, - this@TileGroup.height/2 - height/2) // right - } - improvementType = tileInfo.improvement - } + updateTerrainFeatureImage() + updateTileColor(isViewable) + updateResourceImage() + updateImprovementImage() if (populationImage != null) { if (tileInfo.workingCity != null) @@ -109,6 +69,43 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { populationImage!!.color = Color.GRAY } + updateRoadImages() + updateBorderImages() + } + + private fun updateBorderImages() { + for (border in borderImages) border.remove() //clear + borderImages = arrayListOf() + + if (tileInfo.owner != null) { + for (neighbor in tileInfo.neighbors.filter { it.owner != tileInfo.owner }) { + val image = ImageGetter.getImage(ImageGetter.WhiteDot) + + val relativeHexPosition = tileInfo.position.cpy().sub(neighbor.position) + val relativeWorldPosition = HexMath.Hex2WorldCoords(relativeHexPosition) + + // This is some crazy voodoo magic so I'll explain. + + image.setSize(35f, 2f) + image.moveBy(width / 2 - image.width / 2, // center + height / 2 - image.height / 2) + // in addTiles, we set the position of groups by relative world position *0.8*groupSize, filter groupSize = 50 + // Here, we want to have the borders start HALFWAY THERE and extend towards the tiles, so we give them a position of 0.8*25. + // BUT, we don't actually want it all the way out there, because we want to display the borders of 2 different civs! + // So we set it to 0.75 + image.moveBy(-relativeWorldPosition.x * 0.75f * 25f, -relativeWorldPosition.y * 0.75f * 25f) + + image.color = tileInfo.getOwner()!!.getCivilization().getColor() + image.setOrigin(image.width / 2, image.height / 2) // This is so that the rotation is calculated from the middle of the road and not the edge + image.rotation = (90 + 180 / Math.PI * Math.atan2(relativeWorldPosition.y.toDouble(), relativeWorldPosition.x.toDouble())).toFloat() + addActor(image) + borderImages.add(image) + } + + } + } + + private fun updateRoadImages() { if (tileInfo.roadStatus !== RoadStatus.None) { for (neighbor in tileInfo.neighbors) { if (neighbor.roadStatus === RoadStatus.None) continue @@ -137,37 +134,54 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { roadImages[neighbor.position.toString()]!!.color = Color.BROWN // road } } - - // Borders - if(tileInfo.owner!=null){ - for (border in borderImages) border.remove() - for (neighbor in tileInfo.neighbors.filter { it.owner!=tileInfo.owner }){ - val image = ImageGetter.getImage(ImageGetter.WhiteDot) - - val relativeHexPosition = tileInfo.position.cpy().sub(neighbor.position) - val relativeWorldPosition = HexMath.Hex2WorldCoords(relativeHexPosition) - - // This is some crazy voodoo magic so I'll explain. - - image.setSize(35f, 2f) - image.moveBy(width/2-image.width/2, // center - height/2-image.height/2) - // in addTiles, we set the position of groups by relative world position *0.8*groupSize, filter groupSize = 50 - // Here, we want to have the borders start HALFWAY THERE and extend towards the tiles, so we give them a position of 0.8*25. - // BUT, we don't actually want it all the way out there, because we want to display the borders of 2 different civs! - // So we set it to 0.75 - image.moveBy(-relativeWorldPosition.x * 0.75f * 25f, -relativeWorldPosition.y * 0.75f * 25f) - - image.color = tileInfo.getOwner()!!.getCivilization().getColor() - image.setOrigin(image.width/2, image.height/2) // This is so that the rotation is calculated from the middle of the road and not the edge - image.rotation = (90 + 180 / Math.PI * Math.atan2(relativeWorldPosition.y.toDouble(), relativeWorldPosition.x.toDouble())).toFloat() - addActor(image) - borderImages.add(image) - } - - } - } -} + private fun updateTileColor(isViewable: Boolean) { + val RGB = tileInfo.getBaseTerrain().RGB!! + hexagon.color = Color(RGB[0] / 255f, RGB[1] / 255f, RGB[2] / 255f, 1f) + if (!isViewable) hexagon.color = hexagon.color.lerp(Color.BLACK, 0.6f) + } + private fun updateTerrainFeatureImage() { + if (terrainFeatureImage == null && tileInfo.terrainFeature != null) { + terrainFeatureImage = ImageGetter.getImage("TerrainIcons/${tileInfo.terrainFeature}.png") + addActor(terrainFeatureImage) + terrainFeatureImage!!.run { + setSize(30f, 30f) + setColor(1f, 1f, 1f, 0.5f) + setPosition(this@TileGroup.width / 2 - width / 2, + this@TileGroup.height / 2 - height / 2) + } + } + + if (terrainFeatureImage != null && tileInfo.terrainFeature == null) { + terrainFeatureImage!!.remove() + terrainFeatureImage = null + } + } + + private fun updateImprovementImage() { + if (tileInfo.improvement != null && tileInfo.improvement != improvementType) { + improvementImage = ImageGetter.getImage("ImprovementIcons/" + tileInfo.improvement!!.replace(' ', '_') + "_(Civ5).png") + addActor(improvementImage) + improvementImage!!.run { + setSize(20f, 20f) + + setPosition(this@TileGroup.width / 2 - width / 2 + 20f, + this@TileGroup.height / 2 - height / 2) // right + } + improvementType = tileInfo.improvement + } + } + + private fun updateResourceImage() { + if (tileInfo.hasViewableResource(tileInfo.tileMap.gameInfo.getPlayerCivilization()) && resourceImage == null) { // Need to add the resource image! + val fileName = "ResourceIcons/" + tileInfo.resource + "_(Civ5).png" + resourceImage = ImageGetter.getImage(fileName) + resourceImage!!.setSize(20f, 20f) + resourceImage!!.setPosition(width / 2 - resourceImage!!.width / 2 - 20f, + height / 2 - resourceImage!!.height / 2) // left + addActor(resourceImage!!) + } + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/BattleTable.kt index f3f8dceb61..c8c3443bb5 100644 --- a/core/src/com/unciv/ui/worldscreen/BattleTable.kt +++ b/core/src/com/unciv/ui/worldscreen/BattleTable.kt @@ -108,21 +108,38 @@ class BattleTable(val worldScreen: WorldScreen): Table() { row().pad(5f) val attackButton = TextButton("Attack", skin) - attackButton.addClickListener { - if(attacker.getCombatantType() == CombatantType.Melee) - attacker.unit.headTowards(defender.getTile().position) - battle.attack(attacker,defender) - worldScreen.update() + val attackerDistanceToTiles = attacker.unit.getDistanceToTiles() + + if(attacker.getCombatantType() == CombatantType.Melee){ + val tilesCanAttackFrom = attackerDistanceToTiles.filter { + attacker.unit.currentMovement - it.value > 0 // once we reach it we'll still have energy to attack + && it.key.unit==null + && it.key.neighbors.contains(defender.getTile()) } + + if(tilesCanAttackFrom.isEmpty()) attackButton.disable() + else { + val tileToMoveTo = tilesCanAttackFrom.minBy { it.value }!!.key // travel least distance + attackButton.addClickListener { + attacker.unit.moveToTile(tileToMoveTo) + battle.attack(attacker,defender) + worldScreen.update() + } + } } - val attackerCanReachDefender:Boolean - if (attacker.getCombatantType() == CombatantType.Ranged) { + else { // ranged val tilesInRange = UnCivGame.Current.gameInfo.tileMap.getTilesInDistance(attacker.getTile().position, 2) - attackerCanReachDefender = tilesInRange.contains(defender.getTile()) + val attackerCanReachDefender = tilesInRange.contains(defender.getTile()) + if(!attackerCanReachDefender) attackButton.disable() + else { + attackButton.addClickListener { + battle.attack(attacker, defender) + worldScreen.update() + } + } } - else attackerCanReachDefender = attacker.unit.getDistanceToTiles().containsKey(defender.getTile()) - if(attacker.unit.currentMovement==0f || !attackerCanReachDefender) attackButton.disable() + if(attacker.unit.currentMovement==0f) attackButton.disable() add(attackButton).colspan(2) pack()