Save attacks per civ for arrows for cities, missiles, dead units. (#5855)

* Save attacks per civ for arrows for cities, missiles, dead units.

* Tweak docs.
This commit is contained in:
will-ca
2021-12-27 10:57:30 -08:00
committed by GitHub
parent f14ec29468
commit a1dfa53664
6 changed files with 48 additions and 7 deletions

View File

@ -61,6 +61,12 @@ object Battle {
val attackedTile = defender.getTile()
if (attacker is MapUnitCombatant) {
attacker.unit.attacksSinceTurnStart.add(Vector2(attackedTile.position))
} else {
attacker.getCivInfo().attacksSinceTurnStart.add(CivilizationInfo.HistoricalAttackMemory(
null,
Vector2(attacker.getTile().position),
Vector2(attackedTile.position)
))
}
if (attacker is MapUnitCombatant && attacker.unit.baseUnit.isAirUnit()) {
@ -634,6 +640,8 @@ object Battle {
}
if (attacker.isDefeated()) return
attacker.unit.attacksSinceTurnStart.add(Vector2(targetTile.position))
// Destroy units on the target tile
// Needs the toList() because if we're destroying the units, they're no longer part of the sequence
for (defender in targetTile.getUnits().filter { it != attacker.unit }.toList()) {

View File

@ -178,6 +178,33 @@ class CivilizationInfo {
var totalCultureForContests = 0
var totalFaithForContests = 0
/**
* Container class to represent a historical attack recently performed by this civilization.
*
* @property attackingUnit Name key of [BaseUnit] type that performed the attack, or null (E.G. for city bombardments).
* @property source Position of the tile from which the attack was made.
* @property target Position of the tile targetted by the attack.
* @see [MapUnit.UnitMovementMemory], [attacksSinceTurnStart]
*/
class HistoricalAttackMemory() {
constructor(attackingUnit: String?, source: Vector2, target: Vector2): this() {
this.attackingUnit = attackingUnit
this.source = source
this.target = target
}
var attackingUnit: String? = null
lateinit var source: Vector2
lateinit var target: Vector2
fun clone() = HistoricalAttackMemory(attackingUnit, Vector2(source), Vector2(target))
}
/** Deep clone an ArrayList of [HistoricalAttackMemory]s. */
private fun ArrayList<HistoricalAttackMemory>.copy() = ArrayList(this.map { it.clone() })
/**
* List of attacks that this civilization has performed since the start of its most recent turn. Does not include attacks already tracked in [MapUnit.attacksSinceTurnStart] of living units. Used in movement arrow overlay.
* @see [MapUnit.attacksSinceTurnStart]
*/
var attacksSinceTurnStart = ArrayList<HistoricalAttackMemory>()
var hasMovedAutomatedUnits = false
@Transient
@ -230,6 +257,7 @@ class CivilizationInfo {
toReturn.numMinorCivsAttacked = numMinorCivsAttacked
toReturn.totalCultureForContests = totalCultureForContests
toReturn.totalFaithForContests = totalFaithForContests
toReturn.attacksSinceTurnStart = attacksSinceTurnStart.copy()
toReturn.hasMovedAutomatedUnits = hasMovedAutomatedUnits
return toReturn
}
@ -782,6 +810,7 @@ class CivilizationInfo {
fun startTurn() {
civConstructions.startTurn()
attacksSinceTurnStart.clear()
updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
// Generate great people at the start of the turn,

View File

@ -177,6 +177,7 @@ class MapUnit {
*
* @property position Position on the map at this instant.
* @property type Category of the last change in position that brought the unit to this position.
* @see [movementMemories]
* */
class UnitMovementMemory() {
constructor(position: Vector2, type: UnitMovementMemoryType): this() {
@ -827,6 +828,8 @@ class MapUnit {
}
fun destroy() {
val currentPosition = Vector2(getTile().position)
civInfo.attacksSinceTurnStart.addAll(attacksSinceTurnStart.asSequence().map { CivilizationInfo.HistoricalAttackMemory(this.name, currentPosition, it) })
removeFromTile()
civInfo.removeUnit(this)
civInfo.updateViewableTiles()

View File

@ -22,6 +22,6 @@ class MapVisualization(val gameInfo: GameInfo, val viewingCiv: CivilizationInfo)
// Plans should be visible always for own units and never for foreign units.
/** @return Whether an attack by a unit to a target should be visible to the player. */
fun isAttackVisible(attacker: MapUnit, target: Vector2) = (attacker.civInfo == viewingCiv || attacker.getTile() in viewingCiv.viewableTiles || gameInfo.tileMap[target] in viewingCiv.viewableTiles)
fun isAttackVisible(attacker: CivilizationInfo, source: Vector2, target: Vector2) = (attacker == viewingCiv || gameInfo.tileMap[source] in viewingCiv.viewableTiles || gameInfo.tileMap[target] in viewingCiv.viewableTiles)
// Attacks by the player civ should always be visible, and attacks by foreign civs should be visible if either the tile they targeted or the attacker's tile are visible. E.G. Civ V shows bombers coming out of the Fog of War.
}

View File

@ -455,9 +455,9 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
*
* @param pastVisibleUnits Sequence of [MapUnit]s for which the last turn's movement history can be displayed.
* @param targetVisibleUnits Sequence of [MapUnit]s for which the active movement target can be displayed.
* @param visibleAttacks Sequence of pairs of MapUnits to the target coordinates of attacks that they have done and can be displayed.
* @param visibleAttacks Sequence of pairs of [Vector2] positions of the sources and the targets of all attacks that can be displayed.
* */
internal fun updateMovementOverlay(pastVisibleUnits: Sequence<MapUnit>, targetVisibleUnits: Sequence<MapUnit>, visibleAttacks: Sequence<Pair<MapUnit, Vector2>>) {
internal fun updateMovementOverlay(pastVisibleUnits: Sequence<MapUnit>, targetVisibleUnits: Sequence<MapUnit>, visibleAttacks: Sequence<Pair<Vector2, Vector2>>) {
if (!UncivGame.Current.settings.showUnitMovements) {
return
}
@ -481,7 +481,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
addArrow(unit.getTile(), toTile, MiscArrowTypes.UnitMoving)
}
for ((from, to) in visibleAttacks) {
addArrow(tileMap[from.getTile().position], tileMap[to], MiscArrowTypes.UnitHasAttacked)
addArrow(tileMap[from], tileMap[to], MiscArrowTypes.UnitHasAttacked)
}
}

View File

@ -19,7 +19,6 @@ import com.unciv.logic.GameSaver
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.ReligionState
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.MapVisualization
import com.unciv.logic.trade.TradeEvaluation
import com.unciv.models.Tutorial
@ -405,11 +404,13 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
mapHolder.resetArrows()
val allUnits = gameInfo.civilizations.asSequence().flatMap { it.getCivUnits() }
val allAttacks = allUnits.map { unit -> unit.attacksSinceTurnStart.asSequence().map { attacked -> unit to attacked } }.flatten()
val allAttacks = allUnits.map { unit -> unit.attacksSinceTurnStart.asSequence().map { attacked -> Triple(unit.civInfo, unit.getTile().position, attacked) } }.flatten() +
gameInfo.civilizations.asSequence().flatMap { civInfo -> civInfo.attacksSinceTurnStart.asSequence().map { Triple(civInfo, it.source, it.target) } }
mapHolder.updateMovementOverlay(
allUnits.filter(mapVisualization::isUnitPastVisible),
allUnits.filter(mapVisualization::isUnitFutureVisible),
allAttacks.filter { (attacker, target) -> mapVisualization.isAttackVisible(attacker, target) }
allAttacks.filter { (attacker, source, target) -> mapVisualization.isAttackVisible(attacker, source, target) }
.map { (attacker, source, target) -> source to target }
)
// if we use the clone, then when we update viewable tiles