Redid how finding attackable enemies works

Bottom line: AI now moves ranged units and attacks in the same turn
This commit is contained in:
Yair Morgenstern 2018-06-20 19:41:00 +03:00
parent b596ca9863
commit bfad59ca9b
7 changed files with 38 additions and 44 deletions

View File

@ -16,7 +16,7 @@ class UnCivGame : Game() {
* This exists so that when debugging we can see the entire map. * This exists so that when debugging we can see the entire map.
* Remember to turn this to false before commit and upload! * Remember to turn this to false before commit and upload!
*/ */
val viewEntireMapForDebug = false val viewEntireMapForDebug = true

View File

@ -39,29 +39,23 @@ class UnitAutomation{
return tileCombatant.getCivilization()!=civInfo return tileCombatant.getCivilization()!=civInfo
} }
fun getAttackableEnemies(unit: MapUnit): List<TileInfo> { class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo)
val attackableTiles = unit.civInfo.getViewableTiles()
fun getAttackableEnemies(unit: MapUnit): ArrayList<AttackableTile> {
val tilesWithEnemies = unit.civInfo.getViewableTiles()
.filter { containsAttackableEnemy(it,unit.civInfo) } .filter { containsAttackableEnemy(it,unit.civInfo) }
if(MapUnitCombatant(unit).isMelee()) { val distanceToTiles = unit.getDistanceToTiles()
val distanceToTiles = unit.getDistanceToTiles() val rangeOfAttack = if(MapUnitCombatant(unit).isMelee()) 1 else unit.getBaseUnit().range
// If we're conducting a melee attack,
// then there needs to be a tile adjacent to the enemy that we can get to,
// AND STILL HAVE MOVEMENT POINTS REMAINING,
return attackableTiles.filter {
it.neighbors.any {
unit.getTile()==it || // We're already right nearby
unit.canMoveTo(it)
&& distanceToTiles.containsKey(it)
&& distanceToTiles[it]!! < unit.currentMovement // We can get there
}
}
}
else { // Range attack, so enemy needs to be in range val attackableTiles = ArrayList<AttackableTile>()
return attackableTiles.filter { unit.getTile().getTilesInDistance(unit.getBaseUnit().range).contains(it) } val tilesToAttackFrom = distanceToTiles.filter { it.value!=unit.currentMovement }.map { it.key }
.filter { unit.canMoveTo(it) || it==unit.getTile() }
for(reachableTile in tilesToAttackFrom){ // tiles we'll still have energy after we reach there
attackableTiles += reachableTile.getTilesInDistance(rangeOfAttack).filter { it in tilesWithEnemies }
.map { AttackableTile(reachableTile,it) }
} }
return attackableTiles
} }
fun automateUnitMoves(unit: MapUnit) { fun automateUnitMoves(unit: MapUnit) {
@ -93,21 +87,20 @@ class UnitAutomation{
} // do nothing but heal } // do nothing but heal
// if there is an attackable unit in the vicinity, attack! // if there is an attackable unit in the vicinity, attack!
val enemyTileToAttack = getAttackableEnemies(unit).firstOrNull() val enemyTileToAttack = getAttackableEnemies(unit)
// Only take enemies we can fight without dying
.filter { BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit),
Battle().getMapCombatantOfTile(it.tileToAttack)!!) < unit.health }
.firstOrNull()
if (enemyTileToAttack != null) { if (enemyTileToAttack != null) {
val enemy = Battle().getMapCombatantOfTile(enemyTileToAttack.tileToAttack)!!
unit.moveToTile(enemyTileToAttack.tileToAttackFrom)
val setupAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).firstOrNull{ it.name == "Set up" } val setupAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).firstOrNull{ it.name == "Set up" }
if(setupAction!=null) setupAction.action() if(setupAction!=null) setupAction.action()
if(unit.currentMovement>0) // This can be 0, if the set up action took away what action points we had left...
val enemy = Battle().getMapCombatantOfTile(enemyTileToAttack)!!
val damageToAttacker = BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit), enemy)
if (damageToAttacker < unit.health) { // don't attack if we'll die from the attack
if(MapUnitCombatant(unit).isMelee())
unit.movementAlgs().headTowards(enemyTileToAttack)
Battle(unit.civInfo.gameInfo).attack(MapUnitCombatant(unit), enemy) Battle(unit.civInfo.gameInfo).attack(MapUnitCombatant(unit), enemy)
return return
}
} }
if(unit.getTile().isCityCenter()) return // It's always good to have a unit in the city center, so if you havn't found annyonw aroud to attack, forget it. if(unit.getTile().isCityCenter()) return // It's always good to have a unit in the city center, so if you havn't found annyonw aroud to attack, forget it.

