mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 03:53:12 -04:00
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:
parent
bd1a191b15
commit
5d00adb7fe
@ -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
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -3,21 +3,24 @@ 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
|
||||||
|
@ -40,8 +40,14 @@ class UnCivGame : Game() {
|
|||||||
setWorldScreen()
|
setWorldScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startNewGame() {
|
fun startNewGame(saveTutorialState:Boolean = false) {
|
||||||
|
if(saveTutorialState) {
|
||||||
|
val tutorials = gameInfo.tutorial
|
||||||
gameInfo = GameInfo()
|
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()
|
||||||
|
@ -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())
|
||||||
|
123
core/src/com/unciv/ui/worldscreen/BattleTable.kt
Normal file
123
core/src/com/unciv/ui/worldscreen/BattleTable.kt
Normal 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()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
@ -143,27 +141,3 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user