3.12.2-patch1

This commit is contained in:
Yair Morgenstern
2020-12-21 20:59:47 +02:00
parent 4e62d0b722
commit 43d9813ce1
8 changed files with 257 additions and 209 deletions

View File

@ -3,8 +3,8 @@ package com.unciv.build
object BuildConfig { object BuildConfig {
const val kotlinVersion = "1.3.71" const val kotlinVersion = "1.3.71"
const val appName = "Unciv" const val appName = "Unciv"
const val appCodeNumber = 512 const val appCodeNumber = 513
const val appVersion = "3.12.2" const val appVersion = "3.12.2-patch1"
const val gdxVersion = "1.9.12" const val gdxVersion = "1.9.12"
const val roboVMVersion = "2.3.1" const val roboVMVersion = "2.3.1"

View File

@ -22,12 +22,17 @@ import kotlin.collections.ArrayList
class UncivShowableException(missingMods: String) : Exception(missingMods) class UncivShowableException(missingMods: String) : Exception(missingMods)
class GameInfo { class GameInfo {
@Transient lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn @Transient
@Transient lateinit var currentPlayerCiv:CivilizationInfo // this is called thousands of times, no reason to search for it with a find{} every time lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn
@Transient
lateinit var currentPlayerCiv: CivilizationInfo // this is called thousands of times, no reason to search for it with a find{} every time
/** This is used in multiplayer games, where I may have a saved game state on my phone /** This is used in multiplayer games, where I may have a saved game state on my phone
* that is inconsistent with the saved game on the cloud */ * that is inconsistent with the saved game on the cloud */
@Transient var isUpToDate=false @Transient
@Transient lateinit var ruleSet:Ruleset var isUpToDate = false
@Transient
lateinit var ruleSet: Ruleset
var civilizations = mutableListOf<CivilizationInfo>() var civilizations = mutableListOf<CivilizationInfo>()
var difficulty = "Chieftain" // difficulty is game-wide, think what would happen if 2 human players could play on different difficulties? var difficulty = "Chieftain" // difficulty is game-wide, think what would happen if 2 human players could play on different difficulties?
@ -37,6 +42,7 @@ class GameInfo {
var oneMoreTurnMode = false var oneMoreTurnMode = false
var currentPlayer = "" var currentPlayer = ""
var gameId = UUID.randomUUID().toString() // random string var gameId = UUID.randomUUID().toString() // random string
@Volatile @Volatile
var customSaveLocation: String? = null var customSaveLocation: String? = null
@ -160,8 +166,7 @@ class GameInfo {
val unitName = tile.militaryUnit!!.name val unitName = tile.militaryUnit!!.name
thisPlayer.addNotification("An enemy [$unitName] was spotted $inOrNear our territory", tile.position, Color.RED) thisPlayer.addNotification("An enemy [$unitName] was spotted $inOrNear our territory", tile.position, Color.RED)
} }
} } else {
else {
val positions = tiles.map { it.position } val positions = tiles.map { it.position }
thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", Color.RED, LocationAction(positions)) thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", Color.RED, LocationAction(positions))
} }
@ -235,8 +240,10 @@ class GameInfo {
* adopted Honor policy and have explored the [tile] where the Barbarian Encampent has spawned. * adopted Honor policy and have explored the [tile] where the Barbarian Encampent has spawned.
*/ */
fun notifyCivsOfBarbarianEncampment(tile: TileInfo) { fun notifyCivsOfBarbarianEncampment(tile: TileInfo) {
civilizations.filter { it.hasUnique("Notified of new Barbarian encampments") civilizations.filter {
&& it.exploredTiles.contains(tile.position) } it.hasUnique("Notified of new Barbarian encampments")
&& it.exploredTiles.contains(tile.position)
}
.forEach { it.addNotification("A new barbarian encampment has spawned!", tile.position, Color.RED) } .forEach { it.addNotification("A new barbarian encampment has spawned!", tile.position, Color.RED) }
} }

View File

@ -26,9 +26,7 @@ object GameStarter {
gameInfo.tileMap = MapSaver.loadScenario(gameSetupInfo.mapParameters.name).tileMap gameInfo.tileMap = MapSaver.loadScenario(gameSetupInfo.mapParameters.name).tileMap
else if (gameSetupInfo.mapParameters.name != "") { else if (gameSetupInfo.mapParameters.name != "") {
gameInfo.tileMap = MapSaver.loadMap(gameSetupInfo.mapFile!!) gameInfo.tileMap = MapSaver.loadMap(gameSetupInfo.mapFile!!)
} } else gameInfo.tileMap = MapGenerator(ruleset).generateMap(gameSetupInfo.mapParameters)
else gameInfo.tileMap = MapGenerator(ruleset).generateMap(gameSetupInfo.mapParameters)
gameInfo.tileMap.mapParameters = gameSetupInfo.mapParameters gameInfo.tileMap.mapParameters = gameSetupInfo.mapParameters
gameInfo.tileMap.gameInfo = gameInfo // need to set this transient before placing units in the map gameInfo.tileMap.gameInfo = gameInfo // need to set this transient before placing units in the map
@ -168,6 +166,7 @@ object GameStarter {
fun placeNearStartingPosition(unitName: String) { fun placeNearStartingPosition(unitName: String) {
civ.placeUnitNearTile(startingLocation.position, unitName) civ.placeUnitNearTile(startingLocation.position, unitName)
} }
val warriorEquivalent = getWarriorEquivalent(civ) val warriorEquivalent = getWarriorEquivalent(civ)
val startingUnits = when { val startingUnits = when {
civ.isPlayerCivilization() -> gameInfo.getDifficulty().startingUnits civ.isPlayerCivilization() -> gameInfo.getDifficulty().startingUnits

View File

@ -32,12 +32,18 @@ import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
class CityInfo { class CityInfo {
@Transient lateinit var civInfo: CivilizationInfo @Transient
@Transient lateinit private var centerTileInfo:TileInfo // cached for better performance lateinit var civInfo: CivilizationInfo
@Transient val range = 2 @Transient
@Transient lateinit var tileMap: TileMap lateinit private var centerTileInfo: TileInfo // cached for better performance
@Transient lateinit var tilesInRange:HashSet<TileInfo> @Transient
@Transient var hasJustBeenConquered = false // this is so that military units can enter the city, even before we decide what to do with it val range = 2
@Transient
lateinit var tileMap: TileMap
@Transient
lateinit var tilesInRange: HashSet<TileInfo>
@Transient
var hasJustBeenConquered = false // this is so that military units can enter the city, even before we decide what to do with it
var location: Vector2 = Vector2.Zero var location: Vector2 = Vector2.Zero
var id: String = UUID.randomUUID().toString() var id: String = UUID.randomUUID().toString()
@ -55,14 +61,17 @@ class CityInfo {
/** All tiles that this city controls */ /** All tiles that this city controls */
var tiles = HashSet<Vector2>() var tiles = HashSet<Vector2>()
/** Tiles that have population assigned to them */ /** Tiles that have population assigned to them */
var workedTiles = HashSet<Vector2>() var workedTiles = HashSet<Vector2>()
/** Tiles that the population in them won't be reassigned */ /** Tiles that the population in them won't be reassigned */
var lockedTiles = HashSet<Vector2>() var lockedTiles = HashSet<Vector2>()
var isBeingRazed = false var isBeingRazed = false
var attackedThisTurn = false var attackedThisTurn = false
var hasSoldBuildingThisTurn = false var hasSoldBuildingThisTurn = false
var isPuppet = false var isPuppet = false
/** The very first found city is the _original_ capital, /** The very first found city is the _original_ capital,
* while the _current_ capital can be any other city after the original one is captured. * while the _current_ capital can be any other city after the original one is captured.
* It is important to distinct them since the original cannot be razed and defines the Domination Victory. */ * It is important to distinct them since the original cannot be razed and defines the Domination Victory. */
@ -154,7 +163,6 @@ class CityInfo {
} }
fun getCenterTile(): TileInfo = centerTileInfo fun getCenterTile(): TileInfo = centerTileInfo
fun getTiles(): Sequence<TileInfo> = tiles.asSequence().map { tileMap[it] } fun getTiles(): Sequence<TileInfo> = tiles.asSequence().map { tileMap[it] }
fun getWorkableTiles() = tilesInRange.asSequence().filter { it.getOwner() == civInfo } fun getWorkableTiles() = tilesInRange.asSequence().filter { it.getOwner() == civInfo }
@ -167,6 +175,7 @@ class CityInfo {
val mediumTypes = civInfo.citiesConnectedToCapitalToMediums[this] ?: return false val mediumTypes = civInfo.citiesConnectedToCapitalToMediums[this] ?: return false
return connectionTypePredicate(mediumTypes) return connectionTypePredicate(mediumTypes)
} }
fun isInResistance() = resistanceCounter > 0 fun isInResistance() = resistanceCounter > 0
@ -196,7 +205,8 @@ class CityInfo {
for (unique in cityConstructions.builtBuildingUniqueMap.getUniques("Provides [] []")) { // E.G "Provides [1] [Iron]" for (unique in cityConstructions.builtBuildingUniqueMap.getUniques("Provides [] []")) { // E.G "Provides [1] [Iron]"
val resource = getRuleset().tileResources[unique.params[1]] val resource = getRuleset().tileResources[unique.params[1]]
if (resource != null) { if (resource != null) {
cityResources.add(resource, unique.params[0].toInt() * civInfo.getResourceModifier(resource), "Tiles") } cityResources.add(resource, unique.params[0].toInt() * civInfo.getResourceModifier(resource), "Tiles")
}
} }
return cityResources return cityResources
@ -297,7 +307,9 @@ class CityInfo {
return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth } return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth }
} }
override fun toString(): String {return name} // for debug override fun toString(): String {
return name
} // for debug
//endregion //endregion
//region state-changing functions //region state-changing functions
@ -575,9 +587,7 @@ class CityInfo {
val roadImprovement = getRuleset().tileImprovements["Road"] val roadImprovement = getRuleset().tileImprovements["Road"]
if (roadImprovement != null && roadImprovement.techRequired in civInfo.tech.techsResearched) if (roadImprovement != null && roadImprovement.techRequired in civInfo.tech.techsResearched)
getCenterTile().roadStatus = RoadStatus.Road getCenterTile().roadStatus = RoadStatus.Road
} } else if (getCenterTile().roadStatus != RoadStatus.Railroad) {
else if (getCenterTile().roadStatus != RoadStatus.Railroad) {
val railroadImprovement = getRuleset().tileImprovements["Railroad"] val railroadImprovement = getRuleset().tileImprovements["Railroad"]
if (railroadImprovement != null && railroadImprovement.techRequired in civInfo.tech.techsResearched) if (railroadImprovement != null && railroadImprovement.techRequired in civInfo.tech.techsResearched)
getCenterTile().roadStatus = RoadStatus.Railroad getCenterTile().roadStatus = RoadStatus.Railroad
@ -628,8 +638,7 @@ class CityInfo {
} }
fun canPurchase(construction: IConstruction): Boolean { fun canPurchase(construction: IConstruction): Boolean {
if (construction is BaseUnit) if (construction is BaseUnit) {
{
val tile = getCenterTile() val tile = getCenterTile()
if (construction.unitType.isCivilian()) if (construction.unitType.isCivilian())
return tile.civilianUnit == null return tile.civilianUnit == null

View File

@ -32,28 +32,40 @@ import kotlin.math.roundToInt
class CivilizationInfo { class CivilizationInfo {
@Transient lateinit var gameInfo: GameInfo @Transient
@Transient lateinit var nation:Nation lateinit var gameInfo: GameInfo
@Transient
lateinit var nation: Nation
/** /**
* We never add or remove from here directly, could cause comodification problems. * We never add or remove from here directly, could cause comodification problems.
* Instead, we create a copy list with the change, and replace this list. * Instead, we create a copy list with the change, and replace this list.
* The other solution, casting toList() every "get", has a performance cost * The other solution, casting toList() every "get", has a performance cost
*/ */
@Transient private var units = listOf<MapUnit>() @Transient
@Transient var viewableTiles = setOf<TileInfo>() private var units = listOf<MapUnit>()
@Transient var viewableInvisibleUnitsTiles = setOf<TileInfo>() @Transient
var viewableTiles = setOf<TileInfo>()
@Transient
var viewableInvisibleUnitsTiles = setOf<TileInfo>()
/** Contains mapping of cities to travel mediums from ALL civilizations connected by trade routes to the capital */ /** Contains mapping of cities to travel mediums from ALL civilizations connected by trade routes to the capital */
@Transient var citiesConnectedToCapitalToMediums = mapOf<CityInfo, Set<String>>() @Transient
var citiesConnectedToCapitalToMediums = mapOf<CityInfo, Set<String>>()
/** This is for performance since every movement calculation depends on this, see MapUnit comment */ /** This is for performance since every movement calculation depends on this, see MapUnit comment */
@Transient var hasActiveGreatWall = false @Transient
@Transient var statsForNextTurn = Stats() var hasActiveGreatWall = false
@Transient var happinessForNextTurn = 0 @Transient
@Transient var detailedCivResources = ResourceSupplyList() var statsForNextTurn = Stats()
@Transient
var happinessForNextTurn = 0
@Transient
var detailedCivResources = ResourceSupplyList()
var playerType = PlayerType.AI var playerType = PlayerType.AI
/** Used in online multiplayer for human players */ var playerId = "" /** Used in online multiplayer for human players */
var playerId = ""
var gold = 0 var gold = 0
var civName = "" var civName = ""
var tech = TechManager() var tech = TechManager()
@ -119,8 +131,10 @@ class CivilizationInfo {
if (isPlayerCivilization()) return gameInfo.getDifficulty() if (isPlayerCivilization()) return gameInfo.getDifficulty()
return gameInfo.ruleSet.difficulties["Chieftain"]!! return gameInfo.ruleSet.difficulties["Chieftain"]!!
} }
fun getDiplomacyManager(civInfo: CivilizationInfo) = getDiplomacyManager(civInfo.civName) fun getDiplomacyManager(civInfo: CivilizationInfo) = getDiplomacyManager(civInfo.civName)
fun getDiplomacyManager(civName: String) = diplomacy[civName]!! fun getDiplomacyManager(civName: String) = diplomacy[civName]!!
/** Returns only undefeated civs, aka the ones we care about */ /** Returns only undefeated civs, aka the ones we care about */
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }.filter { !it.isDefeated() } fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }.filter { !it.isDefeated() }
fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName) fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName)
@ -131,6 +145,7 @@ class CivilizationInfo {
fun isOneCityChallenger() = ( fun isOneCityChallenger() = (
playerType == PlayerType.Human && playerType == PlayerType.Human &&
gameInfo.gameParameters.oneCityChallenge) gameInfo.gameParameters.oneCityChallenge)
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization() == this fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization() == this
fun isBarbarian() = nation.isBarbarian() fun isBarbarian() = nation.isBarbarian()
fun isSpectator() = nation.isSpectator() fun isSpectator() = nation.isSpectator()
@ -260,7 +275,6 @@ class CivilizationInfo {
} }
fun getEquivalentBuilding(buildingName: String): Building { fun getEquivalentBuilding(buildingName: String): Building {
val baseBuilding = gameInfo.ruleSet.buildings[buildingName]!!.getBaseBuilding(gameInfo.ruleSet) val baseBuilding = gameInfo.ruleSet.buildings[buildingName]!!.getBaseBuilding(gameInfo.ruleSet)
@ -293,12 +307,13 @@ class CivilizationInfo {
UncivGame.Current.settings.addCompletedTutorialTask("Meet another civilization") UncivGame.Current.settings.addCompletedTutorialTask("Meet another civilization")
} }
fun discoverNaturalWonder(naturalWonderName: String) fun discoverNaturalWonder(naturalWonderName: String) {
{
naturalWonders.add(naturalWonderName) naturalWonders.add(naturalWonderName)
} }
override fun toString(): String {return civName} // for debug override fun toString(): String {
return civName
} // for debug
/** Returns true if the civ was fully initialized and has no cities remaining */ /** Returns true if the civ was fully initialized and has no cities remaining */
fun isDefeated(): Boolean { fun isDefeated(): Boolean {

View File

@ -14,15 +14,22 @@ import com.unciv.ui.utils.Fonts
import kotlin.math.abs import kotlin.math.abs
open class TileInfo { open class TileInfo {
@Transient lateinit var tileMap: TileMap @Transient
@Transient lateinit var ruleset: Ruleset // a tile can be a tile with a ruleset, even without a map. lateinit var tileMap: TileMap
@Transient var owningCity:CityInfo?=null @Transient
@Transient private lateinit var baseTerrainObject:Terrain lateinit var ruleset: Ruleset // a tile can be a tile with a ruleset, even without a map.
@Transient
var owningCity: CityInfo? = null
@Transient
private lateinit var baseTerrainObject: Terrain
// These are for performance - checked with every tile movement and "canEnter" check, which makes them performance-critical // These are for performance - checked with every tile movement and "canEnter" check, which makes them performance-critical
@Transient var isLand = false @Transient
@Transient var isWater = false var isLand = false
@Transient var isOcean = false @Transient
var isWater = false
@Transient
var isOcean = false
// This will be called often - farm can be built on Hill and tundra if adjacent to fresh water // This will be called often - farm can be built on Hill and tundra if adjacent to fresh water
// and farms on adjacent to fresh water tiles will have +1 additional Food after researching Civil Service // and farms on adjacent to fresh water tiles will have +1 additional Food after researching Civil Service

View File

@ -12,13 +12,20 @@ import kotlin.math.abs
class TileMap { class TileMap {
@Transient lateinit var gameInfo: GameInfo @Transient
@Transient var tileMatrix = ArrayList<ArrayList<TileInfo?>>() // this works several times faster than a hashmap, the performance difference is really astounding lateinit var gameInfo: GameInfo
@Transient var leftX = 0 @Transient
@Transient var bottomY = 0 var tileMatrix = ArrayList<ArrayList<TileInfo?>>() // this works several times faster than a hashmap, the performance difference is really astounding
@delegate:Transient val maxLatitude: Float by lazy { if (values.isEmpty()) 0f else values.map { abs(it.latitude) }.max()!! } @Transient
@delegate:Transient val maxLongitude: Float by lazy { if (values.isEmpty()) 0f else values.map { abs(it.longitude) }.max()!! } var leftX = 0
@delegate:Transient val naturalWonders: List<String> by lazy { tileList.asSequence().filter { it.isNaturalWonder() }.map { it.naturalWonder!! }.distinct().toList() } @Transient
var bottomY = 0
@delegate:Transient
val maxLatitude: Float by lazy { if (values.isEmpty()) 0f else values.map { abs(it.latitude) }.max()!! }
@delegate:Transient
val maxLongitude: Float by lazy { if (values.isEmpty()) 0f else values.map { abs(it.longitude) }.max()!! }
@delegate:Transient
val naturalWonders: List<String> by lazy { tileList.asSequence().filter { it.isNaturalWonder() }.map { it.naturalWonder!! }.distinct().toList() }
var mapParameters = MapParameters() var mapParameters = MapParameters()
@ -43,7 +50,8 @@ class TileMap {
for (y in -height / 2..height / 2) for (y in -height / 2..height / 2)
tileList.add(TileInfo().apply { tileList.add(TileInfo().apply {
position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(), y.toFloat())) position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(), y.toFloat()))
baseTerrain = Constants.grassland }) baseTerrain = Constants.grassland
})
setTransients(ruleset) setTransients(ruleset)
} }
@ -237,7 +245,9 @@ class TileMap {
*/ */
fun stripPlayer(player: Player) { fun stripPlayer(player: Player) {
tileList.forEach { tileList.forEach {
if (it.improvement == "StartingLocation " + player.chosenCiv) { it.improvement = null } if (it.improvement == "StartingLocation " + player.chosenCiv) {
it.improvement = null
}
for (unit in it.getUnits()) if (unit.owner == player.chosenCiv) unit.removeFromTile() for (unit in it.getUnits()) if (unit.owner == player.chosenCiv) unit.removeFromTile()
} }
} }
@ -249,7 +259,9 @@ class TileMap {
*/ */
fun switchPlayersNation(player: Player, newNation: Nation) { fun switchPlayersNation(player: Player, newNation: Nation) {
tileList.forEach { tileList.forEach {
if (it.improvement == "StartingLocation " + player.chosenCiv) { it.improvement = "StartingLocation "+newNation.name } if (it.improvement == "StartingLocation " + player.chosenCiv) {
it.improvement = "StartingLocation " + newNation.name
}
for (unit in it.getUnits()) if (unit.owner == player.chosenCiv) { for (unit in it.getUnits()) if (unit.owner == player.chosenCiv) {
unit.owner = newNation.name unit.owner = newNation.name
unit.civInfo = CivilizationInfo(newNation.name).apply { nation = newNation } unit.civInfo = CivilizationInfo(newNation.name).apply { nation = newNation }
@ -278,4 +290,3 @@ class TileMap {
} }
} }
} }