Added GetTile function to MapUnit, which simplifies and normalizes a lot of the unit logic

Battle simulation and actual battle work as expected!
This commit is contained in:
Yair Morgenstern 2018-04-02 15:16:28 +03:00
parent bd1a191b15
commit 5d00adb7fe
16 changed files with 291 additions and 134 deletions

View File

@ -3,13 +3,15 @@
name:"Worker", name:"Worker",
description: "Can build improvements on tiles", description: "Can build improvements on tiles",
movement:2, movement:2,
unitType:"Civilian",
hurryCostModifier:20, hurryCostModifier:20,
cost:60 cost:70
}, },
{ {
name:"Settler", name:"Settler",
description: "Founds a new city", description: "Founds a new city",
movement:2, movement:2,
unitType:"Civilian",
cost:106, cost:106,
hurryCostModifier:20 hurryCostModifier:20
}, },
@ -17,30 +19,43 @@
name:"Scout", name:"Scout",
description: "Has no abilites, can only explore", description: "Has no abilites, can only explore",
unbuildable:true, unbuildable:true,
unitType:"Melee",
movement:2 movement:2
}, },
{
name:"Warrior",
description: "A basic fighting unit",
unitType:"Melee",
movement:2,
cost: 40,
hurryCostModifier:20
},
{ {
name:"Great Artist", name:"Great Artist",
description: "Can start an 8-turn golden age or construcct a landmark (+5 culture)", description: "Can start an 8-turn golden age or construcct a landmark (+5 culture)",
unbuildable:true, unbuildable:true,
unitType:"Civilian",
movement:2 movement:2
}, },
{ {
name:"Great Scientist", name:"Great Scientist",
description: "Can discover a technology, or construct an academy (+4 science(", description: "Can discover a technology, or construct an academy (+4 science(",
unbuildable:true, unbuildable:true,
unitType:"Civilian",
movement:2 movement:2
}, },
{ {
name:"Great Merchant", name:"Great Merchant",
description: "Can undertake a trade mission, giving a large sum of gold, or construct a ?", description: "Can undertake a trade mission, giving a large sum of gold, or construct a ?",
unbuildable:true, unbuildable:true,
unitType:"Civilian",
movement:2 movement:2
}, },
{ {
name:"Great Engineer", name:"Great Engineer",
description: "Can speed up construction of a wonder, or construct a refinery, giving +? production?", description: "Can speed up construction of a wonder, or construct a refinery, giving +? production?",
unbuildable:true, unbuildable:true,
unitType:"Civilian",
movement:2 movement:2
}, },

View File

