diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index ce7b625718..535f645e47 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -60,7 +60,7 @@ class GameInfo { player.startTurn() - val enemyUnitsCloseToTerritory = player.getViewableTiles() + val enemyUnitsCloseToTerritory = player.viewableTiles .filter { it.militaryUnit != null && it.militaryUnit!!.civInfo != player && player.isAtWarWith(it.militaryUnit!!.civInfo) @@ -80,7 +80,7 @@ class GameInfo { if (tileToPlace == null) { // Barbarians will only spawn in places that no one can see val allViewableTiles = civilizations.filterNot { it.isBarbarianCivilization() } - .flatMap { it.getViewableTiles() }.toHashSet() + .flatMap { it.viewableTiles }.toHashSet() val viableTiles = tileMap.values.filterNot { allViewableTiles.contains(it) || it.militaryUnit != null || it.civilianUnit != null } if (viableTiles.isEmpty()) return // no place for more barbs =( tile = viableTiles.getRandom() @@ -92,10 +92,10 @@ class GameInfo { tileMap.gameInfo = this tileMap.setTransients() - for (civInfo in civilizations) { - civInfo.gameInfo = this - civInfo.setTransients() - } + // this is separated into 2 loops because when we activate updateViewableTiles in civ.setTransients, + // we try to find new civs, and we check if civ is barbarian, which we can't know unless the gameInfo is already set. + for (civInfo in civilizations) civInfo.gameInfo = this + for (civInfo in civilizations) civInfo.setTransients() for (civInfo in civilizations) { // we have to remove hydro plants from all cities BEFORE we update a single one, diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index b37eb62b78..a11937d6aa 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -146,7 +146,7 @@ class UnitAutomation{ class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo) fun getAttackableEnemies(unit: MapUnit, unitDistanceToTiles: HashMap): ArrayList { - val tilesWithEnemies = unit.civInfo.getViewableTiles() + val tilesWithEnemies = unit.civInfo.viewableTiles .filter { containsAttackableEnemy(it,unit) } val rangeOfAttack = unit.getRange() diff --git a/core/src/com/unciv/logic/city/CityExpansionManager.kt b/core/src/com/unciv/logic/city/CityExpansionManager.kt index c3f531cf33..e34dad211b 100644 --- a/core/src/com/unciv/logic/city/CityExpansionManager.kt +++ b/core/src/com/unciv/logic/city/CityExpansionManager.kt @@ -95,6 +95,8 @@ class CityExpansionManager { for(unit in tileInfo.getUnits()) if(!unit.civInfo.canEnterTiles(cityInfo.civInfo)) unit.movementAlgs().teleportToClosestMoveableTile() + + cityInfo.civInfo.updateViewableTiles() } diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index cc54060542..728c88ce63 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -35,6 +35,7 @@ class CityInfo { constructor() // for json parsing, we need to have a default constructor constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { this.civInfo = civInfo + this.location = cityLocation setTransients() // Since cities can be captures between civilizations, @@ -48,7 +49,6 @@ class CityInfo { else name = civInfo.getNation().cities.map { "Newer $it" }.first{ !allExistingCityNames.contains(it) } } - this.location = cityLocation civInfo.cities = civInfo.cities.toMutableList().apply { add(this@CityInfo) } if(civInfo == civInfo.gameInfo.getPlayerCivilization()) civInfo.addNotification("[$name] has been founded!", cityLocation, Color.PURPLE) diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index d11c4cda1d..d175fd0c77 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -26,6 +26,7 @@ import kotlin.math.roundToInt class CivilizationInfo { @Transient lateinit var gameInfo: GameInfo @Transient var units=ArrayList() + @Transient var viewableTiles = HashSet() var gold = 0 var happiness = 15 @@ -203,25 +204,26 @@ class CivilizationInfo { return units.toList() // to avoid comodification problems (ie concurrency again...) } - fun getViewableTiles(): List { - var viewablePositions = emptyList() - viewablePositions += cities.flatMap { it.getTiles() } - .flatMap { it.neighbors } // tiles adjacent to city tiles - viewablePositions += getCivUnits() - .flatMap { it.getViewableTiles()} // Tiles within 2 tiles of units - viewablePositions.map { it.position }.filterNot { exploredTiles.contains(it) }.toCollection(exploredTiles) - val viewedCivs = viewablePositions - .flatMap { it.getUnits().map { u->u.civInfo }.union(listOf(it.getOwner())) } - .filterNotNull().filterNot { it==this || it.isBarbarianCivilization() } + fun updateViewableTiles() { + viewableTiles.clear() + viewableTiles.addAll(cities.flatMap { it.getTiles() }.flatMap { it.neighbors }) // tiles adjacent to city tiles + viewableTiles.addAll(getCivUnits().flatMap { it.getViewableTiles()}) + + // updating the viewable tiles also affects the explored tiles, obvs + viewableTiles.asSequence().map { it.position } + .filterNot { exploredTiles.contains(it) }.toCollection(exploredTiles) + + val viewedCivs = viewableTiles + .flatMap { it.getUnits().map { unit->unit.civInfo }.union(listOf(it.getOwner())) } + // we can meet a civ either by meeting its unit, or its tile + .asSequence().filterNotNull().filterNot { it==this || it.isBarbarianCivilization() } for(otherCiv in viewedCivs) if(!diplomacy.containsKey(otherCiv.civName)){ meetCivilization(otherCiv) addNotification("We have encountered [${otherCiv.civName}]!".tr(),null, Color.GOLD) } - - return viewablePositions.distinct() } fun meetCivilization(otherCiv: CivilizationInfo) { @@ -254,7 +256,6 @@ class CivilizationInfo { //region state-changing functions fun setTransients() { - if(civName=="") civName="Babylon" // this is because it used to be a default but now it isn't so we can change it. goldenAges.civInfo = this policies.civInfo = this if(policies.adoptedPolicies.size>0 && policies.numberOfAdoptedPolicies == 0) @@ -263,16 +264,13 @@ class CivilizationInfo { tech.civInfo = this diplomacy.values.forEach { it.civInfo=this} - for (unit in gameInfo.tileMap.values.flatMap { it.getUnits() }.filter { it.owner==civName }) { - unit.assignOwner(this) - unit.setTransients() - } for (cityInfo in cities) { cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the civInfo cityInfo.setTransients() } setCitiesConnectedToCapitalTransients() + updateViewableTiles() } fun endTurn() { @@ -320,7 +318,7 @@ class CivilizationInfo { } fun startTurn(){ - getViewableTiles() // adds explored tiles so that the units will be able to perform automated actions better + updateViewableTiles() // adds explored tiles so that the units will be able to perform automated actions better setCitiesConnectedToCapitalTransients() for (city in cities) city.cityStats.update() diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 5480ae9377..50045cc805 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -314,6 +314,7 @@ class MapUnit { currentTile = tile if(tile.improvement=="Ancient ruins" && !civInfo.isBarbarianCivilization()) getAncientRuinBonus() + civInfo.updateViewableTiles() } private fun getAncientRuinBonus() { diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 803a65037c..92e687733e 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -200,7 +200,7 @@ open class TileInfo { override fun toString(): String { val SB = StringBuilder() - val isViewableToPlayer = UnCivGame.Current.gameInfo.getPlayerCivilization().getViewableTiles().contains(this) + val isViewableToPlayer = UnCivGame.Current.gameInfo.getPlayerCivilization().viewableTiles.contains(this) || UnCivGame.Current.viewEntireMapForDebug if (isCityCenter()) { diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 425c9f83e4..cb2d9959c8 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -117,6 +117,11 @@ class TileMap { tileInfo.tileMap = this if(tileInfo.militaryUnit!=null) tileInfo.militaryUnit!!.currentTile = tileInfo if(tileInfo.civilianUnit!=null) tileInfo.civilianUnit!!.currentTile = tileInfo + + for (unit in tileInfo.getUnits()) { + unit.assignOwner(gameInfo.civilizations.first { it.civName == unit.owner }) + unit.setTransients() + } } } diff --git a/core/src/com/unciv/logic/map/UnitPromotions.kt b/core/src/com/unciv/logic/map/UnitPromotions.kt index 6d4dafa424..9398f0d43c 100644 --- a/core/src/com/unciv/logic/map/UnitPromotions.kt +++ b/core/src/com/unciv/logic/map/UnitPromotions.kt @@ -20,6 +20,7 @@ class UnitPromotions{ promotions.add(promotionName) numberOfPromotions++ unit.updateUniques() + unit.civInfo.updateViewableTiles() // some promotions give the unit bonus sight } fun getAvailablePromotions(): List { diff --git a/core/src/com/unciv/ui/tilegroups/TileGroup.kt b/core/src/com/unciv/ui/tilegroups/TileGroup.kt index d383ad2174..69b0b3a65d 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroup.kt @@ -331,17 +331,19 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { return newImage } + fun getBackgroundImageForUnit(unit: MapUnit):Image{ + return when { + unit.isEmbarked() -> ImageGetter.getImage("OtherIcons/Banner") + unit.isFortified() -> ImageGetter.getImage("OtherIcons/Shield.png") + else -> ImageGetter.getImage("OtherIcons/Circle.png") + } + } fun getUnitImage(unit: MapUnit, size: Float): Group { val unitBaseImage = ImageGetter.getUnitIcon(unit.name, unit.civInfo.getNation().getSecondaryColor()) .apply { setSize(size*0.75f, size*0.75f) } - val background = - when { - unit.isEmbarked() -> ImageGetter.getImage("OtherIcons/Banner") - unit.isFortified() -> ImageGetter.getImage("OtherIcons/Shield.png") - else -> ImageGetter.getImage("OtherIcons/Circle.png") - } + val background = getBackgroundImageForUnit(unit) background.apply { this.color = unit.civInfo.getNation().getColor() setSize(size, size) diff --git a/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt b/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt index 9b7878d992..cfe0af646e 100644 --- a/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt @@ -5,7 +5,6 @@ import com.unciv.logic.city.CityInfo import com.unciv.logic.map.MapUnit import com.unciv.logic.map.TileInfo import com.unciv.ui.utils.CameraStageBaseScreen -import com.unciv.ui.utils.ImageGetter import com.unciv.ui.utils.center @@ -13,8 +12,7 @@ class WorldTileGroup(tileInfo: TileInfo) : TileGroup(tileInfo) { var cityButton: CityButton? = null fun addWhiteHaloAroundUnit(unit: MapUnit) { - val whiteHalo = if(unit.isFortified()) ImageGetter.getImage("OtherIcons/Shield.png") - else ImageGetter.getImage("OtherIcons/Circle.png") + val whiteHalo = getBackgroundImageForUnit(unit) whiteHalo.setSize(30f,30f) val unitImage = if(unit.type.isCivilian()) civilianUnitImage else militaryUnitImage diff --git a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt index 6d7190894d..c2297708d5 100644 --- a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt @@ -145,7 +145,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: } internal fun updateTiles(civInfo: CivilizationInfo) { - val playerViewableTilePositions = civInfo.getViewableTiles().map { it.position }.toHashSet() + val playerViewableTilePositions = civInfo.viewableTiles.map { it.position }.toHashSet() cityButtonOverlays.forEach{it.remove()} cityButtonOverlays.clear() diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 53802772d8..076d4e0a1a 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -265,7 +265,7 @@ class WorldScreen : CameraStageBaseScreen() { val shownTutorials = UnCivGame.Current.settings.tutorialsShown displayTutorials("NextTurn") if("BarbarianEncountered" !in shownTutorials - && civInfo.getViewableTiles().any { it.getUnits().any { unit -> unit.civInfo.isBarbarianCivilization() } }) + && civInfo.viewableTiles.any { it.getUnits().any { unit -> unit.civInfo.isBarbarianCivilization() } }) displayTutorials("BarbarianEncountered") if(civInfo.cities.size > 2) displayTutorials("SecondCity") if(civInfo.happiness<0) displayTutorials("Unhappiness") diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt b/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt index 3157623918..9cc336dcd8 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt @@ -127,7 +127,7 @@ class WorldScreenTopBar(val screen: WorldScreen) : Table() { else -> 2020+(turns-440)/2 } - turnsLabel.setText("Turn".tr()+" " + civInfo.gameInfo.turns + " | "+ abs(year)+(if (year<0) " BE" else " AD")) + turnsLabel.setText("Turn".tr()+" " + civInfo.gameInfo.turns + " | "+ abs(year)+(if (year<0) " BC" else " AD")) val nextTurnStats = civInfo.getStatsForNextTurn() val goldPerTurn = "(" + (if (nextTurnStats.gold > 0) "+" else "") + Math.round(nextTurnStats.gold) + ")"