diff --git a/android/assets/jsons/Translations/Notifications.json b/android/assets/jsons/Translations/Notifications.json index 3076c5de2e..02abeb3247 100644 --- a/android/assets/jsons/Translations/Notifications.json +++ b/android/assets/jsons/Translations/Notifications.json @@ -303,6 +303,18 @@ Portuguese:"Um(a) [unit] inimigo(a) foi destruido(a) enquanto atacando nosso(a) [ourUit]" 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" diff --git a/android/assets/jsons/Translations/Units,Promotions.json b/android/assets/jsons/Translations/Units,Promotions.json index 78d2e3dfb8..fcc4c5c52c 100644 --- a/android/assets/jsons/Translations/Units,Promotions.json +++ b/android/assets/jsons/Translations/Units,Promotions.json @@ -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":{ diff --git a/android/assets/jsons/Units.json b/android/assets/jsons/Units.json index c86211c6d1..78dcd22822 100644 --- a/android/assets/jsons/Units.json +++ b/android/assets/jsons/Units.json @@ -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" }, diff --git a/android/build.gradle b/android/build.gradle index e8865ca573..c713a401d7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -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 diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index c80becc1c8..266e2be82c 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -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 + } + } + } + } \ No newline at end of file diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 4ef2301e7f..e2c4e01df5 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -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): Boolean { + private fun tryDisembarkUnitToAttackPosition(unit: MapUnit, unitDistanceToTiles: HashMap): 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 { diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index f80bf71768..5677a9b6df 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -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 + } + } } diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 6dc1cf90a9..bbffbadbd7 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -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 } \ No newline at end of file diff --git a/core/src/com/unciv/models/gamebasics/unit/UnitType.kt b/core/src/com/unciv/models/gamebasics/unit/UnitType.kt index 68cb0c283e..43296abb44 100644 --- a/core/src/com/unciv/models/gamebasics/unit/UnitType.kt +++ b/core/src/com/unciv/models/gamebasics/unit/UnitType.kt @@ -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 } } \ No newline at end of file