@ -1,6 +1,7 @@
package com.unciv.logic package com.unciv.logic
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.UnitType
/** /**
* Created by LENOVO on 3/26/2018. * Created by LENOVO on 3/26/2018.
@ -8,6 +9,10 @@ import com.unciv.logic.map.MapUnit
class Battle(){ class Battle(){
fun calculateDamage(attacker:MapUnit, defender:MapUnit): Int { 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)
} }
} }

View File

@ -25,7 +25,7 @@ class GameInfo {
for (civInfo in civilizations) civInfo.nextTurn() for (civInfo in civilizations) civInfo.nextTurn()
for (tile in tileMap.values.filter { it.unit != null }) for (tile in tileMap.values)
tile.nextTurn() tile.nextTurn()
// We need to update the stats after ALL the cities are done updating because // We need to update the stats after ALL the cities are done updating because

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.math.Vector2
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.gamebasics.GameBasics import com.unciv.models.gamebasics.GameBasics
import com.unciv.models.gamebasics.TileImprovement import com.unciv.models.gamebasics.TileImprovement
import com.unciv.models.gamebasics.Unit
import java.text.DecimalFormat import java.text.DecimalFormat
@ -17,24 +18,31 @@ class MapUnit {
@Transient @Transient
lateinit var civInfo: CivilizationInfo lateinit var civInfo: CivilizationInfo
var owner: String? = null lateinit var owner: String
var name: String? = null lateinit var name: String
var maxMovement: Int = 0 var maxMovement: Int = 0
var currentMovement: Float = 0f var currentMovement: Float = 0f
lateinit var unitType:UnitType var health:Int = 100
var health:Int = 10
var strength:Int = 1
var rangedStrength:Int = 0
var action: String? = null // work, automation, fortifying, I dunno what. 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 getMovementString(): String = DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + maxMovement
fun getTile(): TileInfo {
return civInfo.gameInfo.tileMap.values.first{it.unit==this}
}
fun getDistanceToTiles(): HashMap<TileInfo, Float> {
val tile = getTile()
return tile.tileMap.getDistanceToTilesWithinTurn(tile.position,currentMovement,
civInfo.tech.isResearched("Machinery"))
}
fun doPreTurnAction(tile: TileInfo) { fun doPreTurnAction(tile: TileInfo) {
if (currentMovement == 0f) return // We've already done stuff this turn, and can't do any more stuff if (currentMovement == 0f) return // We've already done stuff this turn, and can't do any more stuff
if (action != null && action!!.startsWith("moveTo")) { if (action != null && action!!.startsWith("moveTo")) {
val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray() val destination = action!!.replace("moveTo ", "").split(",").dropLastWhile { it.isEmpty() }.toTypedArray()
val destinationVector = Vector2(Integer.parseInt(destination[0]).toFloat(), Integer.parseInt(destination[1]).toFloat()) 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 if(gotTo==tile) // We didn't move at all
return return
if (gotTo.position == destinationVector) action = null if (gotTo.position == destinationVector) action = null
@ -42,7 +50,7 @@ class MapUnit {
return return
} }
if ("automation" == action) doAutomatedAction(tile) if ("automation" == action) doAutomatedAction()
} }
private fun doPostTurnAction(tile: TileInfo) { private fun doPostTurnAction(tile: TileInfo) {
@ -80,11 +88,11 @@ class MapUnit {
else return currentTile else return currentTile
} }
fun doAutomatedAction(tile: TileInfo) { fun doAutomatedAction() {
var tile = tile var tile = getTile()
val tileToWork = findTileToWork(tile) val tileToWork = findTileToWork(tile)
if (tileToWork != tile) { if (tileToWork != tile) {
tile = headTowards(tile.position, tileToWork.position) tile = headTowards(tileToWork.position)
doPreTurnAction(tile) doPreTurnAction(tile)
return return
} }
@ -116,34 +124,75 @@ class MapUnit {
} }
/** /**
*
* @param origin * @param origin
* @param destination * @param destination
* @return The tile that we reached this turn * @return The tile that we reached this turn
*/ */
fun headTowards(origin: Vector2, destination: Vector2): TileInfo { fun headTowards(destination: Vector2): TileInfo {
val tileMap = civInfo.gameInfo.tileMap val currentTile = getTile()
val tileMap = currentTile.tileMap
val finalDestinationTile = tileMap.get(destination)
val isMachineryResearched = civInfo.tech.isResearched("Machinery") val isMachineryResearched = civInfo.tech.isResearched("Machinery")
val distanceToTiles = tileMap.getDistanceToTilesWithinTurn(origin, currentMovement, isMachineryResearched) val distanceToTiles = getDistanceToTiles()
val destinationTileThisTurn:TileInfo val destinationTileThisTurn:TileInfo
if (distanceToTiles.containsKey(tileMap.get(destination))) if (distanceToTiles.containsKey(finalDestinationTile)) { // we can get there this turn
destinationTileThisTurn = tileMap.get(destination) 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 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() destinationTileThisTurn = path.first()
} }
if (destinationTileThisTurn.unit != null) return tileMap[origin] // Someone is blocking tohe path to the final tile...
val distanceToTile: Float = distanceToTiles[destinationTileThisTurn]!! moveToTile(destinationTileThisTurn)
tileMap[origin].moveUnitToTile(destinationTileThisTurn, distanceToTile)
return destinationTileThisTurn return destinationTileThisTurn
} }
fun nextTurn(tileInfo: TileInfo) { private fun heal(){
doPostTurnAction(tileInfo) 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() currentMovement = maxMovement.toFloat()
doPreTurnAction(tileInfo) doPreTurnAction(tile)
} }
} }