View File

@ -159,6 +159,7 @@ open class TileInfo {
if (improvement != null) SB.appendln(improvement!!.tr()) if (improvement != null) SB.appendln(improvement!!.tr())
if (improvementInProgress != null) SB.appendln("{$improvementInProgress} in ${this.turnsToImprovement} {turns}".tr()) if (improvementInProgress != null) SB.appendln("{$improvementInProgress} in ${this.turnsToImprovement} {turns}".tr())
val isViewableToPlayer = UnCivGame.Current.gameInfo.getPlayerCivilization().getViewableTiles().contains(this) val isViewableToPlayer = UnCivGame.Current.gameInfo.getPlayerCivilization().getViewableTiles().contains(this)
|| UnCivGame.Current.viewEntireMapForDebug
if (civilianUnit != null && isViewableToPlayer) SB.appendln(civilianUnit!!.name) if (civilianUnit != null && isViewableToPlayer) SB.appendln(civilianUnit!!.name)
if(militaryUnit!=null && isViewableToPlayer){ if(militaryUnit!=null && isViewableToPlayer){
var milUnitString = militaryUnit!!.name var milUnitString = militaryUnit!!.name

View File

@ -25,7 +25,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): HashMap<TileInfo, Float> { fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): HashMap<TileInfo, Float> {
if(unitMovement==0f) return hashMapOf() if(unitMovement==0f) return hashMapOf()
val distanceToTiles = HashMap<TileInfo, Float>() val distanceToTiles = LinkedHashMap<TileInfo, Float>()
val unitTile = tileMap[origin] val unitTile = tileMap[origin]
distanceToTiles[unitTile] = 0f distanceToTiles[unitTile] = 0f
var tilesToCheck = listOf(unitTile) var tilesToCheck = listOf(unitTile)

View File

@ -44,7 +44,8 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) {
addPopulationIcon() addPopulationIcon()
if (tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position) if (tileInfo.tileMap.gameInfo.getPlayerCivilization().exploredTiles.contains(tileInfo.position)
|| UnCivGame.Current.viewEntireMapForDebug) updateCityButton(city, isViewable) // needs to be before the update so the units will be above the city button || UnCivGame.Current.viewEntireMapForDebug)
updateCityButton(city, isViewable || UnCivGame.Current.viewEntireMapForDebug) // needs to be before the update so the units will be above the city button
super.update(isViewable || UnCivGame.Current.viewEntireMapForDebug) super.update(isViewable || UnCivGame.Current.viewEntireMapForDebug)

View File

@ -6,7 +6,9 @@ import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.InputEvent import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener
import com.unciv.UnCivGame
import com.unciv.logic.HexMath import com.unciv.logic.HexMath
import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap import com.unciv.logic.map.TileMap
@ -33,7 +35,6 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
group.addClickListener { group.addClickListener {
worldScreen.displayTutorials("TileClicked") worldScreen.displayTutorials("TileClicked")
selectedTile = tileInfo selectedTile = tileInfo
worldScreen.bottomBar.unitTable.tileSelected(tileInfo) worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
worldScreen.update() worldScreen.update()
@ -92,7 +93,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
for (WG in tileGroups.values){ for (WG in tileGroups.values){
WG.update(playerViewableTiles.contains(WG.tileInfo)) WG.update(playerViewableTiles.contains(WG.tileInfo))
val unitsInTile = WG.tileInfo.getUnits() val unitsInTile = WG.tileInfo.getUnits()
if(playerViewableTiles.contains(WG.tileInfo) if((playerViewableTiles.contains(WG.tileInfo) || UnCivGame.Current.viewEntireMapForDebug)
&& unitsInTile.isNotEmpty() && unitsInTile.first().civInfo!=civInfo) && unitsInTile.isNotEmpty() && unitsInTile.first().civInfo!=civInfo)
WG.showCircle(Color.RED) WG.showCircle(Color.RED)
} // Display ALL viewable enemies ewith a red circle so that users don't need to go "hunting" for enemy units } // Display ALL viewable enemies ewith a red circle so that users don't need to go "hunting" for enemy units
@ -107,16 +108,14 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
val unitType = unit.getBaseUnit().unitType val unitType = unit.getBaseUnit().unitType
val attackableTiles: List<TileInfo> = when{ val attackableTiles: List<TileInfo> = when{
unitType==UnitType.Civilian -> unit.getDistanceToTiles().keys.toList() unitType==UnitType.Civilian -> unit.getDistanceToTiles().keys.toList()
unit.getBaseUnit().unitType.isMelee() -> unit.getDistanceToTiles().keys.toList() else -> UnitAutomation().getAttackableEnemies(unit).map { it.tileToAttack }
unitType.isRanged() -> unit.getTile().getTilesInDistance(2)
else -> throw Exception("UnitType isn't Civilian, Melee or Ranged???")
} }
for (tile in attackableTiles.filter { for (tile in attackableTiles.filter {
it.getUnits().isNotEmpty() it.getUnits().isNotEmpty()
&& it.getUnits().first().owner != unit.owner && it.getUnits().first().owner != unit.owner
&& playerViewableTiles.contains(it)}) { && (playerViewableTiles.contains(it) || UnCivGame.Current.viewEntireMapForDebug)}) {
if(unit.getBaseUnit().unitType== UnitType.Civilian) tileGroups[tile]!!.hideCircle() if(unit.getBaseUnit().unitType== UnitType.Civilian) tileGroups[tile]!!.hideCircle()
else tileGroups[tile]!!.showCircle(colorFromRGB(237, 41, 57)) else tileGroups[tile]!!.showCircle(colorFromRGB(237, 41, 57))
} }

View File

@ -3,6 +3,7 @@ package com.unciv.ui.worldscreen.bottombar
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.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.UnCivGame
import com.unciv.logic.automation.UnitAutomation import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.battle.Battle import com.unciv.logic.battle.Battle
import com.unciv.logic.battle.BattleDamage import com.unciv.logic.battle.BattleDamage
@ -43,7 +44,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
val defender: ICombatant? = Battle().getMapCombatantOfTile(selectedTile) val defender: ICombatant? = Battle().getMapCombatantOfTile(selectedTile)
if(defender==null || defender.getCivilization()==worldScreen.civInfo if(defender==null || defender.getCivilization()==worldScreen.civInfo
|| !attacker.getCivilization().exploredTiles.contains(selectedTile.position)) { || !(attacker.getCivilization().exploredTiles.contains(selectedTile.position) || UnCivGame.Current.viewEntireMapForDebug)) {
hide() hide()
return return
} }
@ -120,14 +121,13 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
attacker.unit.getDistanceToTiles() attacker.unit.getDistanceToTiles()
val attackerCanReachDefender = UnitAutomation().getAttackableEnemies(attacker.unit) val attackableEnemy = UnitAutomation().getAttackableEnemies(attacker.unit)
.contains(defender.getTile()) .firstOrNull{ it.tileToAttack == defender.getTile()}
if(!attackerCanReachDefender || !attacker.unit.canAttack()) attackButton.disable() if(attackableEnemy==null || !attacker.unit.canAttack()) attackButton.disable()
else { else {
attackButton.addClickListener { attackButton.addClickListener {
if(attacker.isMelee()) attacker.unit.moveToTile(attackableEnemy.tileToAttackFrom)
attacker.unit.movementAlgs().headTowards(defender.getTile())
battle.attack(attacker, defender) battle.attack(attacker, defender)
worldScreen.update() worldScreen.update()
} }