From 5d00adb7fe8b2c93ff4ec6e5566fe95d8dcc1a84 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Mon, 2 Apr 2018 15:16:28 +0300 Subject: [PATCH] Added GetTile function to MapUnit, which simplifies and normalizes a lot of the unit logic Battle simulation and actual battle work as expected! --- android/assets/jsons/Units.json | 17 ++- core/src/com/unciv/logic/Battle.kt | 7 +- core/src/com/unciv/logic/GameInfo.kt | 2 +- core/src/com/unciv/logic/map/MapUnit.kt | 97 ++++++++++---- core/src/com/unciv/logic/map/TileInfo.kt | 13 +- core/src/com/unciv/logic/map/TileMap.kt | 2 +- core/src/com/unciv/models/gamebasics/Unit.kt | 29 +++-- core/src/com/unciv/ui/UnCivGame.kt | 10 +- .../com/unciv/ui/tilegroups/WorldTileGroup.kt | 11 +- .../com/unciv/ui/worldscreen/BattleTable.kt | 123 ++++++++++++++++++ .../com/unciv/ui/worldscreen/TileInfoTable.kt | 7 - .../com/unciv/ui/worldscreen/TileMapHolder.kt | 2 +- .../com/unciv/ui/worldscreen/WorldScreen.kt | 40 +----- .../ui/worldscreen/WorldScreenOptionsTable.kt | 2 +- .../unciv/ui/worldscreen/unit/UnitActions.kt | 24 ++-- .../unciv/ui/worldscreen/unit/UnitTable.kt | 39 +++--- 16 files changed, 291 insertions(+), 134 deletions(-) create mode 100644 core/src/com/unciv/ui/worldscreen/BattleTable.kt diff --git a/android/assets/jsons/Units.json b/android/assets/jsons/Units.json index 14e746a64d..d48e70c3f9 100644 --- a/android/assets/jsons/Units.json +++ b/android/assets/jsons/Units.json @@ -3,13 +3,15 @@ name:"Worker", description: "Can build improvements on tiles", movement:2, + unitType:"Civilian", hurryCostModifier:20, - cost:60 + cost:70 }, { name:"Settler", description: "Founds a new city", movement:2, + unitType:"Civilian", cost:106, hurryCostModifier:20 }, @@ -17,30 +19,43 @@ name:"Scout", description: "Has no abilites, can only explore", unbuildable:true, + unitType:"Melee", movement:2 }, + { + name:"Warrior", + description: "A basic fighting unit", + unitType:"Melee", + movement:2, + cost: 40, + hurryCostModifier:20 + }, { name:"Great Artist", description: "Can start an 8-turn golden age or construcct a landmark (+5 culture)", unbuildable:true, + unitType:"Civilian", movement:2 }, { name:"Great Scientist", description: "Can discover a technology, or construct an academy (+4 science(", unbuildable:true, + unitType:"Civilian", movement:2 }, { name:"Great Merchant", description: "Can undertake a trade mission, giving a large sum of gold, or construct a ?", unbuildable:true, + unitType:"Civilian", movement:2 }, { name:"Great Engineer", description: "Can speed up construction of a wonder, or construct a refinery, giving +? production?", unbuildable:true, + unitType:"Civilian", movement:2 }, diff --git a/core/src/com/unciv/logic/Battle.kt b/core/src/com/unciv/logic/Battle.kt index 0c1cb49552..dbd67a0499 100644 --- a/core/src/com/unciv/logic/Battle.kt +++ b/core/src/com/unciv/logic/Battle.kt @@ -1,6 +1,7 @@ package com.unciv.logic import com.unciv.logic.map.MapUnit +import com.unciv.logic.map.UnitType /** * Created by LENOVO on 3/26/2018. @@ -8,6 +9,10 @@ import com.unciv.logic.map.MapUnit class Battle(){ fun calculateDamage(attacker:MapUnit, defender:MapUnit): Int { - return (attacker.strength*attacker.health*5) / (defender.strength*defender.health) + val attackerStrength = + if (attacker.getBaseUnit().unitType ==UnitType.Ranged) + attacker.getBaseUnit().rangedStrength + else attacker.getBaseUnit().strength + return (attackerStrength*attacker.health*50) / (defender.getBaseUnit().strength*defender.health) } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 95a0d95f34..7925189c63 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -25,7 +25,7 @@ class GameInfo { for (civInfo in civilizations) civInfo.nextTurn() - for (tile in tileMap.values.filter { it.unit != null }) + for (tile in tileMap.values) tile.nextTurn() // We need to update the stats after ALL the cities are done updating because diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 8c44aa3a72..f7227b29e8 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.math.Vector2 import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.gamebasics.GameBasics import com.unciv.models.gamebasics.TileImprovement +import com.unciv.models.gamebasics.Unit import java.text.DecimalFormat @@ -17,24 +18,31 @@ class MapUnit { @Transient lateinit var civInfo: CivilizationInfo - var owner: String? = null - var name: String? = null + lateinit var owner: String + lateinit var name: String var maxMovement: Int = 0 var currentMovement: Float = 0f - lateinit var unitType:UnitType - var health:Int = 10 - var strength:Int = 1 - var rangedStrength:Int = 0 + var health:Int = 100 var action: String? = null // work, automation, fortifying, I dunno what. + fun getBaseUnit(): Unit = GameBasics.Units[name]!! fun getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement + fun getTile(): TileInfo { + return civInfo.gameInfo.tileMap.values.first{it.unit==this} + } + + fun getDistanceToTiles(): HashMap { + val tile = getTile() + return tile.tileMap.getDistanceToTilesWithinTurn(tile.position,currentMovement, + civInfo.tech.isResearched("Machinery")) + } fun doPreTurnAction(tile: TileInfo) { if (currentMovement == 0f) return // We've already done stuff this turn, and can't do any more stuff if (action != null && action!!.startsWith("moveTo")) { val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray() val destinationVector = Vector2(Integer.parseInt(destination[0]).toFloat(), Integer.parseInt(destination[1]).toFloat()) - val gotTo = headTowards(tile.position, destinationVector) + val gotTo = headTowards(destinationVector) if(gotTo==tile) // We didn't move at all return if (gotTo.position == destinationVector) action = null @@ -42,7 +50,7 @@ class MapUnit { return } - if ("automation" == action) doAutomatedAction(tile) + if ("automation" == action) doAutomatedAction() } private fun doPostTurnAction(tile: TileInfo) { @@ -80,11 +88,11 @@ class MapUnit { else return currentTile } - fun doAutomatedAction(tile: TileInfo) { - var tile = tile + fun doAutomatedAction() { + var tile = getTile() val tileToWork = findTileToWork(tile) if (tileToWork != tile) { - tile = headTowards(tile.position, tileToWork.position) + tile = headTowards(tileToWork.position) doPreTurnAction(tile) return } @@ -116,34 +124,75 @@ class MapUnit { } /** - * * @param origin * @param destination * @return The tile that we reached this turn */ - fun headTowards(origin: Vector2, destination: Vector2): TileInfo { - val tileMap = civInfo.gameInfo.tileMap + fun headTowards(destination: Vector2): TileInfo { + val currentTile = getTile() + val tileMap = currentTile.tileMap + + val finalDestinationTile = tileMap.get(destination) val isMachineryResearched = civInfo.tech.isResearched("Machinery") - val distanceToTiles = tileMap.getDistanceToTilesWithinTurn(origin, currentMovement, isMachineryResearched) + val distanceToTiles = getDistanceToTiles() val destinationTileThisTurn:TileInfo - if (distanceToTiles.containsKey(tileMap.get(destination))) - destinationTileThisTurn = tileMap.get(destination) + if (distanceToTiles.containsKey(finalDestinationTile)) { // we can get there this turn + if (finalDestinationTile.unit == null) + destinationTileThisTurn = finalDestinationTile + else // Someone is blocking to the path to the final tile... + { + val destinationNeighbors = tileMap[destination].neighbors + if(destinationNeighbors.contains(currentTile)) // We're right nearby anyway, no need to move + return currentTile + + val reachableDestinationNeighbors = destinationNeighbors.filter { distanceToTiles.containsKey(it) && it.unit==null } + if(reachableDestinationNeighbors.isEmpty()) // We can't get closer... + return currentTile + + destinationTileThisTurn = reachableDestinationNeighbors.minBy { distanceToTiles[it]!! }!! + } + } else { // If the tile is far away, we need to build a path how to get there, and then take the first step - val path = tileMap.getShortestPath(origin, destination, currentMovement, maxMovement, isMachineryResearched) + val path = tileMap.getShortestPath(currentTile.position, destination, currentMovement, maxMovement, isMachineryResearched) destinationTileThisTurn = path.first() } - if (destinationTileThisTurn.unit != null) return tileMap[origin] // Someone is blocking tohe path to the final tile... - val distanceToTile: Float = distanceToTiles[destinationTileThisTurn]!! - tileMap[origin].moveUnitToTile(destinationTileThisTurn, distanceToTile) + + moveToTile(destinationTileThisTurn) return destinationTileThisTurn } - fun nextTurn(tileInfo: TileInfo) { - doPostTurnAction(tileInfo) + private fun heal(){ + val tile = getTile() + health += when{ + tile.isCityCenter -> 20 + tile.owner == owner -> 15 // home territory + tile.owner == null -> 10 // no man's land (neutral) + else -> 5 // enemy territory + } + if(health>100) health=100 + } + + + fun moveToTile(otherTile: TileInfo) { + val distanceToTiles = getDistanceToTiles() + if (!distanceToTiles.containsKey(otherTile)) throw Exception("You can't get there from here!") + + currentMovement -= distanceToTiles[otherTile]!! + if (currentMovement < 0.1) currentMovement = 0f // silly floats which are "almost zero" + getTile().unit = null + otherTile.unit = this + } + + fun nextTurn() { + val tile = getTile() + doPostTurnAction(tile) + if(currentMovement==maxMovement.toFloat()){ + heal() + } currentMovement = maxMovement.toFloat() - doPreTurnAction(tileInfo) + doPreTurnAction(tile) } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 05b7e082a1..4978ddd5d0 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -10,7 +10,7 @@ import com.unciv.models.gamebasics.TileResource import com.unciv.models.stats.Stats class TileInfo { - @Transient @JvmField var tileMap: TileMap? = null + @Transient lateinit var tileMap: TileMap @JvmField var unit: MapUnit? = null @JvmField var position: Vector2 = Vector2.Zero @@ -140,7 +140,7 @@ class TileInfo { } fun nextTurn() { - if (unit != null) unit!!.nextTurn(this) + if (unit != null) unit!!.nextTurn() } override fun toString(): String { @@ -154,7 +154,7 @@ class TileInfo { if (roadStatus !== RoadStatus.None && !isCityCenter) SB.appendln(roadStatus) if (improvement != null) SB.appendln(improvement!!) if (improvementInProgress != null) SB.appendln("$improvementInProgress in ${this.turnsToImprovement} turns") - if (unit != null) SB.appendln(unit!!.name + "(" + unit!!.getMovementString() + ")") + if (unit != null) SB.appendln(unit!!.name + "(" + unit!!.health + ")") return SB.toString() } @@ -169,11 +169,4 @@ class TileInfo { return true } - fun moveUnitToTile(otherTile: TileInfo, movementDistance: Float) { - if (otherTile.unit != null) return // Fail. - unit!!.currentMovement -= movementDistance - if (unit!!.currentMovement < 0.1) unit!!.currentMovement = 0f // silly floats which are "almost zero" - otherTile.unit = unit - unit = null - } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 41f9217273..67dd8f1aba 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -112,7 +112,7 @@ class TileMap { } fun placeUnitNearTile(position: Vector2, unitName: String, civInfo: CivilizationInfo) { - val unit = GameBasics.Units[unitName]!!.mapUnit + val unit = GameBasics.Units[unitName]!!.getMapUnit() unit.owner = civInfo.civName unit.civInfo = civInfo val tilesInDistance = getTilesInDistance(position, 2) diff --git a/core/src/com/unciv/models/gamebasics/Unit.kt b/core/src/com/unciv/models/gamebasics/Unit.kt index e0cee13dac..d61c7abd6d 100644 --- a/core/src/com/unciv/models/gamebasics/Unit.kt +++ b/core/src/com/unciv/models/gamebasics/Unit.kt @@ -3,27 +3,30 @@ package com.unciv.models.gamebasics import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.IConstruction import com.unciv.logic.map.MapUnit +import com.unciv.logic.map.UnitType import com.unciv.models.stats.INamed class Unit : INamed, IConstruction { override lateinit var name: String - @JvmField var description: String? = null - @JvmField var cost: Int = 0 - @JvmField var hurryCostModifier: Int = 0 - @JvmField var movement: Int = 0 - @JvmField internal var unbuildable: Boolean = false // for special units like great people + var description: String? = null + var cost: Int = 0 + var hurryCostModifier: Int = 0 + var movement: Int = 0 + var strength:Int = 1 + var rangedStrength:Int = 0 + lateinit var unitType: UnitType + internal var unbuildable: Boolean = false // for special units like great people val isConstructable: Boolean get() = !unbuildable - val mapUnit: MapUnit - get() { - val unit = MapUnit() - unit.name = name - unit.maxMovement = movement - unit.currentMovement = movement.toFloat() - return unit - } + fun getMapUnit(): MapUnit { + val unit = MapUnit() + unit.name = name + unit.maxMovement = movement + unit.currentMovement = movement.toFloat() + return unit + } override fun getProductionCost(adoptedPolicies: List): Int { diff --git a/core/src/com/unciv/ui/UnCivGame.kt b/core/src/com/unciv/ui/UnCivGame.kt index e6cd40c517..84c79f6df1 100644 --- a/core/src/com/unciv/ui/UnCivGame.kt +++ b/core/src/com/unciv/ui/UnCivGame.kt @@ -40,8 +40,14 @@ class UnCivGame : Game() { setWorldScreen() } - fun startNewGame() { - gameInfo = GameInfo() + fun startNewGame(saveTutorialState:Boolean = false) { + if(saveTutorialState) { + val tutorials = gameInfo.tutorial + gameInfo = GameInfo() + gameInfo.tutorial = tutorials + } + else gameInfo = GameInfo() + gameInfo.tileMap = TileMap(20) gameInfo.civilizations.add(CivilizationInfo("Babylon", Vector2.Zero, gameInfo)) val barbarians = CivilizationInfo() diff --git a/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt b/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt index d566a937fd..db50baae19 100644 --- a/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt @@ -55,7 +55,12 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) { } - if (tileInfo.unit != null && unitImage == null) { + if (unitImage != null) { // The unit can change within one update - for instance, when attacking, the attacker replaces the defender! + unitImage!!.remove() + unitImage = null + } + + if (tileInfo.unit != null) { val unit = tileInfo.unit!! unitImage = getUnitImage(unit.name!!, unit.civInfo.getCivilization().getColor()) addActor(unitImage!!) @@ -64,10 +69,6 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) { height/2 - unitImage!!.height/2 +20) // top } - if (tileInfo.unit == null && unitImage != null) { - unitImage!!.remove() - unitImage = null - } if (unitImage != null) { if (!tileInfo.hasIdleUnit()) diff --git a/core/src/com/unciv/ui/worldscreen/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/BattleTable.kt new file mode 100644 index 0000000000..5cd406cd51 --- /dev/null +++ b/core/src/com/unciv/ui/worldscreen/BattleTable.kt @@ -0,0 +1,123 @@ +package com.unciv.ui.worldscreen + +import com.badlogic.gdx.scenes.scene2d.ui.Label +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.badlogic.gdx.scenes.scene2d.ui.TextButton +import com.unciv.logic.Battle +import com.unciv.logic.map.MapUnit +import com.unciv.logic.map.UnitType +import com.unciv.ui.cityscreen.addClickListener +import com.unciv.ui.utils.CameraStageBaseScreen +import com.unciv.ui.utils.disable +import java.util.* + +class BattleTable(val worldScreen: WorldScreen): Table() { + + private val battle = Battle() + + fun simulateBattle(attacker: MapUnit, defender: MapUnit){ + clear() + + val attackerLabel = Label(attacker.name, CameraStageBaseScreen.skin) + attackerLabel.style= Label.LabelStyle(attackerLabel.style) + attackerLabel.style.fontColor=attacker.civInfo.getCivilization().getColor() + add(attackerLabel) + + val defenderLabel = Label(attacker.name, CameraStageBaseScreen.skin) + defenderLabel.style= Label.LabelStyle(defenderLabel.style) + defenderLabel.style.fontColor=defender.civInfo.getCivilization().getColor() + add(defenderLabel) + + row() + + // todo: when damage exceeds health, it shows negative health numbers! Also not indicative of who is more likely to win + + var damageToDefender = battle.calculateDamage(attacker,defender) + var damageToAttacker = battle.calculateDamage(defender,attacker) + + + when { + damageToAttacker>attacker.health && damageToDefender>defender.health -> // when damage exceeds health, we don't want to show negative health numbers + // Also if both parties are supposed to die it's not indicative of who is more likely to win + // So we "normalize" the damages until one dies + if(damageToDefender/defender.health.toFloat() > damageToAttacker/attacker.health.toFloat()) // defender dies quicker ie first + { + // Both damages *= (defender.health/damageToDefender) + damageToDefender = defender.health + damageToAttacker *= (defender.health/damageToDefender.toFloat()).toInt() + } + else{ // attacker dies first + // Both damages *= (attacker.health/damageToAttacker) + damageToAttacker = attacker.health + damageToDefender *= (attacker.health/damageToAttacker.toFloat()).toInt() + } + + damageToAttacker>attacker.health -> damageToAttacker=attacker.health + damageToDefender>defender.health -> damageToDefender=defender.health + } + + + val attackLabel = Label(attacker.health.toString() + " -> " + + (attacker.health - damageToAttacker), CameraStageBaseScreen.skin) + add(attackLabel) + + val defendLabel = Label(defender.health.toString() + " -> " + + (defender.health - damageToDefender), + CameraStageBaseScreen.skin) + add(defendLabel) + + row() + val attackButton = TextButton("Attack",CameraStageBaseScreen.skin) + + attackButton.addClickListener { + attack(attacker,defender) + } + + val attackerCanReachDefender = attacker.getDistanceToTiles().containsKey(defender.getTile()) + if(attacker.currentMovement==0f || !attackerCanReachDefender) attackButton.disable() + add(attackButton).colspan(2) + + pack() + setPosition(worldScreen.stage.width/2-width/2, + 5f) + } + + fun attack(attacker: MapUnit, defender: MapUnit){ + + var damageToDefender = battle.calculateDamage(attacker,defender) + var damageToAttacker = battle.calculateDamage(defender,attacker) + + // randomize things so + + if(attacker.getBaseUnit().unitType == UnitType.Ranged) defender.health -= 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. + //attacker..moveUnitToTile() + attacker.headTowards(defender.getTile().position) + while(damageToDefender+damageToAttacker>0) { + if (Random().nextInt(damageToDefender + damageToAttacker) < damageToDefender) { + damageToDefender-- + defender.health-- + if(defender.health==0) { + val defenderTile = defender.getTile() + defenderTile.unit = null // Ded + attacker.moveToTile(defenderTile) + break + } + } + else{ + damageToAttacker-- + attacker.health-- + if(attacker.health==0) { + attacker.getTile().unit = null + break + } + } + } + } + attacker.currentMovement=0f + + worldScreen.update() + + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/TileInfoTable.kt b/core/src/com/unciv/ui/worldscreen/TileInfoTable.kt index c9a0d9e0cf..0cad80f332 100644 --- a/core/src/com/unciv/ui/worldscreen/TileInfoTable.kt +++ b/core/src/com/unciv/ui/worldscreen/TileInfoTable.kt @@ -39,13 +39,6 @@ class TileInfoTable(private val worldScreen: WorldScreen, internal val civInfo: } } - -// if (tile.unit != null) { -// for (button in UnitActions().getUnitActions(tile)) -// add(button).colspan(2) -// .size(button.width * worldScreen.buttonScale, button.height * worldScreen.buttonScale).row() -// } - pack() setPosition(worldScreen.stage.width - 10f - width, 10f) diff --git a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt index 7b8c579788..2818cff6a7 100644 --- a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt @@ -105,7 +105,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: fun setCenterPosition(vector: Vector2) { val tileGroup = tileGroups.values.first { it.tileInfo.position == vector } selectedTile = tileGroup.tileInfo - if(selectedTile!!.unit!=null) worldScreen.unitTable.selectedUnitTile = selectedTile + worldScreen.unitTable.tileSelected(selectedTile!!) layout() // Fit the scroll pane to the contents - otherwise, setScroll won't work! // We want to center on the middle of TG (TG.getX()+TG.getWidth()/2) // and so the scroll position (== filter the screen starts) needs to be half a screen away diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index b4befff91a..cea5888e8b 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -2,11 +2,9 @@ package com.unciv.ui.worldscreen import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.scenes.scene2d.ui.Label -import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton -import com.unciv.logic.Battle import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.logic.map.MapUnit +import com.unciv.logic.map.UnitType import com.unciv.ui.cityscreen.addClickListener import com.unciv.ui.pickerscreens.PolicyPickerScreen import com.unciv.ui.pickerscreens.TechPickerScreen @@ -28,7 +26,7 @@ class WorldScreen : CameraStageBaseScreen() { internal val optionsTable: WorldScreenOptionsTable private val notificationsScroll: NotificationsScroll internal val unitTable = UnitTable(this) - internal val battleTable = BattleTable(this) + private val battleTable = BattleTable(this) init { val gameInfo = game.gameInfo @@ -84,9 +82,10 @@ class WorldScreen : CameraStageBaseScreen() { if(tileMapHolder.selectedTile!=null && tileMapHolder.selectedTile!!.unit!=null - && tileMapHolder.selectedTile!!.unit!!.owner!=civInfo.civName - && unitTable.selectedUnitTile!=null) - battleTable.simulateBattle(unitTable.getSelectedUnit(), tileMapHolder.selectedTile!!.unit!!) + && tileMapHolder.selectedTile!!.unit!!.owner!=civInfo.civName // enemy unit on selected tile, + && unitTable.selectedUnit!=null + && unitTable.selectedUnit!!.getBaseUnit().unitType!=UnitType.Civilian) // and non-civilian unit selected for us + battleTable.simulateBattle(unitTable.selectedUnit!!, tileMapHolder.selectedTile!!.unit!!) else battleTable.clear() } @@ -124,7 +123,6 @@ class WorldScreen : CameraStageBaseScreen() { } game.gameInfo.nextTurn() - unitTable.selectedUnitTile = null unitTable.currentlyExecutingAction = null GameSaver.SaveGame(game, "Autosave") update() @@ -142,28 +140,4 @@ class WorldScreen : CameraStageBaseScreen() { game.setWorldScreen() } } -} - -class BattleTable(val worldScreen: WorldScreen): Table() { - fun simulateBattle(attacker:MapUnit,defender:MapUnit){ - clear() - add(Label(attacker.name, CameraStageBaseScreen.skin)) - add(Label(defender.name, CameraStageBaseScreen.skin)) - row() - - val battle = Battle() - val damageToAttacker = battle.calculateDamage(attacker,defender) - add(Label(attacker.health.toString()+"/10 -> " - +(attacker.health-damageToAttacker)+"/10", - CameraStageBaseScreen.skin)) - - val damageToDefender = battle.calculateDamage(defender,attacker) - add(Label(defender.health.toString()+"/10 -> " - +(defender.health-damageToDefender)+"/10", - CameraStageBaseScreen.skin)) - pack() - setPosition(worldScreen.stage.width/2-width/2, - 5f) - } -} - +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreenOptionsTable.kt b/core/src/com/unciv/ui/worldscreen/WorldScreenOptionsTable.kt index 520b3287c3..d8ed87c0db 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreenOptionsTable.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreenOptionsTable.kt @@ -29,7 +29,7 @@ class WorldScreenOptionsTable internal constructor(worldScreen: WorldScreen, pri row() val StartNewGameButton = TextButton("Start new game", CameraStageBaseScreen.skin) - StartNewGameButton.addClickListener { worldScreen.game.startNewGame() } + StartNewGameButton.addClickListener { worldScreen.game.startNewGame(true) } add(StartNewGameButton).pad(10f) row() diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 014498c106..21cb17c621 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -25,9 +25,10 @@ class UnitActions { } } - fun getUnitActions(tile: TileInfo, worldScreen: WorldScreen): List { + fun getUnitActions(unit:MapUnit,worldScreen: WorldScreen): List { + + val tile = unit.getTile() - val unit = tile.unit!! val tileMapHolder = worldScreen.tileMapHolder val unitTable = worldScreen.unitTable @@ -40,10 +41,7 @@ class UnitActions { // Set all tiles transparent except those in unit range for (TG in tileMapHolder.tileGroups.values) TG.setColor(0f, 0f, 0f, 0.3f) - val distanceToTiles = tileMapHolder.tileMap.getDistanceToTilesWithinTurn( - unitTable.selectedUnitTile!!.position, - unitTable.getSelectedUnit().currentMovement, - unit.civInfo.tech.isResearched("Machinery")) + val distanceToTiles = unitTable.selectedUnit!!.getDistanceToTiles() for (tileInRange in distanceToTiles.keys) { tileMapHolder.tileGroups[tileInRange.position.toString()]!!.color = Color.WHITE @@ -62,7 +60,7 @@ class UnitActions { } if (unit.name == "Settler") { - actionList += getUnitActionButton(unit, "Found City", + actionList += getUnitActionButton(unit, "Found city", !tileMapHolder.tileMap.getTilesInDistance(tile.position, 2).any { it.isCityCenter }, { worldScreen.displayTutorials("CityFounded") @@ -75,16 +73,16 @@ class UnitActions { } if (unit.name == "Worker") { - val improvementButtonText = - if (tile.improvementInProgress == null) "Construct\r\nimprovement" - else tile.improvementInProgress!! + "\r\nin progress" + val improvementButtonText: String + if (tile.improvementInProgress == null) improvementButtonText = "Construct\r\nimprovement" + else improvementButtonText = tile.improvementInProgress!! + "\r\nin progress" actionList += getUnitActionButton(unit, improvementButtonText, !tile.isCityCenter || GameBasics.TileImprovements.values.any { tile.canBuildImprovement(it, unit.civInfo) }, { worldScreen.game.screen = ImprovementPickerScreen(tile) }) - if("automation" == tile.unit!!.action){ + if("automation" == unit.action){ val automationAction = getUnitActionButton(unit,"Stop automation",true, - {tile.unit!!.action = null}) + {unit.action = null}) automationAction.enable() // Stopping automation is always enabled; actionList += automationAction } @@ -92,7 +90,7 @@ class UnitActions { actionList += getUnitActionButton(unit, "Automate", true, { tile.unit!!.action = "automation" - tile.unit!!.doAutomatedAction(tile) + tile.unit!!.doAutomatedAction() } ) } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 8acf8056e4..1dcaadc0a7 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -13,16 +13,11 @@ import com.unciv.ui.worldscreen.WorldScreen class UnitTable(val worldScreen: WorldScreen) : Table(){ private val idleUnitButton = IdleUnitButton(worldScreen) private val unitLabel = Label("",CameraStageBaseScreen.skin) - var selectedUnitTile : TileInfo? = null + var selectedUnit : MapUnit? = null var currentlyExecutingAction : String? = null private val unitActionsTable = Table() - fun getSelectedUnit(): MapUnit { - if(selectedUnitTile==null) throw Exception("getSelectedUnit was called when no unit was selected!") - else return selectedUnitTile!!.unit!! - } - init { val tileTableBackground = ImageGetter.getDrawable("skin/tileTableBackground.png") .tint(Color(0x004085bf)) @@ -37,11 +32,19 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ fun update() { idleUnitButton.update() unitActionsTable.clear() - if(selectedUnitTile!=null && selectedUnitTile!!.unit==null) selectedUnitTile=null // The unit that was there no longer exists + if(selectedUnit!=null) + { + try{ selectedUnit!!.getTile()} + catch(ex:Exception) {selectedUnit=null} // The unit that was there no longer exists} + } - if(selectedUnitTile!=null) { - unitLabel.setText(getSelectedUnit().name+" "+getSelectedUnit().getMovementString()) - for (button in UnitActions().getUnitActions(selectedUnitTile!!,worldScreen)) + if(selectedUnit!=null) { + val unit = selectedUnit!! + unitLabel.setText(unit.name + +"\r\nMovement: " +unit.getMovementString() + +"\r\nHealth: "+unit.health + ) + for (button in UnitActions().getUnitActions(selectedUnit!!, worldScreen)) unitActionsTable.add(button).colspan(2).pad(5f) .size(button.width * worldScreen.buttonScale, button.height * worldScreen.buttonScale).row() } @@ -54,27 +57,21 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ fun tileSelected(selectedTile: TileInfo) { if(currentlyExecutingAction=="moveTo"){ - val reachedTile = getSelectedUnit().headTowards(selectedUnitTile!!.position, selectedTile.position) - selectedUnitTile = reachedTile + val reachedTile = selectedUnit!!.headTowards(selectedTile.position) + if(reachedTile!=selectedTile) // Didn't get all the way there - getSelectedUnit().action = "moveTo " + selectedTile.position.x.toInt() + "," + selectedTile.position.y.toInt() + selectedUnit!!.action = "moveTo " + selectedTile.position.x.toInt() + "," + selectedTile.position.y.toInt() currentlyExecutingAction = null } if(selectedTile.unit!=null && selectedTile.unit!!.civInfo == worldScreen.civInfo) - selectedUnitTile = selectedTile - } - - private fun getDistanceToTiles(): HashMap { - return worldScreen.tileMapHolder.tileMap.getDistanceToTilesWithinTurn(selectedUnitTile!!.position, - getSelectedUnit().currentMovement, - getSelectedUnit().civInfo.tech.isResearched("Machinery")) + selectedUnit= selectedTile.unit } fun getViewablePositionsForExecutingAction(): List { if(currentlyExecutingAction == "moveTo") - return getDistanceToTiles().keys.map { it.position } + return selectedUnit!!.getDistanceToTiles().keys.map { it.position } return emptyList() } }