Submarines are now visible to adjacent units, and once turned visible, can be attacked by all enemy units (#5001)

* Submarines are now visible to adjacent units, and once turned visible, can attack be all enemy units

* Deprecation & translation of unique saving
This commit is contained in:
Xander Lenstra
2021-08-27 16:00:12 +02:00
committed by GitHub
parent 7dd7e0b278
commit 85e4a68ea1
12 changed files with 55 additions and 39 deletions

View File

@ -169,7 +169,7 @@ class GameInfo {
it.militaryUnit != null && it.militaryUnit!!.civInfo != thisPlayer
&& thisPlayer.isAtWarWith(it.militaryUnit!!.civInfo)
&& (it.getOwner() == thisPlayer || it.neighbors.any { neighbor -> neighbor.getOwner() == thisPlayer }
&& (!it.militaryUnit!!.isInvisible() || viewableInvisibleTiles.contains(it.position)))
&& (!it.militaryUnit!!.isInvisible(thisPlayer) || viewableInvisibleTiles.contains(it.position)))
}
// enemy units ON our territory

View File

@ -95,15 +95,11 @@ object BattleHelper {
)
return false
//only submarine and destroyer can attack submarine
//garrisoned submarine can be attacked by anyone, or the city will be in invincible
if (tileCombatant.isInvisible() && !tile.isCityCenter()) {
if (combatant is MapUnitCombatant
&& combatant.unit.hasUnique("Can attack submarines")
&& combatant.getCivInfo().viewableInvisibleUnitsTiles.map { it.position }.contains(tile.position)) {
return true
}
return false
// Only units with the right unique can view submarines (or other invisible units) from more then one tile away.
// Garrisoned invisible units can be attacked by anyone, as else the city will be in invincible.
if (tileCombatant.isInvisible(combatant.getCivInfo()) && !tile.isCityCenter()) {
return combatant is MapUnitCombatant
&& combatant.getCivInfo().viewableInvisibleUnitsTiles.map { it.position }.contains(tile.position)
}
return true
}

View File

