mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 17:59:11 +07:00
New tile visibility framework!
This commit is contained in:
@ -122,7 +122,7 @@
|
|||||||
"defenceBonus": 0.25,
|
"defenceBonus": 0.25,
|
||||||
"RGB": [120, 120, 120],
|
"RGB": [120, 120, 120],
|
||||||
"uniques": ["Rough terrain",
|
"uniques": ["Rough terrain",
|
||||||
"Has an elevation of [4] for visibility calculations",
|
"Has an elevation of [2] for visibility calculations",
|
||||||
"Occurs in chains at high elevations",
|
"Occurs in chains at high elevations",
|
||||||
"Units ending their turn on this terrain take [50] damage",
|
"Units ending their turn on this terrain take [50] damage",
|
||||||
"Always Fertility [-2] for Map Generation",
|
"Always Fertility [-2] for Map Generation",
|
||||||
@ -158,7 +158,7 @@
|
|||||||
"occursOn": ["Tundra","Plains","Grassland","Desert","Snow"],
|
"occursOn": ["Tundra","Plains","Grassland","Desert","Snow"],
|
||||||
"uniques": ["Rough terrain",
|
"uniques": ["Rough terrain",
|
||||||
"[+5] Strength for cities built on this terrain",
|
"[+5] Strength for cities built on this terrain",
|
||||||
"Has an elevation of [2] for visibility calculations",
|
"Has an elevation of [1] for visibility calculations",
|
||||||
"Occurs in groups around high elevations",
|
"Occurs in groups around high elevations",
|
||||||
"[+1] to Fertility for Map Generation",
|
"[+1] to Fertility for Map Generation",
|
||||||
"A Region is formed with at least [40]% [Hill] tiles, with priority [5]",
|
"A Region is formed with at least [40]% [Hill] tiles, with priority [5]",
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
"defenceBonus": 0.25,
|
"defenceBonus": 0.25,
|
||||||
"RGB": [120, 120, 120],
|
"RGB": [120, 120, 120],
|
||||||
"uniques": ["Rough terrain",
|
"uniques": ["Rough terrain",
|
||||||
"Has an elevation of [4] for visibility calculations",
|
"Has an elevation of [2] for visibility calculations",
|
||||||
"Occurs in chains at high elevations",
|
"Occurs in chains at high elevations",
|
||||||
"Units ending their turn on this terrain take [50] damage",
|
"Units ending their turn on this terrain take [50] damage",
|
||||||
"Always Fertility [-2] for Map Generation",
|
"Always Fertility [-2] for Map Generation",
|
||||||
@ -157,7 +157,7 @@
|
|||||||
"occursOn": ["Tundra","Plains","Grassland","Desert","Snow"],
|
"occursOn": ["Tundra","Plains","Grassland","Desert","Snow"],
|
||||||
"uniques": ["Rough terrain",
|
"uniques": ["Rough terrain",
|
||||||
"[+5] Strength for cities built on this terrain",
|
"[+5] Strength for cities built on this terrain",
|
||||||
"Has an elevation of [2] for visibility calculations",
|
"Has an elevation of [1] for visibility calculations",
|
||||||
"Occurs in groups around high elevations",
|
"Occurs in groups around high elevations",
|
||||||
"[+1] to Fertility for Map Generation",
|
"[+1] to Fertility for Map Generation",
|
||||||
"A Region is formed with at least [40]% [Hill] tiles, with priority [5]",
|
"A Region is formed with at least [40]% [Hill] tiles, with priority [5]",
|
||||||
|
@ -40,7 +40,7 @@ object UnitAutomation {
|
|||||||
unit.movement.getDistanceToTiles().keys.filter { isGoodTileToExplore(unit, it) }
|
unit.movement.getDistanceToTiles().keys.filter { isGoodTileToExplore(unit, it) }
|
||||||
if (explorableTilesThisTurn.any()) {
|
if (explorableTilesThisTurn.any()) {
|
||||||
val bestTile = explorableTilesThisTurn
|
val bestTile = explorableTilesThisTurn
|
||||||
.sortedByDescending { it.height } // secondary sort is by 'how far can you see'
|
.sortedByDescending { it.tileHeight } // secondary sort is by 'how far can you see'
|
||||||
.maxByOrNull { it.aerialDistanceTo(unit.currentTile) }!! // primary sort is by 'how far can you go'
|
.maxByOrNull { it.aerialDistanceTo(unit.currentTile) }!! // primary sort is by 'how far can you go'
|
||||||
unit.movement.headTowards(bestTile)
|
unit.movement.headTowards(bestTile)
|
||||||
return true
|
return true
|
||||||
|
@ -345,7 +345,13 @@ open class TileInfo : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@delegate:Transient
|
@delegate:Transient
|
||||||
val height : Int by lazy {
|
val tileHeight : Int by lazy { // for e.g. hill+forest this is 2, since forest is visible above units
|
||||||
|
if (terrainHasUnique(UniqueType.BlocksLineOfSightAtSameElevation)) unitHeight + 1
|
||||||
|
else unitHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
@delegate:Transient
|
||||||
|
val unitHeight : Int by lazy { // for e.g. hill+forest this is 1, since only hill provides height for units
|
||||||
allTerrains.flatMap { it.getMatchingUniques(UniqueType.VisibilityElevation) }
|
allTerrains.flatMap { it.getMatchingUniques(UniqueType.VisibilityElevation) }
|
||||||
.map { it.params[0].toInt() }.sum()
|
.map { it.params[0].toInt() }.sum()
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import com.unciv.models.ruleset.Nation
|
|||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.tile.TerrainType
|
import com.unciv.models.ruleset.tile.TerrainType
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
|
import java.lang.Integer.max
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
/** An Unciv map with all properties as produced by the [map editor][com.unciv.ui.mapeditor.MapEditorScreen]
|
/** An Unciv map with all properties as produced by the [map editor][com.unciv.ui.mapeditor.MapEditorScreen]
|
||||||
@ -325,52 +326,50 @@ class TileMap : IsPartOfGameInfoSerialization {
|
|||||||
vectorUnwrappedLeft
|
vectorUnwrappedLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class ViewableTile(val tile:TileInfo, val maxHeightSeenToTile:Int, val isVisible:Boolean, val isAttackable: Boolean)
|
||||||
|
|
||||||
/** @return List of tiles visible from location [position] for a unit with sight range [sightDistance] */
|
/** @return List of tiles visible from location [position] for a unit with sight range [sightDistance] */
|
||||||
fun getViewableTiles(position: Vector2, sightDistance: Int): List<TileInfo> {
|
fun getViewableTiles(position: Vector2, sightDistance: Int): List<TileInfo> {
|
||||||
val viewableTiles = getTilesInDistance(position, 1).toMutableList()
|
val aUnitHeight = get(position).unitHeight
|
||||||
val currentTileHeight = get(position).height
|
val viewableTiles = mutableListOf(ViewableTile(
|
||||||
|
get(position),
|
||||||
|
aUnitHeight,
|
||||||
|
isVisible = true,
|
||||||
|
isAttackable = false
|
||||||
|
))
|
||||||
|
|
||||||
for (i in 1..sightDistance) { // in each layer,
|
for (i in 1..sightDistance) { // in each layer,
|
||||||
// This is so we don't use tiles in the same distance to "see over",
|
// This is so we don't use tiles in the same distance to "see over",
|
||||||
// that is to say, the "viewableTiles.contains(it) check will return false for neighbors from the same distance
|
// that is to say, the "viewableTiles.contains(it) check will return false for neighbors from the same distance
|
||||||
val tilesToAddInDistanceI = ArrayList<TileInfo>()
|
val tilesToAddInDistanceI = ArrayList<ViewableTile>()
|
||||||
|
|
||||||
for (cTile in getTilesAtDistance(position, i)) { // for each tile in that layer,
|
for (cTile in getTilesAtDistance(position, i)) { // for each tile in that layer,
|
||||||
val cTileHeight = cTile.height
|
val cTileHeight = cTile.tileHeight
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Okay so, if we're looking at a tile from a to c with b in the middle,
|
Okay so, if we're looking at a tile from height a to one with height c with a MAXIMUM HEIGHT of b in the middle,
|
||||||
we have several scenarios:
|
we have several scenarios:
|
||||||
1. a>b - - I can see everything, b does not hide c
|
1. a>=b - I can see everything, b does not hide c (equals is 'flat plain' or 'string of hills' or 'hill viewing over forests')
|
||||||
2. a==b
|
|
||||||
2.1 c>b - c is tall enough I can see it over b!
|
|
||||||
2.2 b blocks view from same-elevation tiles - hides c
|
|
||||||
2.3 none of the above - I can see c
|
|
||||||
3. a<b
|
3. a<b
|
||||||
3.1 b>=c - b hides c
|
3.1 b>=c - b hides c (hills hide other hills, forests, etc)
|
||||||
3.2 b<c - c is tall enough I can see it over b!
|
3.2 b<c - c is tall enough I can see it over b (hill+forest, mountain)
|
||||||
|
|
||||||
This can all be summed up as "I can see c if a>b || c>b || (a==b && b !blocks same-elevation view)"
|
This can all be summed up as "I can see c if a=>b || c>b"
|
||||||
*/
|
*/
|
||||||
|
val bMinimumHighestSeenTerrainSoFar = viewableTiles.filter { it.tile in cTile.neighbors }
|
||||||
|
.minOf { it.maxHeightSeenToTile }
|
||||||
|
|
||||||
val containsViewableNeighborThatCanSeeOver = cTile.neighbors.any { bNeighbor: TileInfo ->
|
tilesToAddInDistanceI.add(ViewableTile(
|
||||||
val bNeighborHeight = bNeighbor.height
|
cTile,
|
||||||
viewableTiles.contains(bNeighbor)
|
max(cTileHeight, bMinimumHighestSeenTerrainSoFar),
|
||||||
&& (
|
aUnitHeight >= bMinimumHighestSeenTerrainSoFar || cTileHeight > bMinimumHighestSeenTerrainSoFar,
|
||||||
currentTileHeight > bNeighborHeight // a>b
|
aUnitHeight >= bMinimumHighestSeenTerrainSoFar || cTile.unitHeight > bMinimumHighestSeenTerrainSoFar,
|
||||||
|| cTileHeight > bNeighborHeight // c>b
|
))
|
||||||
|| (
|
|
||||||
currentTileHeight == bNeighborHeight // a==b
|
|
||||||
&& !bNeighbor.terrainHasUnique(UniqueType.BlocksLineOfSightAtSameElevation)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (containsViewableNeighborThatCanSeeOver) tilesToAddInDistanceI.add(cTile)
|
|
||||||
}
|
}
|
||||||
viewableTiles.addAll(tilesToAddInDistanceI)
|
viewableTiles.addAll(tilesToAddInDistanceI)
|
||||||
}
|
}
|
||||||
|
|
||||||
return viewableTiles
|
return viewableTiles.filter { it.isVisible }.map { it.tile }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Strips all units from [TileMap]
|
/** Strips all units from [TileMap]
|
||||||
|
@ -67,7 +67,7 @@ class VisibilityTests {
|
|||||||
fun canSeeForestOverPlains() {
|
fun canSeeForestOverPlains() {
|
||||||
val grassland = addTile("Grassland", Vector2(0f,0f))
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
addTile("Plains", Vector2(1f,0f))
|
addTile("Plains", Vector2(1f,0f))
|
||||||
val forest = addTile(listOf("Grassland", "Forest"), Vector2(2f, 1f))
|
val forest = addTile(listOf("Grassland", "Forest"), Vector2(2f, 0f))
|
||||||
val viewableTiles = grassland.getViewableTilesList(2)
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
assert(viewableTiles.contains(forest))
|
assert(viewableTiles.contains(forest))
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ class VisibilityTests {
|
|||||||
@Test
|
@Test
|
||||||
fun cannotSeePlainsOverForest() {
|
fun cannotSeePlainsOverForest() {
|
||||||
val grassland = addTile("Grassland", Vector2(0f,0f))
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
addTile(listOf("Grassland", "Forest"), Vector2(1f, 1f))
|
addTile(listOf("Grassland", "Forest"), Vector2(1f, 0f))
|
||||||
val plains = addTile("Plains", Vector2(2f,0f))
|
val plains = addTile("Plains", Vector2(2f,0f))
|
||||||
val viewableTiles = grassland.getViewableTilesList(2)
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
assert(!viewableTiles.contains(plains))
|
assert(!viewableTiles.contains(plains))
|
||||||
@ -84,7 +84,7 @@ class VisibilityTests {
|
|||||||
@Test
|
@Test
|
||||||
fun cannotSeeForestOverForest() {
|
fun cannotSeeForestOverForest() {
|
||||||
val grassland = addTile("Grassland", Vector2(0f,0f))
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
addTile(listOf("Grassland", "Forest"), Vector2(1f, 1f))
|
addTile(listOf("Grassland", "Forest"), Vector2(1f, 0f))
|
||||||
val plains = addTile(listOf("Plains", "Forest"), Vector2(2f,0f))
|
val plains = addTile(listOf("Plains", "Forest"), Vector2(2f,0f))
|
||||||
val viewableTiles = grassland.getViewableTilesList(2)
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
assert(!viewableTiles.contains(plains))
|
assert(!viewableTiles.contains(plains))
|
||||||
@ -94,7 +94,7 @@ class VisibilityTests {
|
|||||||
fun canSeeHillOverPlains() {
|
fun canSeeHillOverPlains() {
|
||||||
val grassland = addTile("Grassland", Vector2(0f,0f))
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
addTile("Plains", Vector2(1f,0f))
|
addTile("Plains", Vector2(1f,0f))
|
||||||
val hill = addTile(listOf("Grassland", "Hill"), Vector2(2f, 1f))
|
val hill = addTile(listOf("Grassland", "Hill"), Vector2(2f, 0f))
|
||||||
val viewableTiles = grassland.getViewableTilesList(2)
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
assert(viewableTiles.contains(hill))
|
assert(viewableTiles.contains(hill))
|
||||||
}
|
}
|
||||||
@ -102,10 +102,65 @@ class VisibilityTests {
|
|||||||
@Test
|
@Test
|
||||||
fun cannotSeePlainsOverHill() {
|
fun cannotSeePlainsOverHill() {
|
||||||
val grassland = addTile("Grassland", Vector2(0f,0f))
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
addTile(listOf("Grassland", "Hill"), Vector2(1f, 1f))
|
addTile(listOf("Grassland", "Hill"), Vector2(1f, 0f))
|
||||||
val plains = addTile("Plains", Vector2(2f,0f))
|
val plains = addTile("Plains", Vector2(2f,0f))
|
||||||
val viewableTiles = grassland.getViewableTilesList(2)
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
assert(!viewableTiles.contains(plains))
|
assert(!viewableTiles.contains(plains))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cannotSeeHillOverHill() {
|
||||||
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
|
addTile(listOf("Grassland", "Hill"), Vector2(1f,0f))
|
||||||
|
val hill = addTile(listOf("Grassland", "Hill"), Vector2(2f, 0f))
|
||||||
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
|
assert(!viewableTiles.contains(hill))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cannotSeeHillOverForest() {
|
||||||
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
|
addTile(listOf("Grassland", "Forest"), Vector2(1f,0f))
|
||||||
|
val hill = addTile(listOf("Grassland", "Hill"), Vector2(2f, 0f))
|
||||||
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
|
assert(!viewableTiles.contains(hill))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cannotSeeForestOverHill() {
|
||||||
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
|
addTile(listOf("Grassland", "Hill"), Vector2(1f,0f))
|
||||||
|
val hill = addTile(listOf("Grassland", "Forest"), Vector2(2f, 0f))
|
||||||
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
|
assert(!viewableTiles.contains(hill))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canSeeHillForestOverHill() {
|
||||||
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
|
addTile(listOf("Grassland", "Forest"), Vector2(1f,0f))
|
||||||
|
val hill = addTile(listOf("Grassland", "Hill", "Forest"), Vector2(2f, 0f))
|
||||||
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
|
assert(viewableTiles.contains(hill))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canSeeMountainOverHill() {
|
||||||
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
|
addTile(listOf("Grassland", "Hill"), Vector2(1f,0f))
|
||||||
|
val hill = addTile(listOf("Mountain"), Vector2(2f, 0f))
|
||||||
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
|
assert(viewableTiles.contains(hill))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cannotSeeMountainOverHillForest() {
|
||||||
|
val grassland = addTile("Grassland", Vector2(0f,0f))
|
||||||
|
addTile(listOf("Grassland", "Hill", "Forest"), Vector2(1f,0f))
|
||||||
|
val hill = addTile(listOf("Mountain"), Vector2(2f, 0f))
|
||||||
|
val viewableTiles = grassland.getViewableTilesList(2)
|
||||||
|
assert(!viewableTiles.contains(hill))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user