diff --git a/android/build.gradle b/android/build.gradle index 2c6ec698..f8d5e430 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.game" minSdkVersion 14 targetSdkVersion 26 - versionCode 107 - versionName "2.6.9" + versionCode 108 + versionName "2.6.10" } buildTypes { release { diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 42d0a35e..55d04234 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -38,7 +38,7 @@ class UnitAutomation{ fun containsAttackableEnemy(tile: TileInfo, civInfo: CivilizationInfo): Boolean { val tileCombatant = Battle().getMapCombatantOfTile(tile) if(tileCombatant==null) return false - return tileCombatant.getCivilization()!=civInfo// && civInfo.isAtWarWith(tileCombatant.getCivilization()) + return tileCombatant.getCivilization()!=civInfo && civInfo.isAtWarWith(tileCombatant.getCivilization()) } class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo) diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index f51834f9..74b2936b 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -103,22 +103,13 @@ class Battle(val gameInfo:GameInfo=UnCivGame.Current.gameInfo) { private fun conquerCity(city: CityInfo, attacker: ICombatant) { val enemyCiv = city.civInfo attacker.getCivilization().addNotification("We have conquered the city of [${city.name}]!",city.location, Color.RED) - enemyCiv.cities.remove(city) - attacker.getCivilization().cities.add(city) - city.civInfo = attacker.getCivilization() + city.moveToCiv(attacker.getCivilization()) city.health = city.getMaxHealth() / 2 // I think that cities recover to half health when conquered? city.getCenterTile().apply { militaryUnit = null if(civilianUnit!=null) captureCivilianUnit(attacker,MapUnitCombatant(civilianUnit!!)) } - city.expansion.cultureStored = 0 - city.expansion.reset() - - // now that the tiles have changed, we need to reassign population - city.workedTiles.filterNot { city.tiles.contains(it) } - .forEach { city.workedTiles.remove(it); city.population.autoAssignPopulation() } - if(city.cityConstructions.isBuilt("Palace")){ city.cityConstructions.builtBuildings.remove("Palace") if(enemyCiv.isDefeated()) { @@ -131,12 +122,7 @@ class Battle(val gameInfo:GameInfo=UnCivGame.Current.gameInfo) { } } - // Remove all national wonders when conquering a city - for(building in city.cityConstructions.getBuiltBuildings().filter { it.requiredBuildingInAllCities!=null }) - city.cityConstructions.builtBuildings.remove(building.name) - (attacker as MapUnitCombatant).unit.moveToTile(city.getCenterTile()) - city.civInfo.gameInfo.updateTilesToCities() } fun getMapCombatantOfTile(tile:TileInfo): ICombatant? { diff --git a/core/src/com/unciv/logic/battle/CityCombatant.kt b/core/src/com/unciv/logic/battle/CityCombatant.kt index 22974ebd..bc416296 100644 --- a/core/src/com/unciv/logic/battle/CityCombatant.kt +++ b/core/src/com/unciv/logic/battle/CityCombatant.kt @@ -25,7 +25,7 @@ class CityCombatant(val city: CityInfo) : ICombatant { return getCityStrength() } - private fun getCityStrength(): Int { // Civ fanatics forum, from a modder who went through the original code + fun getCityStrength(): Int { // Civ fanatics forum, from a modder who went through the original code var strength = 8f if(city.isCapital()) strength+=2.5f strength += (city.population.population/5) * 2 // Each 5 pop gives 2 defence diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 175a66ad..cc2f4534 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -149,6 +149,25 @@ class CityInfo { fun isCapital() = cityConstructions.isBuilt("Palace") + fun moveToCiv(newCivInfo: CivilizationInfo){ + civInfo.cities.remove(this) + newCivInfo.cities.add(this) + civInfo = newCivInfo + + expansion.cultureStored = 0 + expansion.reset() + + // now that the tiles have changed, we need to reassign population + workedTiles.filterNot { tiles.contains(it) } + .forEach { workedTiles.remove(it); population.autoAssignPopulation() } + + // Remove all national wonders + for(building in cityConstructions.getBuiltBuildings().filter { it.requiredBuildingInAllCities!=null }) + cityConstructions.builtBuildings.remove(building.name) + + civInfo.gameInfo.updateTilesToCities() + } + internal fun getMaxHealth(): Int { return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth } } diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index fe303450..e7569538 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -289,12 +289,17 @@ class CivilizationInfo { else return TechEra.Ancient } -// fun isAtWarWith(otherCiv:CivilizationInfo): Boolean { -// if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true -// if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet -// return false -// return diplomacy[otherCiv.civName]!!.diplomaticStatus == DiplomaticStatus.War -// } + fun isAtWarWith(otherCiv:CivilizationInfo): Boolean { + if(otherCiv.isBarbarianCivilization() || isBarbarianCivilization()) return true + if(!diplomacy.containsKey(otherCiv.civName)) // not encountered yet + return false + return diplomacy[otherCiv.civName]!!.diplomaticStatus == DiplomaticStatus.War + } + + fun canEnterTiles(otherCiv: CivilizationInfo): Boolean { + if(isAtWarWith(otherCiv)) return true + return false + } } diff --git a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt index cb3c12d2..495bdf60 100644 --- a/core/src/com/unciv/logic/civilization/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/DiplomacyManager.kt @@ -22,7 +22,6 @@ class DiplomacyManager() { otherCivName=OtherCivName } -// var status:DiplomaticStatus = DiplomaticStatus.War var trades = ArrayList() fun goldPerTurn():Int{ @@ -80,9 +79,9 @@ class DiplomacyManager() { fun otherCiv() = civInfo.gameInfo.civilizations.first{it.civName==otherCivName} - //var diplomaticStatus = DiplomaticStatus.War -// fun declareWar(){ -// diplomaticStatus = DiplomaticStatus.War -// otherCiv().diplomacy[civInfo.civName]!!.diplomaticStatus = DiplomaticStatus.War -// } + var diplomaticStatus = DiplomaticStatus.War + fun declareWar(){ + diplomaticStatus = DiplomaticStatus.War + otherCiv().diplomacy[civInfo.civName]!!.diplomaticStatus = DiplomaticStatus.War + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index b3e500e4..027e45f7 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -189,7 +189,10 @@ class MapUnit { * Designates whether we can walk to the tile - without attacking */ fun canMoveTo(tile: TileInfo): Boolean { - if(tile.isCityCenter() && tile.getOwner()!!.civName!=owner) return false + val tileOwner = tile.getOwner() + if(tileOwner!=null && tileOwner.civName!=owner) { + if (tile.isCityCenter() || !civInfo.canEnterTiles(tileOwner)) return false + } if (getBaseUnit().unitType== UnitType.Civilian) return tile.civilianUnit==null && (tile.militaryUnit==null || tile.militaryUnit!!.owner==owner) else return tile.militaryUnit==null && (tile.civilianUnit==null || tile.civilianUnit!!.owner==owner) diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 87c2ae67..c98b12a4 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -1,6 +1,7 @@ package com.unciv.logic.map import com.badlogic.gdx.math.Vector2 +import com.unciv.models.gamebasics.unit.UnitType class UnitMovementAlgorithms(val unit:MapUnit) { val tileMap = unit.getTile().tileMap @@ -167,14 +168,19 @@ class UnitMovementAlgorithms(val unit:MapUnit) { throw Exception("We couldn't get the path between the two tiles") } -// fun moveToClosestMoveableTile(){ -// val unitCurrentTile = unit.getTile() -// var allowedTile:TileInfo? = null -// var distance=0 -// while(allowedTile==null){ -// distance++ -// allowedTile = tileMap.tilesat -// } -// } + fun teleportToClosestMoveableTile(){ + val unitCurrentTilePosition = unit.getTile().position + var allowedTile:TileInfo? = null + var distance=0 + while(allowedTile==null){ + distance++ + allowedTile = tileMap.getTilesAtDistance(unitCurrentTilePosition,distance) + .firstOrNull{unit.canMoveTo(it)} + } + unit.removeFromTile() // we "teleport" them away + if(unit.getBaseUnit().unitType==UnitType.Civilian) + allowedTile.civilianUnit=unit + else allowedTile.militaryUnit=unit + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/trade/OffersList.kt b/core/src/com/unciv/logic/trade/OffersList.kt index 50848464..d7a0ada5 100644 --- a/core/src/com/unciv/logic/trade/OffersList.kt +++ b/core/src/com/unciv/logic/trade/OffersList.kt @@ -10,6 +10,7 @@ import com.unciv.ui.utils.tr import kotlin.math.min class OffersList(val offers: TradeOffersList, val correspondingOffers: TradeOffersList, + val otherCivOffers: TradeOffersList, val otherCivCorrespondingOffers:TradeOffersList, val onChange: () -> Unit) : ScrollPane(null) { val table= Table(CameraStageBaseScreen.skin).apply { defaults().pad(5f) } init { @@ -32,6 +33,11 @@ class OffersList(val offers: TradeOffersList, val correspondingOffers: TradeOffe val amountTransferred = min(amountPerClick, offer.amount) offers += offer.copy(amount = -amountTransferred) correspondingOffers += offer.copy(amount = amountTransferred) + if(offer.type==TradeType.Treaty) { // this goes both ways, so it doesn't matter which side you click + otherCivOffers += offer.copy(amount = -amountTransferred) + otherCivCorrespondingOffers += offer.copy(amount = amountTransferred) + } + onChange() update() } diff --git a/core/src/com/unciv/logic/trade/TradeType.kt b/core/src/com/unciv/logic/trade/TradeType.kt index 15922415..083df87e 100644 --- a/core/src/com/unciv/logic/trade/TradeType.kt +++ b/core/src/com/unciv/logic/trade/TradeType.kt @@ -1,11 +1,11 @@ package com.unciv.logic.trade enum class TradeType{ - Luxury_Resource, - Strategic_Resource, Gold, Gold_Per_Turn, - City, + Treaty, + Luxury_Resource, + Strategic_Resource, Technology, - Treaty + City } \ No newline at end of file diff --git a/core/src/com/unciv/ui/TradeScreen.kt b/core/src/com/unciv/ui/TradeScreen.kt index 18c75d08..022f1d08 100644 --- a/core/src/com/unciv/ui/TradeScreen.kt +++ b/core/src/com/unciv/ui/TradeScreen.kt @@ -4,7 +4,9 @@ import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.unciv.UnCivGame +import com.unciv.logic.battle.CityCombatant import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.civilization.DiplomaticStatus import com.unciv.logic.trade.OffersList import com.unciv.logic.trade.Trade import com.unciv.logic.trade.TradeOffersList @@ -12,6 +14,7 @@ import com.unciv.logic.trade.TradeType import com.unciv.models.gamebasics.GameBasics import com.unciv.models.gamebasics.tile.ResourceType import com.unciv.ui.utils.* +import kotlin.math.max import kotlin.math.sqrt @@ -45,12 +48,18 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre tradeText.setText("What do you have in mind?".tr()) } - val ourAvailableOffersTable = OffersList(ourAvailableOffers, currentTrade.ourOffers) { onChange() } - val ourOffersTable = OffersList(currentTrade.ourOffers, ourAvailableOffers) { onChange() } - val theirOffersTable = OffersList(currentTrade.theirOffers, theirAvailableOffers) { onChange() } - val theirAvailableOffersTable = OffersList(theirAvailableOffers, currentTrade.theirOffers) { onChange() } + val ourAvailableOffersTable = OffersList(ourAvailableOffers, currentTrade.ourOffers, + theirAvailableOffers, currentTrade.theirOffers) { onChange() } + val ourOffersTable = OffersList(currentTrade.ourOffers, ourAvailableOffers, + currentTrade.theirOffers, theirAvailableOffers) { onChange() } + val theirOffersTable = OffersList(currentTrade.theirOffers, theirAvailableOffers, + currentTrade.ourOffers, ourAvailableOffers) { onChange() } + val theirAvailableOffersTable = OffersList(theirAvailableOffers, currentTrade.theirOffers, + ourAvailableOffers, currentTrade.ourOffers) { onChange() } init { + val peaceCost = evaluatePeaceCostForThem() + val closeButton = TextButton("Close".tr(), skin) closeButton.addClickListener { UnCivGame.Current.setWorldScreen() } closeButton.y = stage.height - closeButton.height - 5 @@ -90,9 +99,15 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre } if(offer.type==TradeType.City){ val city = them.cities.first { it.name==offer.name } - us.cities.add(city) - them.cities.remove(city) - + city.moveToCiv(us) + city.getCenterTile().getUnits().forEach { it.movementAlgs().teleportToClosestMoveableTile() } + } + if(offer.type==TradeType.Treaty){ + if(offer.name=="Peace Treaty"){ + us.diplomacy[them.civName]!!.diplomaticStatus=DiplomaticStatus.Peace + for(unit in us.getCivUnits().filter { it.getTile().getOwner()==them }) + unit.movementAlgs().teleportToClosestMoveableTile() + } } } } @@ -138,6 +153,8 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre fun getAvailableOffers(civInfo: CivilizationInfo, otherCivilization: CivilizationInfo): TradeOffersList { val offers = TradeOffersList() + if(civInfo.isAtWarWith(otherCivilization)) + offers.add(TradeOffer("Peace Treaty",TradeType.Treaty,20,1)) for(entry in civInfo.getCivResources().filterNot { it.key.resourceType == ResourceType.Bonus }) { val resourceTradeType = if(entry.key.resourceType==ResourceType.Luxury) TradeType.Luxury_Resource else TradeType.Strategic_Resource @@ -150,13 +167,14 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre } offers.add(TradeOffer("Gold".tr(), TradeType.Gold,0,civInfo.gold)) offers.add(TradeOffer("Gold per turn".tr(), TradeType.Gold_Per_Turn,30,civInfo.getStatsForNextTurn().gold.toInt())) -// for(city in civInfo.cities.filterNot { it.isCapital() }) -// offers.add(TradeOffer(city.name,TradeType.City,0,1)) + for(city in civInfo.cities.filterNot { it.isCapital() }) + offers.add(TradeOffer(city.name,TradeType.City,0,1)) return offers } fun isTradeAcceptable(trade: Trade): Boolean { - val sumOfTheirOffers = trade.theirOffers.map { evaluateOffer(it,false) }.sum() + val sumOfTheirOffers = trade.theirOffers.filter { it.type!=TradeType.Treaty } // since treaties should only be evaluated once for 2 sides + .map { evaluateOffer(it,false) }.sum() val sumOfOurOffers = trade.ourOffers.map { evaluateOffer(it,true)}.sum() return sumOfOurOffers >= sumOfTheirOffers } @@ -180,18 +198,49 @@ class TradeScreen(val otherCivilization: CivilizationInfo) : CameraStageBaseScre } } - TradeType.Technology -> return sqrt(GameBasics.Technologies[offer.name]!!.cost.toDouble()).toInt()*10 // gold cost is science cost + TradeType.Technology -> return sqrt(GameBasics.Technologies[offer.name]!!.cost.toDouble()).toInt()*10 TradeType.Strategic_Resource -> return 50 * offer.amount TradeType.City -> { val civ = if(otherCivIsRecieving) civInfo else otherCivilization val city = civ.cities.first { it.name==offer.name } val stats = city.cityStats.currentCityStats val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food - return sumOfStats.toInt() * 10 + return sumOfStats.toInt() * 100 + } + TradeType.Treaty -> { + if(offer.name=="Peace Treaty") + return evaluatePeaceCostForThem() // Since it will be evaluated twice, once when they evaluate our offer and once when they evaluate theirs + else return 1000 } // Dunno what this is? else -> return 1000 } } + + fun evaluteCombatStrength(civInfo: CivilizationInfo): Int { + // Since units become exponentially stronger per combat strength increase, we square em all + fun square(x:Int) = x*x + val unitStrength = civInfo.getCivUnits().map { square(max(it.getBaseUnit().strength,it.getBaseUnit().rangedStrength)) }.sum() + val cityStrength = civInfo.cities.map { square(CityCombatant(it).getCityStrength()) }.sum() + return (sqrt(unitStrength.toDouble()) /*+ sqrt(cityStrength.toDouble())*/).toInt() + } + + fun evaluatePeaceCostForThem(): Int { + val ourCombatStrength = evaluteCombatStrength(civInfo) + val theirCombatStrength = evaluteCombatStrength(otherCivilization) + if(ourCombatStrength==theirCombatStrength) return 0 + if(ourCombatStrength==0) return 1000 + if(theirCombatStrength==0) return -1000 // Chumps got no cities or units + if(ourCombatStrength>theirCombatStrength){ + val absoluteAdvantage = ourCombatStrength-theirCombatStrength + val percentageAdvantage = absoluteAdvantage / theirCombatStrength.toFloat() + return (absoluteAdvantage*percentageAdvantage).toInt() * 10 + } + else{ + val absoluteAdvantage = theirCombatStrength-ourCombatStrength + val percentageAdvantage = absoluteAdvantage / ourCombatStrength.toFloat() + return -(absoluteAdvantage*percentageAdvantage).toInt() * 10 + } + } }