diff --git a/core/src/com/unciv/MainMenuScreen.kt b/core/src/com/unciv/MainMenuScreen.kt index ce96376c56..b37a85c984 100644 --- a/core/src/com/unciv/MainMenuScreen.kt +++ b/core/src/com/unciv/MainMenuScreen.kt @@ -11,6 +11,7 @@ import com.unciv.logic.GameStarter import com.unciv.logic.UncivShowableException import com.unciv.logic.map.MapParameters import com.unciv.logic.map.MapShape +import com.unciv.logic.map.MapSize import com.unciv.logic.map.MapSizeNew import com.unciv.logic.map.MapType import com.unciv.logic.map.mapgenerator.MapGenerator @@ -122,8 +123,9 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { val newMap = MapGenerator(mapRuleset) .generateMap(MapParameters().apply { shape = MapShape.rectangular - mapSize = MapSizeNew(mapWidth.toInt() + 1, mapHeight.toInt() + 1) + mapSize = MapSizeNew(MapSize.Small) type = MapType.pangaea + temperatureExtremeness = 1f waterThreshold = -0.1f // mainly land, gets about 30% water modifyForEasterEgg() }) diff --git a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt index 7c519d01ed..e6c2c814c1 100644 --- a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt +++ b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt @@ -2,7 +2,6 @@ package com.unciv.logic.map.mapunit import com.badlogic.gdx.math.Vector2 import com.unciv.Constants -import com.unciv.UncivGame import com.unciv.logic.IsPartOfGameInfoSerialization import com.unciv.logic.automation.unit.UnitAutomation import com.unciv.logic.automation.unit.WorkerAutomation @@ -10,10 +9,8 @@ import com.unciv.logic.battle.Battle import com.unciv.logic.battle.MapUnitCombatant import com.unciv.logic.city.City import com.unciv.logic.civilization.Civilization -import com.unciv.logic.civilization.LocationAction import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationIcon -import com.unciv.logic.map.tile.RoadStatus import com.unciv.logic.map.tile.Tile import com.unciv.models.UnitActionType import com.unciv.models.helpers.UnitMovementMemoryType @@ -28,7 +25,6 @@ import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.stats.Stats import com.unciv.ui.utils.extensions.filterAndLogic -import com.unciv.ui.utils.extensions.toPercent import java.text.DecimalFormat import kotlin.math.pow @@ -586,74 +582,6 @@ class MapUnit : IsPartOfGameInfoSerialization { if (isExploring()) UnitAutomation.automatedExplore(this) } - - internal fun workOnImprovement() { - val tile = getTile() - if (tile.isMarkedForCreatesOneImprovement()) return - tile.turnsToImprovement -= 1 - if (tile.turnsToImprovement != 0) return - - if (civInfo.isCurrentPlayer()) - UncivGame.Current.settings.addCompletedTutorialTask("Construct an improvement") - - when { - tile.improvementInProgress!!.startsWith(Constants.remove) -> { - val removedFeatureName = tile.improvementInProgress!!.removePrefix(Constants.remove) - val tileImprovement = tile.getTileImprovement() - if (tileImprovement != null - && tile.terrainFeatures.any { - tileImprovement.terrainsCanBeBuiltOn.contains(it) && it == removedFeatureName - } - && !tileImprovement.terrainsCanBeBuiltOn.contains(tile.baseTerrain) - ) { - // We removed a terrain (e.g. Forest) and the improvement (e.g. Lumber mill) requires it! - tile.changeImprovement(null) - if (tile.resource != null) civInfo.cache.updateCivResources() // unlikely, but maybe a mod makes a resource improvement dependent on a terrain feature - } - if (RoadStatus.values().any { tile.improvementInProgress == it.removeAction }) { - tile.removeRoad() - } else { - val removedFeatureObject = tile.ruleset.terrains[removedFeatureName] - if (removedFeatureObject != null && removedFeatureObject.hasUnique(UniqueType.ProductionBonusWhenRemoved)) { - tryProvideProductionToClosestCity(removedFeatureName) - } - tile.removeTerrainFeature(removedFeatureName) - } - } - tile.improvementInProgress == RoadStatus.Road.name -> tile.addRoad(RoadStatus.Road, this.civInfo) - tile.improvementInProgress == RoadStatus.Railroad.name -> tile.addRoad(RoadStatus.Railroad, this.civInfo) - tile.improvementInProgress == Constants.repair -> tile.setRepaired() - else -> { - val improvement = civInfo.gameInfo.ruleSet.tileImprovements[tile.improvementInProgress]!! - improvement.handleImprovementCompletion(this) - tile.changeImprovement(tile.improvementInProgress) - } - } - - tile.improvementInProgress = null - tile.getCity()?.updateCitizens = true - } - - - private fun tryProvideProductionToClosestCity(removedTerrainFeature: String) { - val tile = getTile() - val closestCity = civInfo.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) } - @Suppress("FoldInitializerAndIfToElvis") - if (closestCity == null) return - val distance = closestCity.getCenterTile().aerialDistanceTo(tile) - var productionPointsToAdd = if (distance == 1) 20 else 20 - (distance - 2) * 5 - if (tile.owningCity == null || tile.owningCity!!.civInfo != civInfo) productionPointsToAdd = - productionPointsToAdd * 2 / 3 - if (productionPointsToAdd > 0) { - closestCity.cityConstructions.addProductionPoints(productionPointsToAdd) - val locations = LocationAction(tile.position, closestCity.location) - civInfo.addNotification( - "Clearing a [$removedTerrainFeature] has created [$productionPointsToAdd] Production for [${closestCity.name}]", - locations, NotificationCategory.Production, NotificationIcon.Construction - ) - } - } - fun healBy(amount: Int) { health += amount * if (hasUnique(UniqueType.HealingEffectsDoubled, checkCivInfoUniques = true)) 2 @@ -878,6 +806,7 @@ class MapUnit : IsPartOfGameInfoSerialization { } } + fun canIntercept(attackedTile: Tile): Boolean { if (!canIntercept()) return false if (currentTile.aerialDistanceTo(attackedTile) > baseUnit.interceptRange) return false @@ -899,27 +828,6 @@ class MapUnit : IsPartOfGameInfoSerialization { return getMatchingUniques(UniqueType.ChanceInterceptAirAttacks).sumOf { it.params[0].toInt() } } - fun isTransportTypeOf(mapUnit: MapUnit): Boolean { - // Currently, only missiles and airplanes can be carried - if (!mapUnit.baseUnit.movesLikeAirUnits()) return false - return getMatchingUniques(UniqueType.CarryAirUnits).any { mapUnit.matchesFilter(it.params[1]) } - } - - private fun carryCapacity(unit: MapUnit): Int { - return (getMatchingUniques(UniqueType.CarryAirUnits) - + getMatchingUniques(UniqueType.CarryExtraAirUnits)) - .filter { unit.matchesFilter(it.params[1]) } - .sumOf { it.params[0].toInt() } - } - - fun canTransport(unit: MapUnit): Boolean { - if (owner != unit.owner) return false - if (!isTransportTypeOf(unit)) return false - if (unit.getMatchingUniques(UniqueType.CannotBeCarriedBy).any { matchesFilter(it.params[0]) }) return false - if (currentTile.airUnits.count { it.isTransported } >= carryCapacity(unit)) return false - return true - } - fun interceptDamagePercentBonus(): Int { return getMatchingUniques(UniqueType.DamageWhenIntercepting) .sumOf { it.params[0].toInt() } @@ -946,6 +854,28 @@ class MapUnit : IsPartOfGameInfoSerialization { } + fun isTransportTypeOf(mapUnit: MapUnit): Boolean { + // Currently, only missiles and airplanes can be carried + if (!mapUnit.baseUnit.movesLikeAirUnits()) return false + return getMatchingUniques(UniqueType.CarryAirUnits).any { mapUnit.matchesFilter(it.params[1]) } + } + + private fun carryCapacity(unit: MapUnit): Int { + return (getMatchingUniques(UniqueType.CarryAirUnits) + + getMatchingUniques(UniqueType.CarryExtraAirUnits)) + .filter { unit.matchesFilter(it.params[1]) } + .sumOf { it.params[0].toInt() } + } + + fun canTransport(unit: MapUnit): Boolean { + if (owner != unit.owner) return false + if (!isTransportTypeOf(unit)) return false + if (unit.getMatchingUniques(UniqueType.CannotBeCarriedBy).any { matchesFilter(it.params[0]) }) return false + if (currentTile.airUnits.count { it.isTransported } >= carryCapacity(unit)) return false + return true + } + + /** Implements [UniqueParameterType.MapUnitFilter][com.unciv.models.ruleset.unique.UniqueParameterType.MapUnitFilter] */ fun matchesFilter(filter: String): Boolean { return filter.filterAndLogic { matchesFilter(it) } // multiple types at once - AND logic. Looks like:"{Military} {Land}" diff --git a/core/src/com/unciv/logic/map/mapunit/UnitTurnManager.kt b/core/src/com/unciv/logic/map/mapunit/UnitTurnManager.kt index 2fa4fc4783..522fcb5bf1 100644 --- a/core/src/com/unciv/logic/map/mapunit/UnitTurnManager.kt +++ b/core/src/com/unciv/logic/map/mapunit/UnitTurnManager.kt @@ -1,8 +1,11 @@ package com.unciv.logic.map.mapunit +import com.unciv.Constants +import com.unciv.UncivGame import com.unciv.logic.civilization.LocationAction import com.unciv.logic.civilization.NotificationCategory import com.unciv.logic.civilization.NotificationIcon +import com.unciv.logic.map.tile.RoadStatus import com.unciv.models.ruleset.unique.UniqueType class UnitTurnManager(val unit: MapUnit) { @@ -12,7 +15,7 @@ class UnitTurnManager(val unit: MapUnit) { if (unit.currentMovement > 0 && unit.getTile().improvementInProgress != null && unit.canBuildImprovement(unit.getTile().getTileImprovementInProgress()!!) - ) unit.workOnImprovement() + ) workOnImprovement() if (unit.currentMovement == unit.getMaxMovement().toFloat() && unit.isFortified() && unit.turnsFortified < 2) { unit.turnsFortified++ } @@ -166,4 +169,71 @@ class UnitTurnManager(val unit: MapUnit) { unit.attacksSinceTurnStart.clear() } + private fun workOnImprovement() { + val tile = unit.getTile() + if (tile.isMarkedForCreatesOneImprovement()) return + tile.turnsToImprovement -= 1 + if (tile.turnsToImprovement != 0) return + + if (unit.civInfo.isCurrentPlayer()) + UncivGame.Current.settings.addCompletedTutorialTask("Construct an improvement") + + when { + tile.improvementInProgress!!.startsWith(Constants.remove) -> { + val removedFeatureName = tile.improvementInProgress!!.removePrefix(Constants.remove) + val tileImprovement = tile.getTileImprovement() + if (tileImprovement != null + && tile.terrainFeatures.any { + tileImprovement.terrainsCanBeBuiltOn.contains(it) && it == removedFeatureName + } + && !tileImprovement.terrainsCanBeBuiltOn.contains(tile.baseTerrain) + ) { + // We removed a terrain (e.g. Forest) and the improvement (e.g. Lumber mill) requires it! + tile.changeImprovement(null) + if (tile.resource != null) unit.civInfo.cache.updateCivResources() // unlikely, but maybe a mod makes a resource improvement dependent on a terrain feature + } + if (RoadStatus.values().any { tile.improvementInProgress == it.removeAction }) { + tile.removeRoad() + } else { + val removedFeatureObject = tile.ruleset.terrains[removedFeatureName] + if (removedFeatureObject != null && removedFeatureObject.hasUnique(UniqueType.ProductionBonusWhenRemoved)) { + tryProvideProductionToClosestCity(removedFeatureName) + } + tile.removeTerrainFeature(removedFeatureName) + } + } + tile.improvementInProgress == RoadStatus.Road.name -> tile.addRoad(RoadStatus.Road, unit.civInfo) + tile.improvementInProgress == RoadStatus.Railroad.name -> tile.addRoad(RoadStatus.Railroad, unit.civInfo) + tile.improvementInProgress == Constants.repair -> tile.setRepaired() + else -> { + val improvement = unit.civInfo.gameInfo.ruleSet.tileImprovements[tile.improvementInProgress]!! + improvement.handleImprovementCompletion(unit) + tile.changeImprovement(tile.improvementInProgress) + } + } + + tile.improvementInProgress = null + tile.getCity()?.updateCitizens = true + } + + + private fun tryProvideProductionToClosestCity(removedTerrainFeature: String) { + val tile = unit.getTile() + val closestCity = unit.civInfo.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) } + @Suppress("FoldInitializerAndIfToElvis") + if (closestCity == null) return + val distance = closestCity.getCenterTile().aerialDistanceTo(tile) + var productionPointsToAdd = if (distance == 1) 20 else 20 - (distance - 2) * 5 + if (tile.owningCity == null || tile.owningCity!!.civInfo != unit.civInfo) productionPointsToAdd = + productionPointsToAdd * 2 / 3 + if (productionPointsToAdd > 0) { + closestCity.cityConstructions.addProductionPoints(productionPointsToAdd) + val locations = LocationAction(tile.position, closestCity.location) + unit.civInfo.addNotification( + "Clearing a [$removedTerrainFeature] has created [$productionPointsToAdd] Production for [${closestCity.name}]", + locations, NotificationCategory.Production, NotificationIcon.Construction + ) + } + } + } diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index ca6da1987e..84e88e626e 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -43,9 +43,6 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { var promotions = HashSet() var obsoleteTech: String? = null var upgradesTo: String? = null - val specialUpgradesTo: String? by lazy { - getMatchingUniques(UniqueType.RuinsUpgrade).map { it.params[0] }.firstOrNull() - } var replaces: String? = null var uniqueTo: String? = null var attackSound: String? = null diff --git a/core/src/com/unciv/ui/worldscreen/unit/actions/UnitActionsUpgrade.kt b/core/src/com/unciv/ui/worldscreen/unit/actions/UnitActionsUpgrade.kt index b82af4342b..c1d316beb9 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/actions/UnitActionsUpgrade.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/actions/UnitActionsUpgrade.kt @@ -4,6 +4,7 @@ import com.unciv.logic.map.mapunit.MapUnit import com.unciv.models.Counter import com.unciv.models.UnitAction import com.unciv.models.UnitActionType +import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.translations.tr object UnitActionsUpgrade{ @@ -22,13 +23,13 @@ object UnitActionsUpgrade{ isFree: Boolean, isSpecial: Boolean ): UnitAction? { - if (unit.baseUnit().upgradesTo == null && unit.baseUnit().specialUpgradesTo == null) return null // can't upgrade to anything + val specialUpgradesTo = unit.baseUnit().getMatchingUniques(UniqueType.RuinsUpgrade).map { it.params[0] }.firstOrNull() + if (unit.baseUnit().upgradesTo == null && specialUpgradesTo == null) return null // can't upgrade to anything val unitTile = unit.getTile() val civInfo = unit.civInfo if (!isFree && unitTile.getOwner() != civInfo) return null val upgradesTo = unit.baseUnit().upgradesTo - val specialUpgradesTo = unit.baseUnit().specialUpgradesTo val upgradedUnit = when { isSpecial && specialUpgradesTo != null -> civInfo.getEquivalentUnit(specialUpgradesTo) (isFree || isSpecial) && upgradesTo != null -> civInfo.getEquivalentUnit(upgradesTo) // Only get DIRECT upgrade