AIR UNITS ARE GO!
Added interception and basic air unit AI
This commit is contained in:
parent
51ad40b8bf
commit
214b4880e4
9 changed files with 110 additions and 21 deletions
|
@ -304,6 +304,18 @@
|
|||
Japanese:"「私たちの[ourUnit]を攻撃している間に敵[unit]が破壊された"
|
||||
}
|
||||
|
||||
"Our [attackerName] was destroyed by an intercepting [interceptorName]":{
|
||||
}
|
||||
|
||||
"Our [interceptorName] intercepted and destroyed an enemy [attackerName]":{
|
||||
}
|
||||
|
||||
"Our [$attackerName] was attacked by an intercepting [$interceptorName]":{
|
||||
}
|
||||
|
||||
"Our [$interceptorName] intercepted and attacked an enemy [$attackerName]":{
|
||||
}
|
||||
|
||||
"An enemy [unit] was spotted near our territory":{
|
||||
Italian:"Abbiamo avvistato un'unità nemica [unit] vicino al nostro territorio"
|
||||
Russian:"Вражеский [unit] был замечен у нашей территории"
|
||||
|
|
|
@ -960,15 +960,6 @@
|
|||
French:"Porte-avions"
|
||||
}
|
||||
|
||||
"Great War Bomber":{
|
||||
Italian:"Bombardiere della Grande Guerra"
|
||||
Romanian:"Bombardier din Marele Război"
|
||||
Spanish:"Bombardero de la Gran Guerra"
|
||||
Simplified_Chinese:"早期轰炸机"
|
||||
Russian:"великий бомбардировщик0_0"
|
||||
German:"Weltkriegsbomber"
|
||||
French:"Bombardier de la grande guerre"
|
||||
}
|
||||
|
||||
"Triplane":{
|
||||
Italian:"Triplano"
|
||||
|
@ -980,6 +971,20 @@
|
|||
French:"Triplan"
|
||||
}
|
||||
|
||||
"[percent]% chance to intercept air attacks":{
|
||||
}
|
||||
|
||||
|
||||
"Great War Bomber":{
|
||||
Italian:"Bombardiere della Grande Guerra"
|
||||
Romanian:"Bombardier din Marele Război"
|
||||
Spanish:"Bombardero de la Gran Guerra"
|
||||
Simplified_Chinese:"早期轰炸机"
|
||||
Russian:"великий бомбардировщик0_0"
|
||||
German:"Weltkriegsbomber"
|
||||
French:"Bombardier de la grande guerre"
|
||||
}
|
||||
|
||||
////// Atomic units (not in game yet but will be, not super important but why not)
|
||||
|
||||
"Rocket Artillery":{
|
||||
|
|
|
@ -838,10 +838,10 @@
|
|||
hurryCostModifier:20,
|
||||
attackSound:"shot"
|
||||
},
|
||||
/*
|
||||
|
||||
{
|
||||
name:"Triplane",
|
||||
unitType:"AirFighter",
|
||||
unitType:"Fighter",
|
||||
movement:1,
|
||||
strength:35,
|
||||
rangedStrength:35,
|
||||
|
@ -849,6 +849,8 @@
|
|||
cost: 325,
|
||||
requiredTech:"Flight",
|
||||
hurryCostModifier:20,
|
||||
uniques:["[50]% chance to intercept air attacks","Bonus vs Bomber 150%",
|
||||
"6 tiles in every direction always visible"]
|
||||
attackSound:"shot"
|
||||
},
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ android {
|
|||
applicationId "com.unciv.app"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 28
|
||||
versionCode 270
|
||||
versionName "2.17.15"
|
||||
versionCode 271
|
||||
versionName "2.18.0"
|
||||
}
|
||||
|
||||
// Had to add this crap for Travis to build, it wanted to sign the app
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.unciv.logic.automation
|
|||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UnCivGame
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.civilization.GreatPersonManager
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
|
||||
|
@ -161,4 +162,24 @@ class SpecificUnitAutomation{
|
|||
|
||||
}
|
||||
|
||||
fun automateFighter(unit: MapUnit) {
|
||||
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange())
|
||||
val enemyAirUnitsInRange = tilesInRange
|
||||
.flatMap { it.airUnits }.filter { it.civInfo.isAtWarWith(unit.civInfo) }
|
||||
|
||||
if(enemyAirUnitsInRange.isNotEmpty()) return // we need to be on standby in case they attack
|
||||
if(UnitAutomation().tryAttackNearbyEnemy(unit)) return
|
||||
|
||||
val reachableCities = tilesInRange
|
||||
.filter { it.isCityCenter() && it.getOwner()==unit.civInfo && unit.canMoveTo(it)}
|
||||
|
||||
for(city in reachableCities){
|
||||
if(city.getTilesInDistance(unit.getRange())
|
||||
.any { UnitAutomation().containsAttackableEnemy(it,MapUnitCombatant(unit)) }) {
|
||||
unit.moveToTile(city)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,9 @@ class UnitAutomation{
|
|||
if (unit.name == "Great General")
|
||||
return SpecificUnitAutomation().automateGreatGeneral(unit)
|
||||
|
||||
if(unit.type==UnitType.Fighter)
|
||||
return SpecificUnitAutomation().automateFighter(unit)
|
||||
|
||||
if(unit.name.startsWith("Great")
|
||||
&& unit.name in GreatPersonManager().statToGreatPersonMapping.values){ // So "Great War Infantry" isn't caught here
|
||||
return SpecificUnitAutomation().automateGreatPerson(unit)
|
||||
|
@ -61,7 +64,7 @@ class UnitAutomation{
|
|||
|
||||
// if a embarked melee unit can land and attack next turn, do not attack from water.
|
||||
if (unit.type.isLandUnit() && unit.type.isMelee() && unit.isEmbarked()) {
|
||||
if (tryLandUnitToAttackPosition(unit,unitDistanceToTiles)) return
|
||||
if (tryDisembarkUnitToAttackPosition(unit,unitDistanceToTiles)) return
|
||||
}
|
||||
|
||||
// if there is an attackable unit in the vicinity, attack!
|
||||
|
@ -330,7 +333,7 @@ class UnitAutomation{
|
|||
return false
|
||||
}
|
||||
|
||||
private fun tryLandUnitToAttackPosition(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
|
||||
private fun tryDisembarkUnitToAttackPosition(unit: MapUnit, unitDistanceToTiles: HashMap<TileInfo, Float>): Boolean {
|
||||
if (!unit.type.isMelee() || !unit.type.isLandUnit() || !unit.isEmbarked()) return false
|
||||
val attackableEnemiesNextTurn = getAttackableEnemies(unit, unitDistanceToTiles)
|
||||
// Only take enemies we can fight without dying
|
||||
|
@ -349,7 +352,7 @@ class UnitAutomation{
|
|||
return false
|
||||
}
|
||||
|
||||
private fun tryAttackNearbyEnemy(unit: MapUnit): Boolean {
|
||||
fun tryAttackNearbyEnemy(unit: MapUnit): Boolean {
|
||||
val attackableEnemies = getAttackableEnemies(unit, unit.getDistanceToTiles())
|
||||
// Only take enemies we can fight without dying
|
||||
.filter {
|
||||
|
|
|
@ -34,6 +34,11 @@ class Battle(val gameInfo:GameInfo) {
|
|||
println(attacker.getCivInfo().civName+" "+attacker.getName()+" attacked "+defender.getCivInfo().civName+" "+defender.getName())
|
||||
val attackedTile = defender.getTile()
|
||||
|
||||
if(attacker is MapUnitCombatant && attacker.getUnitType().isAirUnit()){
|
||||
intercept(attacker,defender)
|
||||
if(attacker.isDefeated()) return
|
||||
}
|
||||
|
||||
var damageToDefender = BattleDamage().calculateDamageToDefender(attacker,defender)
|
||||
var damageToAttacker = BattleDamage().calculateDamageToAttacker(attacker,defender)
|
||||
|
||||
|
@ -287,4 +292,30 @@ class Battle(val gameInfo:GameInfo) {
|
|||
capturedUnit.assignOwner(attacker.getCivInfo())
|
||||
capturedUnit.updateViewableTiles()
|
||||
}
|
||||
|
||||
fun intercept(attacker:MapUnitCombatant, defender: ICombatant){
|
||||
val attackedTile = defender.getTile()
|
||||
for(unit in defender.getCivInfo().getCivUnits().filter { it.canIntercept(attackedTile) }){
|
||||
if(Random().nextFloat() > 100f/unit.interceptChance()) continue
|
||||
val damage = BattleDamage().calculateDamageToDefender(MapUnitCombatant(unit),attacker)
|
||||
attacker.takeDamage(damage)
|
||||
|
||||
val attackerName = attacker.getName()
|
||||
val interceptorName = unit.name
|
||||
|
||||
if(attacker.isDefeated()){
|
||||
attacker.getCivInfo().addNotification("Our [$attackerName] was destroyed by an intercepting [$interceptorName]",
|
||||
Color.RED)
|
||||
defender.getCivInfo().addNotification("Our [$interceptorName] intercepted and destroyed an enemy [$attackerName]",
|
||||
unit.currentTile.position, Color.RED)
|
||||
}
|
||||
else{
|
||||
attacker.getCivInfo().addNotification("Our [$attackerName] was attacked by an intercepting [$interceptorName]",
|
||||
Color.RED)
|
||||
defender.getCivInfo().addNotification("Our [$interceptorName] intercepted and attacked an enemy [$attackerName]",
|
||||
unit.currentTile.position, Color.RED)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,9 @@ class MapUnit {
|
|||
// we need to map all the places that this could change: Unit changes locations, owners, gets promotion?
|
||||
fun updateViewableTiles() {
|
||||
if(type.isAirUnit()){
|
||||
viewableTiles = getTile().getTilesInDistance(6) // it's that simple
|
||||
if(hasUnique("6 tiles in every direction always visible"))
|
||||
viewableTiles = getTile().getTilesInDistance(6) // it's that simple
|
||||
else viewableTiles = listOf() // bomber units don't do recon
|
||||
}
|
||||
else {
|
||||
var visibilityRange = 2
|
||||
|
@ -559,5 +561,18 @@ class MapUnit {
|
|||
civInfo.addUnit(this,updateCivInfo)
|
||||
}
|
||||
|
||||
fun canIntercept(attackedTile: TileInfo): Boolean {
|
||||
return interceptChance()!=0 && attacksThisTurn==0
|
||||
&& currentTile.arialDistanceTo(attackedTile) <= getRange()
|
||||
}
|
||||
|
||||
fun interceptChance():Int{
|
||||
val interceptUnique = getUniques()
|
||||
.firstOrNull { it.endsWith(" chance to intercept air attacks") }
|
||||
if(interceptUnique==null) return 0
|
||||
val percent = Regex("\\d+").find(interceptUnique)!!.value
|
||||
return percent.toInt()
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
|
@ -15,8 +15,8 @@ enum class UnitType{
|
|||
WaterRanged,
|
||||
WaterSubmarine,
|
||||
|
||||
AirFighter,
|
||||
AirBomber;
|
||||
Fighter,
|
||||
Bomber;
|
||||
|
||||
fun isMelee(): Boolean {
|
||||
return this == Melee
|
||||
|
@ -57,7 +57,7 @@ enum class UnitType{
|
|||
}
|
||||
|
||||
fun isAirUnit():Boolean{
|
||||
return this==AirBomber
|
||||
|| this==AirFighter
|
||||
return this==Bomber
|
||||
|| this==Fighter
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue