diff --git a/android/Images/UnitIcons/Carrier.png b/android/Images/UnitIcons/Carrier.png new file mode 100644 index 00000000..8b08dc95 Binary files /dev/null and b/android/Images/UnitIcons/Carrier.png differ diff --git a/android/assets/jsons/Units.json b/android/assets/jsons/Units.json index a361693b..8148d616 100644 --- a/android/assets/jsons/Units.json +++ b/android/assets/jsons/Units.json @@ -856,6 +856,16 @@ "hurryCostModifier": 20, "attackSound": "shot" }, + { + "name": "Carrier", + "unitType": "WaterAircraftCarrier", + "movement": 5, + "strength": 40, + "cost": 375, + "requiredTech": "Electronics", + "uniques": ["Can carry 2 aircraft"], + "hurryCostModifier": 20 + }, { "name": "Triplane", "unitType": "Fighter", diff --git a/android/assets/jsons/translationsByLanguage/English.properties b/android/assets/jsons/translationsByLanguage/English.properties index ff8c48e4..3766ce82 100644 --- a/android/assets/jsons/translationsByLanguage/English.properties +++ b/android/assets/jsons/translationsByLanguage/English.properties @@ -3050,6 +3050,8 @@ Ambush I = # Requires translation! Ambush II = # Requires translation! +Can carry 2 aircraft = Can carry 2 aircraft + # Requires translation! Haka War Dance = # Requires translation! -10% combat strength for adjacent enemy units = diff --git a/android/assets/jsons/translationsByLanguage/template.properties b/android/assets/jsons/translationsByLanguage/template.properties index 92d92a5a..7fd26873 100644 --- a/android/assets/jsons/translationsByLanguage/template.properties +++ b/android/assets/jsons/translationsByLanguage/template.properties @@ -1657,6 +1657,7 @@ Reduces damage taken from interception by 50% = Bonus when intercepting [amount]% = Ambush I = Ambush II = +Can carry 2 aircraft = Haka War Dance = -10% combat strength for adjacent enemy units = Rejuvenation = diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index 2ba6a238..0adc86a6 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -190,6 +190,8 @@ class SpecificUnitAutomation{ if(enemyAirUnitsInRange.isNotEmpty()) return // we need to be on standby in case they attack if(battleHelper.tryAttackNearbyEnemy(unit)) return + // TODO Implement consideration for landing on aircraft carrier + val immediatelyReachableCities = tilesInRange .filter { it.isCityCenter() && it.getOwner()==unit.civInfo && unit.movement.canMoveTo(it)} @@ -226,6 +228,8 @@ class SpecificUnitAutomation{ val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange()) + // TODO Implement consideration for landing on aircraft carrier + val immediatelyReachableCities = tilesInRange .filter { it.isCityCenter() && it.getOwner() == unit.civInfo && unit.movement.canMoveTo(it) } diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 129b1504..e4320d55 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -63,6 +63,7 @@ class MapUnit { var attacksThisTurn = 0 var promotions = UnitPromotions() var due: Boolean = true + var isUnitInCity: Boolean = true companion object { private const val ANCIENT_RUIN_MAP_REVEAL_OFFSET = 4 @@ -80,6 +81,7 @@ class MapUnit { toReturn.action=action toReturn.attacksThisTurn=attacksThisTurn toReturn.promotions=promotions.clone() + toReturn.isUnitInCity=isUnitInCity return toReturn } @@ -461,6 +463,7 @@ class MapUnit { type.isCivilian() -> tile.civilianUnit=this else -> tile.militaryUnit=this } + isUnitInCity = tile.isCityCenter() // prevent carriers from sailing away with air units explicitly assigned to city moveThroughTile(tile) } diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index a66a8c58..c4421bb6 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -237,9 +237,20 @@ class UnitMovementAlgorithms(val unit:MapUnit) { if(unit.isFortified() || unit.action==Constants.unitActionSetUp || unit.action==Constants.unitActionSleep) unit.action=null // unfortify/setup after moving + // If this unit is a carrier, keep record of its air payload whereabouts. + var origin = unit.getTile() + unit.removeFromTile() unit.putInTile(destination) + if(unit.type.isAircraftCarrierUnit() || unit.type.isMissileCarrierUnit()){ // bring along the payloads + for(airUnit in origin.airUnits.filter { !it.isUnitInCity }){ + airUnit.removeFromTile() + airUnit.putInTile(destination) + airUnit.isUnitInCity = false // don't leave behind payloads in the city if carrier happens to dock + } + } + // Move through all intermediate tiles to get ancient ruins, barb encampments // and to view tiles along the way // We only activate the moveThroughTile AFTER the putInTile because of a really weird bug - @@ -260,7 +271,19 @@ class UnitMovementAlgorithms(val unit:MapUnit) { */ fun canMoveTo(tile: TileInfo): Boolean { if(unit.type.isAirUnit()) - return tile.airUnits.size<6 && tile.isCityCenter() && tile.getCity()?.civInfo==unit.civInfo + if(tile.isCityCenter()) + return tile.airUnits.size<6 && tile.getCity()?.civInfo==unit.civInfo + else if(tile.militaryUnit!=null) { + val unitAtDestination = tile.militaryUnit!! + + var unitCapacity = if (unitAtDestination.getUniques().contains("Can carry 2 aircraft")) 2 else 0 + // unitCapacity += unitAtDestination.getUniques().count { it == "Can carry 1 extra air unit" } + + return ((unitAtDestination.type.isAircraftCarrierUnit() && !unit.type.isMissileUnit()) || + (unitAtDestination.type.isMissileCarrierUnit() && unit.type.isMissileUnit())) + && unitAtDestination.owner==unit.owner && tile.airUnits.size < unitCapacity + } else + return false if(!canPassThrough(tile)) return false diff --git a/core/src/com/unciv/models/ruleset/unit/UnitType.kt b/core/src/com/unciv/models/ruleset/unit/UnitType.kt index e8713694..3818a538 100644 --- a/core/src/com/unciv/models/ruleset/unit/UnitType.kt +++ b/core/src/com/unciv/models/ruleset/unit/UnitType.kt @@ -14,6 +14,8 @@ enum class UnitType{ WaterMelee, WaterRanged, WaterSubmarine, + WaterAircraftCarrier, + WaterMissileCarrier, Fighter, Bomber, @@ -60,6 +62,8 @@ enum class UnitType{ || this==WaterRanged || this==WaterMelee || this==WaterCivilian + || this==WaterAircraftCarrier + || this==WaterMissileCarrier } fun isAirUnit():Boolean{ @@ -71,4 +75,12 @@ enum class UnitType{ fun isMissileUnit():Boolean{ return this == Missile } + + fun isAircraftCarrierUnit():Boolean{ + return this == WaterAircraftCarrier + } + + fun isMissileCarrierUnit():Boolean{ + return this == WaterMissileCarrier + } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt b/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt index 79ec28f0..06190e10 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt @@ -3,11 +3,11 @@ package com.unciv.ui.tilegroups import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.ui.Image +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.badlogic.gdx.utils.Align import com.unciv.UncivGame import com.unciv.logic.map.MapUnit -import com.unciv.ui.utils.ImageGetter -import com.unciv.ui.utils.UnitGroup -import com.unciv.ui.utils.center +import com.unciv.ui.utils.* /** Helper class for TileGroup, which was getting too full */ class TileGroupIcons(val tileGroup: TileGroup){ @@ -58,6 +58,23 @@ class TileGroupIcons(val tileGroup: TileGroup){ newImage.center(tileGroup) newImage.y += yFromCenter + // Display number of carried air units + if ((unit.type.isAircraftCarrierUnit() || unit.type.isMissileCarrierUnit()) + && !unit.getTile().airUnits.isEmpty() && !unit.getTile().isCityCenter()) { + val holder = Table() + val secondarycolor = unit.civInfo.nation.getInnerColor() + val airUnitTable = Table().apply { defaults().pad(5f) } + airUnitTable.background = ImageGetter.getRoundedEdgeTableBackground(unit.civInfo.nation.getOuterColor()) + val aircraftImage = ImageGetter.getImage("OtherIcons/Aircraft") + aircraftImage.color = secondarycolor + airUnitTable.add(aircraftImage).size(15f) + airUnitTable.add(unit.getTile().airUnits.size.toString().toLabel(secondarycolor, 14)) + holder.add(airUnitTable).row() + holder.setOrigin(Align.center) + holder.center(tileGroup) + newImage.addActor(holder) + } + // Instead of fading out the entire unit with its background, we just fade out its central icon, // that way it remains much more visible on the map if (!unit.isIdle() && unit.civInfo == UncivGame.Current.worldScreen.viewingCiv) diff --git a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt index 0bb77ed3..18d38acf 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt @@ -125,20 +125,27 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap if(moveHereDto!=null) table.add(getMoveHereButton(moveHereDto)) + var unitList = ArrayList() if (tileInfo.isCityCenter() && tileInfo.getOwner()==worldScreen.viewingCiv) { - for (unit in tileInfo.getCity()!!.getCenterTile().getUnits()) { - val unitGroup = UnitGroup(unit, 60f).surroundWithCircle(80f) - unitGroup.circle.color = Color.GRAY.cpy().apply { a = 0.5f } - if (unit.currentMovement == 0f) unitGroup.color.a = 0.5f - unitGroup.touchable = Touchable.enabled - unitGroup.onClick { - worldScreen.bottomUnitTable.selectedUnit = unit - worldScreen.bottomUnitTable.selectedCity = null - worldScreen.shouldUpdate = true - unitActionOverlay?.remove() - } - table.add(unitGroup) + unitList.addAll(tileInfo.getCity()!!.getCenterTile().getUnits()) + } else if (tileInfo.militaryUnit!=null && + (tileInfo.militaryUnit!!.type.isAircraftCarrierUnit() || tileInfo.militaryUnit!!.type.isMissileCarrierUnit()) && + tileInfo.militaryUnit!!.civInfo==worldScreen.viewingCiv && !tileInfo.airUnits.isEmpty()) { + unitList.addAll(tileInfo.getUnits()) + } + + for (unit in unitList) { + val unitGroup = UnitGroup(unit, 60f).surroundWithCircle(80f) + unitGroup.circle.color = Color.GRAY.cpy().apply { a = 0.5f } + if (unit.currentMovement == 0f) unitGroup.color.a = 0.5f + unitGroup.touchable = Touchable.enabled + unitGroup.onClick { + worldScreen.bottomUnitTable.selectedUnit = unit + worldScreen.bottomUnitTable.selectedCity = null + worldScreen.shouldUpdate = true + unitActionOverlay?.remove() } + table.add(unitGroup) } addOverlayOnTileGroup(tileInfo, table) diff --git a/docs/Credits.md b/docs/Credits.md index 163f1afc..5eb732b4 100644 --- a/docs/Credits.md +++ b/docs/Credits.md @@ -99,6 +99,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https: * [Modern Armor](https://thenounproject.com/search/?q=tank&i=218) By Public Domain Nouns for Modern Armor * [Manhattan Project](https://thenounproject.com/search/?q=Nuclear%20Bomb&i=2041074) By corpus delicti, GR * [Nuclear Missile](https://thenounproject.com/marialuisa.iborra/collection/missiles-bombs/?i=1022574) By Lluisa Iborra, ES +* Icon for Carrier made by [JackRainy](https://github.com/JackRainy), based on [Aircraft Carrier](https://thenounproject.com/icolabs/collection/flat-icons-transport/?i=2332914) By IcoLabs, BR ### Great People