mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 09:48:12 +07:00
3.12.2-patch1
This commit is contained in:
@ -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"
|
||||||
|
@ -22,21 +22,27 @@ 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?
|
||||||
var tileMap: TileMap = TileMap()
|
var tileMap: TileMap = TileMap()
|
||||||
var gameParameters= GameParameters()
|
var gameParameters = GameParameters()
|
||||||
var turns = 0
|
var turns = 0
|
||||||
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
|
||||||
|
|
||||||
@ -53,9 +59,9 @@ class GameInfo {
|
|||||||
val toReturn = GameInfo()
|
val toReturn = GameInfo()
|
||||||
toReturn.tileMap = tileMap.clone()
|
toReturn.tileMap = tileMap.clone()
|
||||||
toReturn.civilizations.addAll(civilizations.map { it.clone() })
|
toReturn.civilizations.addAll(civilizations.map { it.clone() })
|
||||||
toReturn.currentPlayer=currentPlayer
|
toReturn.currentPlayer = currentPlayer
|
||||||
toReturn.turns = turns
|
toReturn.turns = turns
|
||||||
toReturn.difficulty=difficulty
|
toReturn.difficulty = difficulty
|
||||||
toReturn.gameParameters = gameParameters
|
toReturn.gameParameters = gameParameters
|
||||||
toReturn.gameId = gameId
|
toReturn.gameId = gameId
|
||||||
toReturn.oneMoreTurnMode = oneMoreTurnMode
|
toReturn.oneMoreTurnMode = oneMoreTurnMode
|
||||||
@ -66,11 +72,11 @@ class GameInfo {
|
|||||||
fun getPlayerToViewAs(): CivilizationInfo {
|
fun getPlayerToViewAs(): CivilizationInfo {
|
||||||
if (!gameParameters.isOnlineMultiplayer) return currentPlayerCiv // non-online, play as human player
|
if (!gameParameters.isOnlineMultiplayer) return currentPlayerCiv // non-online, play as human player
|
||||||
val userId = UncivGame.Current.settings.userId
|
val userId = UncivGame.Current.settings.userId
|
||||||
if (civilizations.any { it.playerId == userId}) return civilizations.first { it.playerId == userId }
|
if (civilizations.any { it.playerId == userId }) return civilizations.first { it.playerId == userId }
|
||||||
else return getBarbarianCivilization()// you aren't anyone. How did you even get this game? Can you spectate?
|
else return getBarbarianCivilization()// you aren't anyone. How did you even get this game? Can you spectate?
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCivilization(civName:String) = civilizations.first { it.civName==civName }
|
fun getCivilization(civName: String) = civilizations.first { it.civName == civName }
|
||||||
fun getCurrentPlayerCivilization() = currentPlayerCiv
|
fun getCurrentPlayerCivilization() = currentPlayerCiv
|
||||||
fun getBarbarianCivilization() = getCivilization(Constants.barbarians)
|
fun getBarbarianCivilization() = getCivilization(Constants.barbarians)
|
||||||
fun getDifficulty() = difficultyObject
|
fun getDifficulty() = difficultyObject
|
||||||
@ -85,10 +91,10 @@ class GameInfo {
|
|||||||
var currentPlayerIndex = civilizations.indexOf(thisPlayer)
|
var currentPlayerIndex = civilizations.indexOf(thisPlayer)
|
||||||
|
|
||||||
|
|
||||||
fun switchTurn(){
|
fun switchTurn() {
|
||||||
thisPlayer.endTurn()
|
thisPlayer.endTurn()
|
||||||
currentPlayerIndex = (currentPlayerIndex+1) % civilizations.size
|
currentPlayerIndex = (currentPlayerIndex + 1) % civilizations.size
|
||||||
if(currentPlayerIndex==0){
|
if (currentPlayerIndex == 0) {
|
||||||
turns++
|
turns++
|
||||||
}
|
}
|
||||||
thisPlayer = civilizations[currentPlayerIndex]
|
thisPlayer = civilizations[currentPlayerIndex]
|
||||||
@ -98,7 +104,7 @@ class GameInfo {
|
|||||||
switchTurn()
|
switchTurn()
|
||||||
|
|
||||||
while (thisPlayer.playerType == PlayerType.AI
|
while (thisPlayer.playerType == PlayerType.AI
|
||||||
|| turns < UncivGame.Current.simulateUntilTurnForDebug
|
|| turns < UncivGame.Current.simulateUntilTurnForDebug
|
||||||
|| turns < simulateMaxTurns && simulateUntilWin
|
|| turns < simulateMaxTurns && simulateUntilWin
|
||||||
// For multiplayer, if there are 3+ players and one is defeated or spectator,
|
// For multiplayer, if there are 3+ players and one is defeated or spectator,
|
||||||
// we'll want to skip over their turn
|
// we'll want to skip over their turn
|
||||||
@ -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))
|
||||||
}
|
}
|
||||||
@ -216,15 +221,15 @@ class GameInfo {
|
|||||||
val barbarianCiv = getBarbarianCivilization()
|
val barbarianCiv = getBarbarianCivilization()
|
||||||
barbarianCiv.tech.techsResearched = allResearchedTechs.toHashSet()
|
barbarianCiv.tech.techsResearched = allResearchedTechs.toHashSet()
|
||||||
val unitList = ruleSet.units.values
|
val unitList = ruleSet.units.values
|
||||||
.filter { !it.unitType.isCivilian()}
|
.filter { !it.unitType.isCivilian() }
|
||||||
.filter { it.isBuildable(barbarianCiv) }
|
.filter { it.isBuildable(barbarianCiv) }
|
||||||
|
|
||||||
val landUnits = unitList.filter { it.unitType.isLandUnit() }
|
val landUnits = unitList.filter { it.unitType.isLandUnit() }
|
||||||
val waterUnits = unitList.filter { it.unitType.isWaterUnit() }
|
val waterUnits = unitList.filter { it.unitType.isWaterUnit() }
|
||||||
|
|
||||||
val unit:String
|
val unit: String
|
||||||
if(waterUnits.isNotEmpty() && tileToPlace.isCoastalTile() && Random().nextBoolean())
|
if (waterUnits.isNotEmpty() && tileToPlace.isCoastalTile() && Random().nextBoolean())
|
||||||
unit=waterUnits.random().name
|
unit = waterUnits.random().name
|
||||||
else unit = landUnits.random().name
|
else unit = landUnits.random().name
|
||||||
|
|
||||||
tileMap.placeUnitNearTile(tileToPlace.position, unit, getBarbarianCivilization())
|
tileMap.placeUnitNearTile(tileToPlace.position, unit, getBarbarianCivilization())
|
||||||
@ -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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,10 +352,10 @@ class GameInfo {
|
|||||||
cityConstructions.builtBuildings.remove(oldBuildingName)
|
cityConstructions.builtBuildings.remove(oldBuildingName)
|
||||||
cityConstructions.builtBuildings.add(newBuildingName)
|
cityConstructions.builtBuildings.add(newBuildingName)
|
||||||
}
|
}
|
||||||
cityConstructions.constructionQueue.replaceAll { if(it==oldBuildingName) newBuildingName else it }
|
cityConstructions.constructionQueue.replaceAll { if (it == oldBuildingName) newBuildingName else it }
|
||||||
if (cityConstructions.inProgressConstructions.containsKey(oldBuildingName)) {
|
if (cityConstructions.inProgressConstructions.containsKey(oldBuildingName)) {
|
||||||
cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[oldBuildingName]!!
|
cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[oldBuildingName]!!
|
||||||
cityConstructions.inProgressConstructions.remove(oldBuildingName)
|
cityConstructions.inProgressConstructions.remove(oldBuildingName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
@ -75,7 +73,7 @@ object GameStarter {
|
|||||||
civInfo.tech.addTechnology(tech)
|
civInfo.tech.addTechnology(tech)
|
||||||
|
|
||||||
// generic start with technology unique
|
// generic start with technology unique
|
||||||
for(unique in civInfo.getMatchingUniques("Starts with []")) {
|
for (unique in civInfo.getMatchingUniques("Starts with []")) {
|
||||||
// get the parameter from the unique
|
// get the parameter from the unique
|
||||||
val techName = unique.params[0]
|
val techName = unique.params[0]
|
||||||
|
|
||||||
@ -106,7 +104,7 @@ object GameStarter {
|
|||||||
availableCivNames.removeAll(newGameParameters.players.map { it.chosenCiv })
|
availableCivNames.removeAll(newGameParameters.players.map { it.chosenCiv })
|
||||||
availableCivNames.remove(Constants.barbarians)
|
availableCivNames.remove(Constants.barbarians)
|
||||||
|
|
||||||
if(!newGameParameters.noBarbarians && ruleset.nations.containsKey(Constants.barbarians)) {
|
if (!newGameParameters.noBarbarians && ruleset.nations.containsKey(Constants.barbarians)) {
|
||||||
val barbarianCivilization = CivilizationInfo(Constants.barbarians)
|
val barbarianCivilization = CivilizationInfo(Constants.barbarians)
|
||||||
gameInfo.civilizations.add(barbarianCivilization)
|
gameInfo.civilizations.add(barbarianCivilization)
|
||||||
}
|
}
|
||||||
@ -138,7 +136,7 @@ object GameStarter {
|
|||||||
val civ = CivilizationInfo(cityStateName)
|
val civ = CivilizationInfo(cityStateName)
|
||||||
civ.cityStatePersonality = CityStatePersonality.values().random()
|
civ.cityStatePersonality = CityStatePersonality.values().random()
|
||||||
gameInfo.civilizations.add(civ)
|
gameInfo.civilizations.add(civ)
|
||||||
for(tech in ruleset.technologies.values.filter { it.uniques.contains("Starting tech") })
|
for (tech in ruleset.technologies.values.filter { it.uniques.contains("Starting tech") })
|
||||||
civ.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
|
civ.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,13 +166,14 @@ 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
|
||||||
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivStartingUnits
|
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivStartingUnits
|
||||||
else -> gameInfo.getDifficulty().aiCityStateStartingUnits
|
else -> gameInfo.getDifficulty().aiCityStateStartingUnits
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unit in startingUnits) {
|
for (unit in startingUnits) {
|
||||||
val unitToAdd = if (unit == "Warrior") warriorEquivalent else unit
|
val unitToAdd = if (unit == "Warrior") warriorEquivalent else unit
|
||||||
if (unitToAdd != null) placeNearStartingPosition(unitToAdd)
|
if (unitToAdd != null) placeNearStartingPosition(unitToAdd)
|
||||||
@ -185,7 +184,7 @@ object GameStarter {
|
|||||||
private fun getStartingLocations(civs: List<CivilizationInfo>, tileMap: TileMap): HashMap<CivilizationInfo, TileInfo> {
|
private fun getStartingLocations(civs: List<CivilizationInfo>, tileMap: TileMap): HashMap<CivilizationInfo, TileInfo> {
|
||||||
var landTiles = tileMap.values
|
var landTiles = tileMap.values
|
||||||
// Games starting on snow might as well start over...
|
// Games starting on snow might as well start over...
|
||||||
.filter { it.isLand && !it.isImpassible() && it.baseTerrain!=Constants.snow }
|
.filter { it.isLand && !it.isImpassible() && it.baseTerrain != Constants.snow }
|
||||||
|
|
||||||
val landTilesInBigEnoughGroup = ArrayList<TileInfo>()
|
val landTilesInBigEnoughGroup = ArrayList<TileInfo>()
|
||||||
while (landTiles.any()) {
|
while (landTiles.any()) {
|
||||||
|
@ -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. */
|
||||||
@ -91,7 +100,7 @@ class CityInfo {
|
|||||||
for (unique in civInfo.getMatchingUniques("Gain a free [] []")) {
|
for (unique in civInfo.getMatchingUniques("Gain a free [] []")) {
|
||||||
val freeBuildingName = unique.params[0]
|
val freeBuildingName = unique.params[0]
|
||||||
val cityFilter = unique.params[1]
|
val cityFilter = unique.params[1]
|
||||||
if (cityFilter == "in every city" || (cityFilter == "in every coastal city" && getCenterTile().isCoastalTile()) ) {
|
if (cityFilter == "in every city" || (cityFilter == "in every coastal city" && getCenterTile().isCoastalTile())) {
|
||||||
if (!cityConstructions.isBuilt(freeBuildingName))
|
if (!cityConstructions.isBuilt(freeBuildingName))
|
||||||
cityConstructions.addBuilding(freeBuildingName)
|
cityConstructions.addBuilding(freeBuildingName)
|
||||||
}
|
}
|
||||||
@ -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 }
|
||||||
@ -163,10 +171,11 @@ class CityInfo {
|
|||||||
fun isCapital(): Boolean = cityConstructions.builtBuildings.contains(capitalCityIndicator())
|
fun isCapital(): Boolean = cityConstructions.builtBuildings.contains(capitalCityIndicator())
|
||||||
fun capitalCityIndicator(): String = getRuleset().buildings.values.first { it.uniques.contains("Indicates the capital city") }.name
|
fun capitalCityIndicator(): String = getRuleset().buildings.values.first { it.uniques.contains("Indicates the capital city") }.name
|
||||||
|
|
||||||
fun isConnectedToCapital(connectionTypePredicate: (Set<String>) -> Boolean = {true}): Boolean {
|
fun isConnectedToCapital(connectionTypePredicate: (Set<String>) -> Boolean = { true }): Boolean {
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@ -193,10 +202,11 @@ class CityInfo {
|
|||||||
val resource = getRuleset().tileResources[building.requiredResource]!!
|
val resource = getRuleset().tileResources[building.requiredResource]!!
|
||||||
cityResources.add(resource, -1, "Buildings")
|
cityResources.add(resource, -1, "Buildings")
|
||||||
}
|
}
|
||||||
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
|
||||||
@ -205,17 +215,17 @@ class CityInfo {
|
|||||||
fun getTileResourceAmount(tileInfo: TileInfo): Int {
|
fun getTileResourceAmount(tileInfo: TileInfo): Int {
|
||||||
if (tileInfo.resource == null) return 0
|
if (tileInfo.resource == null) return 0
|
||||||
val resource = tileInfo.getTileResource()
|
val resource = tileInfo.getTileResource()
|
||||||
if (resource.revealedBy!=null && !civInfo.tech.isResearched(resource.revealedBy!!)) return 0
|
if (resource.revealedBy != null && !civInfo.tech.isResearched(resource.revealedBy!!)) return 0
|
||||||
|
|
||||||
// Even if the improvement exists (we conquered an enemy city or somesuch) or we have a city on it, we won't get the resource until the correct tech is researched
|
// Even if the improvement exists (we conquered an enemy city or somesuch) or we have a city on it, we won't get the resource until the correct tech is researched
|
||||||
if (resource.improvement!=null) {
|
if (resource.improvement != null) {
|
||||||
val improvement = getRuleset().tileImprovements[resource.improvement!!]!!
|
val improvement = getRuleset().tileImprovements[resource.improvement!!]!!
|
||||||
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!)) return 0
|
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!)) return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter()
|
if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter()
|
||||||
// Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources
|
// Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources
|
||||||
|| (resource.resourceType==ResourceType.Strategic && tileInfo.containsGreatImprovement())) {
|
|| (resource.resourceType == ResourceType.Strategic && tileInfo.containsGreatImprovement())) {
|
||||||
var amountToAdd = 1
|
var amountToAdd = 1
|
||||||
if (resource.resourceType == ResourceType.Strategic) {
|
if (resource.resourceType == ResourceType.Strategic) {
|
||||||
amountToAdd = 2
|
amountToAdd = 2
|
||||||
@ -240,7 +250,7 @@ class CityInfo {
|
|||||||
if (!isGrowing()) return null
|
if (!isGrowing()) return null
|
||||||
val roundedFoodPerTurn = foodForNextTurn().toFloat()
|
val roundedFoodPerTurn = foodForNextTurn().toFloat()
|
||||||
val remainingFood = population.getFoodToNextPopulation() - population.foodStored
|
val remainingFood = population.getFoodToNextPopulation() - population.foodStored
|
||||||
var turnsToGrowth = ceil( remainingFood / roundedFoodPerTurn).toInt()
|
var turnsToGrowth = ceil(remainingFood / roundedFoodPerTurn).toInt()
|
||||||
if (turnsToGrowth < 1) turnsToGrowth = 1
|
if (turnsToGrowth < 1) turnsToGrowth = 1
|
||||||
return turnsToGrowth
|
return turnsToGrowth
|
||||||
}
|
}
|
||||||
@ -251,11 +261,11 @@ class CityInfo {
|
|||||||
return population.foodStored / -foodForNextTurn() + 1
|
return population.foodStored / -foodForNextTurn() + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun containsBuildingUnique(unique:String) = cityConstructions.getBuiltBuildings().any { it.uniques.contains(unique) }
|
fun containsBuildingUnique(unique: String) = cityConstructions.getBuiltBuildings().any { it.uniques.contains(unique) }
|
||||||
|
|
||||||
fun getGreatPersonMap():StatMap {
|
fun getGreatPersonMap(): StatMap {
|
||||||
val stats = StatMap()
|
val stats = StatMap()
|
||||||
for((specialist, amount) in population.getNewSpecialists())
|
for ((specialist, amount) in population.getNewSpecialists())
|
||||||
if (getRuleset().specialists.containsKey(specialist)) // To solve problems in total remake mods
|
if (getRuleset().specialists.containsKey(specialist)) // To solve problems in total remake mods
|
||||||
stats.add("Specialists", getRuleset().specialists[specialist]!!.greatPersonPoints.times(amount))
|
stats.add("Specialists", getRuleset().specialists[specialist]!!.greatPersonPoints.times(amount))
|
||||||
|
|
||||||
@ -275,7 +285,7 @@ class CityInfo {
|
|||||||
val statName = greatUnitUnique.getPlaceholderParameters()[0]
|
val statName = greatUnitUnique.getPlaceholderParameters()[0]
|
||||||
val stat = Stat.values().firstOrNull { it.name == statName }
|
val stat = Stat.values().firstOrNull { it.name == statName }
|
||||||
// this is not very efficient, and if it causes problems we can try and think of a way of improving it
|
// this is not very efficient, and if it causes problems we can try and think of a way of improving it
|
||||||
if (stat != null) entry.value.add(stat, entry.value.get(stat) * unique.params[1].toFloat()/100)
|
if (stat != null) entry.value.add(stat, entry.value.get(stat) * unique.params[1].toFloat() / 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unique in civInfo.getMatchingUniques("+[]% great person generation in all cities")
|
for (unique in civInfo.getMatchingUniques("+[]% great person generation in all cities")
|
||||||
@ -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
|
||||||
@ -313,7 +325,7 @@ class CityInfo {
|
|||||||
cityConstructions.setTransients()
|
cityConstructions.setTransients()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startTurn(){
|
fun startTurn() {
|
||||||
// Construct units at the beginning of the turn,
|
// Construct units at the beginning of the turn,
|
||||||
// so they won't be generated out in the open and vulnerable to enemy attacks before you can control them
|
// so they won't be generated out in the open and vulnerable to enemy attacks before you can control them
|
||||||
cityConstructions.constructIfEnough()
|
cityConstructions.constructIfEnough()
|
||||||
@ -439,7 +451,7 @@ class CityInfo {
|
|||||||
|
|
||||||
// How can you conquer a city but not know the civ you conquered it from?!
|
// How can you conquer a city but not know the civ you conquered it from?!
|
||||||
// I don't know either, but some of our players have managed this, and crashed their game!
|
// I don't know either, but some of our players have managed this, and crashed their game!
|
||||||
if(!conqueringCiv.knows(oldCiv))
|
if (!conqueringCiv.knows(oldCiv))
|
||||||
conqueringCiv.meetCivilization(oldCiv)
|
conqueringCiv.meetCivilization(oldCiv)
|
||||||
|
|
||||||
oldCiv.getDiplomacyManager(conqueringCiv)
|
oldCiv.getDiplomacyManager(conqueringCiv)
|
||||||
@ -498,10 +510,10 @@ class CityInfo {
|
|||||||
val respecForLiberatingOurCity = 10f + percentageOfCivPopulationInThatCity.roundToInt()
|
val respecForLiberatingOurCity = 10f + percentageOfCivPopulationInThatCity.roundToInt()
|
||||||
|
|
||||||
// In order to get "plus points" in Diplomacy, you have to establish diplomatic relations if you haven't yet
|
// In order to get "plus points" in Diplomacy, you have to establish diplomatic relations if you haven't yet
|
||||||
if(!conqueringCiv.knows(foundingCiv))
|
if (!conqueringCiv.knows(foundingCiv))
|
||||||
conqueringCiv.meetCivilization(foundingCiv)
|
conqueringCiv.meetCivilization(foundingCiv)
|
||||||
|
|
||||||
if(foundingCiv.isMajorCiv()) {
|
if (foundingCiv.isMajorCiv()) {
|
||||||
foundingCiv.getDiplomacyManager(conqueringCiv)
|
foundingCiv.getDiplomacyManager(conqueringCiv)
|
||||||
.addModifier(DiplomaticModifiers.CapturedOurCities, respecForLiberatingOurCity)
|
.addModifier(DiplomaticModifiers.CapturedOurCities, respecForLiberatingOurCity)
|
||||||
} else {
|
} else {
|
||||||
@ -570,23 +582,21 @@ class CityInfo {
|
|||||||
tryUpdateRoadStatus()
|
tryUpdateRoadStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryUpdateRoadStatus(){
|
private fun tryUpdateRoadStatus() {
|
||||||
if(getCenterTile().roadStatus==RoadStatus.None){
|
if (getCenterTile().roadStatus == RoadStatus.None) {
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGoldForSellingBuilding(buildingName:String) = getRuleset().buildings[buildingName]!!.cost / 10
|
fun getGoldForSellingBuilding(buildingName: String) = getRuleset().buildings[buildingName]!!.cost / 10
|
||||||
|
|
||||||
fun sellBuilding(buildingName:String) {
|
fun sellBuilding(buildingName: String) {
|
||||||
cityConstructions.removeBuilding(buildingName)
|
cityConstructions.removeBuilding(buildingName)
|
||||||
civInfo.gold += getGoldForSellingBuilding(buildingName)
|
civInfo.gold += getGoldForSellingBuilding(buildingName)
|
||||||
hasSoldBuildingThisTurn = true
|
hasSoldBuildingThisTurn = true
|
||||||
@ -617,19 +627,18 @@ class CityInfo {
|
|||||||
But if they don't keep their promise they get a -20 that will only fully disappear in 160 turns.
|
But if they don't keep their promise they get a -20 that will only fully disappear in 160 turns.
|
||||||
There's a lot of triggering going on here.
|
There's a lot of triggering going on here.
|
||||||
*/
|
*/
|
||||||
private fun triggerCitiesSettledNearOtherCiv(){
|
private fun triggerCitiesSettledNearOtherCiv() {
|
||||||
val citiesWithin6Tiles = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() && it!=civInfo }
|
val citiesWithin6Tiles = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() && it != civInfo }
|
||||||
.flatMap { it.cities }
|
.flatMap { it.cities }
|
||||||
.filter { it.getCenterTile().aerialDistanceTo(getCenterTile()) <= 6 }
|
.filter { it.getCenterTile().aerialDistanceTo(getCenterTile()) <= 6 }
|
||||||
val civsWithCloseCities = citiesWithin6Tiles.map { it.civInfo }.distinct()
|
val civsWithCloseCities = citiesWithin6Tiles.map { it.civInfo }.distinct()
|
||||||
.filter { it.knows(civInfo) && it.exploredTiles.contains(location) }
|
.filter { it.knows(civInfo) && it.exploredTiles.contains(location) }
|
||||||
for(otherCiv in civsWithCloseCities)
|
for (otherCiv in civsWithCloseCities)
|
||||||
otherCiv.getDiplomacyManager(civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs,30)
|
otherCiv.getDiplomacyManager(civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs, 30)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -640,4 +649,4 @@ class CityInfo {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
@ -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()
|
||||||
@ -61,7 +73,7 @@ class CivilizationInfo {
|
|||||||
var questManager = QuestManager()
|
var questManager = QuestManager()
|
||||||
var goldenAges = GoldenAgeManager()
|
var goldenAges = GoldenAgeManager()
|
||||||
var greatPeople = GreatPersonManager()
|
var greatPeople = GreatPersonManager()
|
||||||
var victoryManager=VictoryManager()
|
var victoryManager = VictoryManager()
|
||||||
var diplomacy = HashMap<String, DiplomacyManager>()
|
var diplomacy = HashMap<String, DiplomacyManager>()
|
||||||
var notifications = ArrayList<Notification>()
|
var notifications = ArrayList<Notification>()
|
||||||
val popupAlerts = ArrayList<PopupAlert>()
|
val popupAlerts = ArrayList<PopupAlert>()
|
||||||
@ -115,24 +127,27 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//region pure functions
|
//region pure functions
|
||||||
fun getDifficulty():Difficulty {
|
fun getDifficulty(): Difficulty {
|
||||||
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)
|
||||||
fun knows(otherCiv: CivilizationInfo) = knows(otherCiv.civName)
|
fun knows(otherCiv: CivilizationInfo) = knows(otherCiv.civName)
|
||||||
|
|
||||||
fun getCapital()= cities.first { it.isCapital() }
|
fun getCapital() = cities.first { it.isCapital() }
|
||||||
fun isPlayerCivilization() = playerType==PlayerType.Human
|
fun isPlayerCivilization() = playerType == PlayerType.Human
|
||||||
fun isOneCityChallenger() = (
|
fun isOneCityChallenger() = (
|
||||||
playerType==PlayerType.Human &&
|
playerType == PlayerType.Human &&
|
||||||
gameInfo.gameParameters.oneCityChallenge)
|
gameInfo.gameParameters.oneCityChallenge)
|
||||||
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
|
|
||||||
fun isBarbarian() = nation.isBarbarian()
|
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization() == this
|
||||||
|
fun isBarbarian() = nation.isBarbarian()
|
||||||
fun isSpectator() = nation.isSpectator()
|
fun isSpectator() = nation.isSpectator()
|
||||||
fun isCityState(): Boolean = nation.isCityState()
|
fun isCityState(): Boolean = nation.isCityState()
|
||||||
val cityStateType: CityStateType get() = nation.cityStateType!!
|
val cityStateType: CityStateType get() = nation.cityStateType!!
|
||||||
@ -144,10 +159,10 @@ class CivilizationInfo {
|
|||||||
fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
||||||
|
|
||||||
fun victoryType(): VictoryType {
|
fun victoryType(): VictoryType {
|
||||||
if(gameInfo.gameParameters.victoryTypes.size==1)
|
if (gameInfo.gameParameters.victoryTypes.size == 1)
|
||||||
return gameInfo.gameParameters.victoryTypes.first() // That is the most relevant one
|
return gameInfo.gameParameters.victoryTypes.first() // That is the most relevant one
|
||||||
val victoryType = nation.preferredVictoryType
|
val victoryType = nation.preferredVictoryType
|
||||||
if(gameInfo.gameParameters.victoryTypes.contains(victoryType)) return victoryType
|
if (gameInfo.gameParameters.victoryTypes.contains(victoryType)) return victoryType
|
||||||
else return VictoryType.Neutral
|
else return VictoryType.Neutral
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +178,9 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
|
|
||||||
fun getCivResources(): ResourceSupplyList {
|
fun getCivResources(): ResourceSupplyList {
|
||||||
val newResourceSupplyList=ResourceSupplyList()
|
val newResourceSupplyList = ResourceSupplyList()
|
||||||
for(resourceSupply in detailedCivResources)
|
for (resourceSupply in detailedCivResources)
|
||||||
newResourceSupplyList.add(resourceSupply.resource,resourceSupply.amount,"All")
|
newResourceSupplyList.add(resourceSupply.resource, resourceSupply.amount, "All")
|
||||||
return newResourceSupplyList
|
return newResourceSupplyList
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,10 +194,10 @@ class CivilizationInfo {
|
|||||||
/**
|
/**
|
||||||
* Returns a dictionary of ALL resource names, and the amount that the civ has of each
|
* Returns a dictionary of ALL resource names, and the amount that the civ has of each
|
||||||
*/
|
*/
|
||||||
fun getCivResourcesByName():HashMap<String,Int> {
|
fun getCivResourcesByName(): HashMap<String, Int> {
|
||||||
val hashMap = HashMap<String,Int>(gameInfo.ruleSet.tileResources.size)
|
val hashMap = HashMap<String, Int>(gameInfo.ruleSet.tileResources.size)
|
||||||
for(resource in gameInfo.ruleSet.tileResources.keys) hashMap[resource]=0
|
for (resource in gameInfo.ruleSet.tileResources.keys) hashMap[resource] = 0
|
||||||
for(entry in getCivResources())
|
for (entry in getCivResources())
|
||||||
hashMap[entry.resource.name] = entry.amount
|
hashMap[entry.resource.name] = entry.amount
|
||||||
return hashMap
|
return hashMap
|
||||||
}
|
}
|
||||||
@ -199,11 +214,11 @@ class CivilizationInfo {
|
|||||||
return resourceModifier
|
return resourceModifier
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasResource(resourceName:String): Boolean = getCivResourcesByName()[resourceName]!!>0
|
fun hasResource(resourceName: String): Boolean = getCivResourcesByName()[resourceName]!! > 0
|
||||||
|
|
||||||
fun getBuildingUniques(): Sequence<Unique> = cities.asSequence().flatMap { it.cityConstructions.builtBuildingUniqueMap.getAllUniques() }
|
fun getBuildingUniques(): Sequence<Unique> = cities.asSequence().flatMap { it.cityConstructions.builtBuildingUniqueMap.getAllUniques() }
|
||||||
|
|
||||||
fun hasUnique(unique:String) = getMatchingUniques(unique).any()
|
fun hasUnique(unique: String) = getMatchingUniques(unique).any()
|
||||||
|
|
||||||
fun getMatchingUniques(uniqueTemplate: String): Sequence<Unique> {
|
fun getMatchingUniques(uniqueTemplate: String): Sequence<Unique> {
|
||||||
return nation.uniqueObjects.asSequence().filter { it.placeholderText == uniqueTemplate } +
|
return nation.uniqueObjects.asSequence().filter { it.placeholderText == uniqueTemplate } +
|
||||||
@ -215,12 +230,12 @@ class CivilizationInfo {
|
|||||||
fun getCivUnits(): Sequence<MapUnit> = units.asSequence()
|
fun getCivUnits(): Sequence<MapUnit> = units.asSequence()
|
||||||
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.hasUnique("Great Person - []") }
|
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.hasUnique("Great Person - []") }
|
||||||
|
|
||||||
fun addUnit(mapUnit: MapUnit, updateCivInfo:Boolean=true){
|
fun addUnit(mapUnit: MapUnit, updateCivInfo: Boolean = true) {
|
||||||
val newList = ArrayList(units)
|
val newList = ArrayList(units)
|
||||||
newList.add(mapUnit)
|
newList.add(mapUnit)
|
||||||
units=newList
|
units = newList
|
||||||
|
|
||||||
if(updateCivInfo) {
|
if (updateCivInfo) {
|
||||||
// Not relevant when updating tileinfo transients, since some info of the civ itself isn't yet available,
|
// Not relevant when updating tileinfo transients, since some info of the civ itself isn't yet available,
|
||||||
// and in any case it'll be updated once civ info transients are
|
// and in any case it'll be updated once civ info transients are
|
||||||
updateStatsForNextTurn() // unit upkeep
|
updateStatsForNextTurn() // unit upkeep
|
||||||
@ -244,7 +259,7 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
fun getNextDueUnit(): MapUnit? {
|
fun getNextDueUnit(): MapUnit? {
|
||||||
val dueUnits = getDueUnits()
|
val dueUnits = getDueUnits()
|
||||||
if(dueUnits.any()) {
|
if (dueUnits.any()) {
|
||||||
val unit = dueUnits.first()
|
val unit = dueUnits.first()
|
||||||
unit.due = false
|
unit.due = false
|
||||||
return unit
|
return unit
|
||||||
@ -260,17 +275,16 @@ 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)
|
||||||
|
|
||||||
for(building in gameInfo.ruleSet.buildings.values)
|
for (building in gameInfo.ruleSet.buildings.values)
|
||||||
if(building.replaces==baseBuilding.name && building.uniqueTo==civName)
|
if (building.replaces == baseBuilding.name && building.uniqueTo == civName)
|
||||||
return building
|
return building
|
||||||
return baseBuilding
|
return baseBuilding
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEquivalentUnit(baseUnitName:String):BaseUnit {
|
fun getEquivalentUnit(baseUnitName: String): BaseUnit {
|
||||||
for (unit in gameInfo.ruleSet.units.values)
|
for (unit in gameInfo.ruleSet.units.values)
|
||||||
if (unit.replaces == baseUnitName && unit.uniqueTo == civName)
|
if (unit.replaces == baseUnitName && unit.uniqueTo == civName)
|
||||||
return unit
|
return unit
|
||||||
@ -283,22 +297,23 @@ class CivilizationInfo {
|
|||||||
diplomacy[otherCiv.civName] = DiplomacyManager(this, otherCiv.civName)
|
diplomacy[otherCiv.civName] = DiplomacyManager(this, otherCiv.civName)
|
||||||
.apply { diplomaticStatus = DiplomaticStatus.Peace }
|
.apply { diplomaticStatus = DiplomaticStatus.Peace }
|
||||||
|
|
||||||
otherCiv.popupAlerts.add(PopupAlert(AlertType.FirstContact,civName))
|
otherCiv.popupAlerts.add(PopupAlert(AlertType.FirstContact, civName))
|
||||||
|
|
||||||
otherCiv.diplomacy[civName] = DiplomacyManager(otherCiv, civName)
|
otherCiv.diplomacy[civName] = DiplomacyManager(otherCiv, civName)
|
||||||
.apply { diplomaticStatus = DiplomaticStatus.Peace }
|
.apply { diplomaticStatus = DiplomaticStatus.Peace }
|
||||||
popupAlerts.add(PopupAlert(AlertType.FirstContact,otherCiv.civName))
|
popupAlerts.add(PopupAlert(AlertType.FirstContact, otherCiv.civName))
|
||||||
|
|
||||||
if(isCurrentPlayer() || otherCiv.isCurrentPlayer())
|
if (isCurrentPlayer() || otherCiv.isCurrentPlayer())
|
||||||
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 {
|
||||||
@ -314,9 +329,9 @@ class CivilizationInfo {
|
|||||||
fun getEra(): String {
|
fun getEra(): String {
|
||||||
// For scenarios with no techs
|
// For scenarios with no techs
|
||||||
if (gameInfo.ruleSet.technologies.isEmpty()) return "None"
|
if (gameInfo.ruleSet.technologies.isEmpty()) return "None"
|
||||||
if(tech.researchedTechnologies.isEmpty())
|
if (tech.researchedTechnologies.isEmpty())
|
||||||
return gameInfo.ruleSet.getEras().first()
|
return gameInfo.ruleSet.getEras().first()
|
||||||
val maxEraOfTech = tech.researchedTechnologies
|
val maxEraOfTech = tech.researchedTechnologies
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { it.column!! }
|
.map { it.column!! }
|
||||||
.maxBy { it.columnNumber }!!
|
.maxBy { it.columnNumber }!!
|
||||||
@ -328,7 +343,7 @@ class CivilizationInfo {
|
|||||||
return gameInfo.ruleSet.getEraNumber(getEra())
|
return gameInfo.ruleSet.getEraNumber(getEra())
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
val diplomacyManager = diplomacy[otherCiv.civName]
|
val diplomacyManager = diplomacy[otherCiv.civName]
|
||||||
@ -336,7 +351,7 @@ class CivilizationInfo {
|
|||||||
return diplomacyManager.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() }
|
||||||
|
|
||||||
fun getLeaderDisplayName(): String {
|
fun getLeaderDisplayName(): String {
|
||||||
var leaderName = nation.getLeaderDisplayName().tr()
|
var leaderName = nation.getLeaderDisplayName().tr()
|
||||||
@ -349,10 +364,10 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canSignResearchAgreement(): Boolean {
|
fun canSignResearchAgreement(): Boolean {
|
||||||
if(!isMajorCiv()) return false
|
if (!isMajorCiv()) return false
|
||||||
if(!tech.getTechUniques().contains("Enables Research agreements")) return false
|
if (!tech.getTechUniques().contains("Enables Research agreements")) return false
|
||||||
if (gameInfo.ruleSet.technologies.values
|
if (gameInfo.ruleSet.technologies.values
|
||||||
.none { tech.canBeResearched(it.name) && !tech.isResearched(it.name) }) return false
|
.none { tech.canBeResearched(it.name) && !tech.isResearched(it.name) }) return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,8 +381,8 @@ class CivilizationInfo {
|
|||||||
&& gold >= cost && otherCiv.gold >= cost
|
&& gold >= cost && otherCiv.gold >= cost
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStatForRanking(category: RankingType) : Int {
|
fun getStatForRanking(category: RankingType): Int {
|
||||||
return when(category) {
|
return when (category) {
|
||||||
RankingType.Population -> cities.sumBy { it.population.population }
|
RankingType.Population -> cities.sumBy { it.population.population }
|
||||||
RankingType.CropYield -> statsForNextTurn.food.roundToInt()
|
RankingType.CropYield -> statsForNextTurn.food.roundToInt()
|
||||||
RankingType.Production -> statsForNextTurn.production.roundToInt()
|
RankingType.Production -> statsForNextTurn.production.roundToInt()
|
||||||
@ -388,7 +403,7 @@ class CivilizationInfo {
|
|||||||
* and the updateVisibleTiles tries to meet civs...
|
* and the updateVisibleTiles tries to meet civs...
|
||||||
* 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!")
|
?: throw java.lang.Exception("Nation $civName is not found!")
|
||||||
}
|
}
|
||||||
@ -397,25 +412,25 @@ class CivilizationInfo {
|
|||||||
goldenAges.civInfo = this
|
goldenAges.civInfo = this
|
||||||
|
|
||||||
policies.civInfo = this
|
policies.civInfo = this
|
||||||
if(policies.adoptedPolicies.size>0 && policies.numberOfAdoptedPolicies == 0)
|
if (policies.adoptedPolicies.size > 0 && policies.numberOfAdoptedPolicies == 0)
|
||||||
policies.numberOfAdoptedPolicies = policies.adoptedPolicies.count { !it.endsWith("Complete") }
|
policies.numberOfAdoptedPolicies = policies.adoptedPolicies.count { !it.endsWith("Complete") }
|
||||||
policies.setTransients()
|
policies.setTransients()
|
||||||
|
|
||||||
questManager.civInfo = this
|
questManager.civInfo = this
|
||||||
questManager.setTransients()
|
questManager.setTransients()
|
||||||
|
|
||||||
if(citiesCreated==0 && cities.any())
|
if (citiesCreated == 0 && cities.any())
|
||||||
citiesCreated = cities.filter { it.name in nation.cities }.count()
|
citiesCreated = cities.filter { it.name in nation.cities }.count()
|
||||||
|
|
||||||
tech.civInfo = this
|
tech.civInfo = this
|
||||||
tech.setTransients()
|
tech.setTransients()
|
||||||
|
|
||||||
for (diplomacyManager in diplomacy.values) {
|
for (diplomacyManager in diplomacy.values) {
|
||||||
diplomacyManager.civInfo=this
|
diplomacyManager.civInfo = this
|
||||||
diplomacyManager.updateHasOpenBorders()
|
diplomacyManager.updateHasOpenBorders()
|
||||||
}
|
}
|
||||||
|
|
||||||
victoryManager.civInfo=this
|
victoryManager.civInfo = this
|
||||||
|
|
||||||
for (cityInfo in cities) {
|
for (cityInfo in cities) {
|
||||||
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
||||||
@ -452,9 +467,9 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
for (unit in getCivUnits()) unit.startTurn()
|
for (unit in getCivUnits()) unit.startTurn()
|
||||||
|
|
||||||
for(tradeRequest in tradeRequests.toList()) { // remove trade requests where one of the sides can no longer supply
|
for (tradeRequest in tradeRequests.toList()) { // remove trade requests where one of the sides can no longer supply
|
||||||
val offeringCiv = gameInfo.getCivilization(tradeRequest.requestingCiv)
|
val offeringCiv = gameInfo.getCivilization(tradeRequest.requestingCiv)
|
||||||
if (offeringCiv.isDefeated() || !TradeEvaluation().isTradeValid(tradeRequest.trade,this, offeringCiv)) {
|
if (offeringCiv.isDefeated() || !TradeEvaluation().isTradeValid(tradeRequest.trade, this, offeringCiv)) {
|
||||||
tradeRequests.remove(tradeRequest)
|
tradeRequests.remove(tradeRequest)
|
||||||
// Yes, this is the right direction. I checked.
|
// Yes, this is the right direction. I checked.
|
||||||
offeringCiv.addNotification("Our proposed trade is no longer relevant!", Color.GOLD)
|
offeringCiv.addNotification("Our proposed trade is no longer relevant!", Color.GOLD)
|
||||||
@ -512,7 +527,7 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
|
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
|
||||||
if (otherCiv==this) return true
|
if (otherCiv == this) return true
|
||||||
if (otherCiv.isBarbarian()) return true
|
if (otherCiv.isBarbarian()) return true
|
||||||
if (nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles)
|
if (nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles)
|
||||||
return true
|
return true
|
||||||
@ -526,12 +541,12 @@ class CivilizationInfo {
|
|||||||
addNotification(text, color, LocationAction(locations))
|
addNotification(text, color, LocationAction(locations))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addNotification(text: String, color: Color, action: NotificationAction?=null) {
|
fun addNotification(text: String, color: Color, action: NotificationAction? = null) {
|
||||||
if (playerType == PlayerType.AI) return // no point in lengthening the saved game info if no one will read it
|
if (playerType == PlayerType.AI) return // no point in lengthening the saved game info if no one will read it
|
||||||
notifications.add(Notification(text, color, action))
|
notifications.add(Notification(text, color, action))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addUnit(unitName:String, city: CityInfo?=null) {
|
fun addUnit(unitName: String, city: CityInfo? = null) {
|
||||||
if (cities.isEmpty()) return
|
if (cities.isEmpty()) return
|
||||||
val cityToAddTo = city ?: cities.random()
|
val cityToAddTo = city ?: cities.random()
|
||||||
if (!gameInfo.ruleSet.units.containsKey(unitName)) return
|
if (!gameInfo.ruleSet.units.containsKey(unitName)) return
|
||||||
@ -551,32 +566,32 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun destroy(){
|
fun destroy() {
|
||||||
val destructionText = if(isMajorCiv()) "The civilization of [$civName] has been destroyed!"
|
val destructionText = if (isMajorCiv()) "The civilization of [$civName] has been destroyed!"
|
||||||
else "The City-State of [$civName] has been destroyed!"
|
else "The City-State of [$civName] has been destroyed!"
|
||||||
for(civ in gameInfo.civilizations)
|
for (civ in gameInfo.civilizations)
|
||||||
civ.addNotification(destructionText, null, Color.RED)
|
civ.addNotification(destructionText, null, Color.RED)
|
||||||
getCivUnits().forEach { it.destroy() }
|
getCivUnits().forEach { it.destroy() }
|
||||||
tradeRequests.clear() // if we don't do this then there could be resources taken by "pending" trades forever
|
tradeRequests.clear() // if we don't do this then there could be resources taken by "pending" trades forever
|
||||||
for(diplomacyManager in diplomacy.values){
|
for (diplomacyManager in diplomacy.values) {
|
||||||
diplomacyManager.trades.clear()
|
diplomacyManager.trades.clear()
|
||||||
diplomacyManager.otherCiv().getDiplomacyManager(this).trades.clear()
|
diplomacyManager.otherCiv().getDiplomacyManager(this).trades.clear()
|
||||||
for(tradeRequest in diplomacyManager.otherCiv().tradeRequests.filter { it.requestingCiv==civName })
|
for (tradeRequest in diplomacyManager.otherCiv().tradeRequests.filter { it.requestingCiv == civName })
|
||||||
diplomacyManager.otherCiv().tradeRequests.remove(tradeRequest) // it would be really weird to get a trade request from a dead civ
|
diplomacyManager.otherCiv().tradeRequests.remove(tradeRequest) // it would be really weird to get a trade request from a dead civ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun giveGoldGift(otherCiv: CivilizationInfo, giftAmount: Int) {
|
fun giveGoldGift(otherCiv: CivilizationInfo, giftAmount: Int) {
|
||||||
if(!otherCiv.isCityState()) throw Exception("You can only gain influence with City-States!")
|
if (!otherCiv.isCityState()) throw Exception("You can only gain influence with City-States!")
|
||||||
gold -= giftAmount
|
gold -= giftAmount
|
||||||
otherCiv.getDiplomacyManager(this).influence += giftAmount/10
|
otherCiv.getDiplomacyManager(this).influence += giftAmount / 10
|
||||||
otherCiv.updateAllyCivForCityState()
|
otherCiv.updateAllyCivForCityState()
|
||||||
updateStatsForNextTurn()
|
updateStatsForNextTurn()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getResearchAgreementCost(otherCiv: CivilizationInfo): Int {
|
fun getResearchAgreementCost(otherCiv: CivilizationInfo): Int {
|
||||||
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
|
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
|
||||||
val basicGoldCostOfSignResearchAgreement = when(getEra()){
|
val basicGoldCostOfSignResearchAgreement = when (getEra()) {
|
||||||
Constants.medievalEra, Constants.renaissanceEra -> 250
|
Constants.medievalEra, Constants.renaissanceEra -> 250
|
||||||
Constants.industrialEra -> 300
|
Constants.industrialEra -> 300
|
||||||
Constants.modernEra -> 350
|
Constants.modernEra -> 350
|
||||||
@ -601,7 +616,7 @@ class CivilizationInfo {
|
|||||||
var newAllyName = ""
|
var newAllyName = ""
|
||||||
if (!isCityState()) return
|
if (!isCityState()) return
|
||||||
val maxInfluence = diplomacy
|
val maxInfluence = diplomacy
|
||||||
.filter{ !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() }
|
.filter { !it.value.otherCiv().isCityState() && !it.value.otherCiv().isDefeated() }
|
||||||
.maxBy { it.value.influence }
|
.maxBy { it.value.influence }
|
||||||
if (maxInfluence != null && maxInfluence.value.influence >= 60) {
|
if (maxInfluence != null && maxInfluence.value.influence >= 60) {
|
||||||
newAllyName = maxInfluence.key
|
newAllyName = maxInfluence.key
|
||||||
@ -613,7 +628,7 @@ class CivilizationInfo {
|
|||||||
|
|
||||||
// If the city-state is captured by a civ, it stops being the ally of the civ it was previously an ally of.
|
// If the city-state is captured by a civ, it stops being the ally of the civ it was previously an ally of.
|
||||||
// This means that it will NOT HAVE a capital at that time, so if we run getCapital we'll get a crash!
|
// This means that it will NOT HAVE a capital at that time, so if we run getCapital we'll get a crash!
|
||||||
val capitalLocation = if(cities.isNotEmpty()) getCapital().location else null
|
val capitalLocation = if (cities.isNotEmpty()) getCapital().location else null
|
||||||
|
|
||||||
if (newAllyName != "") {
|
if (newAllyName != "") {
|
||||||
val newAllyCiv = gameInfo.getCivilization(newAllyName)
|
val newAllyCiv = gameInfo.getCivilization(newAllyName)
|
||||||
@ -631,4 +646,4 @@ class CivilizationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
@ -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
|
||||||
@ -80,7 +87,7 @@ open class TileInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun containsUnfinishedGreatImprovement(): Boolean {
|
fun containsUnfinishedGreatImprovement(): Boolean {
|
||||||
if(improvementInProgress==null) return false
|
if (improvementInProgress == null) return false
|
||||||
return ruleset.tileImprovements[improvementInProgress!!]!!.isGreatImprovement()
|
return ruleset.tileImprovements[improvementInProgress!!]!!.isGreatImprovement()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +305,7 @@ open class TileInfo {
|
|||||||
/** Without regards to what civinfo it is, a lot of the checks are just for the improvement on the tile.
|
/** Without regards to what civinfo it is, a lot of the checks are just for the improvement on the tile.
|
||||||
* Doubles as a check for the map editor.
|
* Doubles as a check for the map editor.
|
||||||
*/
|
*/
|
||||||
fun canImprovementBeBuiltHere(improvement: TileImprovement, resourceIsVisible:Boolean = resource!=null): Boolean {
|
fun canImprovementBeBuiltHere(improvement: TileImprovement, resourceIsVisible: Boolean = resource != null): Boolean {
|
||||||
val topTerrain = getLastTerrain()
|
val topTerrain = getLastTerrain()
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
@ -325,7 +332,7 @@ open class TileInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun matchesUniqueFilter(filter:String): Boolean {
|
fun matchesUniqueFilter(filter: String): Boolean {
|
||||||
return filter == baseTerrain
|
return filter == baseTerrain
|
||||||
|| filter == Constants.hill && isHill()
|
|| filter == Constants.hill && isHill()
|
||||||
|| filter == "River" && isAdjacentToRiver()
|
|| filter == "River" && isAdjacentToRiver()
|
||||||
@ -382,8 +389,8 @@ open class TileInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The two tiles have a river between them */
|
/** The two tiles have a river between them */
|
||||||
fun isConnectedByRiver(otherTile:TileInfo): Boolean {
|
fun isConnectedByRiver(otherTile: TileInfo): Boolean {
|
||||||
if(otherTile==this) throw Exception("Should not be called to compare to self!")
|
if (otherTile == this) throw Exception("Should not be called to compare to self!")
|
||||||
val xDifference = this.position.x - otherTile.position.x
|
val xDifference = this.position.x - otherTile.position.x
|
||||||
val yDifference = this.position.y - otherTile.position.y
|
val yDifference = this.position.y - otherTile.position.y
|
||||||
|
|
||||||
@ -496,4 +503,4 @@ open class TileInfo {
|
|||||||
&& (terrainFeature == Constants.jungle || terrainFeature == Constants.forest)
|
&& (terrainFeature == Constants.jungle || terrainFeature == Constants.forest)
|
||||||
&& isFriendlyTerritory(civInfo)
|
&& isFriendlyTerritory(civInfo)
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
@ -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()
|
||||||
|
|
||||||
@ -31,19 +38,20 @@ class TileMap {
|
|||||||
constructor()
|
constructor()
|
||||||
|
|
||||||
/** generates an hexagonal map of given radius */
|
/** generates an hexagonal map of given radius */
|
||||||
constructor(radius:Int, ruleset: Ruleset){
|
constructor(radius: Int, ruleset: Ruleset) {
|
||||||
for(vector in HexMath.getVectorsInDistance(Vector2.Zero, radius))
|
for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius))
|
||||||
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
||||||
setTransients(ruleset)
|
setTransients(ruleset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** generates a rectangular map of given width and height*/
|
/** generates a rectangular map of given width and height*/
|
||||||
constructor(width: Int, height: Int, ruleset: Ruleset) {
|
constructor(width: Int, height: Int, ruleset: Ruleset) {
|
||||||
for(x in -width/2..width/2)
|
for (x in -width / 2..width / 2)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,17 +66,17 @@ class TileMap {
|
|||||||
return contains(vector.x.toInt(), vector.y.toInt())
|
return contains(vector.x.toInt(), vector.y.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun contains(x:Int, y:Int): Boolean {
|
fun contains(x: Int, y: Int): Boolean {
|
||||||
val arrayXIndex = x-leftX
|
val arrayXIndex = x - leftX
|
||||||
if(arrayXIndex<0 || arrayXIndex>=tileMatrix.size) return false
|
if (arrayXIndex < 0 || arrayXIndex >= tileMatrix.size) return false
|
||||||
val arrayYIndex = y-bottomY
|
val arrayYIndex = y - bottomY
|
||||||
if(arrayYIndex<0 || arrayYIndex>=tileMatrix[arrayXIndex].size) return false
|
if (arrayYIndex < 0 || arrayYIndex >= tileMatrix[arrayXIndex].size) return false
|
||||||
return tileMatrix[arrayXIndex][arrayYIndex] != null
|
return tileMatrix[arrayXIndex][arrayYIndex] != null
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(x:Int, y:Int):TileInfo{
|
operator fun get(x: Int, y: Int): TileInfo {
|
||||||
val arrayXIndex = x-leftX
|
val arrayXIndex = x - leftX
|
||||||
val arrayYIndex = y-bottomY
|
val arrayYIndex = y - bottomY
|
||||||
return tileMatrix[arrayXIndex][arrayYIndex]!!
|
return tileMatrix[arrayXIndex][arrayYIndex]!!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +142,7 @@ class TileMap {
|
|||||||
// both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
|
// both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
|
||||||
unit.assignOwner(civInfo, false)
|
unit.assignOwner(civInfo, false)
|
||||||
|
|
||||||
var unitToPlaceTile : TileInfo? = null
|
var unitToPlaceTile: TileInfo? = null
|
||||||
// try to place at the original point (this is the most probable scenario)
|
// try to place at the original point (this is the most probable scenario)
|
||||||
val currentTile = get(position)
|
val currentTile = get(position)
|
||||||
if (unit.movement.canMoveTo(currentTile)) unitToPlaceTile = currentTile
|
if (unit.movement.canMoveTo(currentTile)) unitToPlaceTile = currentTile
|
||||||
@ -145,7 +153,7 @@ class TileMap {
|
|||||||
var potentialCandidates = getPassableNeighbours(currentTile)
|
var potentialCandidates = getPassableNeighbours(currentTile)
|
||||||
while (unitToPlaceTile == null && tryCount++ < 10) {
|
while (unitToPlaceTile == null && tryCount++ < 10) {
|
||||||
unitToPlaceTile = potentialCandidates
|
unitToPlaceTile = potentialCandidates
|
||||||
.sortedByDescending { if(unit.type.isLandUnit()) it.isLand else true } // Land units should prefer to go into land tiles
|
.sortedByDescending { if (unit.type.isLandUnit()) it.isLand else true } // Land units should prefer to go into land tiles
|
||||||
.firstOrNull { unit.movement.canMoveTo(it) }
|
.firstOrNull { unit.movement.canMoveTo(it) }
|
||||||
if (unitToPlaceTile != null) continue
|
if (unitToPlaceTile != null) continue
|
||||||
// if it's not found yet, let's check their neighbours
|
// if it's not found yet, let's check their neighbours
|
||||||
@ -228,7 +236,7 @@ class TileMap {
|
|||||||
* @return stripped clone of [TileMap]
|
* @return stripped clone of [TileMap]
|
||||||
*/
|
*/
|
||||||
fun stripAllUnits(): TileMap {
|
fun stripAllUnits(): TileMap {
|
||||||
return clone().apply { tileList.forEach {it.stripUnits()} }
|
return clone().apply { tileList.forEach { it.stripUnits() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Strips all units and starting location from [TileMap] for specified [Player]
|
/** Strips all units and starting location from [TileMap] for specified [Player]
|
||||||
@ -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,33 +259,34 @@ 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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTransients(ruleset: Ruleset, setUnitCivTransients:Boolean=true) { // In the map editor, no Civs or Game exist, so we won't set the unit transients
|
fun setTransients(ruleset: Ruleset, setUnitCivTransients: Boolean = true) { // In the map editor, no Civs or Game exist, so we won't set the unit transients
|
||||||
val topY= tileList.asSequence().map { it.position.y.toInt() }.max()!!
|
val topY = tileList.asSequence().map { it.position.y.toInt() }.max()!!
|
||||||
bottomY= tileList.asSequence().map { it.position.y.toInt() }.min()!!
|
bottomY = tileList.asSequence().map { it.position.y.toInt() }.min()!!
|
||||||
val rightX= tileList.asSequence().map { it.position.x.toInt() }.max()!!
|
val rightX = tileList.asSequence().map { it.position.x.toInt() }.max()!!
|
||||||
leftX = tileList.asSequence().map { it.position.x.toInt() }.min()!!
|
leftX = tileList.asSequence().map { it.position.x.toInt() }.min()!!
|
||||||
|
|
||||||
for(x in leftX..rightX){
|
for (x in leftX..rightX) {
|
||||||
val row = ArrayList<TileInfo?>()
|
val row = ArrayList<TileInfo?>()
|
||||||
for(y in bottomY..topY) row.add(null)
|
for (y in bottomY..topY) row.add(null)
|
||||||
tileMatrix.add(row)
|
tileMatrix.add(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (tileInfo in values){
|
for (tileInfo in values) {
|
||||||
tileMatrix[tileInfo.position.x.toInt()-leftX][tileInfo.position.y.toInt()-bottomY] = tileInfo
|
tileMatrix[tileInfo.position.x.toInt() - leftX][tileInfo.position.y.toInt() - bottomY] = tileInfo
|
||||||
tileInfo.tileMap = this
|
tileInfo.tileMap = this
|
||||||
tileInfo.ruleset = ruleset
|
tileInfo.ruleset = ruleset
|
||||||
tileInfo.setTerrainTransients()
|
tileInfo.setTerrainTransients()
|
||||||
tileInfo.setUnitTransients(setUnitCivTransients)
|
tileInfo.setUnitTransients(setUnitCivTransients)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,9 +41,9 @@ class TileImprovementConstructionTests {
|
|||||||
val terrain = improvement.terrainsCanBeBuiltOn.firstOrNull() ?: continue
|
val terrain = improvement.terrainsCanBeBuiltOn.firstOrNull() ?: continue
|
||||||
tile.baseTerrain = terrain
|
tile.baseTerrain = terrain
|
||||||
tile.setTransients()
|
tile.setTransients()
|
||||||
if(improvement.uniqueTo!=null) civInfo.civName = improvement.uniqueTo!!
|
if (improvement.uniqueTo != null) civInfo.civName = improvement.uniqueTo!!
|
||||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||||
Assert.assertTrue( improvement.name, canBeBuilt)
|
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,12 +51,12 @@ class TileImprovementConstructionTests {
|
|||||||
fun allResourceImprovementsCanBeBuilt() {
|
fun allResourceImprovementsCanBeBuilt() {
|
||||||
for (improvement in ruleSet.tileImprovements.values) {
|
for (improvement in ruleSet.tileImprovements.values) {
|
||||||
tile.resource = ruleSet.tileResources.values
|
tile.resource = ruleSet.tileResources.values
|
||||||
.firstOrNull { it.improvement == improvement.name}?.name
|
.firstOrNull { it.improvement == improvement.name }?.name
|
||||||
if (tile.resource == null) continue
|
if (tile.resource == null) continue
|
||||||
tile.baseTerrain = "Plains"
|
tile.baseTerrain = "Plains"
|
||||||
tile.setTransients()
|
tile.setTransients()
|
||||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||||
Assert.assertTrue( improvement.name, canBeBuilt)
|
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class TileImprovementConstructionTests {
|
|||||||
if (!improvement.uniques.contains("Can only be built on Coastal tiles")) continue
|
if (!improvement.uniques.contains("Can only be built on Coastal tiles")) continue
|
||||||
civInfo.civName = improvement.uniqueTo ?: "OtherCiv"
|
civInfo.civName = improvement.uniqueTo ?: "OtherCiv"
|
||||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||||
Assert.assertTrue( improvement.name, canBeBuilt)
|
Assert.assertTrue(improvement.name, canBeBuilt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ class TileImprovementConstructionTests {
|
|||||||
tile.baseTerrain = "Plains"
|
tile.baseTerrain = "Plains"
|
||||||
tile.setTransients()
|
tile.setTransients()
|
||||||
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
val canBeBuilt = tile.canBuildImprovement(improvement, civInfo)
|
||||||
Assert.assertFalse( improvement.name, canBeBuilt)
|
Assert.assertFalse(improvement.name, canBeBuilt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user