diff --git a/core/src/com/unciv/logic/Battle.kt b/core/src/com/unciv/logic/Battle.kt index dbd67a0499..ea9ee4b4d5 100644 --- a/core/src/com/unciv/logic/Battle.kt +++ b/core/src/com/unciv/logic/Battle.kt @@ -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 + } + } \ 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 7925189c63..8e7b2f1fea 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -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()) + } + } + } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index a12701f4bb..829a8b6739 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -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 { + return gameInfo.tileMap.values.filter { it.unit!=null && it.unit!!.owner==civName }.map { it.unit!! } + } + } fun List.getRandom(): E = if (size == 0) throw Exception() else get((Math.random() * size).toInt()) \ No newline at end of file diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 460bd2e80f..27e78b2c47 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -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) diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index f7227b29e8..297dce85a5 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -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() diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 4978ddd5d0..393fcc7cb4 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -41,7 +41,7 @@ class TileInfo { get() = if (improvement == null) null else GameBasics.TileImprovements[improvement!!] val neighbors: List - 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") diff --git a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt index e127af1133..b9445c5533 100644 --- a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt @@ -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()) } } diff --git a/core/src/com/unciv/ui/worldscreen/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/BattleTable.kt index 5cd406cd51..1c30d22f81 100644 --- a/core/src/com/unciv/ui/worldscreen/BattleTable.kt +++ b/core/src/com/unciv/ui/worldscreen/BattleTable.kt @@ -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() - - } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index cea5888e8b..4e74da8e43 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -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) diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 21cb17c621..627d1dc7a3 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -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() }) diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 1dcaadc0a7..c4a710f6bd 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -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()