Enemy troops now have movement and will attack
This commit is contained in:
parent
5d00adb7fe
commit
889072c725
11 changed files with 129 additions and 64 deletions
|
@ -2,17 +2,72 @@ package com.unciv.logic
|
|||
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.UnitType
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by LENOVO on 3/26/2018.
|
||||
*/
|
||||
|
||||
class Battle(){
|
||||
class Battle{
|
||||
fun calculateDamage(attacker:MapUnit, defender:MapUnit): Int {
|
||||
|
||||
// TODO:
|
||||
// Terrain and city defence bonuses
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fun attack(attacker: MapUnit, defender: MapUnit){
|
||||
|
||||
var damageToDefender = calculateDamage(attacker,defender)
|
||||
var damageToAttacker = calculateDamage(defender,attacker)
|
||||
|
||||
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.headTowards(defender.getTile().position)
|
||||
while(damageToDefender+damageToAttacker>0) {
|
||||
if (Random().nextInt(damageToDefender + damageToAttacker) < damageToDefender) {
|
||||
damageToDefender--
|
||||
defender.health--
|
||||
if(defender.health==0) break
|
||||
}
|
||||
else{
|
||||
damageToAttacker--
|
||||
attacker.health--
|
||||
if(attacker.health==0) break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After dust as settled
|
||||
|
||||
val defenderDestroyed = defender.health <= 0
|
||||
val attackerDestroyed = attacker.health <= 0
|
||||
|
||||
if(defender.civInfo.isPlayerCivilization()) {
|
||||
val whatHappenedString =
|
||||
if (attackerDestroyed) " was destroyed while attacking"
|
||||
else " has " + (if (defenderDestroyed) "destroyed" else "attacked")
|
||||
val notificationString = "An enemy " + attacker.name + whatHappenedString + " our " + defender.name
|
||||
defender.civInfo.gameInfo.addNotification(notificationString, defender.getTile().position)
|
||||
}
|
||||
|
||||
if(defenderDestroyed) {
|
||||
val defenderTile = defender.getTile()
|
||||
defenderTile.unit = null // Ded
|
||||
if (attacker.getBaseUnit().unitType != UnitType.Ranged)
|
||||
attacker.moveToTile(defenderTile)
|
||||
}
|
||||
attacker.currentMovement=0f
|
||||
|
||||
|
||||
if (attackerDestroyed) attacker.getTile().unit = null
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.unciv.logic
|
|||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.Notification
|
||||
import com.unciv.logic.civilization.getRandom
|
||||
import com.unciv.logic.map.TileMap
|
||||
|
||||
class GameInfo {
|
||||
|
@ -35,9 +36,12 @@ class GameInfo {
|
|||
for (city in civInfo.cities)
|
||||
city.cityStats.update()
|
||||
civInfo.happiness = civInfo.getHappinessForNextTurn()
|
||||
if(!civInfo.isPlayerCivilization())
|
||||
automateMoves(civInfo)
|
||||
}
|
||||
|
||||
|
||||
|
||||
turns++
|
||||
}
|
||||
|
||||
|
@ -58,4 +62,35 @@ class GameInfo {
|
|||
for (cityInfo in civInfo.cities)
|
||||
cityInfo.cityStats.update()
|
||||
}
|
||||
|
||||
|
||||
private fun automateMoves(civInfo: CivilizationInfo) {
|
||||
for(unit in civInfo.getCivUnits()){
|
||||
// if there is an attackable unit in the vicinity, attack!
|
||||
val distanceToTiles = unit.getDistanceToTiles()
|
||||
val unitTileToAttack = distanceToTiles.keys.firstOrNull{ it.unit!=null && it.unit!!.owner!=civInfo.civName }
|
||||
if(unitTileToAttack!=null){
|
||||
Battle().attack(unit,unitTileToAttack.unit!!)
|
||||
continue
|
||||
}
|
||||
|
||||
// else, if there is a reachable spot from which we can attack this turn
|
||||
// (say we're an archer and there's a unit 3 tiles away), go there and attack
|
||||
// todo
|
||||
|
||||
// else, find the closest enemy unit that we know of within 5 spaces and advance towards it
|
||||
// todo this doesn't take into account which tiles are visible to the civ
|
||||
val closestUnit = tileMap.getTilesInDistance(unit.getTile().position, 5)
|
||||
.firstOrNull{ it.unit!=null && it.unit!!.owner!=civInfo.civName }
|
||||
|
||||
if(closestUnit!=null){
|
||||
unit.headTowards(closestUnit.position)
|
||||
continue
|
||||
}
|
||||
|
||||
// else, go to a random space
|
||||
unit.moveToTile(distanceToTiles.keys.toList().getRandom())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.unciv.logic.civilization
|
|||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.models.gamebasics.Civilization
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
|
@ -34,6 +35,9 @@ class CivilizationInfo {
|
|||
val capital: CityInfo
|
||||
get() = cities.first { it.cityConstructions.isBuilt("Palace") }
|
||||
|
||||
fun isPlayerCivilization() = gameInfo.getPlayerCivilization()==this
|
||||
|
||||
|
||||
// negative gold hurts science
|
||||
fun getStatsForNextTurn(): Stats {
|
||||
val statsForTurn = Stats()
|
||||
|
@ -105,10 +109,6 @@ class CivilizationInfo {
|
|||
}
|
||||
}
|
||||
|
||||
fun turnsToTech(TechName: String): Int {
|
||||
return Math.ceil(((GameBasics.Technologies[TechName]!!.cost - tech.researchOfTech(TechName))
|
||||
/ getStatsForNextTurn().science).toDouble()).toInt()
|
||||
}
|
||||
|
||||
fun addCity(location: Vector2) {
|
||||
val newCity = CityInfo(this, location)
|
||||
|
@ -144,6 +144,11 @@ class CivilizationInfo {
|
|||
fun placeUnitNearTile(location: Vector2, unitName: String) {
|
||||
gameInfo.tileMap.placeUnitNearTile(location, unitName, this)
|
||||
}
|
||||
|
||||
fun getCivUnits(): List<MapUnit> {
|
||||
return gameInfo.tileMap.values.filter { it.unit!=null && it.unit!!.owner==civName }.map { it.unit!! }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun <E> List<E>.getRandom(): E = if (size == 0) throw Exception() else get((Math.random() * size).toInt())
|
|
@ -27,11 +27,15 @@ class TechManager {
|
|||
else return techsToResearch[0]
|
||||
}
|
||||
|
||||
fun researchOfTech(TechName: String?): Int {
|
||||
private fun researchOfTech(TechName: String?): Int {
|
||||
if (techsInProgress.containsKey(TechName)) return techsInProgress[TechName]!!
|
||||
else return 0
|
||||
}
|
||||
|
||||
fun turnsToTech(TechName: String): Int {
|
||||
return Math.ceil(((GameBasics.Technologies[TechName]!!.cost - researchOfTech(TechName))
|
||||
/ civInfo.getStatsForNextTurn().science).toDouble()).toInt()
|
||||
}
|
||||
|
||||
fun isResearched(TechName: String): Boolean = techsResearched.contains(TechName)
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ class MapUnit {
|
|||
fun nextTurn() {
|
||||
val tile = getTile()
|
||||
doPostTurnAction(tile)
|
||||
if(currentMovement==maxMovement.toFloat()){
|
||||
if(currentMovement==maxMovement.toFloat()){ // didn't move this turn
|
||||
heal()
|
||||
}
|
||||
currentMovement = maxMovement.toFloat()
|
||||
|
|
|
@ -41,7 +41,7 @@ class TileInfo {
|
|||
get() = if (improvement == null) null else GameBasics.TileImprovements[improvement!!]
|
||||
|
||||
val neighbors: List<TileInfo>
|
||||
get() = tileMap!!.getTilesAtDistance(position, 1)
|
||||
get() = tileMap.getTilesAtDistance(position, 1)
|
||||
|
||||
val height: Int
|
||||
get() {
|
||||
|
@ -57,7 +57,7 @@ class TileInfo {
|
|||
|
||||
fun getOwner(): CivilizationInfo? {
|
||||
return if (owner == null) null
|
||||
else tileMap!!.gameInfo!!.civilizations.first { it.civName == owner }
|
||||
else tileMap.gameInfo!!.civilizations.first { it.civName == owner }
|
||||
}
|
||||
|
||||
fun getTerrainFeature(): Terrain? {
|
||||
|
@ -150,7 +150,7 @@ class TileInfo {
|
|||
}
|
||||
SB.appendln(this.baseTerrain)
|
||||
if (terrainFeature != null) SB.appendln(terrainFeature!!)
|
||||
if (hasViewableResource(tileMap!!.gameInfo!!.getPlayerCivilization())) SB.appendln(resource!!)
|
||||
if (hasViewableResource(tileMap.gameInfo!!.getPlayerCivilization())) SB.appendln(resource!!)
|
||||
if (roadStatus !== RoadStatus.None && !isCityCenter) SB.appendln(roadStatus)
|
||||
if (improvement != null) SB.appendln(improvement!!)
|
||||
if (improvementInProgress != null) SB.appendln("$improvementInProgress in ${this.turnsToImprovement} turns")
|
||||
|
|
|
@ -97,7 +97,7 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo) : PickerScreen()
|
|||
text.append(" (").append(techsToResearch.indexOf(techName) + 1).append(")")
|
||||
}
|
||||
|
||||
if (!civTech.isResearched(techName)) text.append("\r\n" + civInfo.turnsToTech(techName) + " turns")
|
||||
if (!civTech.isResearched(techName)) text.append("\r\n" + civInfo.tech.turnsToTech(techName) + " turns")
|
||||
TB.setText(text.toString())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@ 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() {
|
||||
|
||||
|
@ -30,8 +28,6 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
|||
|
||||
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)
|
||||
|
||||
|
@ -70,7 +66,8 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
|||
val attackButton = TextButton("Attack",CameraStageBaseScreen.skin)
|
||||
|
||||
attackButton.addClickListener {
|
||||
attack(attacker,defender)
|
||||
battle.attack(attacker,defender)
|
||||
worldScreen.update()
|
||||
}
|
||||
|
||||
val attackerCanReachDefender = attacker.getDistanceToTiles().containsKey(defender.getTile())
|
||||
|
@ -82,42 +79,4 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
|||
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()
|
||||
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||
techButton.setText("Choose a tech!")
|
||||
else
|
||||
techButton.setText(civInfo.tech.currentTechnology() + "\r\n"
|
||||
+ civInfo.turnsToTech(civInfo.tech.currentTechnology()!!) + " turns")
|
||||
+ civInfo.tech.turnsToTech(civInfo.tech.currentTechnology()!!) + " turns")
|
||||
|
||||
techButton.setSize(techButton.prefWidth, techButton.prefHeight)
|
||||
techButton.setPosition(10f, civTable.y - techButton.height - 5f)
|
||||
|
|
|
@ -145,7 +145,6 @@ class UnitActions {
|
|||
return actionList
|
||||
}
|
||||
|
||||
|
||||
private fun getUnitActionButton(unit: MapUnit, actionText: String, canAct: Boolean, action: () -> Unit): TextButton {
|
||||
val actionButton = TextButton(actionText, CameraStageBaseScreen.skin)
|
||||
actionButton.addClickListener({ action(); UnCivGame.Current.worldScreen!!.update() })
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Label
|
|||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.UnitType
|
||||
import com.unciv.ui.utils.CameraStageBaseScreen
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.worldscreen.WorldScreen
|
||||
|
@ -40,10 +41,17 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
|||
|
||||
if(selectedUnit!=null) {
|
||||
val unit = selectedUnit!!
|
||||
unitLabel.setText(unit.name
|
||||
+"\r\nMovement: " +unit.getMovementString()
|
||||
+"\r\nHealth: "+unit.health
|
||||
)
|
||||
if (unit.getBaseUnit().unitType == UnitType.Civilian) {
|
||||
unitLabel.setText(unit.name
|
||||
+ "\r\nMovement: " + unit.getMovementString()
|
||||
)
|
||||
} else {
|
||||
unitLabel.setText(unit.name
|
||||
+ "\r\nMovement: " + unit.getMovementString()
|
||||
+ "\r\nHealth: " + unit.health
|
||||
+ "\r\nStrength: " + unit.getBaseUnit().strength
|
||||
)
|
||||
}
|
||||
for (button in UnitActions().getUnitActions(selectedUnit!!, worldScreen))
|
||||
unitActionsTable.add(button).colspan(2).pad(5f)
|
||||
.size(button.width * worldScreen.buttonScale, button.height * worldScreen.buttonScale).row()
|
||||
|
|
Loading…
Reference in a new issue