Merge branch 'master' of https://github.com/yairm210/Unciv
This commit is contained in:
commit
935071e206
14 changed files with 404 additions and 78 deletions
|
@ -5,6 +5,7 @@ object Constants {
|
||||||
const val settler = "Settler"
|
const val settler = "Settler"
|
||||||
const val greatGeneral = "Great General"
|
const val greatGeneral = "Great General"
|
||||||
|
|
||||||
|
const val impassable = "Impassable"
|
||||||
const val ocean = "Ocean"
|
const val ocean = "Ocean"
|
||||||
const val coast = "Coast"
|
const val coast = "Coast"
|
||||||
const val mountain = "Mountain"
|
const val mountain = "Mountain"
|
||||||
|
|
|
@ -210,7 +210,8 @@ class CityConstructions {
|
||||||
|
|
||||||
//region state changing functions
|
//region state changing functions
|
||||||
fun setTransients(){
|
fun setTransients(){
|
||||||
builtBuildingObjects = ArrayList(builtBuildings.map { cityInfo.getRuleset().buildings[it]!! })
|
builtBuildingObjects = ArrayList(builtBuildings.map { cityInfo.getRuleset().buildings[it]
|
||||||
|
?: throw java.lang.Exception("Building $it is not found!")})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addProductionPoints(productionToAdd: Int) {
|
fun addProductionPoints(productionToAdd: Int) {
|
||||||
|
|
|
@ -280,9 +280,9 @@ class CivilizationInfo {
|
||||||
fun isAtWarWith(otherCiv:CivilizationInfo): Boolean {
|
fun isAtWarWith(otherCiv:CivilizationInfo): Boolean {
|
||||||
if (otherCiv.civName == civName) return false // never at war with itself
|
if (otherCiv.civName == civName) return false // never at war with itself
|
||||||
if (otherCiv.isBarbarian() || isBarbarian()) return true
|
if (otherCiv.isBarbarian() || isBarbarian()) return true
|
||||||
if (!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
|
val diplomacyManager = diplomacy[otherCiv.civName]
|
||||||
return false
|
?: return false // not encountered yet
|
||||||
return getDiplomacyManager(otherCiv).diplomaticStatus == DiplomaticStatus.War
|
return diplomacyManager.diplomaticStatus == DiplomaticStatus.War
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAtWar() = diplomacy.values.any { it.diplomaticStatus== DiplomaticStatus.War && !it.otherCiv().isDefeated() }
|
fun isAtWar() = diplomacy.values.any { it.diplomaticStatus== DiplomaticStatus.War && !it.otherCiv().isDefeated() }
|
||||||
|
@ -338,7 +338,8 @@ class CivilizationInfo {
|
||||||
* And if they civs on't yet know who they are then they don;t know if they're barbarians =\
|
* And if they civs on't yet know who they are then they don;t know if they're barbarians =\
|
||||||
* */
|
* */
|
||||||
fun setNationTransient(){
|
fun setNationTransient(){
|
||||||
nation = gameInfo.ruleSet.nations[civName]!!
|
nation = gameInfo.ruleSet.nations[civName]
|
||||||
|
?: throw java.lang.Exception("Nation $civName is not found!")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTransients() {
|
fun setTransients() {
|
||||||
|
@ -452,12 +453,12 @@ class CivilizationInfo {
|
||||||
|
|
||||||
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
|
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
|
||||||
if (otherCiv==this) return true
|
if (otherCiv==this) return true
|
||||||
if(nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles) return true
|
if (otherCiv.isBarbarian()) return true
|
||||||
if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet
|
if (nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles)
|
||||||
return false
|
return true
|
||||||
if(isAtWarWith(otherCiv)) return true
|
val diplomacyManager = diplomacy[otherCiv.civName]
|
||||||
if(getDiplomacyManager(otherCiv).hasOpenBorders) return true
|
?: return false // not encountered yet
|
||||||
return false
|
return (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addNotification(text: String, location: Vector2?, color: Color) {
|
fun addNotification(text: String, location: Vector2?, color: Color) {
|
||||||
|
|
|
@ -39,6 +39,9 @@ class MapUnit {
|
||||||
@Transient var doubleMovementInCoast = false
|
@Transient var doubleMovementInCoast = false
|
||||||
@Transient var doubleMovementInForestAndJungle = false
|
@Transient var doubleMovementInForestAndJungle = false
|
||||||
@Transient var doubleMovementInSnowTundraAndHills = false
|
@Transient var doubleMovementInSnowTundraAndHills = false
|
||||||
|
@Transient var canEnterIceTiles = false
|
||||||
|
@Transient var cannotEnterOceanTiles = false
|
||||||
|
@Transient var cannotEnterOceanTilesUntilAstronomy = false
|
||||||
|
|
||||||
lateinit var owner: String
|
lateinit var owner: String
|
||||||
lateinit var name: String
|
lateinit var name: String
|
||||||
|
@ -134,11 +137,14 @@ class MapUnit {
|
||||||
uniques.addAll(promotions.promotions.map { currentTile.tileMap.gameInfo.ruleSet.unitPromotions[it]!!.effect })
|
uniques.addAll(promotions.promotions.map { currentTile.tileMap.gameInfo.ruleSet.unitPromotions[it]!!.effect })
|
||||||
tempUniques = uniques
|
tempUniques = uniques
|
||||||
|
|
||||||
if("Ignores terrain cost" in uniques) ignoresTerrainCost = true
|
ignoresTerrainCost = ("Ignores terrain cost" in uniques)
|
||||||
if("Rough terrain penalty" in uniques) roughTerrainPenalty = true
|
roughTerrainPenalty = ("Rough terrain penalty" in uniques)
|
||||||
if("Double movement in coast" in uniques) doubleMovementInCoast = true
|
doubleMovementInCoast = ("Double movement in coast" in uniques)
|
||||||
if("Double movement rate through Forest and Jungle" in uniques) doubleMovementInForestAndJungle = true
|
doubleMovementInForestAndJungle = ("Double movement rate through Forest and Jungle" in uniques)
|
||||||
if("Double movement in Snow, Tundra and Hills" in uniques) doubleMovementInSnowTundraAndHills = true
|
doubleMovementInSnowTundraAndHills = ("Double movement in Snow, Tundra and Hills" in uniques)
|
||||||
|
canEnterIceTiles = ("Can enter ice tiles" in uniques)
|
||||||
|
cannotEnterOceanTiles = ("Cannot enter ocean tiles" in uniques)
|
||||||
|
cannotEnterOceanTilesUntilAstronomy = ("Cannot enter ocean tiles until Astronomy" in uniques)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasUnique(unique:String): Boolean {
|
fun hasUnique(unique:String): Boolean {
|
||||||
|
@ -307,7 +313,8 @@ class MapUnit {
|
||||||
fun setTransients(ruleset: Ruleset) {
|
fun setTransients(ruleset: Ruleset) {
|
||||||
promotions.unit=this
|
promotions.unit=this
|
||||||
mapUnitAction?.unit = this
|
mapUnitAction?.unit = this
|
||||||
baseUnit=ruleset.units[name]!!
|
baseUnit=ruleset.units[name]
|
||||||
|
?: throw java.lang.Exception("Unit $name is not found!")
|
||||||
updateUniques()
|
updateUniques()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -363,7 +363,7 @@ open class TileInfo {
|
||||||
if(!defencePercentString.startsWith("-")) defencePercentString = "+$defencePercentString"
|
if(!defencePercentString.startsWith("-")) defencePercentString = "+$defencePercentString"
|
||||||
lineList += "[$defencePercentString] to unit defence".tr()
|
lineList += "[$defencePercentString] to unit defence".tr()
|
||||||
}
|
}
|
||||||
if(getBaseTerrain().impassable) lineList += "Impassable".tr()
|
if(getBaseTerrain().impassable) lineList += Constants.impassable.tr()
|
||||||
|
|
||||||
return lineList.joinToString("\n")
|
return lineList.joinToString("\n")
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,8 +325,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||||
&& !(tile.isCityCenter() && tile.isCoastalTile()))
|
&& !(tile.isCityCenter() && tile.isCoastalTile()))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (tile.terrainFeature == Constants.ice
|
if (tile.terrainFeature == Constants.ice && !unit.canEnterIceTiles)
|
||||||
&& !unit.baseUnit.uniques.contains("Can enter ice tiles"))
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (tile.isWater && unit.type.isLandUnit()) {
|
if (tile.isWater && unit.type.isLandUnit()) {
|
||||||
|
@ -335,8 +334,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (tile.isOcean && unit.civInfo.nation.unique != UniqueAbility.WAYFINDING) {
|
if (tile.isOcean && unit.civInfo.nation.unique != UniqueAbility.WAYFINDING) {
|
||||||
if (unit.baseUnit.uniques.contains("Cannot enter ocean tiles")) return false
|
if (unit.cannotEnterOceanTiles) return false
|
||||||
if (unit.baseUnit.uniques.contains("Cannot enter ocean tiles until Astronomy")
|
if (unit.cannotEnterOceanTilesUntilAstronomy
|
||||||
&& !unit.civInfo.tech.isResearched("Astronomy"))
|
&& !unit.civInfo.tech.isResearched("Astronomy"))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -350,12 +349,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||||
// AIs won't enter city-state's border.
|
// AIs won't enter city-state's border.
|
||||||
}
|
}
|
||||||
|
|
||||||
val unitsInTile = tile.getUnits()
|
val firstUnit = tile.getUnits().firstOrNull()
|
||||||
if (unitsInTile.isNotEmpty()) {
|
if (firstUnit != null && firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo))
|
||||||
val firstUnit = unitsInTile.first()
|
|
||||||
if (firstUnit.civInfo != unit.civInfo && unit.civInfo.isAtWarWith(firstUnit.civInfo))
|
|
||||||
return false
|
return false
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.unciv.models.ruleset.tile
|
package com.unciv.models.ruleset.tile
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
|
import com.unciv.Constants
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.stats.NamedStats
|
import com.unciv.models.stats.NamedStats
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
|
@ -23,6 +24,9 @@ class Terrain : NamedStats() {
|
||||||
if(uniques.isNotEmpty())
|
if(uniques.isNotEmpty())
|
||||||
sb.appendln(uniques.joinToString { it.tr() })
|
sb.appendln(uniques.joinToString { it.tr() })
|
||||||
|
|
||||||
|
if (impassable)
|
||||||
|
sb.appendln(Constants.impassable.tr())
|
||||||
|
else
|
||||||
sb.appendln("{Movement cost}: $movementCost".tr())
|
sb.appendln("{Movement cost}: $movementCost".tr())
|
||||||
|
|
||||||
if (defenceBonus != 0f)
|
if (defenceBonus != 0f)
|
||||||
|
|
|
@ -3,9 +3,15 @@ package com.unciv.ui
|
||||||
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
||||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.*
|
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||||
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
|
import com.unciv.logic.map.TileInfo
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
|
import com.unciv.models.ruleset.tile.Terrain
|
||||||
|
import com.unciv.models.ruleset.tile.TerrainType
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
|
import com.unciv.ui.tilegroups.TileGroup
|
||||||
|
import com.unciv.ui.tilegroups.TileSetStrings
|
||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -15,15 +21,19 @@ class CivilopediaScreen(ruleset: Ruleset) : CameraStageBaseScreen() {
|
||||||
private val categoryToEntries = LinkedHashMap<String, Collection<CivilopediaEntry>>()
|
private val categoryToEntries = LinkedHashMap<String, Collection<CivilopediaEntry>>()
|
||||||
private val categoryToButtons = LinkedHashMap<String, Button>()
|
private val categoryToButtons = LinkedHashMap<String, Button>()
|
||||||
|
|
||||||
private val entrySelectTable = Table().apply { defaults().pad(5f) }
|
private val entrySelectTable = Table().apply { defaults().pad(6f) }
|
||||||
val description = "".toLabel()
|
val description = "".toLabel()
|
||||||
|
|
||||||
|
|
||||||
fun select(category: String) {
|
fun select(category: String) {
|
||||||
entrySelectTable.clear()
|
entrySelectTable.clear()
|
||||||
for (entry in categoryToEntries[category]!!
|
for (entry in categoryToEntries[category]!!
|
||||||
.sortedBy { it.name.tr() }){ // Alphabetical order of localized names
|
.sortedBy { it.name.tr() }){ // Alphabetical order of localized names
|
||||||
val entryButton = Button(skin)
|
val entryButton = Button(skin)
|
||||||
if(entry.image!=null)
|
if(entry.image!=null)
|
||||||
|
if (category=="Terrains")
|
||||||
|
entryButton.add(entry.image).padRight(24f)
|
||||||
|
else
|
||||||
entryButton.add(entry.image).size(50f).padRight(10f)
|
entryButton.add(entry.image).size(50f).padRight(10f)
|
||||||
entryButton.add(entry.name.toLabel())
|
entryButton.add(entry.name.toLabel())
|
||||||
entryButton.onClick {
|
entryButton.onClick {
|
||||||
|
@ -35,29 +45,8 @@ class CivilopediaScreen(ruleset: Ruleset) : CameraStageBaseScreen() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
|
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
|
||||||
val buttonTable = Table()
|
|
||||||
buttonTable.pad(15f)
|
|
||||||
buttonTable.defaults().pad(10f)
|
|
||||||
val buttonTableScroll = ScrollPane(buttonTable)
|
|
||||||
|
|
||||||
val goToGameButton = TextButton("Close".tr(), skin)
|
val tileSetStrings = TileSetStrings()
|
||||||
goToGameButton.onClick {
|
|
||||||
game.setWorldScreen()
|
|
||||||
dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
val topTable = Table()
|
|
||||||
topTable.add(goToGameButton).pad(10f)
|
|
||||||
topTable.add(buttonTableScroll)
|
|
||||||
|
|
||||||
val entryTable = Table()
|
|
||||||
val splitPane = SplitPane(topTable, entryTable, true, skin)
|
|
||||||
splitPane.splitAmount = 0.2f
|
|
||||||
splitPane.setFillParent(true)
|
|
||||||
|
|
||||||
stage.addActor(splitPane)
|
|
||||||
|
|
||||||
description.setWrap(true)
|
|
||||||
|
|
||||||
categoryToEntries["Buildings"] = ruleset.buildings.values
|
categoryToEntries["Buildings"] = ruleset.buildings.values
|
||||||
.map { CivilopediaEntry(it.name,it.getDescription(false, null,ruleset),
|
.map { CivilopediaEntry(it.name,it.getDescription(false, null,ruleset),
|
||||||
|
@ -66,7 +55,8 @@ class CivilopediaScreen(ruleset: Ruleset) : CameraStageBaseScreen() {
|
||||||
.map { CivilopediaEntry(it.name,it.getDescription(ruleset),
|
.map { CivilopediaEntry(it.name,it.getDescription(ruleset),
|
||||||
ImageGetter.getResourceImage(it.name,50f)) }
|
ImageGetter.getResourceImage(it.name,50f)) }
|
||||||
categoryToEntries["Terrains"] = ruleset.terrains.values
|
categoryToEntries["Terrains"] = ruleset.terrains.values
|
||||||
.map { CivilopediaEntry(it.name,it.getDescription(ruleset)) }
|
.map { CivilopediaEntry(it.name,it.getDescription(ruleset),
|
||||||
|
terrainImage(it, ruleset, tileSetStrings) ) }
|
||||||
categoryToEntries["Tile Improvements"] = ruleset.tileImprovements.values
|
categoryToEntries["Tile Improvements"] = ruleset.tileImprovements.values
|
||||||
.map { CivilopediaEntry(it.name,it.getDescription(ruleset,false),
|
.map { CivilopediaEntry(it.name,it.getDescription(ruleset,false),
|
||||||
ImageGetter.getImprovementIcon(it.name,50f)) }
|
ImageGetter.getImprovementIcon(it.name,50f)) }
|
||||||
|
@ -87,6 +77,10 @@ class CivilopediaScreen(ruleset: Ruleset) : CameraStageBaseScreen() {
|
||||||
categoryToEntries["Tutorials"] = tutorialController.getCivilopediaTutorials()
|
categoryToEntries["Tutorials"] = tutorialController.getCivilopediaTutorials()
|
||||||
.map { CivilopediaEntry(it.key.replace("_"," "), it.value.joinToString("\n\n") { line -> line.tr() }) }
|
.map { CivilopediaEntry(it.key.replace("_"," "), it.value.joinToString("\n\n") { line -> line.tr() }) }
|
||||||
|
|
||||||
|
val buttonTable = Table()
|
||||||
|
buttonTable.pad(15f)
|
||||||
|
buttonTable.defaults().pad(10f)
|
||||||
|
|
||||||
for (category in categoryToEntries.keys) {
|
for (category in categoryToEntries.keys) {
|
||||||
val button = TextButton(category.tr(), skin)
|
val button = TextButton(category.tr(), skin)
|
||||||
button.style = TextButton.TextButtonStyle(button.style)
|
button.style = TextButton.TextButtonStyle(button.style)
|
||||||
|
@ -94,19 +88,74 @@ class CivilopediaScreen(ruleset: Ruleset) : CameraStageBaseScreen() {
|
||||||
button.onClick { select(category) }
|
button.onClick { select(category) }
|
||||||
buttonTable.add(button)
|
buttonTable.add(button)
|
||||||
}
|
}
|
||||||
select("Tutorials")
|
|
||||||
val sp = ScrollPane(entrySelectTable)
|
buttonTable.pack()
|
||||||
sp.setupOverscroll(5f, 1f, 200f)
|
buttonTable.width = stage.width
|
||||||
entryTable.add(sp).width(Value.percentWidth(0.25f, entryTable)).height(Value.percentHeight(0.7f, entryTable))
|
val buttonTableScroll = ScrollPane(buttonTable)
|
||||||
|
|
||||||
|
val goToGameButton = TextButton("Close".tr(), skin)
|
||||||
|
goToGameButton.onClick {
|
||||||
|
game.setWorldScreen()
|
||||||
|
dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
val topTable = Table()
|
||||||
|
topTable.add(goToGameButton).pad(10f)
|
||||||
|
topTable.add(buttonTableScroll)
|
||||||
|
topTable.pack()
|
||||||
|
//buttonTable.height = topTable.height
|
||||||
|
|
||||||
|
val entryTable = Table()
|
||||||
|
val splitPane = SplitPane(topTable, entryTable, true, skin)
|
||||||
|
splitPane.splitAmount = topTable.prefHeight / stage.height
|
||||||
|
entryTable.height = stage.height - topTable.prefHeight
|
||||||
|
splitPane.setFillParent(true)
|
||||||
|
|
||||||
|
stage.addActor(splitPane)
|
||||||
|
|
||||||
|
description.setWrap(true)
|
||||||
|
|
||||||
|
val entrySelectScroll = ScrollPane(entrySelectTable)
|
||||||
|
entrySelectScroll.setupOverscroll(5f, 1f, 200f)
|
||||||
|
entryTable.add(entrySelectScroll)
|
||||||
|
.width(Value.percentWidth(0.25f, entryTable))
|
||||||
|
.fillY()
|
||||||
.pad(Value.percentWidth(0.02f, entryTable))
|
.pad(Value.percentWidth(0.02f, entryTable))
|
||||||
entryTable.add(ScrollPane(description)).colspan(4)
|
entryTable.add(ScrollPane(description)).colspan(4)
|
||||||
.width(Value.percentWidth(0.65f, entryTable))
|
.width(Value.percentWidth(0.65f, entryTable))
|
||||||
.height(Value.percentHeight(0.7f, entryTable))
|
.fillY()
|
||||||
.pad(Value.percentWidth(0.02f, entryTable))
|
.pad(Value.percentWidth(0.02f, entryTable))
|
||||||
// Simply changing these to x*width, y*height won't work
|
// Simply changing these to x*width, y*height won't work
|
||||||
|
|
||||||
buttonTable.width = stage.width
|
select("Tutorials")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun terrainImage (terrain: Terrain, ruleset: Ruleset, tileSetStrings: TileSetStrings ): Actor? {
|
||||||
|
val tileInfo = TileInfo()
|
||||||
|
tileInfo.ruleset = ruleset
|
||||||
|
when(terrain.type) {
|
||||||
|
TerrainType.NaturalWonder -> {
|
||||||
|
tileInfo.naturalWonder = terrain.name
|
||||||
|
tileInfo.baseTerrain = terrain.turnsInto ?: Constants.grassland
|
||||||
|
}
|
||||||
|
TerrainType.TerrainFeature -> {
|
||||||
|
tileInfo.terrainFeature = terrain.name
|
||||||
|
tileInfo.baseTerrain = terrain.occursOn?.last() ?: Constants.grassland
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
tileInfo.baseTerrain = terrain.name
|
||||||
|
}
|
||||||
|
tileInfo.setTransients()
|
||||||
|
val group = TileGroup(tileInfo, TileSetStrings())
|
||||||
|
group.showEntireMap = true
|
||||||
|
group.forMapEditorIcon = true
|
||||||
|
group.update()
|
||||||
|
return group
|
||||||
|
// val wrapper = Table()
|
||||||
|
// wrapper.add(group).pad(24f)
|
||||||
|
// wrapper.pad(2f,24f,2f,24f)
|
||||||
|
// wrapper.debug = true
|
||||||
|
// return wrapper
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ import com.unciv.logic.map.RoadStatus
|
||||||
import com.unciv.models.translations.tr
|
import com.unciv.models.translations.tr
|
||||||
import com.unciv.ui.saves.Gzip
|
import com.unciv.ui.saves.Gzip
|
||||||
import com.unciv.ui.utils.Popup
|
import com.unciv.ui.utils.Popup
|
||||||
|
import com.unciv.ui.utils.enable
|
||||||
|
import com.unciv.ui.utils.isEnabled
|
||||||
import com.unciv.ui.utils.onClick
|
import com.unciv.ui.utils.onClick
|
||||||
import com.unciv.ui.worldscreen.mainmenu.DropBox
|
import com.unciv.ui.worldscreen.mainmenu.DropBox
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
@ -19,8 +21,10 @@ import kotlin.concurrent.thread
|
||||||
class MapEditorMenuPopup(mapEditorScreen: MapEditorScreen): Popup(mapEditorScreen){
|
class MapEditorMenuPopup(mapEditorScreen: MapEditorScreen): Popup(mapEditorScreen){
|
||||||
init{
|
init{
|
||||||
val mapNameEditor = TextField(mapEditorScreen.mapName, skin)
|
val mapNameEditor = TextField(mapEditorScreen.mapName, skin)
|
||||||
mapNameEditor.addListener{ mapEditorScreen.mapName=mapNameEditor.text; true }
|
add(mapNameEditor).fillX().row()
|
||||||
add(mapNameEditor).row()
|
mapNameEditor.selectAll()
|
||||||
|
mapNameEditor.maxLength = 240 // A few under max for most filesystems
|
||||||
|
mapEditorScreen.stage.keyboardFocus = mapNameEditor
|
||||||
|
|
||||||
val newMapButton = TextButton("New map".tr(),skin)
|
val newMapButton = TextButton("New map".tr(),skin)
|
||||||
newMapButton.onClick {
|
newMapButton.onClick {
|
||||||
|
@ -51,10 +55,28 @@ class MapEditorMenuPopup(mapEditorScreen: MapEditorScreen): Popup(mapEditorScree
|
||||||
saveMapButton.onClick {
|
saveMapButton.onClick {
|
||||||
mapEditorScreen.tileMap.mapParameters.name=mapEditorScreen.mapName
|
mapEditorScreen.tileMap.mapParameters.name=mapEditorScreen.mapName
|
||||||
mapEditorScreen.tileMap.mapParameters.type=MapType.custom
|
mapEditorScreen.tileMap.mapParameters.type=MapType.custom
|
||||||
|
thread ( name = "SaveMap" ) {
|
||||||
|
try {
|
||||||
MapSaver.saveMap(mapEditorScreen.mapName, mapEditorScreen.tileMap)
|
MapSaver.saveMap(mapEditorScreen.mapName, mapEditorScreen.tileMap)
|
||||||
UncivGame.Current.setWorldScreen()
|
UncivGame.Current.setWorldScreen()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
Gdx.app.postRunnable {
|
||||||
|
val cantLoadGamePopup = Popup(mapEditorScreen)
|
||||||
|
cantLoadGamePopup.addGoodSizedLabel("It looks like your map can't be saved!").row()
|
||||||
|
cantLoadGamePopup.addCloseButton()
|
||||||
|
cantLoadGamePopup.open(force = true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveMapButton.isEnabled = mapNameEditor.text.isNotEmpty()
|
||||||
add(saveMapButton).row()
|
add(saveMapButton).row()
|
||||||
|
mapNameEditor.addListener {
|
||||||
|
mapEditorScreen.mapName = mapNameEditor.text
|
||||||
|
saveMapButton.isEnabled = mapNameEditor.text.isNotEmpty()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
val copyMapAsTextButton = TextButton("Copy to clipboard".tr(), skin)
|
val copyMapAsTextButton = TextButton("Copy to clipboard".tr(), skin)
|
||||||
copyMapAsTextButton.onClick {
|
copyMapAsTextButton.onClick {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import com.unciv.ui.utils.setFontSize
|
||||||
|
|
||||||
class MapEditorScreen(): CameraStageBaseScreen() {
|
class MapEditorScreen(): CameraStageBaseScreen() {
|
||||||
val ruleset = RulesetCache.getBaseRuleset()
|
val ruleset = RulesetCache.getBaseRuleset()
|
||||||
var mapName = "My first map"
|
var mapName = ""
|
||||||
|
|
||||||
var tileMap = TileMap()
|
var tileMap = TileMap()
|
||||||
lateinit var mapHolder: EditorMapHolder
|
lateinit var mapHolder: EditorMapHolder
|
||||||
|
|
|
@ -42,11 +42,13 @@ class LoadGameScreen : PickerScreen() {
|
||||||
if (ex is UncivShowableException && ex.localizedMessage != null) {
|
if (ex is UncivShowableException && ex.localizedMessage != null) {
|
||||||
// thrown exceptions are our own tests and can be shown to the user
|
// thrown exceptions are our own tests and can be shown to the user
|
||||||
cantLoadGamePopup.addGoodSizedLabel(ex.localizedMessage).row()
|
cantLoadGamePopup.addGoodSizedLabel(ex.localizedMessage).row()
|
||||||
|
cantLoadGamePopup.addCloseButton()
|
||||||
cantLoadGamePopup.open()
|
cantLoadGamePopup.open()
|
||||||
} else {
|
} else {
|
||||||
cantLoadGamePopup.addGoodSizedLabel("If you could copy your game data (\"Copy saved game to clipboard\" - ").row()
|
cantLoadGamePopup.addGoodSizedLabel("If you could copy your game data (\"Copy saved game to clipboard\" - ").row()
|
||||||
cantLoadGamePopup.addGoodSizedLabel(" paste into an email to yairm210@hotmail.com)").row()
|
cantLoadGamePopup.addGoodSizedLabel(" paste into an email to yairm210@hotmail.com)").row()
|
||||||
cantLoadGamePopup.addGoodSizedLabel("I could maybe help you figure out what went wrong, since this isn't supposed to happen!").row()
|
cantLoadGamePopup.addGoodSizedLabel("I could maybe help you figure out what went wrong, since this isn't supposed to happen!").row()
|
||||||
|
cantLoadGamePopup.addCloseButton()
|
||||||
cantLoadGamePopup.open()
|
cantLoadGamePopup.open()
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,10 @@ fun Button.enable() {
|
||||||
color = Color.WHITE
|
color = Color.WHITE
|
||||||
touchable = Touchable.enabled
|
touchable = Touchable.enabled
|
||||||
}
|
}
|
||||||
|
var Button.isEnabled: Boolean
|
||||||
|
//Todo: Use in PromotionPickerScreen, TradeTable, WorldScreen.updateNextTurnButton
|
||||||
|
get() = touchable == Touchable.enabled
|
||||||
|
set(value) = if (value) enable() else disable()
|
||||||
|
|
||||||
fun colorFromRGB(r: Int, g: Int, b: Int): Color {
|
fun colorFromRGB(r: Int, g: Int, b: Int): Color {
|
||||||
return Color(r/255f, g/255f, b/255f, 1f)
|
return Color(r/255f, g/255f, b/255f, 1f)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.unciv.logic.automation.UnitAutomation
|
||||||
import com.unciv.logic.city.CityInfo
|
import com.unciv.logic.city.CityInfo
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.logic.map.*
|
import com.unciv.logic.map.*
|
||||||
|
import com.unciv.models.AttackableTile
|
||||||
import com.unciv.models.UncivSound
|
import com.unciv.models.UncivSound
|
||||||
import com.unciv.models.ruleset.unit.UnitType
|
import com.unciv.models.ruleset.unit.UnitType
|
||||||
import com.unciv.ui.map.TileGroupMap
|
import com.unciv.ui.map.TileGroupMap
|
||||||
|
@ -261,23 +262,24 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||||
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
|
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
|
||||||
}
|
}
|
||||||
|
|
||||||
val unitType = unit.type
|
val attackableTiles: List<AttackableTile> = if (unit.type.isCivilian()) listOf()
|
||||||
val attackableTiles: List<TileInfo> = if (unitType.isCivilian()) listOf()
|
|
||||||
else {
|
else {
|
||||||
val tiles = BattleHelper.getAttackableEnemies(unit, unit.movement.getDistanceToTiles()).map { it.tileToAttack }
|
BattleHelper.getAttackableEnemies(unit, unit.movement.getDistanceToTiles())
|
||||||
tiles.filter { (UncivGame.Current.viewEntireMapForDebug || playerViewableTilePositions.contains(it.position)) }
|
.filter { (UncivGame.Current.viewEntireMapForDebug ||
|
||||||
|
playerViewableTilePositions.contains(it.tileToAttack.position)) }
|
||||||
|
.distinctBy { it.tileToAttack }
|
||||||
}
|
}
|
||||||
|
|
||||||
for (attackableTile in attackableTiles) {
|
for (attackableTile in attackableTiles) {
|
||||||
|
|
||||||
tileGroups[attackableTile]!!.showCircle(colorFromRGB(237, 41, 57))
|
tileGroups[attackableTile.tileToAttack]!!.showCircle(colorFromRGB(237, 41, 57))
|
||||||
|
|
||||||
val distance = unit.currentTile.aerialDistanceTo(attackableTile)
|
|
||||||
if (distance > unit.getRange())
|
|
||||||
tileGroups[attackableTile]!!.showCrosshair(colorFromRGB(255, 75, 0))
|
|
||||||
else
|
|
||||||
tileGroups[attackableTile]!!.showCrosshair(Color.RED)
|
|
||||||
|
|
||||||
|
tileGroups[attackableTile.tileToAttack]!!.showCrosshair (
|
||||||
|
// the targets which cannot be attacked without movements shown as orange-ish
|
||||||
|
if (attackableTile.tileToAttackFrom != unit.currentTile)
|
||||||
|
colorFromRGB(255, 75, 0)
|
||||||
|
else Color.RED
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade out less relevant images if a military unit is selected
|
// Fade out less relevant images if a military unit is selected
|
||||||
|
|
238
tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt
Normal file
238
tests/src/com/unciv/logic/map/UnitMovementAlgorithmsTests.kt
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
// Taken from https://github.com/TomGrill/gdx-testing
|
||||||
|
package com.unciv.logic.map
|
||||||
|
|
||||||
|
import com.unciv.Constants
|
||||||
|
import com.unciv.logic.city.CityInfo
|
||||||
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
|
import com.unciv.logic.civilization.diplomacy.DiplomacyManager
|
||||||
|
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||||
|
import com.unciv.models.ruleset.Nation
|
||||||
|
import com.unciv.models.ruleset.Ruleset
|
||||||
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
|
import com.unciv.models.ruleset.unit.BaseUnit
|
||||||
|
import com.unciv.models.ruleset.unit.UnitType
|
||||||
|
import com.unciv.testing.GdxTestRunner
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(GdxTestRunner::class)
|
||||||
|
class UnitMovementAlgorithmsTests {
|
||||||
|
|
||||||
|
private var tile = TileInfo()
|
||||||
|
private var civInfo = CivilizationInfo()
|
||||||
|
private var ruleSet = Ruleset()
|
||||||
|
private var unit = MapUnit()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun initTheWorld() {
|
||||||
|
RulesetCache.loadRulesets()
|
||||||
|
ruleSet = RulesetCache.getBaseRuleset()
|
||||||
|
tile.ruleset = ruleSet
|
||||||
|
civInfo.tech.techsResearched.addAll(ruleSet.technologies.keys)
|
||||||
|
civInfo.tech.embarkedUnitsCanEnterOcean = true
|
||||||
|
civInfo.tech.unitsCanEmbark = true
|
||||||
|
civInfo.nation = Nation().apply {
|
||||||
|
name = "My nation"
|
||||||
|
cities = arrayListOf("The Capital")
|
||||||
|
}
|
||||||
|
unit.civInfo = civInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canPassThroughPassableTerrains() {
|
||||||
|
for (terrain in ruleSet.terrains.values) {
|
||||||
|
tile.baseTerrain = terrain.name
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = UnitType.Melee }
|
||||||
|
|
||||||
|
Assert.assertTrue(terrain.name, terrain.impassable != unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unitCanEnterTheCity() {
|
||||||
|
val map = TileMap()
|
||||||
|
tile.baseTerrain = Constants.hill
|
||||||
|
tile.tileMap = map
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
val otherTile = tile.clone()
|
||||||
|
otherTile.baseTerrain = Constants.coast
|
||||||
|
otherTile.position.y = 1f
|
||||||
|
|
||||||
|
map.tileMatrix.add(arrayListOf(tile, otherTile))
|
||||||
|
|
||||||
|
val city = CityInfo()
|
||||||
|
city.location = tile.position
|
||||||
|
city.civInfo = civInfo
|
||||||
|
tile.owningCity = city
|
||||||
|
|
||||||
|
for (type in UnitType.values())
|
||||||
|
{
|
||||||
|
unit.owner = civInfo.civName
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
Assert.assertTrue(type.name, unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun waterUnitCanNOTEnterLand() {
|
||||||
|
for (terrain in ruleSet.terrains.values) {
|
||||||
|
if (terrain.impassable) continue
|
||||||
|
tile.baseTerrain = terrain.name
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
if (type == UnitType.City) continue
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
Assert.assertTrue("%s cannot be at %s".format(type, terrain.name),
|
||||||
|
(type.isWaterUnit() && tile.isLand) != unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTEnterIce() {
|
||||||
|
tile.baseTerrain = Constants.ocean
|
||||||
|
tile.terrainFeature = Constants.ice
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
|
||||||
|
if (type == UnitType.WaterSubmarine) {
|
||||||
|
unit.baseUnit.uniques.add("Can enter ice tiles")
|
||||||
|
}
|
||||||
|
unit.updateUniques()
|
||||||
|
|
||||||
|
Assert.assertTrue("$type cannot be in Ice",
|
||||||
|
(type == UnitType.WaterSubmarine) == unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTEnterNaturalWonder() {
|
||||||
|
tile.baseTerrain = Constants.plains
|
||||||
|
tile.naturalWonder = "Wonder Thunder"
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
|
||||||
|
Assert.assertFalse("$type must not enter Wonder tile", unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTEnterCoastUntilProperTechIsResearched() {
|
||||||
|
|
||||||
|
civInfo.tech.unitsCanEmbark = false
|
||||||
|
|
||||||
|
tile.baseTerrain = Constants.coast
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
|
||||||
|
Assert.assertTrue("$type cannot be in Coast",
|
||||||
|
unit.type.isLandUnit() != unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTEnterOceanUntilProperTechIsResearched() {
|
||||||
|
|
||||||
|
civInfo.tech.embarkedUnitsCanEnterOcean = false
|
||||||
|
|
||||||
|
tile.baseTerrain = Constants.ocean
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
|
||||||
|
Assert.assertTrue("$type cannot be in Ocean",
|
||||||
|
unit.type.isLandUnit() != unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTEnterOceanWithLimitations() {
|
||||||
|
|
||||||
|
tile.baseTerrain = Constants.ocean
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
unit.baseUnit = BaseUnit().apply {
|
||||||
|
unitType = type
|
||||||
|
if (type == UnitType.Melee)
|
||||||
|
uniques.add("Cannot enter ocean tiles")
|
||||||
|
if (type == UnitType.Ranged)
|
||||||
|
uniques.add("Cannot enter ocean tiles until Astronomy")
|
||||||
|
}
|
||||||
|
unit.updateUniques()
|
||||||
|
|
||||||
|
Assert.assertTrue("$type cannot be in Ocean",
|
||||||
|
(type == UnitType.Melee) != unit.movement.canPassThrough(tile))
|
||||||
|
|
||||||
|
civInfo.tech.techsResearched.remove("Astronomy")
|
||||||
|
|
||||||
|
Assert.assertTrue("$type cannot be in Ocean until Astronomy",
|
||||||
|
(type == UnitType.Melee ||
|
||||||
|
type == UnitType.Ranged) != unit.movement.canPassThrough(tile))
|
||||||
|
|
||||||
|
civInfo.tech.techsResearched.add("Astronomy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTPassThroughTileWithEnemyUnits() {
|
||||||
|
tile.baseTerrain = Constants.grassland
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
val otherCiv = CivilizationInfo()
|
||||||
|
otherCiv.civName = "Barbarians" // they are always enemies
|
||||||
|
otherCiv.nation = Nation().apply { name = "Barbarians" }
|
||||||
|
val otherUnit = MapUnit()
|
||||||
|
otherUnit.civInfo = otherCiv
|
||||||
|
tile.militaryUnit = otherUnit
|
||||||
|
|
||||||
|
for (type in UnitType.values()) {
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = type }
|
||||||
|
|
||||||
|
Assert.assertFalse("$type must not enter occupied tile", unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canNOTPassForeignTiles() {
|
||||||
|
tile.baseTerrain = Constants.desert
|
||||||
|
tile.setTransients()
|
||||||
|
|
||||||
|
val otherCiv = CivilizationInfo()
|
||||||
|
otherCiv.civName = "Other civ"
|
||||||
|
otherCiv.nation = Nation().apply { name = "Other nation" }
|
||||||
|
|
||||||
|
val city = CityInfo()
|
||||||
|
city.location = tile.position.cpy().add(1f,1f)
|
||||||
|
city.civInfo = otherCiv
|
||||||
|
tile.owningCity = city
|
||||||
|
|
||||||
|
unit.baseUnit = BaseUnit().apply { unitType = UnitType.Melee }
|
||||||
|
unit.owner = civInfo.civName
|
||||||
|
|
||||||
|
Assert.assertFalse("Unit must not enter other civ tile", unit.movement.canPassThrough(tile))
|
||||||
|
|
||||||
|
city.location = tile.position
|
||||||
|
|
||||||
|
Assert.assertFalse("Unit must not enter other civ city", unit.movement.canPassThrough(tile))
|
||||||
|
|
||||||
|
city.hasJustBeenConquered = true
|
||||||
|
civInfo.diplomacy["Other civ"] = DiplomacyManager(otherCiv, "Other civ")
|
||||||
|
civInfo.getDiplomacyManager(otherCiv).diplomaticStatus = DiplomaticStatus.War
|
||||||
|
|
||||||
|
Assert.assertTrue("Unit can capture other civ city", unit.movement.canPassThrough(tile))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue