mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 07:17:50 +07:00
Carthage civ (#5224)
* Add Carthage * Implement uniques * performance improvement, better elephant * AI avoids taking too much damage from mountains * more performance * better AI * can't settle cities on mountains * AI improvement * AI improvement * revisions, damagePerTurn in Terrains.json * terrain damage stored as unique in json, damage also works for terrain features * don't change game.png
This commit is contained in:
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
BIN
android/Images.Construction/UnitIcons/Quinquereme.png
Normal file
BIN
android/Images.Construction/UnitIcons/Quinquereme.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
BIN
android/Images/NationIcons/Carthage.png
Normal file
BIN
android/Images/NationIcons/Carthage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -773,6 +773,33 @@
|
||||
"Kapfenberg", "Hallein", "Bischofshofen", "Waidhofen", "Saalbach", "Lienz", "Steyr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Carthage",
|
||||
"leaderName": "Dido",
|
||||
"adjective": ["Carthaginian"],
|
||||
"startBias": ["Coast"],
|
||||
"preferredVictoryType": "Domination",
|
||||
|
||||
"startIntroPart1": "Blessings and salutations to you, revered Queen Dido, founder of the legendary kingdom of Carthage. Chronicled by the words of the great poet Virgil, your husband Acerbas was murdered at the hands of your own brother, King Pygmalion of Tyre, who subsequently claimed the treasures of Acerbas that were now rightfully yours. Fearing the lengths from which your brother would pursue this vast wealth, you and your compatriots sailed for new lands. Arriving on the shores of North Africa, you tricked the local king with the simple manipulation of an ox hide, laying out a vast expanse of territory for your new home, the future kingdom of Carthage.",
|
||||
"startIntroPart2": "Clever and inquisitive Dido, the world longs for a leader who can provide a shelter from the coming storm, guided by brilliant intuition and cunning. Can you lead the people in the creation of a new kingdom to rival that of once mighty Carthage? Can you build a civilization that will stand the test of time?",
|
||||
|
||||
"declaringWar": "Tell me, do you all know how numerous my armies, elephants and the gdadons are? No? Today, you shall find out!",
|
||||
"attacked": "Fate is against you. You earned the animosity of Carthage in your exploration. Your days are numbered.",
|
||||
"defeated": "The fates became to hate me. This is it? You wouldn't destroy us so without their help.",
|
||||
"introduction": "The Phoenicians welcome you to this most pleasant kingdom. I am Dido, the queen of Carthage and all that belongs to it.",
|
||||
"neutralHello": "It is done.",
|
||||
"hateHello": "What is it now?",
|
||||
"tradeRequest": "I had an idea and I realized I should tell it to you!",
|
||||
"outerColor": [205,205,205],
|
||||
"innerColor": [81,0,137],
|
||||
"uniqueName": "Phoenician Heritage",
|
||||
"uniques": ["Gain a free [Harbor] [in all coastal cities]","Land units may cross [Mountain] tiles after the first [Great General] is earned",
|
||||
"Units ending their turn on [Mountain] tiles take [50] damage"],
|
||||
"cities": ["Carthage","Utique","Hippo Regius","Gades","Saguntum","Carthago Nova","Panormus","Lilybaeum","Hadrumetum","Zama Regia",
|
||||
"Karalis","Malaca","Leptis Magna","Hippo Diarrhytus","Motya","Sulci","Leptis Parva","Tharros","Soluntum","Lixus",
|
||||
"Oea","Theveste","Ibossim","Thapsus","Aleria","Tingis","Abyla","Sabratha","Rusadir","Baecula",
|
||||
"Saldae"]
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
@ -86,7 +86,7 @@
|
||||
"impassable": true,
|
||||
"defenceBonus": 0.25,
|
||||
"RGB": [120, 120, 120],
|
||||
"uniques":["Rough terrain", "Has an elevation of [4] for visibility calculations", "Occurs in chains at high elevations"]
|
||||
"uniques":["Rough terrain", "Has an elevation of [4] for visibility calculations", "Occurs in chains at high elevations", "Units ending their turn on this terrain take [50] damage"]
|
||||
},
|
||||
{
|
||||
"name": "Snow",
|
||||
|
@ -161,6 +161,20 @@
|
||||
"obsoleteTech": "Astronomy",
|
||||
"attackSound": "nonmetalhit"
|
||||
},
|
||||
{
|
||||
"name": "Quinquereme",
|
||||
"unitType": "Melee Water",
|
||||
"uniqueTo": "Carthage",
|
||||
"replaces": "Trireme",
|
||||
"movement": 4,
|
||||
"strength": 13,
|
||||
"cost": 45,
|
||||
"requiredTech": "Sailing",
|
||||
"uniques": ["Cannot enter ocean tiles"],
|
||||
"upgradesTo": "Caravel",
|
||||
"obsoleteTech": "Astronomy",
|
||||
"attackSound": "nonmetalhit"
|
||||
},
|
||||
/*
|
||||
{
|
||||
"name": "Galley",
|
||||
@ -339,6 +353,22 @@
|
||||
"hurryCostModifier": 20,
|
||||
"attackSound": "horse"
|
||||
},
|
||||
{
|
||||
"name": "African Forest Elephant",
|
||||
"unitType": "Mounted",
|
||||
"uniqueTo": "Carthage",
|
||||
"replaces": "Horseman",
|
||||
"movement": 3,
|
||||
"strength": 14,
|
||||
"cost": 100,
|
||||
"requiredTech": "Horseback Riding",
|
||||
"upgradesTo": "Knight",
|
||||
"obsoleteTech": "Metallurgy",
|
||||
"promotions": ["Great Generals II"],
|
||||
"uniques": ["Can move after attacking", "No defensive terrain bonus", "-[33]% Strength vs [City]","[-10]% Strength for enemy [Military] units in adjacent [All] tiles"],
|
||||
"hurryCostModifier": 20,
|
||||
"attackSound": "elephant"
|
||||
},
|
||||
{
|
||||
"name": "Catapult",
|
||||
"unitType": "Siege",
|
||||
|
@ -156,7 +156,8 @@ object SpecificUnitAutomation {
|
||||
}
|
||||
|
||||
if (unit.getTile().militaryUnit == null // Don't move until you're accompanied by a military unit
|
||||
&& !unit.civInfo.isCityState()) return // ..unless you're a city state that was unable to settle its city on turn 1
|
||||
&& !unit.civInfo.isCityState() // ..unless you're a city state that was unable to settle its city on turn 1
|
||||
&& unit.getDamageFromTerrain() < unit.health) return // Also make sure we won't die waiting
|
||||
|
||||
val tilesNearCities = unit.civInfo.gameInfo.getCities().asSequence()
|
||||
.flatMap {
|
||||
@ -177,7 +178,7 @@ object SpecificUnitAutomation {
|
||||
val possibleCityLocations = unit.getTile().getTilesInDistance(5)
|
||||
.filter {
|
||||
val tileOwner = it.getOwner()
|
||||
it.isLand && (tileOwner == null || tileOwner == unit.civInfo) // don't allow settler to settle inside other civ's territory
|
||||
it.isLand && !it.isImpassible() && (tileOwner == null || tileOwner == unit.civInfo) // don't allow settler to settle inside other civ's territory
|
||||
&& (unit.currentTile == it || unit.movement.canMoveTo(it))
|
||||
&& it !in tilesNearCities
|
||||
}.toList()
|
||||
|
@ -19,7 +19,8 @@ object UnitAutomation {
|
||||
&& (tile.getOwner() == null || !tile.getOwner()!!.isCityState())
|
||||
&& tile.neighbors.any { it.position !in unit.civInfo.exploredTiles }
|
||||
&& unit.movement.canReach(tile)
|
||||
&& (!unit.civInfo.isCityState() || tile.neighbors.any { it.getOwner() == unit.civInfo }) // Don't want city-states exploring far outside their borders
|
||||
&& (!unit.civInfo.isCityState() || tile.neighbors.any { it.getOwner() == unit.civInfo } // Don't want city-states exploring far outside their borders
|
||||
&& unit.getDamageFromTerrain(tile) <= 0) // Don't take unnecessary damage
|
||||
}
|
||||
|
||||
internal fun tryExplore(unit: MapUnit): Boolean {
|
||||
@ -66,7 +67,8 @@ object UnitAutomation {
|
||||
.filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) }
|
||||
|
||||
val reachableTilesMaxWalkingDistance = reachableTiles
|
||||
.filter { it.value.totalDistance == unit.currentMovement }
|
||||
.filter { it.value.totalDistance == unit.currentMovement
|
||||
&& unit.getDamageFromTerrain(it.key) <= 0 } // Don't end turn on damaging terrain for no good reason
|
||||
if (reachableTilesMaxWalkingDistance.any()) unit.movement.moveToTile(reachableTilesMaxWalkingDistance.toList().random().first)
|
||||
else if (reachableTiles.any()) unit.movement.moveToTile(reachableTiles.keys.random())
|
||||
}
|
||||
@ -87,6 +89,9 @@ object UnitAutomation {
|
||||
if (unit.civInfo.isBarbarian())
|
||||
throw IllegalStateException("Barbarians is not allowed here.")
|
||||
|
||||
// Might die next turn - move!
|
||||
if (unit.health <= unit.getDamageFromTerrain() && tryHealUnit(unit)) return
|
||||
|
||||
if (unit.isCivilian()) {
|
||||
if (tryRunAwayIfNeccessary(unit)) return
|
||||
|
||||
@ -209,7 +214,10 @@ object UnitAutomation {
|
||||
.filter { it.isCityCenter() && it.getCity()!!.civInfo.isAtWarWith(unit.civInfo) }
|
||||
.flatMap { it.getTilesInDistance(it.getCity()!!.range) }
|
||||
|
||||
val dangerousTiles = (tilesInRangeOfAttack + tilesWithinBombardmentRange).toHashSet()
|
||||
val tilesWithTerrainDamage = unit.currentTile.getTilesInDistance(3)
|
||||
.filter { unit.getDamageFromTerrain(it) > 0 }
|
||||
|
||||
val dangerousTiles = (tilesInRangeOfAttack + tilesWithinBombardmentRange + tilesWithTerrainDamage).toHashSet()
|
||||
|
||||
|
||||
val viableTilesForHealing = unitDistanceToTiles.keys
|
||||
@ -230,7 +238,7 @@ object UnitAutomation {
|
||||
val bestTileForHealingRank = unit.rankTileForHealing(bestTileForHealing)
|
||||
|
||||
if (currentUnitTile != bestTileForHealing
|
||||
&& bestTileForHealingRank > unit.rankTileForHealing(currentUnitTile))
|
||||
&& bestTileForHealingRank > unit.rankTileForHealing(currentUnitTile) - unit.getDamageFromTerrain())
|
||||
unit.movement.moveToTile(bestTileForHealing)
|
||||
|
||||
unit.fortifyIfCan()
|
||||
@ -272,16 +280,18 @@ object UnitAutomation {
|
||||
unitDistanceToTiles,
|
||||
tilesToCheck = unit.getTile().getTilesInDistance(CLOSE_ENEMY_TILES_AWAY_LIMIT).toList()
|
||||
).filter {
|
||||
// Ignore units that would 1-shot you if you attacked
|
||||
// Ignore units that would 1-shot you if you attacked. Account for taking terrain damage after the fact.
|
||||
BattleDamage.calculateDamageToAttacker(MapUnitCombatant(unit),
|
||||
it.tileToAttackFrom,
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!) < unit.health
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!)
|
||||
+ unit.getDamageFromTerrain(it.tileToAttackFrom) < unit.health
|
||||
}
|
||||
|
||||
if (unit.baseUnit.isRanged())
|
||||
closeEnemies = closeEnemies.filterNot { it.tileToAttack.isCityCenter() && it.tileToAttack.getCity()!!.health == 1 }
|
||||
|
||||
val closestEnemy = closeEnemies.minByOrNull { it.tileToAttack.aerialDistanceTo(unit.getTile()) }
|
||||
val closestEnemy = closeEnemies.filter { unit.getDamageFromTerrain(it.tileToAttackFrom) <= 0 } // Don't attack from a mountain
|
||||
.minByOrNull { it.tileToAttack.aerialDistanceTo(unit.getTile()) }
|
||||
|
||||
if (closestEnemy != null) {
|
||||
unit.movement.headTowards(closestEnemy.tileToAttackFrom)
|
||||
@ -313,7 +323,8 @@ object UnitAutomation {
|
||||
val reachableTileNearSiegedCity = siegedCities
|
||||
.flatMap { it.getCenterTile().getTilesAtDistance(2) }
|
||||
.sortedBy { it.aerialDistanceTo(unit.currentTile) }
|
||||
.firstOrNull { unit.movement.canMoveTo(it) && unit.movement.canReach(it) }
|
||||
.firstOrNull { unit.movement.canMoveTo(it) && unit.movement.canReach(it)
|
||||
&& unit.getDamageFromTerrain(it) <= 0 } // Avoid ending up on damaging terrain
|
||||
|
||||
if (reachableTileNearSiegedCity != null) {
|
||||
unit.movement.headTowards(reachableTileNearSiegedCity)
|
||||
@ -359,6 +370,7 @@ object UnitAutomation {
|
||||
.filter {
|
||||
it.key.aerialDistanceTo(closestReachableEnemyCity) <=
|
||||
unitRange && it.key !in tilesInBombardRange
|
||||
&& unit.getDamageFromTerrain(it.key) <= 0 // Don't set up on a mountain
|
||||
}
|
||||
.minByOrNull { it.value.totalDistance }?.key
|
||||
|
||||
@ -377,7 +389,7 @@ object UnitAutomation {
|
||||
// don't head straight to the city, try to head to landing grounds -
|
||||
// this is against tha AI's brilliant plan of having everyone embarked and attacking via sea when unnecessary.
|
||||
val tileToHeadTo = closestReachableEnemyCity.getTilesInDistanceRange(3..4)
|
||||
.filter { it.isLand }
|
||||
.filter { it.isLand && unit.getDamageFromTerrain(it) <= 0 } // Don't head for hurty terrain
|
||||
.sortedBy { it.aerialDistanceTo(unit.currentTile) }
|
||||
.firstOrNull { unit.movement.canReach(it) }
|
||||
|
||||
@ -500,7 +512,7 @@ object UnitAutomation {
|
||||
}
|
||||
|
||||
/** Returns whether the civilian spends its turn hiding and not moving */
|
||||
fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean {
|
||||
fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean {
|
||||
// This is a little 'Bugblatter Beast of Traal': Run if we can attack an enemy
|
||||
// Cheaper than determining which enemies could attack us next turn
|
||||
//todo - stay when we're stacked with a good military unit???
|
||||
@ -534,7 +546,7 @@ object UnitAutomation {
|
||||
return
|
||||
}
|
||||
val tileFurthestFromEnemy = reachableTiles.keys
|
||||
.filter { unit.movement.canMoveTo(it) }
|
||||
.filter { unit.movement.canMoveTo(it) && unit.getDamageFromTerrain(it) < unit.health }
|
||||
.maxByOrNull { countDistanceToClosestEnemy(unit, it) }
|
||||
?: return // can't move anywhere!
|
||||
unit.movement.moveToTile(tileFurthestFromEnemy)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.UncivShowableException
|
||||
@ -97,6 +98,12 @@ class CivilizationInfo {
|
||||
@Transient
|
||||
private var cachedMilitaryMight = -1
|
||||
|
||||
@Transient
|
||||
var passThroughImpassableUnlocked = false // Cached Boolean equal to passableImpassables.isNotEmpty()
|
||||
|
||||
@Transient
|
||||
var nonStandardTerrainDamage = false
|
||||
|
||||
var playerType = PlayerType.AI
|
||||
|
||||
/** Used in online multiplayer for human players */
|
||||
@ -149,6 +156,8 @@ class CivilizationInfo {
|
||||
// default false once we no longer want legacy save-game compatibility
|
||||
var hasEverOwnedOriginalCapital: Boolean? = null
|
||||
|
||||
val passableImpassables = HashSet<String>() // For Carthage-like uniques
|
||||
|
||||
// For Aggressor, Warmonger status
|
||||
private var numMinorCivsAttacked = 0
|
||||
|
||||
@ -197,6 +206,7 @@ class CivilizationInfo {
|
||||
toReturn.boughtConstructionsWithGloballyIncreasingPrice.putAll(boughtConstructionsWithGloballyIncreasingPrice)
|
||||
//
|
||||
toReturn.hasEverOwnedOriginalCapital = hasEverOwnedOriginalCapital
|
||||
toReturn.passableImpassables.addAll(passableImpassables)
|
||||
toReturn.numMinorCivsAttacked = numMinorCivsAttacked
|
||||
return toReturn
|
||||
}
|
||||
@ -643,6 +653,11 @@ class CivilizationInfo {
|
||||
cityInfo.civInfo = this // must be before the city's setTransients because it depends on the tilemap, that comes from the currentPlayerCivInfo
|
||||
cityInfo.setTransients()
|
||||
}
|
||||
|
||||
passThroughImpassableUnlocked = passableImpassables.isNotEmpty()
|
||||
// Cache whether this civ gets nonstandard terrain damage for performance reasons.
|
||||
nonStandardTerrainDamage = getMatchingUniques("Units ending their turn on [] tiles take [] damage")
|
||||
.any { gameInfo.ruleSet.terrains[it.params[0]]!!.damagePerTurn != it.params[1].toInt() }
|
||||
}
|
||||
|
||||
fun updateSightAndResources() {
|
||||
@ -913,6 +928,13 @@ class CivilizationInfo {
|
||||
}
|
||||
placedUnit.setupAbilityUses()
|
||||
}
|
||||
|
||||
for (unique in getMatchingUniques("Land units may cross [] tiles after the first [] is earned")) {
|
||||
if (unit.matchesFilter(unique.params[1])) {
|
||||
passThroughImpassableUnlocked = true // Update the cached Boolean
|
||||
passableImpassables.add(unique.params[0]) // Add to list of passable impassables
|
||||
}
|
||||
}
|
||||
|
||||
return placedUnit
|
||||
}
|
||||
|
@ -672,8 +672,8 @@ class MapUnit {
|
||||
}
|
||||
}
|
||||
|
||||
getCitadelDamage()
|
||||
getTerrainDamage()
|
||||
doCitadelDamage()
|
||||
doTerrainDamage()
|
||||
}
|
||||
|
||||
fun startTurn() {
|
||||
@ -882,30 +882,38 @@ class MapUnit {
|
||||
return damageFactor
|
||||
}
|
||||
|
||||
private fun getTerrainDamage() {
|
||||
// hard coded mountain damage for now
|
||||
if (getTile().baseTerrain == Constants.mountain) {
|
||||
val tileDamage = 50
|
||||
health -= tileDamage
|
||||
private fun doTerrainDamage() {
|
||||
val tileDamage = getDamageFromTerrain()
|
||||
health -= tileDamage
|
||||
|
||||
if (health <= 0) {
|
||||
civInfo.addNotification(
|
||||
"Our [$name] took [$tileDamage] tile damage and was destroyed",
|
||||
currentTile.position,
|
||||
name,
|
||||
NotificationIcon.Death
|
||||
)
|
||||
destroy()
|
||||
} else civInfo.addNotification(
|
||||
"Our [$name] took [$tileDamage] tile damage",
|
||||
if (health <= 0) {
|
||||
civInfo.addNotification(
|
||||
"Our [$name] took [$tileDamage] tile damage and was destroyed",
|
||||
currentTile.position,
|
||||
name
|
||||
name,
|
||||
NotificationIcon.Death
|
||||
)
|
||||
}
|
||||
|
||||
destroy()
|
||||
} else if (tileDamage > 0) civInfo.addNotification(
|
||||
"Our [$name] took [$tileDamage] tile damage",
|
||||
currentTile.position,
|
||||
name
|
||||
)
|
||||
}
|
||||
|
||||
private fun getCitadelDamage() {
|
||||
fun getDamageFromTerrain(tile: TileInfo = currentTile): Int {
|
||||
if (civInfo.nonStandardTerrainDamage) {
|
||||
for (unique in getMatchingUniques("Units ending their turn on [] tiles take [] damage")) {
|
||||
if (unique.params[0] in tile.getAllTerrains().map { it.name }) {
|
||||
return unique.params[1].toInt() // Use the damage from the unique
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise fall back to the defined standard damage
|
||||
return tile.getAllTerrains().sumBy { it.damagePerTurn }
|
||||
}
|
||||
|
||||
private fun doCitadelDamage() {
|
||||
// Check for Citadel damage - note: 'Damage does not stack with other Citadels'
|
||||
val citadelTile = currentTile.neighbors
|
||||
.firstOrNull {
|
||||
|
@ -167,7 +167,13 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
* Does not consider if the [destination] tile can actually be entered, use [canMoveTo] for that.
|
||||
* Returns an empty list if there's no way to get to the destination.
|
||||
*/
|
||||
fun getShortestPath(destination: TileInfo): List<TileInfo> {
|
||||
fun getShortestPath(destination: TileInfo, avoidDamagingTerrain: Boolean = false): List<TileInfo> {
|
||||
// First try and find a path without damaging terrain
|
||||
if (!avoidDamagingTerrain && unit.civInfo.passThroughImpassableUnlocked && unit.baseUnit.isLandUnit()) {
|
||||
val damageFreePath = getShortestPath(destination, true)
|
||||
if (damageFreePath.isNotEmpty()) return damageFreePath
|
||||
}
|
||||
|
||||
val currentTile = unit.getTile()
|
||||
if (currentTile.position == destination) return listOf(currentTile) // edge case that's needed, so that workers will know that they can reach their own tile. *sigh*
|
||||
|
||||
@ -187,6 +193,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
for (tileToCheck in tilesToCheck) {
|
||||
val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn)
|
||||
for (reachableTile in distanceToTilesThisTurn.keys) {
|
||||
// Avoid damaging terrain on first pass
|
||||
if (avoidDamagingTerrain && unit.getDamageFromTerrain(reachableTile) > 0)
|
||||
continue
|
||||
if (reachableTile == destination)
|
||||
distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!.totalDistance
|
||||
else {
|
||||
@ -546,7 +555,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
if (tile.isImpassible()) {
|
||||
// special exception - ice tiles are technically impassible, but some units can move through them anyway
|
||||
// helicopters can pass through impassable tiles like mountains
|
||||
if (!(tile.terrainFeatures.contains(Constants.ice) && unit.canEnterIceTiles) && !unit.canPassThroughImpassableTiles)
|
||||
if (!(tile.terrainFeatures.contains(Constants.ice) && unit.canEnterIceTiles) && !unit.canPassThroughImpassableTiles
|
||||
// carthage-like uniques sometimes allow passage through impassible tiles
|
||||
&& !(unit.civInfo.passThroughImpassableUnlocked && unit.civInfo.passableImpassables.contains(tile.getLastTerrain().name)))
|
||||
return false
|
||||
}
|
||||
if (tile.isLand
|
||||
|
@ -170,7 +170,10 @@ class Ruleset {
|
||||
if (buildingsFile.exists()) buildings += createHashmap(jsonParser.getFromJson(Array<Building>::class.java, buildingsFile))
|
||||
|
||||
val terrainsFile = folderHandle.child("Terrains.json")
|
||||
if (terrainsFile.exists()) terrains += createHashmap(jsonParser.getFromJson(Array<Terrain>::class.java, terrainsFile))
|
||||
if (terrainsFile.exists()) {
|
||||
terrains += createHashmap(jsonParser.getFromJson(Array<Terrain>::class.java, terrainsFile))
|
||||
for (terrain in terrains.values) terrain.setTransients()
|
||||
}
|
||||
|
||||
val resourcesFile = folderHandle.child("TileResources.json")
|
||||
if (resourcesFile.exists()) tileResources += createHashmap(jsonParser.getFromJson(Array<TileResource>::class.java, resourcesFile))
|
||||
|
@ -40,6 +40,9 @@ class Terrain : NamedStats(), ICivilopediaText, IHasUniques {
|
||||
var defenceBonus:Float = 0f
|
||||
var impassable = false
|
||||
|
||||
@Transient
|
||||
var damagePerTurn = 0
|
||||
|
||||
override var civilopediaText = listOf<FormattedLine>()
|
||||
|
||||
fun isRough(): Boolean = uniques.contains("Rough terrain")
|
||||
@ -141,4 +144,10 @@ class Terrain : NamedStats(), ICivilopediaText, IHasUniques {
|
||||
|
||||
return textList
|
||||
}
|
||||
|
||||
fun setTransients() {
|
||||
damagePerTurn = uniqueObjects.sumBy {
|
||||
if (it.placeholderText == "Units ending their turn on this terrain take [] damage") it.params[0].toInt() else 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ object UnitActions {
|
||||
* (no movement left, too close to another city).
|
||||
*/
|
||||
fun getFoundCityAction(unit: MapUnit, tile: TileInfo): UnitAction? {
|
||||
if (!unit.hasUnique("Founds a new city") || tile.isWater) return null
|
||||
if (!unit.hasUnique("Founds a new city") || tile.isWater || tile.isImpassible()) return null
|
||||
|
||||
if (unit.currentMovement <= 0 || tile.getTilesInDistance(3).any { it.isCityCenter() })
|
||||
return UnitAction(UnitActionType.FoundCity, action = null)
|
||||
|
@ -42,6 +42,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
* [Bow](https://thenounproject.com/search/?q=bow&i=101736) By Arthur Shlain for Bowman
|
||||
* [Fishing Vessel](https://thenounproject.com/term/fishing-vessel/23815/) By Luis Prado for Work Boats
|
||||
* [Greek Trireme](https://thenounproject.com/search/?q=ancient%20boat&i=1626303) By Zachary McCune for Trireme
|
||||
* [Greek Trireme](https://thenounproject.com/search/?q=ancient%20boat&i=1626303) By Zachary McCune for Quinquereme
|
||||
* [Chariot](https://thenounproject.com/search/?q=Chariot&i=1189930) By Andrew Doane for Chariot Archer
|
||||
* [Elephant](https://thenounproject.com/Luis/uploads/?i=14048) By Luis Prado for War Elephant
|
||||
* [Centaur](https://thenounproject.com/search/?q=horse+archer&i=1791296) by Michael Wohlwend for Horse Archer
|
||||
@ -62,6 +63,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
* [Roman Helmet](https://thenounproject.com/search/?q=legion&i=440134) By parkjisun for Legion
|
||||
* [Horse](https://thenounproject.com/search/?q=Horse&i=1373793) By AFY Studio for Horseman
|
||||
* [Horse Head](https://thenounproject.com/search/?q=Cavalry&i=374037) By Juan Pablo Bravo for Companion Cavalry
|
||||
* [Elephant](https://thenounproject.com/term/elephant/1302749) By Angriawan Ditya Zulkarnain for African Forest Elephant
|
||||
* [Judge](https://thenounproject.com/search/?q=judge&i=1076388) By Krisztián Mátyás for Courthouse
|
||||
* [Petra](https://thenounproject.com/search/?q=petra&i=2855893) By Ranah Pixel Studio for Petra
|
||||
|
||||
@ -553,6 +555,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
* [Lion](https://thenounproject.com/search/?q=lion&i=76154) by Nikki Rodriguez for The Netherlands
|
||||
* [Three Crowns](https://thenounproject.com/search/?q=three+crowns&i=1155972) by Daniel Falk for Sweden
|
||||
* [Flag of Austria](https://thenounproject.com/term/flag-of-austria/3292053/) by Olena Panasovska, UA for Austria
|
||||
* [Elephant](https://thenounproject.com/term/elephant/564421/) by Hea Poh Lin for Carthage
|
||||
|
||||
|
||||
## Promotions
|
||||
|
Reference in New Issue
Block a user