Enemy troops now have movement and will attack

This commit is contained in:
Yair Morgenstern 2018-04-02 23:14:42 +03:00
parent 5d00adb7fe
commit 889072c725
11 changed files with 129 additions and 64 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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")

View file

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

View file

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

View file

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

View file

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

View file

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