View File

@ -10,7 +10,7 @@ import com.unciv.models.gamebasics.TileResource
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
class TileInfo { class TileInfo {
@Transient @JvmField var tileMap: TileMap? = null @Transient lateinit var tileMap: TileMap
@JvmField var unit: MapUnit? = null @JvmField var unit: MapUnit? = null
@JvmField var position: Vector2 = Vector2.Zero @JvmField var position: Vector2 = Vector2.Zero
@ -140,7 +140,7 @@ class TileInfo {
} }
fun nextTurn() { fun nextTurn() {
if (unit != null) unit!!.nextTurn(this) if (unit != null) unit!!.nextTurn()
} }
override fun toString(): String { override fun toString(): String {
@ -154,7 +154,7 @@ class TileInfo {
if (roadStatus !== RoadStatus.None && !isCityCenter) SB.appendln(roadStatus) if (roadStatus !== RoadStatus.None && !isCityCenter) SB.appendln(roadStatus)
if (improvement != null) SB.appendln(improvement!!) if (improvement != null) SB.appendln(improvement!!)
if (improvementInProgress != null) SB.appendln("$improvementInProgress in ${this.turnsToImprovement} turns") 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() return SB.toString()
} }
@ -169,11 +169,4 @@ class TileInfo {
return true 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
}
} }

View File