@ -18,7 +18,7 @@ class CityCombatant(val city: CityInfo) : ICombatant {
override fun getTile(): TileInfo = city.getCenterTile()
override fun getName(): String = city.name
override fun isDefeated(): Boolean = city.health == 1
override fun isInvisible(): Boolean = false
override fun isInvisible(to: CivilizationInfo): Boolean = false
override fun canAttack(): Boolean = city.canBombard()
override fun matchesCategory(category: String) = category == "City" || category == "All"
override fun getAttackSound() = UncivSound.Bombard

View File

@ -5,7 +5,7 @@ import com.unciv.logic.map.TileInfo
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.unit.UnitType
interface ICombatant{
interface ICombatant {
fun getName(): String
fun getHealth():Int
fun getMaxHealth():Int
@ -16,7 +16,7 @@ interface ICombatant{
fun isDefeated():Boolean
fun getCivInfo(): CivilizationInfo
fun getTile(): TileInfo
fun isInvisible(): Boolean
fun isInvisible(to: CivilizationInfo): Boolean
fun canAttack(): Boolean
fun matchesCategory(category:String): Boolean
fun getAttackSound(): UncivSound

View File

@ -13,7 +13,7 @@ class MapUnitCombatant(val unit: MapUnit) : ICombatant {
override fun getTile(): TileInfo = unit.getTile()
override fun getName(): String = unit.name
override fun isDefeated(): Boolean = unit.health <= 0
override fun isInvisible(): Boolean = unit.isInvisible()
override fun isInvisible(to: CivilizationInfo): Boolean = unit.isInvisible(to)
override fun canAttack(): Boolean = unit.canAttack()
override fun matchesCategory(category:String) = unit.matchesFilter(category)
override fun getAttackSound() = unit.baseUnit.attackSound.let {
@ -36,7 +36,7 @@ class MapUnitCombatant(val unit: MapUnit) : ICombatant {
}
override fun getUnitType(): UnitType {
return unit.type!!
return unit.type
}
override fun toString(): String {

View File

@ -12,12 +12,28 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo) {
val newViewableInvisibleTiles = HashSet<TileInfo>()
newViewableInvisibleTiles.addAll(civInfo.getCivUnits()
.filter { it.hasUnique("Can attack submarines") }
.flatMap { it.viewableTiles.asSequence() })
// "Can attack submarines" unique deprecated since 3.16.9
.filter { attacker -> attacker.hasUnique("Can see invisible [] units") || attacker.hasUnique("Can attack submarines") }
.flatMap { attacker ->
attacker.viewableTiles
.asSequence()
.filter { tile ->
( tile.militaryUnit != null
&& attacker.getMatchingUniques("Can see invisible [] units")
.any { unique -> tile.militaryUnit!!.matchesFilter(unique.params[0]) }
) || (
tile.militaryUnit != null
// "Can attack submarines" unique deprecated since 3.16.9
&& attacker.hasUnique("Can attack submarines")
&& tile.militaryUnit!!.matchesFilter("Submarine")
)
}
}
)
civInfo.viewableInvisibleUnitsTiles = newViewableInvisibleTiles
// updating the viewable tiles also affects the explored tiles, obvs
// updating the viewable tiles also affects the explored tiles, obviously.
// So why don't we play switcharoo with the explored tiles as well?
// Well, because it gets REALLY LARGE so it's a lot of memory space,
// and we never actually iterate on the explored tiles (only check contains()),

View File

@ -354,9 +354,13 @@ class MapUnit {
return currentTile.isWater
}
fun isInvisible(): Boolean {
fun isInvisible(to: CivilizationInfo): Boolean {
if (hasUnique("Invisible to others"))
return true
if (hasUnique("Invisible to non-adjacent units"))
return getTile().getTilesInDistance(1).none {
it.getOwner() == to || it.getUnits().any { unit -> unit.owner == to.civName }
}
return false
}

View File

@ -637,7 +637,7 @@ open class TileInfo {
val unitsInTile = getUnits()
if (unitsInTile.none()) return false
if (unitsInTile.first().civInfo != viewingCiv &&
unitsInTile.firstOrNull { it.isInvisible() } != null) {
unitsInTile.firstOrNull { it.isInvisible(viewingCiv) } != null) {
return true
}
return false

View File

@ -74,20 +74,20 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
val attackerCiv = worldScreen.viewingCiv
val defender: ICombatant? = Battle.getMapCombatantOfTile(selectedTile)
if(defender==null ||
!includeFriendly && defender.getCivInfo()==attackerCiv )
if (defender == null || (!includeFriendly && defender.getCivInfo() == attackerCiv))
return null // no enemy combatant in tile
val canSeeDefender = if(UncivGame.Current.viewEntireMapForDebug) true
else {
when {
defender.isInvisible() -> attackerCiv.viewableInvisibleUnitsTiles.contains(selectedTile)
defender.isCity() -> attackerCiv.exploredTiles.contains(selectedTile.position)
else -> attackerCiv.viewableTiles.contains(selectedTile)
val canSeeDefender =
if (UncivGame.Current.viewEntireMapForDebug) true
else {
when {
defender.isInvisible(attackerCiv) -> attackerCiv.viewableInvisibleUnitsTiles.contains(selectedTile)
defender.isCity() -> attackerCiv.exploredTiles.contains(selectedTile.position)
else -> attackerCiv.viewableTiles.contains(selectedTile)
}
}
}
if(!canSeeDefender) return null
if (!canSeeDefender) return null
return defender
}
@ -97,14 +97,14 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
val attackerNameWrapper = Table()
val attackerLabel = attacker.getName().toLabel()
if(attacker is MapUnitCombatant)
if (attacker is MapUnitCombatant)
attackerNameWrapper.add(UnitGroup(attacker.unit,25f)).padRight(5f)
attackerNameWrapper.add(attackerLabel)
add(attackerNameWrapper)
val defenderNameWrapper = Table()
val defenderLabel = Label(defender.getName().tr(), skin)
if(defender is MapUnitCombatant)
if (defender is MapUnitCombatant)
defenderNameWrapper.add(UnitGroup(defender.unit,25f)).padRight(5f)
defenderNameWrapper.add(defenderLabel)
add(defenderNameWrapper).row()