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.
* 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
}
fun getAttackableEnemies(unit: MapUnit): List<TileInfo> {
val attackableTiles = unit.civInfo.getViewableTiles()
class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo)
fun getAttackableEnemies(unit: MapUnit): ArrayList<AttackableTile> {
val tilesWithEnemies = unit.civInfo.getViewableTiles()
.filter { containsAttackableEnemy(it,unit.civInfo) }
if(MapUnitCombatant(unit).isMelee()) {
val distanceToTiles = unit.getDistanceToTiles()
// 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
}
}
}
val distanceToTiles = unit.getDistanceToTiles()
val rangeOfAttack = if(MapUnitCombatant(unit).isMelee()) 1 else unit.getBaseUnit().range
else { // Range attack, so enemy needs to be in range
return attackableTiles.filter { unit.getTile().getTilesInDistance(unit.getBaseUnit().range).contains(it) }
val attackableTiles = ArrayList<AttackableTile>()
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) {
@ -93,21 +87,20 @@ class UnitAutomation{
} // do nothing but heal
// 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) {
val enemy = Battle().getMapCombatantOfTile(enemyTileToAttack.tileToAttack)!!
unit.moveToTile(enemyTileToAttack.tileToAttackFrom)
val setupAction = UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen).firstOrNull{ it.name == "Set up" }
if(setupAction!=null) setupAction.action()
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)
if(unit.currentMovement>0) // This can be 0, if the set up action took away what action points we had left...
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.

View file

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

View file

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

View file

@ -44,7 +44,8 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) {
addPopulationIcon()
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)

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.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener
import com.unciv.UnCivGame
import com.unciv.logic.HexMath
import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap
@ -33,7 +35,6 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
group.addClickListener {
worldScreen.displayTutorials("TileClicked")
selectedTile = tileInfo
worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
worldScreen.update()
@ -92,7 +93,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
for (WG in tileGroups.values){
WG.update(playerViewableTiles.contains(WG.tileInfo))
val unitsInTile = WG.tileInfo.getUnits()
if(playerViewableTiles.contains(WG.tileInfo)
if((playerViewableTiles.contains(WG.tileInfo) || UnCivGame.Current.viewEntireMapForDebug)
&& unitsInTile.isNotEmpty() && unitsInTile.first().civInfo!=civInfo)
WG.showCircle(Color.RED)
} // 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 attackableTiles: List<TileInfo> = when{
unitType==UnitType.Civilian -> unit.getDistanceToTiles().keys.toList()
unit.getBaseUnit().unitType.isMelee() -> unit.getDistanceToTiles().keys.toList()
unitType.isRanged() -> unit.getTile().getTilesInDistance(2)
else -> throw Exception("UnitType isn't Civilian, Melee or Ranged???")
else -> UnitAutomation().getAttackableEnemies(unit).map { it.tileToAttack }
}
for (tile in attackableTiles.filter {
it.getUnits().isNotEmpty()
&& it.getUnits().first().owner != unit.owner
&& playerViewableTiles.contains(it)}) {
&& (playerViewableTiles.contains(it) || UnCivGame.Current.viewEntireMapForDebug)}) {
if(unit.getBaseUnit().unitType== UnitType.Civilian) tileGroups[tile]!!.hideCircle()
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.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.UnCivGame
import com.unciv.logic.automation.UnitAutomation
import com.unciv.logic.battle.Battle
import com.unciv.logic.battle.BattleDamage
@ -43,7 +44,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
val defender: ICombatant? = Battle().getMapCombatantOfTile(selectedTile)
if(defender==null || defender.getCivilization()==worldScreen.civInfo
|| !attacker.getCivilization().exploredTiles.contains(selectedTile.position)) {
|| !(attacker.getCivilization().exploredTiles.contains(selectedTile.position) || UnCivGame.Current.viewEntireMapForDebug)) {
hide()
return
}
@ -120,14 +121,13 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
attacker.unit.getDistanceToTiles()
val attackerCanReachDefender = UnitAutomation().getAttackableEnemies(attacker.unit)
.contains(defender.getTile())
val attackableEnemy = UnitAutomation().getAttackableEnemies(attacker.unit)
.firstOrNull{ it.tileToAttack == defender.getTile()}
if(!attackerCanReachDefender || !attacker.unit.canAttack()) attackButton.disable()
if(attackableEnemy==null || !attacker.unit.canAttack()) attackButton.disable()
else {
attackButton.addClickListener {
if(attacker.isMelee())
attacker.unit.movementAlgs().headTowards(defender.getTile())
attacker.unit.moveToTile(attackableEnemy.tileToAttackFrom)
battle.attack(attacker, defender)
worldScreen.update()
}