@ -112,7 +112,7 @@ class TileMap {
} }
fun placeUnitNearTile(position: Vector2, unitName: String, civInfo: CivilizationInfo) { 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.owner = civInfo.civName
unit.civInfo = civInfo unit.civInfo = civInfo
val tilesInDistance = getTilesInDistance(position, 2) val tilesInDistance = getTilesInDistance(position, 2)

View File

@ -3,27 +3,30 @@ package com.unciv.models.gamebasics
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.city.IConstruction import com.unciv.logic.city.IConstruction
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.UnitType
import com.unciv.models.stats.INamed import com.unciv.models.stats.INamed
class Unit : INamed, IConstruction { class Unit : INamed, IConstruction {
override lateinit var name: String override lateinit var name: String
@JvmField var description: String? = null var description: String? = null
@JvmField var cost: Int = 0 var cost: Int = 0
@JvmField var hurryCostModifier: Int = 0 var hurryCostModifier: Int = 0
@JvmField var movement: Int = 0 var movement: Int = 0
@JvmField internal var unbuildable: Boolean = false // for special units like great people 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 val isConstructable: Boolean
get() = !unbuildable get() = !unbuildable
val mapUnit: MapUnit fun getMapUnit(): MapUnit {
get() { val unit = MapUnit()
val unit = MapUnit() unit.name = name
unit.name = name unit.maxMovement = movement
unit.maxMovement = movement unit.currentMovement = movement.toFloat()
unit.currentMovement = movement.toFloat() return unit
return unit }
}
override fun getProductionCost(adoptedPolicies: List<String>): Int { override fun getProductionCost(adoptedPolicies: List<String>): Int {

View File

@ -40,8 +40,14 @@ class UnCivGame : Game() {
setWorldScreen() setWorldScreen()
} }
fun startNewGame() { fun startNewGame(saveTutorialState:Boolean = false) {
gameInfo = GameInfo() if(saveTutorialState) {
val tutorials = gameInfo.tutorial
gameInfo = GameInfo()
gameInfo.tutorial = tutorials
}
else gameInfo = GameInfo()
gameInfo.tileMap = TileMap(20) gameInfo.tileMap = TileMap(20)
gameInfo.civilizations.add(CivilizationInfo("Babylon", Vector2.Zero, gameInfo)) gameInfo.civilizations.add(CivilizationInfo("Babylon", Vector2.Zero, gameInfo))
val barbarians = CivilizationInfo() val barbarians = CivilizationInfo()

View File

@ -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!! val unit = tileInfo.unit!!
unitImage = getUnitImage(unit.name!!, unit.civInfo.getCivilization().getColor()) unitImage = getUnitImage(unit.name!!, unit.civInfo.getCivilization().getColor())
addActor(unitImage!!) addActor(unitImage!!)
@ -64,10 +69,6 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) {
height/2 - unitImage!!.height/2 +20) // top height/2 - unitImage!!.height/2 +20) // top
} }
if (tileInfo.unit == null && unitImage != null) {
unitImage!!.remove()
unitImage = null
}
if (unitImage != null) { if (unitImage != null) {
if (!tileInfo.hasIdleUnit()) if (!tileInfo.hasIdleUnit())

View File

@ -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()
}
}

View File

@ -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() pack()
setPosition(worldScreen.stage.width - 10f - width, 10f) setPosition(worldScreen.stage.width - 10f - width, 10f)

View File

@ -105,7 +105,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
fun setCenterPosition(vector: Vector2) { fun setCenterPosition(vector: Vector2) {
val tileGroup = tileGroups.values.first { it.tileInfo.position == vector } val tileGroup = tileGroups.values.first { it.tileInfo.position == vector }
selectedTile = tileGroup.tileInfo 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! 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) // 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 // and so the scroll position (== filter the screen starts) needs to be half a screen away

View File

@ -2,11 +2,9 @@ package com.unciv.ui.worldscreen
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.ui.Label 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.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.logic.Battle
import com.unciv.logic.civilization.CivilizationInfo 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.cityscreen.addClickListener
import com.unciv.ui.pickerscreens.PolicyPickerScreen import com.unciv.ui.pickerscreens.PolicyPickerScreen
import com.unciv.ui.pickerscreens.TechPickerScreen import com.unciv.ui.pickerscreens.TechPickerScreen
@ -28,7 +26,7 @@ class WorldScreen : CameraStageBaseScreen() {
internal val optionsTable: WorldScreenOptionsTable internal val optionsTable: WorldScreenOptionsTable
private val notificationsScroll: NotificationsScroll private val notificationsScroll: NotificationsScroll
internal val unitTable = UnitTable(this) internal val unitTable = UnitTable(this)
internal val battleTable = BattleTable(this) private val battleTable = BattleTable(this)
init { init {
val gameInfo = game.gameInfo val gameInfo = game.gameInfo
@ -84,9 +82,10 @@ class WorldScreen : CameraStageBaseScreen() {
if(tileMapHolder.selectedTile!=null if(tileMapHolder.selectedTile!=null
&& tileMapHolder.selectedTile!!.unit!=null && tileMapHolder.selectedTile!!.unit!=null
&& tileMapHolder.selectedTile!!.unit!!.owner!=civInfo.civName && tileMapHolder.selectedTile!!.unit!!.owner!=civInfo.civName // enemy unit on selected tile,
&& unitTable.selectedUnitTile!=null) && unitTable.selectedUnit!=null
battleTable.simulateBattle(unitTable.getSelectedUnit(), tileMapHolder.selectedTile!!.unit!!) && unitTable.selectedUnit!!.getBaseUnit().unitType!=UnitType.Civilian) // and non-civilian unit selected for us
battleTable.simulateBattle(unitTable.selectedUnit!!, tileMapHolder.selectedTile!!.unit!!)
else battleTable.clear() else battleTable.clear()
} }
@ -124,7 +123,6 @@ class WorldScreen : CameraStageBaseScreen() {
} }
game.gameInfo.nextTurn() game.gameInfo.nextTurn()
unitTable.selectedUnitTile = null
unitTable.currentlyExecutingAction = null unitTable.currentlyExecutingAction = null
GameSaver.SaveGame(game, "Autosave") GameSaver.SaveGame(game, "Autosave")
update() update()
@ -142,28 +140,4 @@ class WorldScreen : CameraStageBaseScreen() {
game.setWorldScreen() 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)
}
}

View File

@ -29,7 +29,7 @@ class WorldScreenOptionsTable internal constructor(worldScreen: WorldScreen, pri
row() row()
val StartNewGameButton = TextButton("Start new game", CameraStageBaseScreen.skin) val StartNewGameButton = TextButton("Start new game", CameraStageBaseScreen.skin)
StartNewGameButton.addClickListener { worldScreen.game.startNewGame() } StartNewGameButton.addClickListener { worldScreen.game.startNewGame(true) }
add(StartNewGameButton).pad(10f) add(StartNewGameButton).pad(10f)
row() row()

View File

@ -25,9 +25,10 @@ class UnitActions {
} }
} }
fun getUnitActions(tile: TileInfo, worldScreen: WorldScreen): List<TextButton> { fun getUnitActions(unit:MapUnit,worldScreen: WorldScreen): List<TextButton> {
val tile = unit.getTile()
val unit = tile.unit!!
val tileMapHolder = worldScreen.tileMapHolder val tileMapHolder = worldScreen.tileMapHolder
val unitTable = worldScreen.unitTable val unitTable = worldScreen.unitTable
@ -40,10 +41,7 @@ class UnitActions {
// Set all tiles transparent except those in unit range // Set all tiles transparent except those in unit range
for (TG in tileMapHolder.tileGroups.values) TG.setColor(0f, 0f, 0f, 0.3f) for (TG in tileMapHolder.tileGroups.values) TG.setColor(0f, 0f, 0f, 0.3f)
val distanceToTiles = tileMapHolder.tileMap.getDistanceToTilesWithinTurn( val distanceToTiles = unitTable.selectedUnit!!.getDistanceToTiles()
unitTable.selectedUnitTile!!.position,
unitTable.getSelectedUnit().currentMovement,
unit.civInfo.tech.isResearched("Machinery"))
for (tileInRange in distanceToTiles.keys) { for (tileInRange in distanceToTiles.keys) {
tileMapHolder.tileGroups[tileInRange.position.toString()]!!.color = Color.WHITE tileMapHolder.tileGroups[tileInRange.position.toString()]!!.color = Color.WHITE
@ -62,7 +60,7 @@ class UnitActions {
} }
if (unit.name == "Settler") { if (unit.name == "Settler") {
actionList += getUnitActionButton(unit, "Found City", actionList += getUnitActionButton(unit, "Found city",
!tileMapHolder.tileMap.getTilesInDistance(tile.position, 2).any { it.isCityCenter }, !tileMapHolder.tileMap.getTilesInDistance(tile.position, 2).any { it.isCityCenter },
{ {
worldScreen.displayTutorials("CityFounded") worldScreen.displayTutorials("CityFounded")
@ -75,16 +73,16 @@ class UnitActions {
} }
if (unit.name == "Worker") { if (unit.name == "Worker") {
val improvementButtonText = val improvementButtonText: String
if (tile.improvementInProgress == null) "Construct\r\nimprovement" if (tile.improvementInProgress == null) improvementButtonText = "Construct\r\nimprovement"
else tile.improvementInProgress!! + "\r\nin progress" else improvementButtonText = tile.improvementInProgress!! + "\r\nin progress"
actionList += getUnitActionButton(unit, improvementButtonText, actionList += getUnitActionButton(unit, improvementButtonText,
!tile.isCityCenter || GameBasics.TileImprovements.values.any { tile.canBuildImprovement(it, unit.civInfo) }, !tile.isCityCenter || GameBasics.TileImprovements.values.any { tile.canBuildImprovement(it, unit.civInfo) },
{ worldScreen.game.screen = ImprovementPickerScreen(tile) }) { worldScreen.game.screen = ImprovementPickerScreen(tile) })
if("automation" == tile.unit!!.action){ if("automation" == unit.action){
val automationAction = getUnitActionButton(unit,"Stop automation",true, val automationAction = getUnitActionButton(unit,"Stop automation",true,
{tile.unit!!.action = null}) {unit.action = null})
automationAction.enable() // Stopping automation is always enabled; automationAction.enable() // Stopping automation is always enabled;
actionList += automationAction actionList += automationAction
} }
@ -92,7 +90,7 @@ class UnitActions {
actionList += getUnitActionButton(unit, "Automate", true, actionList += getUnitActionButton(unit, "Automate", true,
{ {
tile.unit!!.action = "automation" tile.unit!!.action = "automation"
tile.unit!!.doAutomatedAction(tile) tile.unit!!.doAutomatedAction()
} }
) )
} }

View File

@ -13,16 +13,11 @@ import com.unciv.ui.worldscreen.WorldScreen
class UnitTable(val worldScreen: WorldScreen) : Table(){ class UnitTable(val worldScreen: WorldScreen) : Table(){
private val idleUnitButton = IdleUnitButton(worldScreen) private val idleUnitButton = IdleUnitButton(worldScreen)
private val unitLabel = Label("",CameraStageBaseScreen.skin) private val unitLabel = Label("",CameraStageBaseScreen.skin)
var selectedUnitTile : TileInfo? = null var selectedUnit : MapUnit? = null
var currentlyExecutingAction : String? = null var currentlyExecutingAction : String? = null
private val unitActionsTable = Table() 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 { init {
val tileTableBackground = ImageGetter.getDrawable("skin/tileTableBackground.png") val tileTableBackground = ImageGetter.getDrawable("skin/tileTableBackground.png")
.tint(Color(0x004085bf)) .tint(Color(0x004085bf))
@ -37,11 +32,19 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
fun update() { fun update() {
idleUnitButton.update() idleUnitButton.update()
unitActionsTable.clear() 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) { if(selectedUnit!=null) {
unitLabel.setText(getSelectedUnit().name+" "+getSelectedUnit().getMovementString()) val unit = selectedUnit!!
for (button in UnitActions().getUnitActions(selectedUnitTile!!,worldScreen)) 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) unitActionsTable.add(button).colspan(2).pad(5f)
.size(button.width * worldScreen.buttonScale, button.height * worldScreen.buttonScale).row() .size(button.width * worldScreen.buttonScale, button.height * worldScreen.buttonScale).row()
} }
@ -54,27 +57,21 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
fun tileSelected(selectedTile: TileInfo) { fun tileSelected(selectedTile: TileInfo) {
if(currentlyExecutingAction=="moveTo"){ if(currentlyExecutingAction=="moveTo"){
val reachedTile = getSelectedUnit().headTowards(selectedUnitTile!!.position, selectedTile.position) val reachedTile = selectedUnit!!.headTowards(selectedTile.position)
selectedUnitTile = reachedTile
if(reachedTile!=selectedTile) // Didn't get all the way there 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 currentlyExecutingAction = null
} }
if(selectedTile.unit!=null && selectedTile.unit!!.civInfo == worldScreen.civInfo) if(selectedTile.unit!=null && selectedTile.unit!!.civInfo == worldScreen.civInfo)
selectedUnitTile = selectedTile selectedUnit= selectedTile.unit
}
private fun getDistanceToTiles(): HashMap<TileInfo, Float> {
return worldScreen.tileMapHolder.tileMap.getDistanceToTilesWithinTurn(selectedUnitTile!!.position,
getSelectedUnit().currentMovement,
getSelectedUnit().civInfo.tech.isResearched("Machinery"))
} }
fun getViewablePositionsForExecutingAction(): List<Vector2> fun getViewablePositionsForExecutingAction(): List<Vector2>
{ {
if(currentlyExecutingAction == "moveTo") if(currentlyExecutingAction == "moveTo")
return getDistanceToTiles().keys.map { it.position } return selectedUnit!!.getDistanceToTiles().keys.map { it.position }
return emptyList() return emptyList()
} }
} }