mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 01:39:40 +07:00
3.12.2-patch1
This commit is contained in:
@ -22,21 +22,27 @@ import kotlin.collections.ArrayList
|
||||
class UncivShowableException(missingMods: String) : Exception(missingMods)
|
||||
|
||||
class GameInfo {
|
||||
@Transient 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
|
||||
@Transient
|
||||
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
|
||||
* that is inconsistent with the saved game on the cloud */
|
||||
@Transient var isUpToDate=false
|
||||
@Transient lateinit var ruleSet:Ruleset
|
||||
@Transient
|
||||
var isUpToDate = false
|
||||
@Transient
|
||||
lateinit var ruleSet: Ruleset
|
||||
|
||||
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 gameParameters= GameParameters()
|
||||
var gameParameters = GameParameters()
|
||||
var turns = 0
|
||||
var oneMoreTurnMode=false
|
||||
var currentPlayer=""
|
||||
var oneMoreTurnMode = false
|
||||
var currentPlayer = ""
|
||||
var gameId = UUID.randomUUID().toString() // random string
|
||||
|
||||
@Volatile
|
||||
var customSaveLocation: String? = null
|
||||
|
||||
@ -53,9 +59,9 @@ class GameInfo {
|
||||
val toReturn = GameInfo()
|
||||
toReturn.tileMap = tileMap.clone()
|
||||
toReturn.civilizations.addAll(civilizations.map { it.clone() })
|
||||
toReturn.currentPlayer=currentPlayer
|
||||
toReturn.currentPlayer = currentPlayer
|
||||
toReturn.turns = turns
|
||||
toReturn.difficulty=difficulty
|
||||
toReturn.difficulty = difficulty
|
||||
toReturn.gameParameters = gameParameters
|
||||
toReturn.gameId = gameId
|
||||
toReturn.oneMoreTurnMode = oneMoreTurnMode
|
||||
@ -66,11 +72,11 @@ class GameInfo {
|
||||
fun getPlayerToViewAs(): CivilizationInfo {
|
||||
if (!gameParameters.isOnlineMultiplayer) return currentPlayerCiv // non-online, play as human player
|
||||
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?
|
||||
}
|
||||
|
||||
fun getCivilization(civName:String) = civilizations.first { it.civName==civName }
|
||||
fun getCivilization(civName: String) = civilizations.first { it.civName == civName }
|
||||
fun getCurrentPlayerCivilization() = currentPlayerCiv
|
||||
fun getBarbarianCivilization() = getCivilization(Constants.barbarians)
|
||||
fun getDifficulty() = difficultyObject
|
||||
@ -85,10 +91,10 @@ class GameInfo {
|
||||
var currentPlayerIndex = civilizations.indexOf(thisPlayer)
|
||||
|
||||
|
||||
fun switchTurn(){
|
||||
fun switchTurn() {
|
||||
thisPlayer.endTurn()
|
||||
currentPlayerIndex = (currentPlayerIndex+1) % civilizations.size
|
||||
if(currentPlayerIndex==0){
|
||||
currentPlayerIndex = (currentPlayerIndex + 1) % civilizations.size
|
||||
if (currentPlayerIndex == 0) {
|
||||
turns++
|
||||
}
|
||||
thisPlayer = civilizations[currentPlayerIndex]
|
||||
@ -98,7 +104,7 @@ class GameInfo {
|
||||
switchTurn()
|
||||
|
||||
while (thisPlayer.playerType == PlayerType.AI
|
||||
|| turns < UncivGame.Current.simulateUntilTurnForDebug
|
||||
|| turns < UncivGame.Current.simulateUntilTurnForDebug
|
||||
|| turns < simulateMaxTurns && simulateUntilWin
|
||||
// For multiplayer, if there are 3+ players and one is defeated or spectator,
|
||||
// we'll want to skip over their turn
|
||||
@ -160,8 +166,7 @@ class GameInfo {
|
||||
val unitName = tile.militaryUnit!!.name
|
||||
thisPlayer.addNotification("An enemy [$unitName] was spotted $inOrNear our territory", tile.position, Color.RED)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val positions = tiles.map { it.position }
|
||||
thisPlayer.addNotification("[${positions.size}] enemy units were spotted $inOrNear our territory", Color.RED, LocationAction(positions))
|
||||
}
|
||||
@ -216,15 +221,15 @@ class GameInfo {
|
||||
val barbarianCiv = getBarbarianCivilization()
|
||||
barbarianCiv.tech.techsResearched = allResearchedTechs.toHashSet()
|
||||
val unitList = ruleSet.units.values
|
||||
.filter { !it.unitType.isCivilian()}
|
||||
.filter { !it.unitType.isCivilian() }
|
||||
.filter { it.isBuildable(barbarianCiv) }
|
||||
|
||||
val landUnits = unitList.filter { it.unitType.isLandUnit() }
|
||||
val waterUnits = unitList.filter { it.unitType.isWaterUnit() }
|
||||
|
||||
val unit:String
|
||||
if(waterUnits.isNotEmpty() && tileToPlace.isCoastalTile() && Random().nextBoolean())
|
||||
unit=waterUnits.random().name
|
||||
val unit: String
|
||||
if (waterUnits.isNotEmpty() && tileToPlace.isCoastalTile() && Random().nextBoolean())
|
||||
unit = waterUnits.random().name
|
||||
else unit = landUnits.random().name
|
||||
|
||||
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.
|
||||
*/
|
||||
fun notifyCivsOfBarbarianEncampment(tile: TileInfo) {
|
||||
civilizations.filter { it.hasUnique("Notified of new Barbarian encampments")
|
||||
&& it.exploredTiles.contains(tile.position) }
|
||||
civilizations.filter {
|
||||
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) }
|
||||
}
|
||||
|
||||
@ -345,10 +352,10 @@ class GameInfo {
|
||||
cityConstructions.builtBuildings.remove(oldBuildingName)
|
||||
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)) {
|
||||
cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[oldBuildingName]!!
|
||||
cityConstructions.inProgressConstructions.remove(oldBuildingName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -26,9 +26,7 @@ object GameStarter {
|
||||
gameInfo.tileMap = MapSaver.loadScenario(gameSetupInfo.mapParameters.name).tileMap
|
||||
else if (gameSetupInfo.mapParameters.name != "") {
|
||||
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.gameInfo = gameInfo // need to set this transient before placing units in the map
|
||||
@ -75,7 +73,7 @@ object GameStarter {
|
||||
civInfo.tech.addTechnology(tech)
|
||||
|
||||
// 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
|
||||
val techName = unique.params[0]
|
||||
|
||||
@ -106,7 +104,7 @@ object GameStarter {
|
||||
availableCivNames.removeAll(newGameParameters.players.map { it.chosenCiv })
|
||||
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)
|
||||
gameInfo.civilizations.add(barbarianCivilization)
|
||||
}
|
||||
@ -138,7 +136,7 @@ object GameStarter {
|
||||
val civ = CivilizationInfo(cityStateName)
|
||||
civ.cityStatePersonality = CityStatePersonality.values().random()
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -168,13 +166,14 @@ object GameStarter {
|
||||
fun placeNearStartingPosition(unitName: String) {
|
||||
civ.placeUnitNearTile(startingLocation.position, unitName)
|
||||
}
|
||||
|
||||
val warriorEquivalent = getWarriorEquivalent(civ)
|
||||
val startingUnits = when {
|
||||
civ.isPlayerCivilization() -> gameInfo.getDifficulty().startingUnits
|
||||
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivStartingUnits
|
||||
else -> gameInfo.getDifficulty().aiCityStateStartingUnits
|
||||
}
|
||||
|
||||
|
||||
for (unit in startingUnits) {
|
||||
val unitToAdd = if (unit == "Warrior") warriorEquivalent else unit
|
||||
if (unitToAdd != null) placeNearStartingPosition(unitToAdd)
|
||||
@ -185,7 +184,7 @@ object GameStarter {
|
||||
private fun getStartingLocations(civs: List<CivilizationInfo>, tileMap: TileMap): HashMap<CivilizationInfo, TileInfo> {
|
||||
var landTiles = tileMap.values
|
||||
// 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>()
|
||||
while (landTiles.any()) {
|
||||
|
@ -32,12 +32,18 @@ import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class CityInfo {
|
||||
@Transient lateinit var civInfo: CivilizationInfo
|
||||
@Transient lateinit private var centerTileInfo:TileInfo // cached for better performance
|
||||
@Transient 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
|
||||
@Transient
|
||||
lateinit var civInfo: CivilizationInfo
|
||||
@Transient
|
||||
lateinit private var centerTileInfo: TileInfo // cached for better performance
|
||||
@Transient
|
||||
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 id: String = UUID.randomUUID().toString()
|
||||
@ -55,14 +61,17 @@ class CityInfo {
|
||||
|
||||
/** All tiles that this city controls */
|
||||
var tiles = HashSet<Vector2>()
|
||||
|
||||
/** Tiles that have population assigned to them */
|
||||
var workedTiles = HashSet<Vector2>()
|
||||
|
||||
/** Tiles that the population in them won't be reassigned */
|
||||
var lockedTiles = HashSet<Vector2>()
|
||||
var isBeingRazed = false
|
||||
var attackedThisTurn = false
|
||||
var hasSoldBuildingThisTurn = false
|
||||
var isPuppet = false
|
||||
|
||||
/** The very first found city is the _original_ capital,
|
||||
* 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. */
|
||||
@ -91,7 +100,7 @@ class CityInfo {
|
||||
for (unique in civInfo.getMatchingUniques("Gain a free [] []")) {
|
||||
val freeBuildingName = unique.params[0]
|
||||
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))
|
||||
cityConstructions.addBuilding(freeBuildingName)
|
||||
}
|
||||
@ -154,7 +163,6 @@ class CityInfo {
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun getCenterTile(): TileInfo = centerTileInfo
|
||||
fun getTiles(): Sequence<TileInfo> = tiles.asSequence().map { tileMap[it] }
|
||||
fun getWorkableTiles() = tilesInRange.asSequence().filter { it.getOwner() == civInfo }
|
||||
@ -163,10 +171,11 @@ class CityInfo {
|
||||
fun isCapital(): Boolean = cityConstructions.builtBuildings.contains(capitalCityIndicator())
|
||||
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
|
||||
return connectionTypePredicate(mediumTypes)
|
||||
}
|
||||
|
||||
fun isInResistance() = resistanceCounter > 0
|
||||
|
||||
|
||||
@ -193,10 +202,11 @@ class CityInfo {
|
||||
val resource = getRuleset().tileResources[building.requiredResource]!!
|
||||
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]]
|
||||
if(resource!=null){
|
||||
cityResources.add(resource, unique.params[0].toInt() * civInfo.getResourceModifier(resource), "Tiles") }
|
||||
if (resource != null) {
|
||||
cityResources.add(resource, unique.params[0].toInt() * civInfo.getResourceModifier(resource), "Tiles")
|
||||
}
|
||||
}
|
||||
|
||||
return cityResources
|
||||
@ -205,17 +215,17 @@ class CityInfo {
|
||||
fun getTileResourceAmount(tileInfo: TileInfo): Int {
|
||||
if (tileInfo.resource == null) return 0
|
||||
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
|
||||
if (resource.improvement!=null) {
|
||||
if (resource.improvement != null) {
|
||||
val improvement = getRuleset().tileImprovements[resource.improvement!!]!!
|
||||
if (improvement.techRequired != null && !civInfo.tech.isResearched(improvement.techRequired!!)) return 0
|
||||
}
|
||||
|
||||
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
|
||||
|| (resource.resourceType==ResourceType.Strategic && tileInfo.containsGreatImprovement())) {
|
||||
|| (resource.resourceType == ResourceType.Strategic && tileInfo.containsGreatImprovement())) {
|
||||
var amountToAdd = 1
|
||||
if (resource.resourceType == ResourceType.Strategic) {
|
||||
amountToAdd = 2
|
||||
@ -240,7 +250,7 @@ class CityInfo {
|
||||
if (!isGrowing()) return null
|
||||
val roundedFoodPerTurn = foodForNextTurn().toFloat()
|
||||
val remainingFood = population.getFoodToNextPopulation() - population.foodStored
|
||||
var turnsToGrowth = ceil( remainingFood / roundedFoodPerTurn).toInt()
|
||||
var turnsToGrowth = ceil(remainingFood / roundedFoodPerTurn).toInt()
|
||||
if (turnsToGrowth < 1) turnsToGrowth = 1
|
||||
return turnsToGrowth
|
||||
}
|
||||
@ -251,11 +261,11 @@ class CityInfo {
|
||||
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()
|
||||
for((specialist, amount) in population.getNewSpecialists())
|
||||
for ((specialist, amount) in population.getNewSpecialists())
|
||||
if (getRuleset().specialists.containsKey(specialist)) // To solve problems in total remake mods
|
||||
stats.add("Specialists", getRuleset().specialists[specialist]!!.greatPersonPoints.times(amount))
|
||||
|
||||
@ -275,7 +285,7 @@ class CityInfo {
|
||||
val statName = greatUnitUnique.getPlaceholderParameters()[0]
|
||||
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
|
||||
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")
|
||||
@ -297,7 +307,9 @@ class CityInfo {
|
||||
return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth }
|
||||
}
|
||||
|
||||
override fun toString(): String {return name} // for debug
|
||||
override fun toString(): String {
|
||||
return name
|
||||
} // for debug
|
||||
//endregion
|
||||
|
||||
//region state-changing functions
|
||||
@ -313,7 +325,7 @@ class CityInfo {
|
||||
cityConstructions.setTransients()
|
||||
}
|
||||
|
||||
fun startTurn(){
|
||||
fun startTurn() {
|
||||
// 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
|
||||
cityConstructions.constructIfEnough()
|
||||
@ -439,7 +451,7 @@ class CityInfo {
|
||||
|
||||
// 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!
|
||||
if(!conqueringCiv.knows(oldCiv))
|
||||
if (!conqueringCiv.knows(oldCiv))
|
||||
conqueringCiv.meetCivilization(oldCiv)
|
||||
|
||||
oldCiv.getDiplomacyManager(conqueringCiv)
|
||||
@ -498,10 +510,10 @@ class CityInfo {
|
||||
val respecForLiberatingOurCity = 10f + percentageOfCivPopulationInThatCity.roundToInt()
|
||||
|
||||
// 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)
|
||||
|
||||
if(foundingCiv.isMajorCiv()) {
|
||||
if (foundingCiv.isMajorCiv()) {
|
||||
foundingCiv.getDiplomacyManager(conqueringCiv)
|
||||
.addModifier(DiplomaticModifiers.CapturedOurCities, respecForLiberatingOurCity)
|
||||
} else {
|
||||
@ -570,23 +582,21 @@ class CityInfo {
|
||||
tryUpdateRoadStatus()
|
||||
}
|
||||
|
||||
private fun tryUpdateRoadStatus(){
|
||||
if(getCenterTile().roadStatus==RoadStatus.None){
|
||||
private fun tryUpdateRoadStatus() {
|
||||
if (getCenterTile().roadStatus == RoadStatus.None) {
|
||||
val roadImprovement = getRuleset().tileImprovements["Road"]
|
||||
if(roadImprovement!=null && roadImprovement.techRequired in civInfo.tech.techsResearched)
|
||||
getCenterTile().roadStatus=RoadStatus.Road
|
||||
}
|
||||
|
||||
else if (getCenterTile().roadStatus != RoadStatus.Railroad) {
|
||||
if (roadImprovement != null && roadImprovement.techRequired in civInfo.tech.techsResearched)
|
||||
getCenterTile().roadStatus = RoadStatus.Road
|
||||
} else if (getCenterTile().roadStatus != RoadStatus.Railroad) {
|
||||
val railroadImprovement = getRuleset().tileImprovements["Railroad"]
|
||||
if (railroadImprovement != null && railroadImprovement.techRequired in civInfo.tech.techsResearched)
|
||||
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)
|
||||
civInfo.gold += getGoldForSellingBuilding(buildingName)
|
||||
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.
|
||||
There's a lot of triggering going on here.
|
||||
*/
|
||||
private fun triggerCitiesSettledNearOtherCiv(){
|
||||
val citiesWithin6Tiles = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() && it!=civInfo }
|
||||
private fun triggerCitiesSettledNearOtherCiv() {
|
||||
val citiesWithin6Tiles = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() && it != civInfo }
|
||||
.flatMap { it.cities }
|
||||
.filter { it.getCenterTile().aerialDistanceTo(getCenterTile()) <= 6 }
|
||||
val civsWithCloseCities = citiesWithin6Tiles.map { it.civInfo }.distinct()
|
||||
.filter { it.knows(civInfo) && it.exploredTiles.contains(location) }
|
||||
for(otherCiv in civsWithCloseCities)
|
||||
otherCiv.getDiplomacyManager(civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs,30)
|
||||
for (otherCiv in civsWithCloseCities)
|
||||
otherCiv.getDiplomacyManager(civInfo).setFlag(DiplomacyFlags.SettledCitiesNearUs, 30)
|
||||
}
|
||||
|
||||
fun canPurchase(construction : IConstruction) : Boolean {
|
||||
if (construction is BaseUnit)
|
||||
{
|
||||
fun canPurchase(construction: IConstruction): Boolean {
|
||||
if (construction is BaseUnit) {
|
||||
val tile = getCenterTile()
|
||||
if (construction.unitType.isCivilian())
|
||||
return tile.civilianUnit == null
|
||||
@ -640,4 +649,4 @@ class CityInfo {
|
||||
return true
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
}
|
@ -32,28 +32,40 @@ import kotlin.math.roundToInt
|
||||
|
||||
class CivilizationInfo {
|
||||
|
||||
@Transient lateinit var gameInfo: GameInfo
|
||||
@Transient lateinit var nation:Nation
|
||||
@Transient
|
||||
lateinit var gameInfo: GameInfo
|
||||
@Transient
|
||||
lateinit var nation: Nation
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The other solution, casting toList() every "get", has a performance cost
|
||||
*/
|
||||
@Transient private var units = listOf<MapUnit>()
|
||||
@Transient var viewableTiles = setOf<TileInfo>()
|
||||
@Transient var viewableInvisibleUnitsTiles = setOf<TileInfo>()
|
||||
@Transient
|
||||
private var units = listOf<MapUnit>()
|
||||
@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 */
|
||||
@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 */
|
||||
@Transient var hasActiveGreatWall = false
|
||||
@Transient var statsForNextTurn = Stats()
|
||||
@Transient var happinessForNextTurn = 0
|
||||
@Transient var detailedCivResources = ResourceSupplyList()
|
||||
@Transient
|
||||
var hasActiveGreatWall = false
|
||||
@Transient
|
||||
var statsForNextTurn = Stats()
|
||||
@Transient
|
||||
var happinessForNextTurn = 0
|
||||
@Transient
|
||||
var detailedCivResources = ResourceSupplyList()
|
||||
|
||||
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 civName = ""
|
||||
var tech = TechManager()
|
||||
@ -61,7 +73,7 @@ class CivilizationInfo {
|
||||
var questManager = QuestManager()
|
||||
var goldenAges = GoldenAgeManager()
|
||||
var greatPeople = GreatPersonManager()
|
||||
var victoryManager=VictoryManager()
|
||||
var victoryManager = VictoryManager()
|
||||
var diplomacy = HashMap<String, DiplomacyManager>()
|
||||
var notifications = ArrayList<Notification>()
|
||||
val popupAlerts = ArrayList<PopupAlert>()
|
||||
@ -115,24 +127,27 @@ class CivilizationInfo {
|
||||
}
|
||||
|
||||
//region pure functions
|
||||
fun getDifficulty():Difficulty {
|
||||
fun getDifficulty(): Difficulty {
|
||||
if (isPlayerCivilization()) return gameInfo.getDifficulty()
|
||||
return gameInfo.ruleSet.difficulties["Chieftain"]!!
|
||||
}
|
||||
|
||||
fun getDiplomacyManager(civInfo: CivilizationInfo) = getDiplomacyManager(civInfo.civName)
|
||||
fun getDiplomacyManager(civName: String) = diplomacy[civName]!!
|
||||
|
||||
/** Returns only undefeated civs, aka the ones we care about */
|
||||
fun getKnownCivs() = diplomacy.values.map { it.otherCiv() }.filter { !it.isDefeated() }
|
||||
fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName)
|
||||
fun knows(otherCiv: CivilizationInfo) = knows(otherCiv.civName)
|
||||
|
||||
fun getCapital()= cities.first { it.isCapital() }
|
||||
fun isPlayerCivilization() = playerType==PlayerType.Human
|
||||
fun getCapital() = cities.first { it.isCapital() }
|
||||
fun isPlayerCivilization() = playerType == PlayerType.Human
|
||||
fun isOneCityChallenger() = (
|
||||
playerType==PlayerType.Human &&
|
||||
gameInfo.gameParameters.oneCityChallenge)
|
||||
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization()==this
|
||||
fun isBarbarian() = nation.isBarbarian()
|
||||
playerType == PlayerType.Human &&
|
||||
gameInfo.gameParameters.oneCityChallenge)
|
||||
|
||||
fun isCurrentPlayer() = gameInfo.getCurrentPlayerCivilization() == this
|
||||
fun isBarbarian() = nation.isBarbarian()
|
||||
fun isSpectator() = nation.isSpectator()
|
||||
fun isCityState(): Boolean = nation.isCityState()
|
||||
val cityStateType: CityStateType get() = nation.cityStateType!!
|
||||
@ -144,10 +159,10 @@ class CivilizationInfo {
|
||||
fun getCivTerritory() = cities.asSequence().flatMap { it.tiles.asSequence() }
|
||||
|
||||
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
|
||||
val victoryType = nation.preferredVictoryType
|
||||
if(gameInfo.gameParameters.victoryTypes.contains(victoryType)) return victoryType
|
||||
if (gameInfo.gameParameters.victoryTypes.contains(victoryType)) return victoryType
|
||||
else return VictoryType.Neutral
|
||||
}
|
||||
|
||||
@ -163,9 +178,9 @@ class CivilizationInfo {
|
||||
|
||||
|
||||
fun getCivResources(): ResourceSupplyList {
|
||||
val newResourceSupplyList=ResourceSupplyList()
|
||||
for(resourceSupply in detailedCivResources)
|
||||
newResourceSupplyList.add(resourceSupply.resource,resourceSupply.amount,"All")
|
||||
val newResourceSupplyList = ResourceSupplyList()
|
||||
for (resourceSupply in detailedCivResources)
|
||||
newResourceSupplyList.add(resourceSupply.resource, resourceSupply.amount, "All")
|
||||
return newResourceSupplyList
|
||||
}
|
||||
|
||||
@ -179,10 +194,10 @@ class CivilizationInfo {
|
||||
/**
|
||||
* Returns a dictionary of ALL resource names, and the amount that the civ has of each
|
||||
*/
|
||||
fun getCivResourcesByName():HashMap<String,Int> {
|
||||
val hashMap = HashMap<String,Int>(gameInfo.ruleSet.tileResources.size)
|
||||
for(resource in gameInfo.ruleSet.tileResources.keys) hashMap[resource]=0
|
||||
for(entry in getCivResources())
|
||||
fun getCivResourcesByName(): HashMap<String, Int> {
|
||||
val hashMap = HashMap<String, Int>(gameInfo.ruleSet.tileResources.size)
|
||||
for (resource in gameInfo.ruleSet.tileResources.keys) hashMap[resource] = 0
|
||||
for (entry in getCivResources())
|
||||
hashMap[entry.resource.name] = entry.amount
|
||||
return hashMap
|
||||
}
|
||||
@ -199,11 +214,11 @@ class CivilizationInfo {
|
||||
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 hasUnique(unique:String) = getMatchingUniques(unique).any()
|
||||
fun hasUnique(unique: String) = getMatchingUniques(unique).any()
|
||||
|
||||
fun getMatchingUniques(uniqueTemplate: String): Sequence<Unique> {
|
||||
return nation.uniqueObjects.asSequence().filter { it.placeholderText == uniqueTemplate } +
|
||||
@ -215,12 +230,12 @@ class CivilizationInfo {
|
||||
fun getCivUnits(): Sequence<MapUnit> = units.asSequence()
|
||||
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)
|
||||
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,
|
||||
// and in any case it'll be updated once civ info transients are
|
||||
updateStatsForNextTurn() // unit upkeep
|
||||
@ -244,7 +259,7 @@ class CivilizationInfo {
|
||||
|
||||
fun getNextDueUnit(): MapUnit? {
|
||||
val dueUnits = getDueUnits()
|
||||
if(dueUnits.any()) {
|
||||
if (dueUnits.any()) {
|
||||
val unit = dueUnits.first()
|
||||
unit.due = false
|
||||
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)
|
||||
|
||||
for(building in gameInfo.ruleSet.buildings.values)
|
||||
if(building.replaces==baseBuilding.name && building.uniqueTo==civName)
|
||||
for (building in gameInfo.ruleSet.buildings.values)
|
||||
if (building.replaces == baseBuilding.name && building.uniqueTo == civName)
|
||||
return building
|
||||
return baseBuilding
|
||||
}
|
||||
|
||||
fun getEquivalentUnit(baseUnitName:String):BaseUnit {
|
||||
fun getEquivalentUnit(baseUnitName: String): BaseUnit {
|
||||
for (unit in gameInfo.ruleSet.units.values)
|
||||
if (unit.replaces == baseUnitName && unit.uniqueTo == civName)
|
||||
return unit
|
||||
@ -283,22 +297,23 @@ class CivilizationInfo {
|
||||
diplomacy[otherCiv.civName] = DiplomacyManager(this, otherCiv.civName)
|
||||
.apply { diplomaticStatus = DiplomaticStatus.Peace }
|
||||
|
||||
otherCiv.popupAlerts.add(PopupAlert(AlertType.FirstContact,civName))
|
||||
otherCiv.popupAlerts.add(PopupAlert(AlertType.FirstContact, civName))
|
||||
|
||||
otherCiv.diplomacy[civName] = DiplomacyManager(otherCiv, civName)
|
||||
.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")
|
||||
}
|
||||
|
||||
fun discoverNaturalWonder(naturalWonderName: String)
|
||||
{
|
||||
fun discoverNaturalWonder(naturalWonderName: String) {
|
||||
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 */
|
||||
fun isDefeated(): Boolean {
|
||||
@ -314,9 +329,9 @@ class CivilizationInfo {
|
||||
fun getEra(): String {
|
||||
// For scenarios with no techs
|
||||
if (gameInfo.ruleSet.technologies.isEmpty()) return "None"
|
||||
if(tech.researchedTechnologies.isEmpty())
|
||||
if (tech.researchedTechnologies.isEmpty())
|
||||
return gameInfo.ruleSet.getEras().first()
|
||||
val maxEraOfTech = tech.researchedTechnologies
|
||||
val maxEraOfTech = tech.researchedTechnologies
|
||||
.asSequence()
|
||||
.map { it.column!! }
|
||||
.maxBy { it.columnNumber }!!
|
||||
@ -328,7 +343,7 @@ class CivilizationInfo {
|
||||
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.isBarbarian() || isBarbarian()) return true
|
||||
val diplomacyManager = diplomacy[otherCiv.civName]
|
||||
@ -336,7 +351,7 @@ class CivilizationInfo {
|
||||
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 {
|
||||
var leaderName = nation.getLeaderDisplayName().tr()
|
||||
@ -349,10 +364,10 @@ class CivilizationInfo {
|
||||
}
|
||||
|
||||
fun canSignResearchAgreement(): Boolean {
|
||||
if(!isMajorCiv()) return false
|
||||
if(!tech.getTechUniques().contains("Enables Research agreements")) return false
|
||||
if (!isMajorCiv()) return false
|
||||
if (!tech.getTechUniques().contains("Enables Research agreements")) return false
|
||||
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
|
||||
}
|
||||
|
||||
@ -366,8 +381,8 @@ class CivilizationInfo {
|
||||
&& gold >= cost && otherCiv.gold >= cost
|
||||
}
|
||||
|
||||
fun getStatForRanking(category: RankingType) : Int {
|
||||
return when(category) {
|
||||
fun getStatForRanking(category: RankingType): Int {
|
||||
return when (category) {
|
||||
RankingType.Population -> cities.sumBy { it.population.population }
|
||||
RankingType.CropYield -> statsForNextTurn.food.roundToInt()
|
||||
RankingType.Production -> statsForNextTurn.production.roundToInt()
|
||||
@ -388,7 +403,7 @@ class CivilizationInfo {
|
||||
* 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 =\
|
||||
* */
|
||||
fun setNationTransient(){
|
||||
fun setNationTransient() {
|
||||
nation = gameInfo.ruleSet.nations[civName]
|
||||
?: throw java.lang.Exception("Nation $civName is not found!")
|
||||
}
|
||||
@ -397,25 +412,25 @@ class CivilizationInfo {
|
||||
goldenAges.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.setTransients()
|
||||
|
||||
questManager.civInfo = this
|
||||
questManager.setTransients()
|
||||
|
||||
if(citiesCreated==0 && cities.any())
|
||||
if (citiesCreated == 0 && cities.any())
|
||||
citiesCreated = cities.filter { it.name in nation.cities }.count()
|
||||
|
||||
tech.civInfo = this
|
||||
tech.setTransients()
|
||||
|
||||
for (diplomacyManager in diplomacy.values) {
|
||||
diplomacyManager.civInfo=this
|
||||
diplomacyManager.civInfo = this
|
||||
diplomacyManager.updateHasOpenBorders()
|
||||
}
|
||||
|
||||
victoryManager.civInfo=this
|
||||
victoryManager.civInfo = this
|
||||
|
||||
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
|
||||
@ -452,9 +467,9 @@ class CivilizationInfo {
|
||||
|
||||
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)
|
||||
if (offeringCiv.isDefeated() || !TradeEvaluation().isTradeValid(tradeRequest.trade,this, offeringCiv)) {
|
||||
if (offeringCiv.isDefeated() || !TradeEvaluation().isTradeValid(tradeRequest.trade, this, offeringCiv)) {
|
||||
tradeRequests.remove(tradeRequest)
|
||||
// Yes, this is the right direction. I checked.
|
||||
offeringCiv.addNotification("Our proposed trade is no longer relevant!", Color.GOLD)
|
||||
@ -512,7 +527,7 @@ class CivilizationInfo {
|
||||
}
|
||||
|
||||
fun canEnterTiles(otherCiv: CivilizationInfo): Boolean {
|
||||
if (otherCiv==this) return true
|
||||
if (otherCiv == this) return true
|
||||
if (otherCiv.isBarbarian()) return true
|
||||
if (nation.isBarbarian() && gameInfo.turns >= gameInfo.difficultyObject.turnBarbariansCanEnterPlayerTiles)
|
||||
return true
|
||||
@ -526,12 +541,12 @@ class CivilizationInfo {
|
||||
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
|
||||
notifications.add(Notification(text, color, action))
|
||||
}
|
||||
|
||||
fun addUnit(unitName:String, city: CityInfo?=null) {
|
||||
fun addUnit(unitName: String, city: CityInfo? = null) {
|
||||
if (cities.isEmpty()) return
|
||||
val cityToAddTo = city ?: cities.random()
|
||||
if (!gameInfo.ruleSet.units.containsKey(unitName)) return
|
||||
@ -551,32 +566,32 @@ class CivilizationInfo {
|
||||
}
|
||||
|
||||
|
||||
fun destroy(){
|
||||
val destructionText = if(isMajorCiv()) "The civilization of [$civName] has been destroyed!"
|
||||
fun destroy() {
|
||||
val destructionText = if (isMajorCiv()) "The civilization 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)
|
||||
getCivUnits().forEach { it.destroy() }
|
||||
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.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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
otherCiv.getDiplomacyManager(this).influence += giftAmount/10
|
||||
otherCiv.getDiplomacyManager(this).influence += giftAmount / 10
|
||||
otherCiv.updateAllyCivForCityState()
|
||||
updateStatsForNextTurn()
|
||||
}
|
||||
|
||||
fun getResearchAgreementCost(otherCiv: CivilizationInfo): Int {
|
||||
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
|
||||
val basicGoldCostOfSignResearchAgreement = when(getEra()){
|
||||
val basicGoldCostOfSignResearchAgreement = when (getEra()) {
|
||||
Constants.medievalEra, Constants.renaissanceEra -> 250
|
||||
Constants.industrialEra -> 300
|
||||
Constants.modernEra -> 350
|
||||
@ -601,7 +616,7 @@ class CivilizationInfo {
|
||||
var newAllyName = ""
|
||||
if (!isCityState()) return
|
||||
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 }
|
||||
if (maxInfluence != null && maxInfluence.value.influence >= 60) {
|
||||
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.
|
||||
// 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 != "") {
|
||||
val newAllyCiv = gameInfo.getCivilization(newAllyName)
|
||||
@ -631,4 +646,4 @@ class CivilizationInfo {
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
}
|
@ -14,15 +14,22 @@ import com.unciv.ui.utils.Fonts
|
||||
import kotlin.math.abs
|
||||
|
||||
open class TileInfo {
|
||||
@Transient lateinit var tileMap: TileMap
|
||||
@Transient 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
|
||||
@Transient
|
||||
lateinit var tileMap: TileMap
|
||||
@Transient
|
||||
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
|
||||
@Transient var isLand = false
|
||||
@Transient var isWater = false
|
||||
@Transient var isOcean = false
|
||||
@Transient
|
||||
var isLand = 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
|
||||
// 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 {
|
||||
if(improvementInProgress==null) return false
|
||||
if (improvementInProgress == null) return false
|
||||
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.
|
||||
* 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()
|
||||
|
||||
return when {
|
||||
@ -325,7 +332,7 @@ open class TileInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fun matchesUniqueFilter(filter:String): Boolean {
|
||||
fun matchesUniqueFilter(filter: String): Boolean {
|
||||
return filter == baseTerrain
|
||||
|| filter == Constants.hill && isHill()
|
||||
|| filter == "River" && isAdjacentToRiver()
|
||||
@ -382,8 +389,8 @@ open class TileInfo {
|
||||
}
|
||||
|
||||
/** The two tiles have a river between them */
|
||||
fun isConnectedByRiver(otherTile:TileInfo): Boolean {
|
||||
if(otherTile==this) throw Exception("Should not be called to compare to self!")
|
||||
fun isConnectedByRiver(otherTile: TileInfo): Boolean {
|
||||
if (otherTile == this) throw Exception("Should not be called to compare to self!")
|
||||
val xDifference = this.position.x - otherTile.position.x
|
||||
val yDifference = this.position.y - otherTile.position.y
|
||||
|
||||
@ -496,4 +503,4 @@ open class TileInfo {
|
||||
&& (terrainFeature == Constants.jungle || terrainFeature == Constants.forest)
|
||||
&& isFriendlyTerritory(civInfo)
|
||||
//endregion
|
||||
}
|
||||
}
|
@ -12,13 +12,20 @@ import kotlin.math.abs
|
||||
|
||||
class TileMap {
|
||||
|
||||
@Transient lateinit var gameInfo: GameInfo
|
||||
@Transient var tileMatrix = ArrayList<ArrayList<TileInfo?>>() // this works several times faster than a hashmap, the performance difference is really astounding
|
||||
@Transient var leftX = 0
|
||||
@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() }
|
||||
@Transient
|
||||
lateinit var gameInfo: GameInfo
|
||||
@Transient
|
||||
var tileMatrix = ArrayList<ArrayList<TileInfo?>>() // this works several times faster than a hashmap, the performance difference is really astounding
|
||||
@Transient
|
||||
var leftX = 0
|
||||
@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()
|
||||
|
||||
@ -31,19 +38,20 @@ class TileMap {
|
||||
constructor()
|
||||
|
||||
/** generates an hexagonal map of given radius */
|
||||
constructor(radius:Int, ruleset: Ruleset){
|
||||
for(vector in HexMath.getVectorsInDistance(Vector2.Zero, radius))
|
||||
constructor(radius: Int, ruleset: Ruleset) {
|
||||
for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius))
|
||||
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
||||
setTransients(ruleset)
|
||||
}
|
||||
|
||||
/** generates a rectangular map of given width and height*/
|
||||
constructor(width: Int, height: Int, ruleset: Ruleset) {
|
||||
for(x in -width/2..width/2)
|
||||
for (y in -height/2..height/2)
|
||||
for (x in -width / 2..width / 2)
|
||||
for (y in -height / 2..height / 2)
|
||||
tileList.add(TileInfo().apply {
|
||||
position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(),y.toFloat()))
|
||||
baseTerrain = Constants.grassland })
|
||||
position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(), y.toFloat()))
|
||||
baseTerrain = Constants.grassland
|
||||
})
|
||||
setTransients(ruleset)
|
||||
}
|
||||
|
||||
@ -58,17 +66,17 @@ class TileMap {
|
||||
return contains(vector.x.toInt(), vector.y.toInt())
|
||||
}
|
||||
|
||||
fun contains(x:Int, y:Int): Boolean {
|
||||
val arrayXIndex = x-leftX
|
||||
if(arrayXIndex<0 || arrayXIndex>=tileMatrix.size) return false
|
||||
val arrayYIndex = y-bottomY
|
||||
if(arrayYIndex<0 || arrayYIndex>=tileMatrix[arrayXIndex].size) return false
|
||||
fun contains(x: Int, y: Int): Boolean {
|
||||
val arrayXIndex = x - leftX
|
||||
if (arrayXIndex < 0 || arrayXIndex >= tileMatrix.size) return false
|
||||
val arrayYIndex = y - bottomY
|
||||
if (arrayYIndex < 0 || arrayYIndex >= tileMatrix[arrayXIndex].size) return false
|
||||
return tileMatrix[arrayXIndex][arrayYIndex] != null
|
||||
}
|
||||
|
||||
operator fun get(x:Int, y:Int):TileInfo{
|
||||
val arrayXIndex = x-leftX
|
||||
val arrayYIndex = y-bottomY
|
||||
operator fun get(x: Int, y: Int): TileInfo {
|
||||
val arrayXIndex = x - leftX
|
||||
val arrayYIndex = y - bottomY
|
||||
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
|
||||
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)
|
||||
val currentTile = get(position)
|
||||
if (unit.movement.canMoveTo(currentTile)) unitToPlaceTile = currentTile
|
||||
@ -145,7 +153,7 @@ class TileMap {
|
||||
var potentialCandidates = getPassableNeighbours(currentTile)
|
||||
while (unitToPlaceTile == null && tryCount++ < 10) {
|
||||
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) }
|
||||
if (unitToPlaceTile != null) continue
|
||||
// if it's not found yet, let's check their neighbours
|
||||
@ -228,7 +236,7 @@ class TileMap {
|
||||
* @return stripped clone of [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]
|
||||
@ -237,7 +245,9 @@ class TileMap {
|
||||
*/
|
||||
fun stripPlayer(player: Player) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -249,33 +259,34 @@ class TileMap {
|
||||
*/
|
||||
fun switchPlayersNation(player: Player, newNation: Nation) {
|
||||
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) {
|
||||
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
|
||||
val topY= tileList.asSequence().map { it.position.y.toInt() }.max()!!
|
||||
bottomY= tileList.asSequence().map { it.position.y.toInt() }.min()!!
|
||||
val rightX= tileList.asSequence().map { it.position.x.toInt() }.max()!!
|
||||
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()!!
|
||||
bottomY = tileList.asSequence().map { it.position.y.toInt() }.min()!!
|
||||
val rightX = tileList.asSequence().map { it.position.x.toInt() }.max()!!
|
||||
leftX = tileList.asSequence().map { it.position.x.toInt() }.min()!!
|
||||
|
||||
for(x in leftX..rightX){
|
||||
for (x in leftX..rightX) {
|
||||
val row = ArrayList<TileInfo?>()
|
||||
for(y in bottomY..topY) row.add(null)
|
||||
for (y in bottomY..topY) row.add(null)
|
||||
tileMatrix.add(row)
|
||||
}
|
||||
|
||||
for (tileInfo in values){
|
||||
tileMatrix[tileInfo.position.x.toInt()-leftX][tileInfo.position.y.toInt()-bottomY] = tileInfo
|
||||
for (tileInfo in values) {
|
||||
tileMatrix[tileInfo.position.x.toInt() - leftX][tileInfo.position.y.toInt() - bottomY] = tileInfo
|
||||
tileInfo.tileMap = this
|
||||
tileInfo.ruleset = ruleset
|
||||
tileInfo.setTerrainTransients()
|
||||
tileInfo.setUnitTransients(setUnitCivTransients)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user